/*---------------------------------------------------------------------------* Project: NintendoWare File: ParticleLowLayerDemo.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: 25401 $ *---------------------------------------------------------------------------*/ #define NW_DEBUG_CHECK_MEMORY_LEAK #include #include #include #include #include #include #include #include namespace { //---------------------------------------- // メモリ関係 // デモではデバイスメモリから全てのメモリを確保するようになっています。 // デモで使用するメモリサイズです。 const size_t DEMO_MEMORY_SIZE = 0x1000000; // デバイスメモリを確保するためのアロケータです。 nw::demo::DemoAllocator s_DeviceAllocator; nw::demo::GraphicsMemoryAllocator s_GraphicsMemoryAllocator; const int MAX_FILE = 256; const int MAX_DIRECTORY = 16; // メモリのフラッシュコード nw::demo::FlushCache* s_FlushCache; //---------------------------------------- // ファイル名の定義です。 static const wchar_t* MODEL_RESOURCE_FILES[] = { NW_DEMO_FILE_PATH(L"fountain_particle_all.bcptl"), NW_DEMO_FILE_PATH(L"SceneEnvironmentSetting.bcenv"), }; //---------------------------------------- // 描画関係 nw::gfx::IRenderTarget* s_RenderTarget = NULL; nw::gfx::ParticleContext* s_ParticleContext = NULL; nw::gfx::RenderContext* s_RenderContext = NULL; nw::gfx::MeshRenderer* s_MeshRenderer = NULL; nw::demo::DisplayBufferSwapper* s_UpperSwapper = NULL; nw::demo::DisplayBufferSwapper* s_ExtensionSwapper = NULL; const size_t COMMAND_BUFFER_SIZE = 0x100000; const size_t REQUEST_COUNT = 512; nw::demo::CommandListSwapper* s_CommandListSwapper = NULL; //---------------------------------------- // リソース関係 const int RESOURCES_COUNT = 3; struct ResourceSet { nw::ut::MoveArray buffer; nw::gfx::ResGraphicsFile resource; }; typedef nw::ut::FixedSizeArray ResourceArray; ResourceArray s_Resources; //---------------------------------------- // シーン関係 s32 s_FrameCount = 0; nw::gfx::WorldMatrixUpdater* s_WorldMatrixUpdater = NULL; const s32 s_BaseCameraIndex = 0; nw::gfx::Camera* s_BaseCamera = NULL; nw::gfx::Camera* s_LeftCamera = NULL; nw::gfx::Camera* s_RightCamera = NULL; const f32 s_NearPlane = 0.1f; nw::demo::CameraController* s_CameraController = NULL; nn::ulcd::CTR::StereoCamera* s_StereoCamera; const int MODEL_COUNT = 2; const int EMITTER_COUNT = 1; const int PARTICLESET_COUNT = 2; typedef nw::ut::FixedSizeArray ParticleModelArray; typedef nw::ut::FixedSizeArray EmitterArray; typedef nw::ut::FixedSizeArray ParticleSetArray; ParticleModelArray s_ParticleModels; EmitterArray s_ParticleEmitters; ParticleSetArray s_ParticleSets; void UpdateNode(nw::gfx::TransformNode* node); /*!--------------------------------------------------------------------------* @brief グラフィックスメモリのアロケータです。 関数ポインタが nngxInitialize の引数として使用されます。 *---------------------------------------------------------------------------*/ void* AllocateGraphicsMemory(GLenum area, GLenum aim, GLuint id, GLsizei size) { void* buffer = s_GraphicsMemoryAllocator.Allocate(area, aim, id, size); return buffer; } /*!--------------------------------------------------------------------------* @brief グラフィックスメモリのデアロケータです。 関数ポインタが nngxInitialize の引数として使用されます。 *---------------------------------------------------------------------------*/ void DeallocateGraphicsMemory(GLenum area, GLenum aim, GLuint id, void* addr) { s_GraphicsMemoryAllocator.Deallocate(area, aim, id, addr); } /*!--------------------------------------------------------------------------* @brief グラフィックスシステムに必要なメモリなどの初期化を行います。 *---------------------------------------------------------------------------*/ void InitializeGraphicsSystem() { nn::os::Initialize(); nn::fs::Initialize(); nw::demo::InitializeDemoMemory(); nw::demo::InitializeDemoAllocator(&s_DeviceAllocator, DEMO_MEMORY_SIZE, nn::os::ALLOCATE_OPTION_LINEAR); // デバイスメモリを用いるグラフィックスメモリアロケータを初期化します。 s_GraphicsMemoryAllocator.Initialize(&s_DeviceAllocator); s32 workingMemorySize = nn::fs::GetRomRequiredMemorySize(MAX_FILE, MAX_DIRECTORY); void* workingMemory = nw::demo::Alloc(workingMemorySize); nn::Result result = nn::fs::MountRom(MAX_FILE, MAX_DIRECTORY, workingMemory, workingMemorySize); NW_ASSERT(result.IsSuccess()); } /*!--------------------------------------------------------------------------* @brief グラフィックスシステムに必要なメモリなどの終了処理を行います。 *---------------------------------------------------------------------------*/ void FinalizeGraphicsSystem() { s_GraphicsMemoryAllocator.Finalize(); nw::demo::FinalizeDemoAllocator(&s_DeviceAllocator); } /*!--------------------------------------------------------------------------* @brief グラフィックス関連の初期化を行います。 *---------------------------------------------------------------------------*/ void InitializeGraphics() { s_ParticleContext = nw::gfx::ParticleContext::Builder() .MaxEmission(1000) .Create(&s_DeviceAllocator); if (nngxInitialize(AllocateGraphicsMemory, DeallocateGraphicsMemory) == GL_FALSE) { NW_FATAL_ERROR("nngxInitialize failed.\n"); } //----------------------------- nw::demo::DisplayBufferSwapper::Description upperScreenDescription; upperScreenDescription.screenKind = nw::demo::UPPER_SCREEN; upperScreenDescription.width = 400; upperScreenDescription.height = 240; upperScreenDescription.bufferCount = 2; nw::demo::DisplayBufferSwapper::Description extensionScreenDescription; extensionScreenDescription.screenKind = nw::demo::EXTENSION_SCREEN; extensionScreenDescription.width = upperScreenDescription.width; extensionScreenDescription.height = upperScreenDescription.height; extensionScreenDescription.bufferCount = upperScreenDescription.bufferCount; s_UpperSwapper = nw::demo::DisplayBufferSwapper::Builder() .BufferDescription(upperScreenDescription) .Create(&s_DeviceAllocator); s_ExtensionSwapper = nw::demo::DisplayBufferSwapper::Builder() .BufferDescription(extensionScreenDescription) .Create(&s_DeviceAllocator); //----------------------------- nw::demo::CommandListSwapper::Description commandListSwapperDescription; commandListSwapperDescription.commandListCount = 1; commandListSwapperDescription.bufferSize = COMMAND_BUFFER_SIZE; commandListSwapperDescription.requestCount = REQUEST_COUNT; commandListSwapperDescription.reusableBufferSize = COMMAND_BUFFER_SIZE; commandListSwapperDescription.reusableRequestCount = REQUEST_COUNT; commandListSwapperDescription.maxGpuProfilingEntryCount = 0; s_CommandListSwapper = nw::demo::CommandListSwapper::Create(&s_DeviceAllocator, commandListSwapperDescription); s_CommandListSwapper->Bind(); //----------------------------- nngxSetDisplayMode(nw::demo::UPPER_SCREEN_MODE_STEREO); //----------------------------- nw::gfx::CommandCacheManager::SetAllocator( &s_DeviceAllocator ); //----------------------------- s_RenderContext = nw::gfx::RenderContext::Builder() .Create(&s_DeviceAllocator); //----------------------------- void* cameraMemory = s_DeviceAllocator.Alloc(sizeof(nn::ulcd::CTR::StereoCamera)); s_StereoCamera = new(cameraMemory) nn::ulcd::CTR::StereoCamera(); s_StereoCamera->Initialize(); //----------------------------- s_MeshRenderer = nw::gfx::MeshRenderer::Create(&s_DeviceAllocator); s_MeshRenderer->SetRenderContext(s_RenderContext); //----------------------------- nw::gfx::RenderColorFormat renderColorFormat = nw::gfx::RENDER_COLOR_FORMAT_RGBA8; s_RenderTarget = nw::gfx::IRenderTarget::Builder() .BufferSize(upperScreenDescription.height, upperScreenDescription.width) .ColorFormat(renderColorFormat) .Create(&s_DeviceAllocator); NW_NULL_ASSERT(s_RenderTarget); //----------------------------- s_WorldMatrixUpdater = nw::gfx::WorldMatrixUpdater::Builder() .Create(&s_DeviceAllocator); //----------------------------- nngxStartLcdDisplay(); NW_GL_ASSERT(); } /*!--------------------------------------------------------------------------* @brief グラフィックス関連の後始末をします。 *---------------------------------------------------------------------------*/ void TerminateGraphics() { s_StereoCamera->Finalize(); s_DeviceAllocator.Free(s_StereoCamera); nw::gfx::SafeDestroy(s_ParticleContext); nw::gfx::SafeDestroy(s_WorldMatrixUpdater); nw::gfx::SafeDestroy(s_RenderTarget); nw::gfx::SafeDestroy(s_MeshRenderer); nw::gfx::SafeDestroy(s_CommandListSwapper); nw::gfx::SafeDestroy(s_UpperSwapper); nw::gfx::SafeDestroy(s_ExtensionSwapper); nw::gfx::SafeDestroy(s_RenderContext); nngxFinalize(); NW_GL_ASSERT(); } /*!--------------------------------------------------------------------------* @brief グラフィックスリソースをロードします。 @param[in] resourcePath ファイルのパス名 @return ロードしたグラフィックス情報を返します。 *---------------------------------------------------------------------------*/ ResourceSet* LoadResources(const wchar_t* resourcePath) { ResourceSet resourceSet; // 現在、デバイスメモリ上に読み込む方式にのみ対応しています。 // テクスチャをロードするには128byteアライメントを行う必要があります。 static const int resourceAlignment = 128; resourceSet.buffer = nw::demo::Utility::LoadFile(&s_DeviceAllocator , resourcePath, resourceAlignment); if (!resourceSet.buffer) { return NULL; } resourceSet.resource = nw::gfx::ResGraphicsFile(&(resourceSet.buffer.front())); bool isPushed = s_Resources.push_back(resourceSet); NW_ASSERT(isPushed); return &(s_Resources.back()); } /*!--------------------------------------------------------------------------* @brief シーンノードを生成します。 @param[in] resource シーンノードのリソースです。 @return 生成したシーンノードです。。 *---------------------------------------------------------------------------*/ nw::gfx::SceneNode* CreateSceneNode(nw::gfx::ResSceneObject resource) { nw::gfx::SceneObject* sceneObject = nw::gfx::SceneBuilder() .Resource(resource) .MaxChildren(0) .CreateObject(&s_DeviceAllocator, &s_DeviceAllocator); return nw::ut::DynamicCast(sceneObject); } /*!--------------------------------------------------------------------------* @brief リソース関連の構築をします。 *---------------------------------------------------------------------------*/ void BuildResources(ResourceSet* resourceSet) { 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(&s_DeviceAllocator); if (result.IsFailure()) { NW_FATAL_ERROR("Fail to set up model. A result code is 0x%x", result.GetCode()); } /* リソースでの階層組みは無視します。 */ nw::ut::MoveArray sceneNodeArray(&s_DeviceAllocator); nw::gfx::ResModelArray models = resourceSet->resource.GetModels(); nw::gfx::ResModelArray::iterator modelsEnd = models.end(); for (nw::gfx::ResModelArray::iterator modelResource = models.begin(); modelResource != modelsEnd; ++modelResource) { nw::gfx::SceneNode* node = CreateSceneNode(*modelResource); NW_NULL_ASSERT(node); sceneNodeArray.push_back(node); if (nw::ut::IsTypeOf(node)) { s_ParticleModels.push_back(static_cast(node)); nw::gfx::ParticleModel* particleModel = static_cast(node); for (int i = 0; i < particleModel->GetParticleSetsCount(); ++i) { s_ParticleSets.push_back(particleModel->GetParticleSets(i)); } } } nw::gfx::ResEmitterArray emitters = resourceSet->resource.GetEmitters(); nw::gfx::ResEmitterArray::iterator emittersEnd = emitters.end(); for (nw::gfx::ResEmitterArray::iterator emitterResource = emitters.begin(); emitterResource != emittersEnd; ++emitterResource) { nw::gfx::SceneNode* node = CreateSceneNode(*emitterResource); NW_NULL_ASSERT(node); sceneNodeArray.push_back(node); if (nw::ut::IsTypeOf(node)) { s_ParticleEmitters.push_back(static_cast(node)); } } // 親子付け参照関係を解決 //nw::gfx::SceneHelper::ResolveReference(sceneNodeArray); nw::gfx::ParticleUtil::SetupParticleObject(&sceneNodeArray, s_ParticleContext); } /*!--------------------------------------------------------------------------* @brief カメラの構築をします。 *---------------------------------------------------------------------------*/ void BuildCameras() { nw::gfx::LookAtTargetViewUpdater* viewUpdater = nw::gfx::LookAtTargetViewUpdater::Create(&s_DeviceAllocator); nw::gfx::ResLookAtTargetViewUpdater resViewUpdater = nw::gfx::ResStaticCast( viewUpdater->GetResource()); resViewUpdater.SetTargetPosition(0.0f,0.0f,0.0f); resViewUpdater.SetUpwardVector(0.0f,1.0f,0.0f); nw::gfx::PerspectiveProjectionUpdater* projectionUpdater = nw::gfx::PerspectiveProjectionUpdater::Create(&s_DeviceAllocator); nw::gfx::ResPerspectiveProjectionUpdater resProjectionUpdater = nw::gfx::ResStaticCast( projectionUpdater->GetResource()); resProjectionUpdater.SetNear(s_NearPlane); resProjectionUpdater.SetFar(1000.0f); resProjectionUpdater.SetFovy(NW_MATH_DEG_TO_RAD(37.8f)); resProjectionUpdater.SetAspectRatio( static_cast(s_RenderTarget->GetDescription().height) / static_cast(s_RenderTarget->GetDescription().width)); s_BaseCamera = nw::gfx::Camera::DynamicBuilder() .MaxChildren(0) .MaxCallbacks(0) .ViewUpdater(viewUpdater) .ProjectionUpdater(projectionUpdater) .Create(&s_DeviceAllocator); NW_POINTER_ASSERT(s_BaseCamera); s_BaseCamera->Transform().SetTranslate(nw::math::VEC3(28.0f, 22.0f, 28.0f)); s_LeftCamera = nw::gfx::Camera::DynamicBuilder() .MaxChildren(0) .MaxCallbacks(0) .Create(&s_DeviceAllocator); NW_POINTER_ASSERT(s_LeftCamera); s_RightCamera = nw::gfx::Camera::DynamicBuilder() .MaxChildren(0) .MaxCallbacks(0) .Create(&s_DeviceAllocator); NW_POINTER_ASSERT(s_RightCamera); s_CameraController = nw::demo::CameraController::Builder() .MaxCameraCount(1) .Create(&s_DeviceAllocator); s_CameraController->Register(s_BaseCamera); } /*!--------------------------------------------------------------------------* @brief シーンを初期化します。 *---------------------------------------------------------------------------*/ void InitializeScenes() { BuildCameras(); NW_FOREACH(const wchar_t* name, MODEL_RESOURCE_FILES) { BuildResources(LoadResources(name)); } NW_GL_ASSERT(); s_FrameCount = 0; } /*!--------------------------------------------------------------------------* @brief シーン関連の後始末をします。 *---------------------------------------------------------------------------*/ void TerminateScenes() { nw::gfx::SafeDestroyBranchAll(s_ParticleModels); nw::gfx::SafeDestroyAll(s_ParticleEmitters); s_ParticleSets.clear(); nw::demo::SafeCleanupResources(s_Resources); nw::gfx::SafeDestroy(s_BaseCamera); nw::gfx::SafeDestroy(s_LeftCamera); nw::gfx::SafeDestroy(s_RightCamera); nw::gfx::SafeDestroy(s_CameraController); NW_GL_ASSERT(); s_Resources.clear(); } /*!--------------------------------------------------------------------------* @brief ノードを更新します。 *---------------------------------------------------------------------------*/ void UpdateNode(nw::gfx::TransformNode* node) { // 一度計算したら計算処理をスキップする。 // ただし、ツリー構造は正しく解釈されない。 if (node->Transform().IsEnabledFlags(nw::gfx::CalculatedTransform::FLAG_IS_DIRTY)) { s_WorldMatrixUpdater->UpdateBasic( &node->WorldMatrix(), &node->WorldTransform(), node->Transform(), nw::gfx::CalculatedTransform::Identity(), nw::gfx::CalculatedTransform::Identity()); node->Transform().DisableFlags(nw::gfx::CalculatedTransform::FLAG_IS_DIRTY); } } /*!--------------------------------------------------------------------------* @brief カメラを更新します。 *---------------------------------------------------------------------------*/ void UpdateCamera() { s_CameraController->Update(); UpdateNode(s_BaseCamera); s_BaseCamera->UpdateCameraMatrix(); // ステレオカメラの計算 NW_NULL_ASSERT(s_LeftCamera); NW_NULL_ASSERT(s_RightCamera); NW_NULL_ASSERT(s_BaseCamera); nn::math::MTX44& projOriginal = s_BaseCamera->ProjectionMatrix(); nn::math::MTX34& viewOriginal = s_BaseCamera->ViewMatrix(); nn::math::MTX44& projL = s_LeftCamera->ProjectionMatrix(); nn::math::MTX34& viewL = s_LeftCamera->ViewMatrix(); nn::math::MTX44& projR = s_RightCamera->ProjectionMatrix(); nn::math::MTX34& viewR = s_RightCamera->ViewMatrix(); // Ortho カメラでは別の処理を行う必要があります。 // 例として demo::RenderSystem::CalcStereoCamera() を参照してください。 const f32 DEPTH_LEVEL = 5.0f + s_NearPlane; const f32 DEPTH_RANGE = 1.0f; s_StereoCamera->CalculateMatrices( &projL, &viewL, &projR, &viewR, &projOriginal, &viewOriginal, DEPTH_LEVEL, DEPTH_RANGE, false); } /*!--------------------------------------------------------------------------* @brief 生成されたノードを更新します。 *---------------------------------------------------------------------------*/ void UpdateNodes() { // モデル更新 ParticleModelArray::iterator particleModelEnd = s_ParticleModels.end(); for (ParticleModelArray::iterator node = s_ParticleModels.begin(); node != particleModelEnd; ++node) { UpdateNode(*node); (*node)->UpdateParticleFrame(); } EmitterArray::iterator particleEmitterEnd = s_ParticleEmitters.end(); for (EmitterArray::iterator node = s_ParticleEmitters.begin(); node != particleEmitterEnd; ++node) { UpdateNode(*node); (*node)->UpdateParticleFrame(); (*node)->Emission(s_ParticleContext); } ParticleSetArray::iterator particleSetEnd = s_ParticleSets.end(); for (ParticleSetArray::iterator node = s_ParticleSets.begin(); node != particleSetEnd; ++node) { (*node)->UpdateParticles(s_ParticleContext); } } /*!--------------------------------------------------------------------------* @brief カメラ、ライト、フォグなどのシーン環境を設定します。 *---------------------------------------------------------------------------*/ void SetEnvironment() { nw::gfx::SceneEnvironment& sceneEnvironment = s_RenderContext->GetSceneEnvironment(); sceneEnvironment.SetCamera(s_BaseCameraIndex, s_BaseCamera); } /*!--------------------------------------------------------------------------* @brief シーンを更新します。 *---------------------------------------------------------------------------*/ void UpdateScene() { UpdateNodes(); UpdateCamera(); ++s_FrameCount; // デモでは少量のメモリしか扱わないので明示的にフラッシュする必要がある s_FlushCache->Execute(); } /*!--------------------------------------------------------------------------* @brief ビューに関連する更新処理を行います。 *---------------------------------------------------------------------------*/ void SubmitView() { ParticleModelArray::iterator particleModelEnd = s_ParticleModels.end(); for (ParticleModelArray::iterator node = s_ParticleModels.begin(); node != particleModelEnd; ++node) { nw::gfx::ResModel resModel = (*node)->GetResModel(); NW_ASSERT(resModel.IsValid()); if (!resModel.IsVisible()) { continue; } (*node)->UpdateModelViewMatrixAndNormalMatrix(s_BaseCamera->ViewMatrix(), false); } } /*!--------------------------------------------------------------------------* @brief シーンを描画します。 *---------------------------------------------------------------------------*/ void RenderScene() { s_RenderContext->SetRenderTarget(s_RenderTarget); s_RenderContext->SetActiveCamera(0); // 描画コマンドを生成します。 s_CommandListSwapper->StartCommandSave(); NW_GL_ASSERT(); s_RenderContext->ClearBuffer( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, nw::ut::FloatColor(0.5f, 0.5f, 0.5f, 1.0f), 1.0f); ParticleModelArray::iterator modelEnd = s_ParticleModels.end(); for (ParticleModelArray::iterator model = s_ParticleModels.begin(); model != modelEnd; ++model) { if ((*model)->IsVisible()) { nw::gfx::ResMeshArray resMeshes = (*model)->GetResMeshes(); nw::gfx::ResMeshArray::iterator meshEnd = resMeshes.end(); for(nw::gfx::ResMeshArray::iterator mesh = resMeshes.begin(); mesh != meshEnd; ++mesh) { if ((*model)->IsMeshVisible(*mesh)) { s_MeshRenderer->RenderMesh(*mesh, *model); } } } } NW_GL_ASSERT(); s_CommandListSwapper->EndCommandSave(); // 左目の描画を行います。 s_RenderContext->SetCameraMatrix(s_LeftCamera); s_CommandListSwapper->ReuseCommand(false); s_UpperSwapper->MakeTransferBufferCommand(s_RenderTarget, false); // 右目の描画を行います。 s_RenderContext->SetCameraMatrix(s_RightCamera); s_CommandListSwapper->ReuseCommand(false); s_ExtensionSwapper->MakeTransferBufferCommand(s_RenderTarget, false); s_CommandListSwapper->WaitDone(); s_CommandListSwapper->RunAsync(); } /*!--------------------------------------------------------------------------* @brief ディスプレイバッファをスワップして表示します。 *---------------------------------------------------------------------------*/ void PresentBuffer() { s_UpperSwapper->ActivateBuffer(); s_ExtensionSwapper->ActivateBuffer(); nngxSwapBuffers(NN_GX_DISPLAY0); glBindFramebuffer(GL_FRAMEBUFFER, 0); NW_GL_ASSERT(); nngxWaitVSync(NN_GX_DISPLAY0); } /*!--------------------------------------------------------------------------* @brief シーンをデモンストレーションします。 *---------------------------------------------------------------------------*/ void DemoScene() { NW_NULL_ASSERT(s_RenderTarget); InitializeScenes(); s_CommandListSwapper->RunAsync(); bool isContinuing = true; while ( isContinuing ) { nw::demo::DebugUtility::AdvanceAutoTestFrame(); nw::demo::PadFactory::GetPad()->Update(); UpdateScene(); SetEnvironment(); s_RenderContext->SetActiveCamera(s_BaseCameraIndex); SubmitView(); RenderScene(); s_RenderContext->ResetState(); PresentBuffer(); if (nw::demo::Utility::IsTerminating()) { isContinuing = false; } } TerminateScenes(); } } // namespace /*!--------------------------------------------------------------------------* @brief メイン関数です。 *---------------------------------------------------------------------------*/ void nnMain() { InitializeGraphicsSystem(); nw::demo::PadFactory::Initialize(&s_DeviceAllocator); NW_DEMO_TEST_LOOP(&s_DeviceAllocator, &s_DeviceAllocator, NULL) { InitializeGraphics(); s_FlushCache = nw::demo::FlushCache::Create(&s_DeviceAllocator); DemoScene(); // キャッシュフラッシュ用のクラスを破棄 nw::ut::SafeDestroy(s_FlushCache); TerminateGraphics(); } nw::demo::PadFactory::Finalize(); FinalizeGraphicsSystem(); }