/*---------------------------------------------------------------------------* Project: NintendoWare File: demo_GraphicsSystem.cpp Copyright (C)2009-2010 Nintendo Co., Ltd./HAL Laboratory, Inc. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Revision: 25152 $ *---------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //! このマクロを有効にすると、ログ出力にレンダーキューのソート後の状態をレポートします。 //#define REPORT_RENDER_QUEUE_ENABLED //! このマクロを有効にすると、半透明以外の深度ソートを省略するようになります。 #define SORT_DEPTH_OF_TRANSLUCENT_MESH_ENABLED namespace nw { namespace demo { const float RenderSystem::LoadMeterDescription::NANO_TO_MILLI = 1.0f / (1000.0f * 1000.0f); const float RenderSystem::LoadMeterDescription::MILLI_TO_RATE = 1.0f / (1000.0f / 60.0f); namespace { GraphicsMemoryAllocator s_GraphicsMemoryAllocator; nw::demo::DemoAllocator* s_DeviceAllocator; } //---------------------------------------- void* AllocateGraphicsMemory(GLenum area, GLenum aim, GLuint id, GLsizei size) { void* buffer = s_GraphicsMemoryAllocator.Allocate(area, aim, id, size); // NOTE: GL の管理領域を nw::gfx に設定する必要がなくなり、このコードは不要となりました。 #if 0 if (s_GetGlSystemFlag) { if (size == nw::gfx::GlSystem::SHADER_MANAGER_BUFFER_SIZE) { nw::gfx::GlSystem::SetGlShManager( buffer ); } } #endif return buffer; } //---------------------------------------- void DeallocateGraphicsMemory(GLenum area, GLenum aim, GLuint id, void* addr) { s_GraphicsMemoryAllocator.Deallocate(area, aim, id, addr); } //---------------------------------------- RenderSystem* RenderSystem::Create( os::IAllocator* allocator, const Description& description ) { void* memory = allocator->Alloc(sizeof(RenderSystem)); RenderSystem* renderSystem = new(memory) RenderSystem(); renderSystem->m_RenderContext = gfx::RenderContext::Builder() .Create(allocator); #ifdef SORT_DEPTH_OF_TRANSLUCENT_MESH_ENABLED // 半透明以外のメッシュで深度情報の量子化を行わなくするための // レンダーキー・ファクトリクラスを生成します。 renderSystem->m_PriorMaterialRenderKeyFactory = gfx::CreatePriorMaterialAndZeroDepthRenderKeyFactory(allocator); #else renderSystem->m_PriorMaterialRenderKeyFactory = gfx::CreatePriorMaterialRenderKeyFactory(allocator); #endif renderSystem->m_PriorDepthRenderKeyFactory = gfx::CreatePriorDepthReverseDepthRenderKeyFactory(allocator); renderSystem->m_RenderQueue = gfx::RenderQueue::Builder() .MaxRenderElements(256) .Create(allocator); renderSystem->m_RenderQueue->Reset( renderSystem->m_PriorMaterialRenderKeyFactory, renderSystem->m_PriorDepthRenderKeyFactory, renderSystem->m_PriorMaterialRenderKeyFactory, renderSystem->m_PriorMaterialRenderKeyFactory); renderSystem->m_MeshRenderer = gfx::MeshRenderer::Create(allocator); renderSystem->m_MeshRenderer->SetRenderContext(renderSystem->m_RenderContext); if (nngxInitialize(AllocateGraphicsMemory, DeallocateGraphicsMemory) == GL_FALSE) { NW_FATAL_ERROR("nngxInitialize failed.\n"); } // コマンドリスト nw::demo::CommandListSwapper::Description swapperDescription; swapperDescription.commandListCount = description.commandListCount; swapperDescription.bufferSize = description.commandBufferSize; swapperDescription.requestCount = description.commandRequestCount; swapperDescription.reusableBufferSize = description.reusableCommandBufferSize; swapperDescription.reusableRequestCount = description.reusableCommandRequestCount; swapperDescription.maxGpuProfilingEntryCount = description.maxGpuProfilingEntryCount; renderSystem->m_CommandListSwapper = nw::demo::CommandListSwapper::Create(allocator, swapperDescription); NW_POINTER_ASSERT(renderSystem->m_CommandListSwapper); renderSystem->m_CommandListSwapper->Bind(); renderSystem->m_CommandListSwapper->RunAsync(); nngxStartLcdDisplay(); renderSystem->m_UpperSwapper = DisplayBufferSwapper::Builder() .BufferDescription(description.upperScreenDescription) .BufferCount(description.displayBufferCount) .Create(allocator); renderSystem->m_LowerSwapper = DisplayBufferSwapper::Builder() .BufferDescription(description.lowerScreenDescription) .BufferCount(description.displayBufferCount) .Create(allocator); nngxSetDisplayMode(description.upperScreenMode); // ステレオモードの場合は上画面のディスプレイバッファを追加で生成する if (description.upperScreenMode == UPPER_SCREEN_MODE_STEREO) { nw::demo::DisplayBufferSwapper::Description extensionScreenDescription; extensionScreenDescription.screenKind = nw::demo::EXTENSION_SCREEN; extensionScreenDescription.width = description.upperScreenDescription.width; extensionScreenDescription.height = description.upperScreenDescription.height; renderSystem->m_ExtensionSwapper = DisplayBufferSwapper::Builder() .BufferDescription(extensionScreenDescription) .BufferCount(description.displayBufferCount) .Create(allocator); // ステレオカメラを生成する void* cameraMemory = allocator->Alloc(sizeof(nn::ulcd::CTR::StereoCamera)); nn::ulcd::CTR::StereoCamera* stereoCamera = new(cameraMemory) nn::ulcd::CTR::StereoCamera(); stereoCamera->Initialize(); renderSystem->m_StereoCamera = stereoCamera; } NW_GL_ASSERT(); renderSystem->m_Allocator = allocator; return renderSystem; } //---------------------------------------- void RenderSystem::LoadSkyModel(const wchar_t* fileName) { ResourceSet* resourceSet = Utility::LoadResources(m_Resources, fileName, m_Allocator); resourceSet->resource.ForeachTexture(nw::gfx::TextureLocationFlagSetter(NN_GX_MEM_VRAMA | GL_NO_COPY_FCRAM_DMP)); resourceSet->resource.ForeachIndexStream(nw::gfx::IndexStreamLocationFlagSetter(NN_GX_MEM_VRAMB | GL_NO_COPY_FCRAM_DMP)); resourceSet->resource.ForeachVertexStream(nw::gfx::VertexStreamLocationFlagSetter(NN_GX_MEM_VRAMB | GL_NO_COPY_FCRAM_DMP)); nw::gfx::Result result = resourceSet->resource.Setup(m_Allocator); if (result.IsFailure()) { NW_FATAL_ERROR("Fail to set up model. A result code is 0x%x", result.GetCode()); } // モデルのインスタンスを生成します。 nw::gfx::ResModelArray models = resourceSet->resource.GetModels(); nw::gfx::SceneNode* node = nw::demo::Utility::CreateSceneNode( m_Allocator, *(models.begin()), false ); nw::gfx::Model* model = nw::ut::DynamicCast(node); NW_NULL_ASSERT(model); m_SkyModel = model; } //---------------------------------------- void RenderSystem::Destroy() { os::IAllocator* allocator = this->m_Allocator; gfx::SafeDestroy(this->m_SkyModel); nw::demo::SafeCleanupResources(m_Resources); m_Resources.clear(); if (this->m_StereoCamera) { m_StereoCamera->Finalize(); allocator->Free(m_StereoCamera); } gfx::SafeDestroy(this->m_ExtensionSwapper); gfx::SafeDestroy(this->m_LowerSwapper); gfx::SafeDestroy(this->m_UpperSwapper); gfx::SafeDestroy(this->m_MeshRenderer); gfx::SafeDestroy(this->m_RenderQueue); gfx::SafeDestroy(this->m_PriorMaterialRenderKeyFactory); gfx::SafeDestroy(this->m_PriorDepthRenderKeyFactory); gfx::SafeDestroy(this->m_RenderContext); gfx::SafeDestroy(this->m_CommandListSwapper); nngxFinalize(); // TODO: グラフィックスメモリアロケーターのデアロケータを実装する s_GraphicsMemoryAllocator.Finalize(); NW_GL_ASSERT(); void* memory = static_cast(this); this->~RenderSystem(); allocator->Free(memory); } //---------------------------------------- void RenderSystem::SetRenderTarget(gfx::IRenderTarget* renderTarget) { gfx::RenderContext* renderContext = this->GetRenderContext(); renderContext->SetRenderTarget(renderTarget); } //---------------------------------------- void RenderSystem::SetRenderSortMode(gfx::ISceneUpdater::RenderSortMode renderSortMode) { this->m_RenderSortMode = renderSortMode; gfx::SafeDestroy(this->m_PriorDepthRenderKeyFactory); if (this->m_RenderSortMode == gfx::ISceneUpdater::OPAQUE_MESH_BASE_AND_TRANSLUCENT_MODEL_BASE_SORT) { this->m_PriorDepthRenderKeyFactory = gfx::CreateTopPriorDepthReverseDepthRenderKeyFactory(this->m_Allocator); } else { this->m_PriorDepthRenderKeyFactory = gfx::CreatePriorDepthReverseDepthRenderKeyFactory(this->m_Allocator); } NW_NULL_ASSERT(this->m_PriorDepthRenderKeyFactory); } //---------------------------------------- struct RenderSceneInternalFunctor : public std::unary_function { gfx::RenderContext* m_RenderContext; gfx::MeshRenderer* m_MeshRenderer; RenderSceneInternalFunctor(gfx::RenderContext* renderContext, gfx::MeshRenderer* meshRenderer) : m_RenderContext(renderContext), m_MeshRenderer(meshRenderer) { NW_NULL_ASSERT(renderContext); NW_NULL_ASSERT(meshRenderer); } void operator()(gfx::RenderElement& element) { if (element.IsCommand()) { gfx::RenderCommand* command = element.GetCommand(); NW_NULL_ASSERT(command); command->Invoke(this->m_RenderContext); } else { gfx::ResMesh mesh = element.GetMesh(); gfx::Model* model = element.GetModel(); model->PreRenderSignal()(model, mesh, this->m_RenderContext); this->m_MeshRenderer->RenderMesh(mesh, model); model->PostRenderSignal()(model, mesh, this->m_RenderContext); } NW_GL_ASSERT(); } }; //---------------------------------------- void RenderSystem::SetSceneEnvironmentSettings( SceneSystem* sceneSystem, ut::MoveArray* sceneEnvironmentSettings ) { gfx::RenderContext* renderContext = this->GetRenderContext(); gfx::SceneContext* sceneContext = sceneSystem->GetSceneContext(); gfx::SceneEnvironment& sceneEnvironment = renderContext->GetSceneEnvironment(); ut::MoveArray::iterator end = sceneEnvironmentSettings->end(); for (ut::MoveArray::iterator sceneEnvironmentSetting = sceneEnvironmentSettings->begin(); sceneEnvironmentSetting != end; ++sceneEnvironmentSetting) { (*sceneEnvironmentSetting)->ResolveReference(*sceneContext); sceneEnvironment.ApplyFrom(**sceneEnvironmentSetting); } } //---------------------------------------- void RenderSystem::SetEnvironment( SceneSystem* sceneSystem ) { NW_PROFILE("RenderSystem::SetEnvironment"); gfx::RenderContext* renderContext = this->GetRenderContext(); gfx::SceneContext* sceneContext = sceneSystem->GetSceneContext(); gfx::SceneEnvironment& sceneEnvironment = renderContext->GetSceneEnvironment(); if (sceneContext->GetAmbientLightsBegin() != sceneContext->GetAmbientLightsEnd()) { sceneEnvironment.SetAmbientLight(*sceneContext->GetAmbientLightsBegin()); } if (sceneContext->GetHemiSphereLightsBegin() != sceneContext->GetHemiSphereLightsEnd()) { sceneEnvironment.SetHemiSphereLight(*sceneContext->GetHemiSphereLightsBegin()); } std::for_each( sceneContext->GetFragmentLightsBegin(), sceneContext->GetFragmentLightsEnd(), std::bind1st(std::mem_fun(&gfx::SceneEnvironment::SetFragmentLight), &sceneEnvironment)); std::for_each( sceneContext->GetVertexLightsBegin(), sceneContext->GetVertexLightsEnd(), std::bind1st(std::mem_fun(&gfx::SceneEnvironment::SetVertexLight), &sceneEnvironment)); if (sceneContext->GetFogBegin() != sceneContext->GetFogEnd()) { sceneEnvironment.SetFog(0, *sceneContext->GetFogBegin()); } } //---------------------------------------- void RenderSystem::SubmitView( SceneSystem* sceneSystem ) { NW_PROFILE("RenderSystem::SubmitView"); nw::gfx::Camera* camera = this->GetRenderContext()->GetActiveCamera(); gfx::SceneContext* sceneContext = sceneSystem->GetSceneContext(); //---------------------------------------- // フォグを更新します。 std::for_each(sceneContext->GetFogBegin(), sceneContext->GetFogEnd(), gfx::Fog::UpdateFunctor(camera)); //---------------------------------------- // カメラに依存する処理と、描画要素をレンダーキューに積みます。 #ifdef SORT_DEPTH_OF_TRANSLUCENT_MESH_ENABLED // RenderQueue::Reset の引数に true を指定すると、ソート用キーをキャッシュする最適化機能が // 有効になります。 // 半透明以外のソート用キーのみキャッシュされるようになります。 this->m_RenderQueue->Reset(true); // 半透明メッシュの深度のみ計算して、半透明メッシュのみ深度によるソートがされるようになります。 // 深度計算の処理負荷の低減を期待できます。 sceneSystem->GetSceneUpdater()->SetDepthSortMode(gfx::ISceneUpdater::SORT_DEPTH_OF_TRANSLUCENT_MESH); #else this->m_RenderQueue->Reset(); // すべてのメッシュの深度を計算して、深度によってもソートされるようになります。 // 不透明物体等を手前から描画することで、GPUのフィル負荷の低減を期待できます。 sceneSystem->GetSceneUpdater()->SetDepthSortMode(gfx::ISceneUpdater::SORT_DEPTH_OF_ALL_MESH); #endif sceneSystem->GetSceneUpdater()->SubmitView( this->m_RenderQueue, sceneContext, *camera, 0, 1, this->m_RenderSortMode); //---------------------------------------- // レンダーキューをソートします。 // RenderElementCompare() やレンダーキーファクトリーをカスタマイズすることで // 描画順を変更することができます。 // 詳しくは最適化TIPSを参照してください。 std::sort( this->m_RenderQueue->Begin(), this->m_RenderQueue->End(), gfx::RenderElementCompare()); #ifdef REPORT_RENDER_QUEUE_ENABLED // レンダーキューのソート後の状態をレポートします。 // リリース版ではなにも表示されなくなっています。 std::for_each( this->m_RenderQueue->Begin(), this->m_RenderQueue->End(), gfx::RenderQueue::ReportFunctor()); #endif } //---------------------------------------- void RenderSystem::RenderScene( gfx::Camera* camera, s32 screenKind, bool isCommandCacheDump ) { NW_PROFILE("RenderSystem::RenderScene"); gfx::RenderContext* renderContext = this->GetRenderContext(); gfx::SceneEnvironment& sceneEnvironment = renderContext->GetSceneEnvironment(); NW_GL_ASSERT(); // GPU処理時間計測開始 int profilingId = m_CommandListSwapper->AddGpuProfilingStartPoint(true); NW_ASSERT(profilingId >= 0); int cmdBufferSize = m_CommandListSwapper->GetCommandBufferSize(); renderContext->SetCameraMatrix(camera); if (m_SkyModel != NULL) { this->ClearBySkyModel(camera); } else { this->ClearBuffer(nw::ut::FloatColor(0.5f, 0.5f, 0.5f, 1.0f)); } if (isCommandCacheDump) { nw::gfx::internal::CommandCacheBuilder builder; builder.Begin(); std::for_each( this->m_RenderQueue->Begin(), this->m_RenderQueue->End(), RenderSceneInternalFunctor(renderContext, this->m_MeshRenderer)); builder.End(); builder.Report(); } else { std::for_each( this->m_RenderQueue->Begin(), this->m_RenderQueue->End(), RenderSceneInternalFunctor(renderContext, this->m_MeshRenderer)); } m_LoadMeterDescription.drawCommandBufferSize = m_CommandListSwapper->GetCommandBufferSize() - cmdBufferSize; m_CumulativeLoadMeterDescription.drawCommandBufferSize = m_CommandListSwapper->GetCommandBufferSize() - cmdBufferSize; // GPU処理時間計測終了 nngxSplitDrawCmdlist(); m_CommandListSwapper->SetGpuProfilingEndPoint(profilingId); TransferBuffer(screenKind); } //---------------------------------------- void RenderSystem::RenderStereoScene( gfx::Camera* leftCamera, gfx::Camera* rightCamera, bool isCommandCacheDump ) { NW_PROFILE("RenderSystem::RenderStereoScene"); gfx::RenderContext* renderContext = this->GetRenderContext(); gfx::SceneEnvironment& sceneEnvironment = renderContext->GetSceneEnvironment(); //---------------------------------------- // 再利用するためのコマンドを作成し、保存します。 // // ベースカメラを元にしたシーンを描画します。 m_CommandListSwapper->StartCommandSave(); int cmdBufferSize = m_CommandListSwapper->GetCommandBufferSize(); if (m_SkyModel != NULL) { this->ClearBySkyModel(renderContext->GetActiveCamera()); } else { this->ClearBuffer(nw::ut::FloatColor(0.5f, 0.5f, 0.5f, 1.0f)); } if (isCommandCacheDump) { nw::gfx::internal::CommandCacheBuilder builder; builder.Begin(); std::for_each( this->m_RenderQueue->Begin(), this->m_RenderQueue->End(), RenderSceneInternalFunctor(renderContext, this->m_MeshRenderer)); builder.End(); builder.Report(); } else { std::for_each( this->m_RenderQueue->Begin(), this->m_RenderQueue->End(), RenderSceneInternalFunctor(renderContext, this->m_MeshRenderer)); } m_LoadMeterDescription.drawCommandBufferSize = m_CommandListSwapper->GetCommandBufferSize() - cmdBufferSize; m_CommandListSwapper->EndCommandSave(); //---------------------------------------- // 左目用と右目用に保存したコマンドを再利用します。 // GPU処理時間計測開始 int profilerLeft = m_CommandListSwapper->AddGpuProfilingStartPoint(true); NW_ASSERT(profilerLeft >= 0); // 左目用 renderContext->SetCameraMatrix(leftCamera); m_CommandListSwapper->ReuseCommand(false); // GPU処理時間計測終了 m_CommandListSwapper->SetGpuProfilingEndPoint(profilerLeft); TransferBuffer(UPPER_SCREEN); NW_GL_ASSERT(); // GPU処理時間計測開始 int profilerRight = m_CommandListSwapper->AddGpuProfilingStartPoint(true); NW_ASSERT(profilerRight >= 0); // 右目用 renderContext->SetCameraMatrix(rightCamera); m_CommandListSwapper->ReuseCommand(false); // GPU処理時間計測終了 m_CommandListSwapper->SetGpuProfilingEndPoint(profilerRight); TransferBuffer(EXTENSION_SCREEN); NW_GL_ASSERT(); } //---------------------------------------- void RenderSystem::ClearBuffer( nw::ut::FloatColor clearColor ) { if (!nw::demo::DebugUtility::IsCpuProfilingMode()) { this->SuspendLoadMeter(); this->m_RenderContext->ClearBuffer( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, clearColor, 1.0f); NW_GL_ASSERT(); this->ResumeLoadMeter(); } } //---------------------------------------- void RenderSystem::ClearBySkyModel(const gfx::Camera* camera) { NW_NULL_ASSERT(camera); if (m_SkyModel != NULL) { math::VEC3 translate = camera->WorldMatrix().GetColumn(3); m_SkyModel->WorldMatrix()._03 = translate.x; m_SkyModel->WorldMatrix()._13 = translate.y; m_SkyModel->WorldMatrix()._23 = translate.z; // Far の半分まで SkyModel を拡大します。 float scale = (camera->GetFar() / 2.0f); m_SkyModel->WorldMatrix()._00 = scale; m_SkyModel->WorldMatrix()._11 = scale; m_SkyModel->WorldMatrix()._22 = scale; gfx::ResMeshArray meshArray = m_SkyModel->GetResMeshes(); gfx::ResMeshArray::iterator endMesh = meshArray.end(); for (gfx::ResMeshArray::iterator iter = meshArray.begin(); iter != endMesh; ++iter) { this->m_MeshRenderer->RenderMesh(*iter, m_SkyModel); } } } //---------------------------------------- void RenderSystem::TransferBuffer( s32 screenKind ) { gfx::RenderContext* renderContext = this->GetRenderContext(); // コマンドリストが多重化されている場合、 // ディスプレイバッファへの転送するタイミングが変わるため、 // 転送先のディスプレイバッファを変更する必要があります。 // 詳細は PresentBuffer() のコメントを参照してください。 bool isMultiCommandList = m_CommandListSwapper->GetListCount() > 1; if (screenKind & UPPER_SCREEN) { this->m_UpperSwapper->MakeTransferBufferCommand( renderContext->GetRenderTarget(), isMultiCommandList); } if (screenKind & LOWER_SCREEN) { this->m_LowerSwapper->MakeTransferBufferCommand( renderContext->GetRenderTarget(), isMultiCommandList); } if (screenKind & EXTENSION_SCREEN) { this->m_ExtensionSwapper->MakeTransferBufferCommand( renderContext->GetRenderTarget(), isMultiCommandList); } } //---------------------------------------- void RenderSystem::WaitCommandList() { this->SuspendLoadMeter(); NW_PROFILE("RenderSystem::WaitCommandList"); nn::os::Tick startTick = nn::os::Tick::GetSystemCurrent(); m_CommandListSwapper->WaitDone(); s64 span = static_cast(nn::os::Tick::GetSystemCurrent() - startTick).ToTimeSpan().GetNanoSeconds(); m_LoadMeterDescription.cumulativeWaitTime += static_cast(span) * LoadMeterDescription::NANO_TO_MILLI; this->ResumeLoadMeter(); } //---------------------------------------- void RenderSystem::RunCommandList() { m_CommandListSwapper->RunAsync(); } //---------------------------------------- void RenderSystem::SwapCommandList() { m_CommandListSwapper->Swap(); m_CommandListSwapper->Bind(); } //---------------------------------------- void RenderSystem::WaitVSync(s32 screenKind) { NW_MAX_ASSERT(screenKind, (int)BOTH_SCREENS); GLenum display; if (screenKind & (UPPER_SCREEN | LOWER_SCREEN)) { display = NN_GX_DISPLAY_BOTH; } else if (screenKind & UPPER_SCREEN) { display = NN_GX_DISPLAY0; } else if (screenKind & LOWER_SCREEN) { display = NN_GX_DISPLAY1; } EndLoadMeter(); { NW_PROFILE("WaitVSync"); nngxWaitVSync(display); } BeginLoadMeter(); } //---------------------------------------- void RenderSystem::SwapBuffer( s32 screenKind ) { GLenum display; if ((screenKind & NORMAL_SCREENS) == NORMAL_SCREENS) { this->m_UpperSwapper->ActivateBuffer(); this->m_LowerSwapper->ActivateBuffer(); if (screenKind & EXTENSION_SCREEN) { this->m_ExtensionSwapper->ActivateBuffer(); } display = NN_GX_DISPLAY_BOTH; } else if (screenKind & UPPER_SCREEN) { this->m_UpperSwapper->ActivateBuffer(); if (screenKind & EXTENSION_SCREEN) { this->m_ExtensionSwapper->ActivateBuffer(); } display = NN_GX_DISPLAY0; } else if (screenKind & LOWER_SCREEN) { this->m_LowerSwapper->ActivateBuffer(); display = NN_GX_DISPLAY1; } nngxSwapBuffers(display); glBindFramebuffer(GL_FRAMEBUFFER, 0); } //---------------------------------------- void RenderSystem::PresentBuffer(s32 screenKind) { // コマンドリストが多重化されているかどうかで、 // 転送するディスプレイバッファを変更したり、 // コマンドリスト終了待ちのタイミングを変えたりする必要があります。 // これらを正しく行わない場合、テアリングなどが発生することがあります。 if (m_CommandListSwapper->GetListCount() > 1) { // コマンドリストが多重化されている場合、 // この関数が呼ばれるまでに蓄積されたコマンドが実行( RunCommandList() )されるのは // VSync とディスプレイバッファのスワップが行われた後になります。 // // レンダーバッファからディスプレイバッファへの転送もコマンドとして蓄積され、 // 同じくディスプレイバッファのスワップ後に実行されます。 // そのため、この関数内で呼ばれるSwapBuffer()により表示されるディスプレイバッファへの転送をしてしまうと、 // 表示中のディスプレイバッファへの転送が実行され、テアリングが発生します。 // // これを解決するため、ディスプレイバッファへの転送コマンドを蓄積する場合、 // その時点を基準に2回ディスプレイバッファのスワップが行われたときに // 表示されるバッファへ転送する必要があります。 // DisplayBufferSwapper::MakeTransferBufferCommand() の第2引数にtrueを指定することで、 // 内部的にこのような転送先の調整が行われます。 WaitCommandList(); SwapBuffer(screenKind); WaitVSync(screenKind); RunCommandList(); SwapCommandList(); } else { RunCommandList(); WaitCommandList(); SwapBuffer(screenKind); WaitVSync(screenKind); } } //---------------------------------------- void RenderSystem::CalcStereoCamera( gfx::Camera* leftCamera, gfx::Camera* rightCamera, gfx::Camera* baseCamera, f32 depthLevel, f32 depthRange) { NW_NULL_ASSERT(leftCamera); NW_NULL_ASSERT(rightCamera); NW_NULL_ASSERT(baseCamera); nn::math::MTX44& projOriginal = baseCamera->ProjectionMatrix(); nn::math::MTX34& viewOriginal = baseCamera->ViewMatrix(); nn::math::MTX44& projL = leftCamera->ProjectionMatrix(); nn::math::MTX34& viewL = leftCamera->ViewMatrix(); nn::math::MTX44& projR = rightCamera->ProjectionMatrix(); nn::math::MTX34& viewR = rightCamera->ViewMatrix(); // Orthoカメラをステレオ表示する場合は自分で絵を用意しなければいけません。 if (baseCamera->GetResCamera().GetProjectionType() == gfx::ResCamera::PROJTYPE_ORTHO) { // 現在はOrthoカメラのステレオ表示には未対応です。 math::MTX44Copy(&projL, &projOriginal); math::MTX34Copy(&viewL, &viewOriginal); math::MTX44Copy(&projR, &projOriginal); math::MTX34Copy(&viewR, &viewOriginal); // CalculateMatrices(....)で画面にあわせての回転を行っているので、 // Orthoの場合はpivotを設定します。 baseCamera->GetProjectionUpdater()->SetPivotDirection(math::PIVOT_UPSIDE_TO_TOP); leftCamera->GetProjectionUpdater()->SetPivotDirection(math::PIVOT_UPSIDE_TO_TOP); rightCamera->GetProjectionUpdater()->SetPivotDirection(math::PIVOT_UPSIDE_TO_TOP); } else { m_StereoCamera->CalculateMatrices( &projL, &viewL, &projR, &viewR, &projOriginal, &viewOriginal, depthLevel, depthRange, false); } } //---------------------------------------- void RenderSystem::BeginLoadMeter() { m_LoadMeterDescription.startTick = nn::os::Tick::GetSystemCurrent(); m_LoadMeterDescription.drawCommandBufferSize = 0; } //---------------------------------------- void RenderSystem::EndLoadMeter() { nn::os::Tick endTick = nn::os::Tick::GetSystemCurrent(); s64 span = static_cast(endTick - m_LoadMeterDescription.startTick).ToTimeSpan().GetNanoSeconds(); float currentTime = static_cast(span) * LoadMeterDescription::NANO_TO_MILLI; float currentGpuTime = static_cast(this->m_CommandListSwapper->GetGpuProfilingTotalCostTime()); m_LoadMeterDescription.cumulativeTime += currentTime; m_LoadMeterDescription.cumulativeGpuTime += currentGpuTime; m_CumulativeLoadMeterDescription.cumulativeTime += currentTime; m_CumulativeLoadMeterDescription.cumulativeGpuTime += currentGpuTime; m_CumulativeLoadMeterDescription.drawCommandBufferSize = m_LoadMeterDescription.drawCommandBufferSize; ++m_LoadMeterDescription.callCount; ++m_CumulativeLoadMeterDescription.callCount; } //---------------------------------------- void RenderSystem::CalcLoadMeter() { float callCount = static_cast(m_LoadMeterDescription.callCount); m_LoadMeterDescription.loadTime = m_LoadMeterDescription.cumulativeTime / callCount; m_LoadMeterDescription.loadGpuTime = m_LoadMeterDescription.cumulativeGpuTime / callCount; m_LoadMeterDescription.waitTime = m_LoadMeterDescription.cumulativeWaitTime / callCount; m_LoadMeterDescription.cumulativeTime = 0.0f; m_LoadMeterDescription.cumulativeGpuTime = 0.0f; m_LoadMeterDescription.cumulativeWaitTime = 0.0f; m_LoadMeterDescription.callCount = 0; } //---------------------------------------- void RenderSystem::SuspendLoadMeter() { nn::os::Tick endTick = nn::os::Tick::GetSystemCurrent(); s64 span = static_cast(endTick - m_LoadMeterDescription.startTick).ToTimeSpan().GetNanoSeconds(); m_LoadMeterDescription.cumulativeTime += static_cast(span) * LoadMeterDescription::NANO_TO_MILLI; m_CumulativeLoadMeterDescription.cumulativeTime += static_cast(span) * LoadMeterDescription::NANO_TO_MILLI; m_LoadMeterDescription.startTick = 0; } //---------------------------------------- void RenderSystem::ResumeLoadMeter() { m_LoadMeterDescription.startTick = nn::os::Tick::GetSystemCurrent(); } //---------------------------------------- void RenderSystem::DrawLoadMeter( GraphicsDrawing& graphicsDrawing, s32 positionX, s32 positionY ) { float loadRate = this->m_LoadMeterDescription.loadTime * LoadMeterDescription::MILLI_TO_RATE; graphicsDrawing.DrawString(positionX, positionY, " CPU :%5.2f ms/f (%6.2f %%)\n", this->m_LoadMeterDescription.loadTime, loadRate * 100.0f); float loadGpuRate = m_LoadMeterDescription.loadGpuTime * LoadMeterDescription::MILLI_TO_RATE; graphicsDrawing.DrawString(positionX, positionY + 22, " GPU :%5.2f ms/f (%6.2f %%)\n", this->m_LoadMeterDescription.loadGpuTime, loadGpuRate * 100.0f); graphicsDrawing.DrawString(positionX, positionY + 62, " Cmd size :% 8d bytes\n", m_LoadMeterDescription.drawCommandBufferSize); } //---------------------------------------- void RenderSystem::ResetCumulativeLoadMeter() { m_CumulativeLoadMeterDescription.cumulativeTime = 0.0f; m_CumulativeLoadMeterDescription.cumulativeGpuTime = 0.0f; m_CumulativeLoadMeterDescription.cumulativeWaitTime = 0.0f; m_CumulativeLoadMeterDescription.callCount = 0; } //---------------------------------------- void RenderSystem::LogLoadMeter() { float loadRate = this->m_LoadMeterDescription.loadTime * LoadMeterDescription::MILLI_TO_RATE; NW_DEV_LOG(" CPU :%5.2f ms/f (%6.2f %%)\n", this->m_LoadMeterDescription.loadTime, loadRate * 100.0f); float loadGpuRate = m_LoadMeterDescription.loadGpuTime * LoadMeterDescription::MILLI_TO_RATE; NW_DEV_LOG(" GPU :%5.2f ms/f (%6.2f %%)\n", this->m_LoadMeterDescription.loadGpuTime, loadGpuRate * 100.0f); NW_DEV_LOG(" Cmd size :% 8d bytes\n", m_LoadMeterDescription.drawCommandBufferSize); } //---------------------------------------- SceneSystem* SceneSystem::Create( os::IAllocator* allocator, const Description& description ) { void* memory = allocator->Alloc(sizeof(SceneSystem)); SceneSystem* sceneSystem = new(memory) SceneSystem(); sceneSystem->m_SceneContext = gfx::SceneContext::Builder() .MaxSceneNodes(description.maxSceneNodes) .MaxModels(description.maxModels) .MaxSkeletalModels(description.maxSkeletalModels) .MaxCameras(description.maxCameras) .MaxLights(description.maxLights) .MaxFragmentLights(description.maxFragmentLights) .MaxVertexLights(description.maxVertexLights) .MaxHemiSphereLights(description.maxHemiSphereLights) .MaxAmbientLights(description.maxAmbientLights) .MaxFogs(description.maxFogs) .MaxParticleSets(description.maxParticleSets) .MaxParticleEmitters(description.maxParticleEmitters) .MaxParticleModels(description.maxParticleModels) .IsFixedSizeMemory(description.isFixedSizeMemory) .Create(allocator); sceneSystem->m_SceneTraverser = gfx::SceneTraverser::Builder() .Create(allocator); gfx::WorldMatrixUpdater* worldMatrixUpdater = gfx::WorldMatrixUpdater::Builder() .Create(allocator); gfx::SkeletonUpdater* skeletonUpdater = gfx::SkeletonUpdater::Builder() .Create(allocator); sceneSystem->m_SceneUpdater = gfx::SceneUpdater::Builder() .WorldMatrixUpdaterPtr(worldMatrixUpdater) .SkeletonUpdaterPtr(skeletonUpdater) .Create(allocator); nw::gfx::IMaterialIdGenerator* materialIdGenerator = nw::gfx::SortingMaterialIdGenerator::Builder() .IsFixedSizeMemory(description.isFixedSizeMemory) .MaxMaterials(description.maxMaterials) .Create(allocator); sceneSystem->m_SceneInitializer = gfx::SceneInitializer::Builder() .MaterialIdGenerator(materialIdGenerator) .Create(allocator); sceneSystem->m_CameraController = CameraController::Builder() .Create(allocator); NW_GL_ASSERT(); sceneSystem->m_Allocator = allocator; return sceneSystem; } //---------------------------------------- void SceneSystem::Destroy() { gfx::SafeDestroy(this->m_SceneInitializer); gfx::SafeDestroy(this->m_SceneTraverser); gfx::SafeDestroy(this->m_SceneUpdater); gfx::SafeDestroy(this->m_SceneContext); gfx::SafeDestroy(this->m_CameraController); os::IAllocator* allocator = this->m_Allocator; void* memory = static_cast(this); this->~SceneSystem(); allocator->Free(memory); } //---------------------------------------- void SceneSystem::InitializeScene(gfx::SceneNode* sceneRoot) { this->m_SceneInitializer->Begin(); sceneRoot->Accept(this->m_SceneInitializer); this->m_SceneInitializer->End(); TraverseScene(sceneRoot); } //---------------------------------------- void SceneSystem::TraverseScene(gfx::SceneNode* sceneRoot) { this->m_SceneTraverser->Begin(this->m_SceneContext); sceneRoot->Accept(this->m_SceneTraverser); this->m_SceneTraverser->End(); // パーティクルセットの依存順でソートする std::sort( this->m_SceneContext->GetParticleSetBegin(), this->m_SceneContext->GetParticleSetEnd(), gfx::ParticleSetCompare()); } //---------------------------------------- void SceneSystem::UpdateScene() { NW_PROFILE("SceneSystem::UpdateScene"); this->m_SceneUpdater->UpdateAll(this->m_SceneContext); } //---------------------------------------- void FinalizeGraphicsSystem() { s_GraphicsMemoryAllocator.Finalize(); nw::demo::FinalizeDemoAllocator(s_DeviceAllocator); } //---------------------------------------- void InitializeGraphicsSystem(nw::demo::DemoAllocator* deviceAllocator) { s_DeviceAllocator = deviceAllocator; nn::os::Initialize(); nn::fs::Initialize(); // メインメモリとデバイスメモリを初期化します。 // メインメモリはユーザーが自由にアクセスできる領域となり、 // デバイスメモリはGPUがアクセスするFCRAM上の領域となります。 nw::demo::InitializeDemoMemory(); nw::demo::InitializeDemoAllocator(s_DeviceAllocator, nw::demo::DEMO_MEMORY_SIZE, nn::os::ALLOCATE_OPTION_LINEAR); // デバイスメモリを用いるグラフィックスメモリアロケータを初期化します。 s_GraphicsMemoryAllocator.Initialize(s_DeviceAllocator); const int MAX_FILE = 256; const int MAX_DIRECTORY = 16; s32 workingMemorySize = nn::fs::GetRomRequiredMemorySize(MAX_FILE, MAX_DIRECTORY); void* workingMemory = Alloc(workingMemorySize); nn::Result result = nn::fs::MountRom(MAX_FILE, MAX_DIRECTORY, workingMemory, workingMemorySize); NW_ASSERT(result.IsSuccess()); } } // namespace demo } // namespace nw