1 /*---------------------------------------------------------------------------*
2   Project:  NintendoWare
3   File:     demo_GraphicsSystem.cpp
4 
5   Copyright (C)2009-2010 Nintendo Co., Ltd./HAL Laboratory, Inc.  All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law.  They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Revision: 25152 $
14  *---------------------------------------------------------------------------*/
15 
16 #include <nw/demo/demo_GraphicsSystem.h>
17 
18 #include <nw/demo/demo_DisplayBufferSwapper.h>
19 #include <nw/demo/demo_Memory.h>
20 #include <nw/demo/demo_Utility.h>
21 #include <nw/demo/demo_DebugUtility.h>
22 #include <nw/demo/demo_GraphicsDrawing.h>
23 #include <nw/demo/demo_CameraController.h>
24 #include <nw/demo/demo_GraphicsMemoryAllocator.h>
25 
26 #include <nw/gfx/gfx_IRenderTarget.h>
27 #include <nw/gfx/gfx_RenderContext.h>
28 #include <nw/gfx/gfx_RenderElement.h>
29 #include <nw/gfx/gfx_MeshRenderer.h>
30 #include <nw/gfx/gfx_ShaderProgram.h>
31 #include <nw/gfx/gfx_SceneNode.h>
32 #include <nw/gfx/gfx_Model.h>
33 #include <nw/gfx/gfx_FragmentLight.h>
34 #include <nw/gfx/gfx_SceneTraverser.h>
35 #include <nw/gfx/gfx_SceneContext.h>
36 #include <nw/gfx/gfx_WorldMatrixUpdater.h>
37 #include <nw/gfx/gfx_SkeletonUpdater.h>
38 #include <nw/gfx/gfx_SceneUpdater.h>
39 #include <nw/gfx/gfx_SceneInitializer.h>
40 #include <nw/gfx/gfx_Camera.h>
41 #include <nw/gfx/gfx_Fog.h>
42 #include <nw/gfx/gfx_IMaterialIdGenerator.h>
43 #include <nw/gfx/gfx_RelativeHashMaterialIdGenerator.h>
44 #include <nw/gfx/gfx_CommandUtil.h>
45 #include <nw/gfx/gfx_ParticleUtil.h>
46 
47 #include <nw/ut/ut_Color.h>
48 #include <nw/ut/ut_Foreach.h>
49 
50 #include <nw/dev.h>
51 
52 #include <nn/fs.h>
53 #include <nn/fnd.h>
54 #include <nn/gx.h>
55 
56 //! このマクロを有効にすると、ログ出力にレンダーキューのソート後の状態をレポートします。
57 //#define REPORT_RENDER_QUEUE_ENABLED
58 
59 //! このマクロを有効にすると、半透明以外の深度ソートを省略するようになります。
60 #define SORT_DEPTH_OF_TRANSLUCENT_MESH_ENABLED
61 
62 namespace nw
63 {
64 namespace demo
65 {
66 
67 const float RenderSystem::LoadMeterDescription::NANO_TO_MILLI = 1.0f / (1000.0f * 1000.0f);
68 const float RenderSystem::LoadMeterDescription::MILLI_TO_RATE = 1.0f / (1000.0f / 60.0f);
69 
70 namespace
71 {
72     GraphicsMemoryAllocator s_GraphicsMemoryAllocator;
73     nw::demo::DemoAllocator* s_DeviceAllocator;
74 }
75 
76 //----------------------------------------
AllocateGraphicsMemory(GLenum area,GLenum aim,GLuint id,GLsizei size)77 void* AllocateGraphicsMemory(GLenum area, GLenum aim, GLuint id, GLsizei size)
78 {
79 
80     void* buffer = s_GraphicsMemoryAllocator.Allocate(area, aim, id, size);
81 
82     // NOTE: GL の管理領域を nw::gfx に設定する必要がなくなり、このコードは不要となりました。
83 #if 0
84     if (s_GetGlSystemFlag)
85     {
86         if (size == nw::gfx::GlSystem::SHADER_MANAGER_BUFFER_SIZE)
87         {
88             nw::gfx::GlSystem::SetGlShManager( buffer );
89         }
90     }
91 #endif
92 
93     return buffer;
94 }
95 
96 //----------------------------------------
DeallocateGraphicsMemory(GLenum area,GLenum aim,GLuint id,void * addr)97 void DeallocateGraphicsMemory(GLenum area, GLenum aim, GLuint id, void* addr)
98 {
99     s_GraphicsMemoryAllocator.Deallocate(area, aim, id, addr);
100 }
101 
102 //----------------------------------------
103 RenderSystem*
Create(os::IAllocator * allocator,const Description & description)104 RenderSystem::Create(
105     os::IAllocator* allocator,
106     const Description& description
107 )
108 {
109     void* memory = allocator->Alloc(sizeof(RenderSystem));
110     RenderSystem* renderSystem = new(memory) RenderSystem();
111 
112     renderSystem->m_RenderContext = gfx::RenderContext::Builder()
113         .Create(allocator);
114 
115 #ifdef SORT_DEPTH_OF_TRANSLUCENT_MESH_ENABLED
116     // 半透明以外のメッシュで深度情報の量子化を行わなくするための
117     // レンダーキー・ファクトリクラスを生成します。
118     renderSystem->m_PriorMaterialRenderKeyFactory =
119         gfx::CreatePriorMaterialAndZeroDepthRenderKeyFactory(allocator);
120 #else
121     renderSystem->m_PriorMaterialRenderKeyFactory =
122         gfx::CreatePriorMaterialRenderKeyFactory(allocator);
123 #endif
124 
125     renderSystem->m_PriorDepthRenderKeyFactory =
126         gfx::CreatePriorDepthReverseDepthRenderKeyFactory(allocator);
127 
128     renderSystem->m_RenderQueue = gfx::RenderQueue::Builder()
129         .MaxRenderElements(256)
130         .Create(allocator);
131 
132     renderSystem->m_RenderQueue->Reset(
133         renderSystem->m_PriorMaterialRenderKeyFactory,
134         renderSystem->m_PriorDepthRenderKeyFactory,
135         renderSystem->m_PriorMaterialRenderKeyFactory,
136         renderSystem->m_PriorMaterialRenderKeyFactory);
137 
138     renderSystem->m_MeshRenderer = gfx::MeshRenderer::Create(allocator);
139     renderSystem->m_MeshRenderer->SetRenderContext(renderSystem->m_RenderContext);
140 
141 
142     if (nngxInitialize(AllocateGraphicsMemory, DeallocateGraphicsMemory) == GL_FALSE)
143     {
144         NW_FATAL_ERROR("nngxInitialize failed.\n");
145     }
146 
147     // コマンドリスト
148     nw::demo::CommandListSwapper::Description swapperDescription;
149     swapperDescription.commandListCount = description.commandListCount;
150     swapperDescription.bufferSize = description.commandBufferSize;
151     swapperDescription.requestCount = description.commandRequestCount;
152     swapperDescription.reusableBufferSize = description.reusableCommandBufferSize;
153     swapperDescription.reusableRequestCount = description.reusableCommandRequestCount;
154     swapperDescription.maxGpuProfilingEntryCount = description.maxGpuProfilingEntryCount;
155     renderSystem->m_CommandListSwapper =
156         nw::demo::CommandListSwapper::Create(allocator, swapperDescription);
157 
158     NW_POINTER_ASSERT(renderSystem->m_CommandListSwapper);
159 
160     renderSystem->m_CommandListSwapper->Bind();
161     renderSystem->m_CommandListSwapper->RunAsync();
162 
163     nngxStartLcdDisplay();
164 
165 
166     renderSystem->m_UpperSwapper = DisplayBufferSwapper::Builder()
167         .BufferDescription(description.upperScreenDescription)
168         .BufferCount(description.displayBufferCount)
169         .Create(allocator);
170 
171     renderSystem->m_LowerSwapper = DisplayBufferSwapper::Builder()
172         .BufferDescription(description.lowerScreenDescription)
173         .BufferCount(description.displayBufferCount)
174         .Create(allocator);
175 
176     nngxSetDisplayMode(description.upperScreenMode);
177 
178     // ステレオモードの場合は上画面のディスプレイバッファを追加で生成する
179     if (description.upperScreenMode == UPPER_SCREEN_MODE_STEREO)
180     {
181         nw::demo::DisplayBufferSwapper::Description extensionScreenDescription;
182         extensionScreenDescription.screenKind = nw::demo::EXTENSION_SCREEN;
183         extensionScreenDescription.width  = description.upperScreenDescription.width;
184         extensionScreenDescription.height = description.upperScreenDescription.height;
185 
186         renderSystem->m_ExtensionSwapper = DisplayBufferSwapper::Builder()
187             .BufferDescription(extensionScreenDescription)
188             .BufferCount(description.displayBufferCount)
189             .Create(allocator);
190 
191         // ステレオカメラを生成する
192         void* cameraMemory = allocator->Alloc(sizeof(nn::ulcd::CTR::StereoCamera));
193         nn::ulcd::CTR::StereoCamera* stereoCamera = new(cameraMemory) nn::ulcd::CTR::StereoCamera();
194         stereoCamera->Initialize();
195         renderSystem->m_StereoCamera = stereoCamera;
196     }
197 
198     NW_GL_ASSERT();
199 
200     renderSystem->m_Allocator = allocator;
201 
202     return renderSystem;
203 }
204 
205 //----------------------------------------
206 void
LoadSkyModel(const wchar_t * fileName)207 RenderSystem::LoadSkyModel(const wchar_t* fileName)
208 {
209     ResourceSet* resourceSet = Utility::LoadResources(m_Resources, fileName, m_Allocator);
210 
211     resourceSet->resource.ForeachTexture(nw::gfx::TextureLocationFlagSetter(NN_GX_MEM_VRAMA | GL_NO_COPY_FCRAM_DMP));
212     resourceSet->resource.ForeachIndexStream(nw::gfx::IndexStreamLocationFlagSetter(NN_GX_MEM_VRAMB | GL_NO_COPY_FCRAM_DMP));
213     resourceSet->resource.ForeachVertexStream(nw::gfx::VertexStreamLocationFlagSetter(NN_GX_MEM_VRAMB | GL_NO_COPY_FCRAM_DMP));
214 
215     nw::gfx::Result result = resourceSet->resource.Setup(m_Allocator);
216     if (result.IsFailure())
217     {
218         NW_FATAL_ERROR("Fail to set up model. A result code is 0x%x", result.GetCode());
219     }
220 
221     // モデルのインスタンスを生成します。
222     nw::gfx::ResModelArray models = resourceSet->resource.GetModels();
223     nw::gfx::SceneNode* node = nw::demo::Utility::CreateSceneNode(
224         m_Allocator,
225         *(models.begin()),
226         false
227     );
228 
229     nw::gfx::Model* model = nw::ut::DynamicCast<nw::gfx::Model*>(node);
230     NW_NULL_ASSERT(model);
231 
232     m_SkyModel = model;
233 }
234 
235 //----------------------------------------
236 void
Destroy()237 RenderSystem::Destroy()
238 {
239     os::IAllocator* allocator = this->m_Allocator;
240 
241     gfx::SafeDestroy(this->m_SkyModel);
242     nw::demo::SafeCleanupResources(m_Resources);
243     m_Resources.clear();
244 
245     if (this->m_StereoCamera)
246     {
247         m_StereoCamera->Finalize();
248         allocator->Free(m_StereoCamera);
249     }
250 
251     gfx::SafeDestroy(this->m_ExtensionSwapper);
252 
253     gfx::SafeDestroy(this->m_LowerSwapper);
254     gfx::SafeDestroy(this->m_UpperSwapper);
255     gfx::SafeDestroy(this->m_MeshRenderer);
256     gfx::SafeDestroy(this->m_RenderQueue);
257     gfx::SafeDestroy(this->m_PriorMaterialRenderKeyFactory);
258     gfx::SafeDestroy(this->m_PriorDepthRenderKeyFactory);
259     gfx::SafeDestroy(this->m_RenderContext);
260     gfx::SafeDestroy(this->m_CommandListSwapper);
261 
262     nngxFinalize();
263     // TODO: グラフィックスメモリアロケーターのデアロケータを実装する
264     s_GraphicsMemoryAllocator.Finalize();
265     NW_GL_ASSERT();
266 
267     void* memory = static_cast<void*>(this);
268     this->~RenderSystem();
269 
270     allocator->Free(memory);
271 }
272 
273 //----------------------------------------
274 void
SetRenderTarget(gfx::IRenderTarget * renderTarget)275 RenderSystem::SetRenderTarget(gfx::IRenderTarget* renderTarget)
276 {
277     gfx::RenderContext* renderContext = this->GetRenderContext();
278     renderContext->SetRenderTarget(renderTarget);
279 }
280 
281 //----------------------------------------
282 void
SetRenderSortMode(gfx::ISceneUpdater::RenderSortMode renderSortMode)283 RenderSystem::SetRenderSortMode(gfx::ISceneUpdater::RenderSortMode renderSortMode)
284 {
285     this->m_RenderSortMode = renderSortMode;
286 
287     gfx::SafeDestroy(this->m_PriorDepthRenderKeyFactory);
288     if (this->m_RenderSortMode == gfx::ISceneUpdater::OPAQUE_MESH_BASE_AND_TRANSLUCENT_MODEL_BASE_SORT)
289     {
290         this->m_PriorDepthRenderKeyFactory =
291             gfx::CreateTopPriorDepthReverseDepthRenderKeyFactory(this->m_Allocator);
292     }
293     else
294     {
295         this->m_PriorDepthRenderKeyFactory =
296             gfx::CreatePriorDepthReverseDepthRenderKeyFactory(this->m_Allocator);
297     }
298 
299     NW_NULL_ASSERT(this->m_PriorDepthRenderKeyFactory);
300 }
301 
302 //----------------------------------------
303 struct RenderSceneInternalFunctor
304 : public std::unary_function<gfx::RenderElement&, void>
305 {
306     gfx::RenderContext* m_RenderContext;
307     gfx::MeshRenderer* m_MeshRenderer;
308 
RenderSceneInternalFunctornw::demo::RenderSceneInternalFunctor309     RenderSceneInternalFunctor(gfx::RenderContext* renderContext, gfx::MeshRenderer* meshRenderer)
310     : m_RenderContext(renderContext), m_MeshRenderer(meshRenderer)
311     {
312         NW_NULL_ASSERT(renderContext);
313         NW_NULL_ASSERT(meshRenderer);
314     }
315 
operator ()nw::demo::RenderSceneInternalFunctor316     void operator()(gfx::RenderElement& element)
317     {
318         if (element.IsCommand())
319         {
320             gfx::RenderCommand* command = element.GetCommand();
321             NW_NULL_ASSERT(command);
322             command->Invoke(this->m_RenderContext);
323         }
324         else
325         {
326             gfx::ResMesh mesh = element.GetMesh();
327             gfx::Model* model = element.GetModel();
328             model->PreRenderSignal()(model, mesh, this->m_RenderContext);
329             this->m_MeshRenderer->RenderMesh(mesh, model);
330             model->PostRenderSignal()(model, mesh, this->m_RenderContext);
331         }
332         NW_GL_ASSERT();
333     }
334 };
335 
336 //----------------------------------------
337 void
SetSceneEnvironmentSettings(SceneSystem * sceneSystem,ut::MoveArray<gfx::SceneEnvironmentSetting * > * sceneEnvironmentSettings)338 RenderSystem::SetSceneEnvironmentSettings(
339     SceneSystem* sceneSystem,
340     ut::MoveArray<gfx::SceneEnvironmentSetting*>* sceneEnvironmentSettings
341 )
342 {
343     gfx::RenderContext* renderContext = this->GetRenderContext();
344     gfx::SceneContext* sceneContext = sceneSystem->GetSceneContext();
345     gfx::SceneEnvironment& sceneEnvironment = renderContext->GetSceneEnvironment();
346 
347     ut::MoveArray<gfx::SceneEnvironmentSetting*>::iterator end = sceneEnvironmentSettings->end();
348     for (ut::MoveArray<gfx::SceneEnvironmentSetting*>::iterator sceneEnvironmentSetting = sceneEnvironmentSettings->begin();
349         sceneEnvironmentSetting != end;
350         ++sceneEnvironmentSetting)
351     {
352         (*sceneEnvironmentSetting)->ResolveReference(*sceneContext);
353         sceneEnvironment.ApplyFrom(**sceneEnvironmentSetting);
354     }
355 }
356 
357 //----------------------------------------
358 void
SetEnvironment(SceneSystem * sceneSystem)359 RenderSystem::SetEnvironment(
360     SceneSystem* sceneSystem
361 )
362 {
363     NW_PROFILE("RenderSystem::SetEnvironment");
364 
365     gfx::RenderContext* renderContext = this->GetRenderContext();
366     gfx::SceneContext* sceneContext = sceneSystem->GetSceneContext();
367     gfx::SceneEnvironment& sceneEnvironment = renderContext->GetSceneEnvironment();
368 
369     if (sceneContext->GetAmbientLightsBegin() != sceneContext->GetAmbientLightsEnd())
370     {
371         sceneEnvironment.SetAmbientLight(*sceneContext->GetAmbientLightsBegin());
372     }
373 
374     if (sceneContext->GetHemiSphereLightsBegin() != sceneContext->GetHemiSphereLightsEnd())
375     {
376         sceneEnvironment.SetHemiSphereLight(*sceneContext->GetHemiSphereLightsBegin());
377     }
378 
379     std::for_each(
380         sceneContext->GetFragmentLightsBegin(),
381         sceneContext->GetFragmentLightsEnd(),
382         std::bind1st(std::mem_fun(&gfx::SceneEnvironment::SetFragmentLight), &sceneEnvironment));
383 
384     std::for_each(
385         sceneContext->GetVertexLightsBegin(),
386         sceneContext->GetVertexLightsEnd(),
387         std::bind1st(std::mem_fun(&gfx::SceneEnvironment::SetVertexLight), &sceneEnvironment));
388 
389     if (sceneContext->GetFogBegin() != sceneContext->GetFogEnd())
390     {
391         sceneEnvironment.SetFog(0, *sceneContext->GetFogBegin());
392     }
393 }
394 
395 //----------------------------------------
396 void
SubmitView(SceneSystem * sceneSystem)397 RenderSystem::SubmitView(
398     SceneSystem* sceneSystem
399 )
400 {
401     NW_PROFILE("RenderSystem::SubmitView");
402 
403     nw::gfx::Camera* camera = this->GetRenderContext()->GetActiveCamera();
404     gfx::SceneContext* sceneContext = sceneSystem->GetSceneContext();
405 
406     //----------------------------------------
407     // フォグを更新します。
408     std::for_each(sceneContext->GetFogBegin(), sceneContext->GetFogEnd(), gfx::Fog::UpdateFunctor(camera));
409 
410     //----------------------------------------
411     // カメラに依存する処理と、描画要素をレンダーキューに積みます。
412 
413     #ifdef SORT_DEPTH_OF_TRANSLUCENT_MESH_ENABLED
414     // RenderQueue::Reset の引数に true を指定すると、ソート用キーをキャッシュする最適化機能が
415     // 有効になります。
416     // 半透明以外のソート用キーのみキャッシュされるようになります。
417     this->m_RenderQueue->Reset(true);
418 
419     // 半透明メッシュの深度のみ計算して、半透明メッシュのみ深度によるソートがされるようになります。
420     // 深度計算の処理負荷の低減を期待できます。
421     sceneSystem->GetSceneUpdater()->SetDepthSortMode(gfx::ISceneUpdater::SORT_DEPTH_OF_TRANSLUCENT_MESH);
422     #else
423     this->m_RenderQueue->Reset();
424 
425     // すべてのメッシュの深度を計算して、深度によってもソートされるようになります。
426     // 不透明物体等を手前から描画することで、GPUのフィル負荷の低減を期待できます。
427     sceneSystem->GetSceneUpdater()->SetDepthSortMode(gfx::ISceneUpdater::SORT_DEPTH_OF_ALL_MESH);
428     #endif
429 
430     sceneSystem->GetSceneUpdater()->SubmitView(
431         this->m_RenderQueue,
432         sceneContext,
433         *camera,
434         0,
435         1,
436         this->m_RenderSortMode);
437 
438     //----------------------------------------
439     // レンダーキューをソートします。
440     // RenderElementCompare() やレンダーキーファクトリーをカスタマイズすることで
441     // 描画順を変更することができます。
442     // 詳しくは最適化TIPSを参照してください。
443     std::sort(
444         this->m_RenderQueue->Begin(),
445         this->m_RenderQueue->End(),
446         gfx::RenderElementCompare());
447 
448 #ifdef REPORT_RENDER_QUEUE_ENABLED
449     // レンダーキューのソート後の状態をレポートします。
450     // リリース版ではなにも表示されなくなっています。
451     std::for_each(
452         this->m_RenderQueue->Begin(),
453         this->m_RenderQueue->End(),
454         gfx::RenderQueue::ReportFunctor());
455 #endif
456 }
457 
458 //----------------------------------------
459 void
RenderScene(gfx::Camera * camera,s32 screenKind,bool isCommandCacheDump)460 RenderSystem::RenderScene(
461     gfx::Camera* camera,
462     s32 screenKind,
463     bool isCommandCacheDump
464 )
465 {
466     NW_PROFILE("RenderSystem::RenderScene");
467 
468     gfx::RenderContext* renderContext = this->GetRenderContext();
469     gfx::SceneEnvironment& sceneEnvironment = renderContext->GetSceneEnvironment();
470 
471     NW_GL_ASSERT();
472 
473     // GPU処理時間計測開始
474     int profilingId = m_CommandListSwapper->AddGpuProfilingStartPoint(true);
475     NW_ASSERT(profilingId >= 0);
476     int cmdBufferSize = m_CommandListSwapper->GetCommandBufferSize();
477 
478     renderContext->SetCameraMatrix(camera);
479 
480     if (m_SkyModel != NULL)
481     {
482         this->ClearBySkyModel(camera);
483     }
484     else
485     {
486         this->ClearBuffer(nw::ut::FloatColor(0.5f, 0.5f, 0.5f, 1.0f));
487     }
488 
489     if (isCommandCacheDump)
490     {
491         nw::gfx::internal::CommandCacheBuilder builder;
492         builder.Begin();
493 
494         std::for_each(
495             this->m_RenderQueue->Begin(),
496             this->m_RenderQueue->End(),
497             RenderSceneInternalFunctor(renderContext, this->m_MeshRenderer));
498 
499         builder.End();
500         builder.Report();
501     }
502     else
503     {
504         std::for_each(
505             this->m_RenderQueue->Begin(),
506             this->m_RenderQueue->End(),
507             RenderSceneInternalFunctor(renderContext, this->m_MeshRenderer));
508     }
509 
510     m_LoadMeterDescription.drawCommandBufferSize =
511         m_CommandListSwapper->GetCommandBufferSize() - cmdBufferSize;
512     m_CumulativeLoadMeterDescription.drawCommandBufferSize =
513         m_CommandListSwapper->GetCommandBufferSize() - cmdBufferSize;
514 
515     // GPU処理時間計測終了
516     nngxSplitDrawCmdlist();
517 
518     m_CommandListSwapper->SetGpuProfilingEndPoint(profilingId);
519 
520     TransferBuffer(screenKind);
521 }
522 
523 //----------------------------------------
524 void
RenderStereoScene(gfx::Camera * leftCamera,gfx::Camera * rightCamera,bool isCommandCacheDump)525 RenderSystem::RenderStereoScene(
526     gfx::Camera* leftCamera,
527     gfx::Camera* rightCamera,
528     bool isCommandCacheDump
529 )
530 {
531     NW_PROFILE("RenderSystem::RenderStereoScene");
532 
533     gfx::RenderContext* renderContext = this->GetRenderContext();
534     gfx::SceneEnvironment& sceneEnvironment = renderContext->GetSceneEnvironment();
535 
536     //----------------------------------------
537     // 再利用するためのコマンドを作成し、保存します。
538     //
539     // ベースカメラを元にしたシーンを描画します。
540     m_CommandListSwapper->StartCommandSave();
541     int cmdBufferSize = m_CommandListSwapper->GetCommandBufferSize();
542 
543     if (m_SkyModel != NULL)
544     {
545         this->ClearBySkyModel(renderContext->GetActiveCamera());
546     }
547     else
548     {
549         this->ClearBuffer(nw::ut::FloatColor(0.5f, 0.5f, 0.5f, 1.0f));
550     }
551 
552     if (isCommandCacheDump)
553     {
554         nw::gfx::internal::CommandCacheBuilder builder;
555         builder.Begin();
556 
557         std::for_each(
558             this->m_RenderQueue->Begin(),
559             this->m_RenderQueue->End(),
560             RenderSceneInternalFunctor(renderContext, this->m_MeshRenderer));
561 
562         builder.End();
563         builder.Report();
564     }
565     else
566     {
567         std::for_each(
568             this->m_RenderQueue->Begin(),
569             this->m_RenderQueue->End(),
570             RenderSceneInternalFunctor(renderContext, this->m_MeshRenderer));
571     }
572 
573     m_LoadMeterDescription.drawCommandBufferSize =
574         m_CommandListSwapper->GetCommandBufferSize() - cmdBufferSize;
575 
576     m_CommandListSwapper->EndCommandSave();
577 
578     //----------------------------------------
579     // 左目用と右目用に保存したコマンドを再利用します。
580 
581     // GPU処理時間計測開始
582     int profilerLeft = m_CommandListSwapper->AddGpuProfilingStartPoint(true);
583     NW_ASSERT(profilerLeft >= 0);
584 
585     // 左目用
586     renderContext->SetCameraMatrix(leftCamera);
587     m_CommandListSwapper->ReuseCommand(false);
588 
589     // GPU処理時間計測終了
590     m_CommandListSwapper->SetGpuProfilingEndPoint(profilerLeft);
591 
592     TransferBuffer(UPPER_SCREEN);
593     NW_GL_ASSERT();
594 
595     // GPU処理時間計測開始
596     int profilerRight = m_CommandListSwapper->AddGpuProfilingStartPoint(true);
597     NW_ASSERT(profilerRight >= 0);
598 
599     // 右目用
600     renderContext->SetCameraMatrix(rightCamera);
601     m_CommandListSwapper->ReuseCommand(false);
602 
603     // GPU処理時間計測終了
604     m_CommandListSwapper->SetGpuProfilingEndPoint(profilerRight);
605 
606     TransferBuffer(EXTENSION_SCREEN);
607     NW_GL_ASSERT();
608 }
609 
610 //----------------------------------------
611 void
ClearBuffer(nw::ut::FloatColor clearColor)612 RenderSystem::ClearBuffer( nw::ut::FloatColor clearColor )
613 {
614     if (!nw::demo::DebugUtility::IsCpuProfilingMode())
615     {
616         this->SuspendLoadMeter();
617 
618         this->m_RenderContext->ClearBuffer(
619             GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT,
620             clearColor,
621             1.0f);
622         NW_GL_ASSERT();
623 
624         this->ResumeLoadMeter();
625     }
626 }
627 
628 //----------------------------------------
629 void
ClearBySkyModel(const gfx::Camera * camera)630 RenderSystem::ClearBySkyModel(const gfx::Camera* camera)
631 {
632     NW_NULL_ASSERT(camera);
633 
634     if (m_SkyModel != NULL)
635     {
636         math::VEC3 translate = camera->WorldMatrix().GetColumn(3);
637         m_SkyModel->WorldMatrix()._03 = translate.x;
638         m_SkyModel->WorldMatrix()._13 = translate.y;
639         m_SkyModel->WorldMatrix()._23 = translate.z;
640 
641         // Far の半分まで SkyModel を拡大します。
642         float scale = (camera->GetFar() / 2.0f);
643         m_SkyModel->WorldMatrix()._00 = scale;
644         m_SkyModel->WorldMatrix()._11 = scale;
645         m_SkyModel->WorldMatrix()._22 = scale;
646 
647         gfx::ResMeshArray meshArray = m_SkyModel->GetResMeshes();
648         gfx::ResMeshArray::iterator endMesh = meshArray.end();
649         for (gfx::ResMeshArray::iterator iter = meshArray.begin();
650             iter != endMesh;
651             ++iter)
652         {
653             this->m_MeshRenderer->RenderMesh(*iter, m_SkyModel);
654         }
655     }
656 }
657 
658 //----------------------------------------
659 void
TransferBuffer(s32 screenKind)660 RenderSystem::TransferBuffer(
661     s32 screenKind
662 )
663 {
664     gfx::RenderContext* renderContext = this->GetRenderContext();
665 
666     // コマンドリストが多重化されている場合、
667     // ディスプレイバッファへの転送するタイミングが変わるため、
668     // 転送先のディスプレイバッファを変更する必要があります。
669     // 詳細は PresentBuffer() のコメントを参照してください。
670 
671     bool isMultiCommandList = m_CommandListSwapper->GetListCount() > 1;
672 
673     if (screenKind & UPPER_SCREEN)
674     {
675         this->m_UpperSwapper->MakeTransferBufferCommand(
676             renderContext->GetRenderTarget(),
677             isMultiCommandList);
678     }
679 
680     if (screenKind & LOWER_SCREEN)
681     {
682         this->m_LowerSwapper->MakeTransferBufferCommand(
683             renderContext->GetRenderTarget(),
684             isMultiCommandList);
685     }
686 
687     if (screenKind & EXTENSION_SCREEN)
688     {
689         this->m_ExtensionSwapper->MakeTransferBufferCommand(
690             renderContext->GetRenderTarget(),
691             isMultiCommandList);
692     }
693 }
694 
695 //----------------------------------------
696 void
WaitCommandList()697 RenderSystem::WaitCommandList()
698 {
699     this->SuspendLoadMeter();
700 
701     NW_PROFILE("RenderSystem::WaitCommandList");
702 
703     nn::os::Tick startTick = nn::os::Tick::GetSystemCurrent();
704 
705     m_CommandListSwapper->WaitDone();
706 
707     s64 span = static_cast<nn::os::Tick>(nn::os::Tick::GetSystemCurrent() - startTick).ToTimeSpan().GetNanoSeconds();
708     m_LoadMeterDescription.cumulativeWaitTime +=
709         static_cast<float>(span) * LoadMeterDescription::NANO_TO_MILLI;
710 
711     this->ResumeLoadMeter();
712 }
713 
714 //----------------------------------------
715 void
RunCommandList()716 RenderSystem::RunCommandList()
717 {
718     m_CommandListSwapper->RunAsync();
719 }
720 
721 //----------------------------------------
722 void
SwapCommandList()723 RenderSystem::SwapCommandList()
724 {
725     m_CommandListSwapper->Swap();
726     m_CommandListSwapper->Bind();
727 }
728 
729 //----------------------------------------
730 void
WaitVSync(s32 screenKind)731 RenderSystem::WaitVSync(s32 screenKind)
732 {
733     NW_MAX_ASSERT(screenKind, (int)BOTH_SCREENS);
734 
735     GLenum display;
736     if (screenKind & (UPPER_SCREEN | LOWER_SCREEN)) { display = NN_GX_DISPLAY_BOTH; }
737     else if (screenKind & UPPER_SCREEN) { display = NN_GX_DISPLAY0; }
738     else if (screenKind & LOWER_SCREEN) { display = NN_GX_DISPLAY1; }
739 
740 
741     EndLoadMeter();
742 
743     {
744         NW_PROFILE("WaitVSync");
745         nngxWaitVSync(display);
746     }
747 
748     BeginLoadMeter();
749 }
750 
751 //----------------------------------------
752 void
SwapBuffer(s32 screenKind)753 RenderSystem::SwapBuffer(
754     s32 screenKind
755 )
756 {
757     GLenum display;
758     if ((screenKind & NORMAL_SCREENS) == NORMAL_SCREENS)
759     {
760         this->m_UpperSwapper->ActivateBuffer();
761         this->m_LowerSwapper->ActivateBuffer();
762 
763         if (screenKind & EXTENSION_SCREEN)
764         {
765             this->m_ExtensionSwapper->ActivateBuffer();
766         }
767 
768         display = NN_GX_DISPLAY_BOTH;
769     }
770     else if (screenKind & UPPER_SCREEN)
771     {
772         this->m_UpperSwapper->ActivateBuffer();
773 
774         if (screenKind & EXTENSION_SCREEN)
775         {
776             this->m_ExtensionSwapper->ActivateBuffer();
777         }
778 
779         display = NN_GX_DISPLAY0;
780     }
781     else if (screenKind & LOWER_SCREEN)
782     {
783         this->m_LowerSwapper->ActivateBuffer();
784 
785         display = NN_GX_DISPLAY1;
786     }
787 
788     nngxSwapBuffers(display);
789     glBindFramebuffer(GL_FRAMEBUFFER, 0);
790 }
791 
792 //----------------------------------------
793 void
PresentBuffer(s32 screenKind)794 RenderSystem::PresentBuffer(s32 screenKind)
795 {
796     // コマンドリストが多重化されているかどうかで、
797     // 転送するディスプレイバッファを変更したり、
798     // コマンドリスト終了待ちのタイミングを変えたりする必要があります。
799     // これらを正しく行わない場合、テアリングなどが発生することがあります。
800     if (m_CommandListSwapper->GetListCount() > 1)
801     {
802         // コマンドリストが多重化されている場合、
803         // この関数が呼ばれるまでに蓄積されたコマンドが実行( RunCommandList() )されるのは
804         // VSync とディスプレイバッファのスワップが行われた後になります。
805         //
806         // レンダーバッファからディスプレイバッファへの転送もコマンドとして蓄積され、
807         // 同じくディスプレイバッファのスワップ後に実行されます。
808         // そのため、この関数内で呼ばれるSwapBuffer()により表示されるディスプレイバッファへの転送をしてしまうと、
809         // 表示中のディスプレイバッファへの転送が実行され、テアリングが発生します。
810         //
811         // これを解決するため、ディスプレイバッファへの転送コマンドを蓄積する場合、
812         // その時点を基準に2回ディスプレイバッファのスワップが行われたときに
813         // 表示されるバッファへ転送する必要があります。
814         // DisplayBufferSwapper::MakeTransferBufferCommand() の第2引数にtrueを指定することで、
815         // 内部的にこのような転送先の調整が行われます。
816         WaitCommandList();
817         SwapBuffer(screenKind);
818         WaitVSync(screenKind);
819         RunCommandList();
820         SwapCommandList();
821     }
822     else
823     {
824         RunCommandList();
825         WaitCommandList();
826         SwapBuffer(screenKind);
827         WaitVSync(screenKind);
828     }
829 }
830 
831 //----------------------------------------
832 void
CalcStereoCamera(gfx::Camera * leftCamera,gfx::Camera * rightCamera,gfx::Camera * baseCamera,f32 depthLevel,f32 depthRange)833 RenderSystem::CalcStereoCamera(
834     gfx::Camera* leftCamera,
835     gfx::Camera* rightCamera,
836     gfx::Camera* baseCamera,
837     f32 depthLevel,
838     f32 depthRange)
839 {
840     NW_NULL_ASSERT(leftCamera);
841     NW_NULL_ASSERT(rightCamera);
842     NW_NULL_ASSERT(baseCamera);
843     nn::math::MTX44& projOriginal = baseCamera->ProjectionMatrix();
844     nn::math::MTX34& viewOriginal = baseCamera->ViewMatrix();
845     nn::math::MTX44& projL = leftCamera->ProjectionMatrix();
846     nn::math::MTX34& viewL = leftCamera->ViewMatrix();
847     nn::math::MTX44& projR = rightCamera->ProjectionMatrix();
848     nn::math::MTX34& viewR = rightCamera->ViewMatrix();
849 
850     // Orthoカメラをステレオ表示する場合は自分で絵を用意しなければいけません。
851     if (baseCamera->GetResCamera().GetProjectionType() == gfx::ResCamera::PROJTYPE_ORTHO)
852     {
853         // 現在はOrthoカメラのステレオ表示には未対応です。
854         math::MTX44Copy(&projL, &projOriginal);
855         math::MTX34Copy(&viewL, &viewOriginal);
856         math::MTX44Copy(&projR, &projOriginal);
857         math::MTX34Copy(&viewR, &viewOriginal);
858 
859         // CalculateMatrices(....)で画面にあわせての回転を行っているので、
860         // Orthoの場合はpivotを設定します。
861         baseCamera->GetProjectionUpdater()->SetPivotDirection(math::PIVOT_UPSIDE_TO_TOP);
862         leftCamera->GetProjectionUpdater()->SetPivotDirection(math::PIVOT_UPSIDE_TO_TOP);
863         rightCamera->GetProjectionUpdater()->SetPivotDirection(math::PIVOT_UPSIDE_TO_TOP);
864     }
865     else
866     {
867         m_StereoCamera->CalculateMatrices(
868             &projL,
869             &viewL,
870             &projR,
871             &viewR,
872             &projOriginal,
873             &viewOriginal,
874             depthLevel,
875             depthRange,
876             false);
877     }
878 }
879 
880 //----------------------------------------
881 void
BeginLoadMeter()882 RenderSystem::BeginLoadMeter()
883 {
884     m_LoadMeterDescription.startTick = nn::os::Tick::GetSystemCurrent();
885     m_LoadMeterDescription.drawCommandBufferSize = 0;
886 }
887 
888 //----------------------------------------
889 void
EndLoadMeter()890 RenderSystem::EndLoadMeter()
891 {
892     nn::os::Tick endTick = nn::os::Tick::GetSystemCurrent();
893 
894     s64 span = static_cast<nn::os::Tick>(endTick - m_LoadMeterDescription.startTick).ToTimeSpan().GetNanoSeconds();
895 
896     float currentTime         = static_cast<float>(span) * LoadMeterDescription::NANO_TO_MILLI;
897     float currentGpuTime      = static_cast<float>(this->m_CommandListSwapper->GetGpuProfilingTotalCostTime());
898 
899     m_LoadMeterDescription.cumulativeTime += currentTime;
900     m_LoadMeterDescription.cumulativeGpuTime += currentGpuTime;
901 
902     m_CumulativeLoadMeterDescription.cumulativeTime += currentTime;
903     m_CumulativeLoadMeterDescription.cumulativeGpuTime += currentGpuTime;
904 
905     m_CumulativeLoadMeterDescription.drawCommandBufferSize = m_LoadMeterDescription.drawCommandBufferSize;
906 
907     ++m_LoadMeterDescription.callCount;
908     ++m_CumulativeLoadMeterDescription.callCount;
909 }
910 
911 //----------------------------------------
912 void
CalcLoadMeter()913 RenderSystem::CalcLoadMeter()
914 {
915     float callCount = static_cast<float>(m_LoadMeterDescription.callCount);
916     m_LoadMeterDescription.loadTime = m_LoadMeterDescription.cumulativeTime / callCount;
917     m_LoadMeterDescription.loadGpuTime = m_LoadMeterDescription.cumulativeGpuTime / callCount;
918     m_LoadMeterDescription.waitTime = m_LoadMeterDescription.cumulativeWaitTime / callCount;
919     m_LoadMeterDescription.cumulativeTime = 0.0f;
920     m_LoadMeterDescription.cumulativeGpuTime = 0.0f;
921     m_LoadMeterDescription.cumulativeWaitTime = 0.0f;
922     m_LoadMeterDescription.callCount = 0;
923 }
924 
925 //----------------------------------------
926 void
SuspendLoadMeter()927 RenderSystem::SuspendLoadMeter()
928 {
929     nn::os::Tick endTick = nn::os::Tick::GetSystemCurrent();
930 
931     s64 span = static_cast<nn::os::Tick>(endTick - m_LoadMeterDescription.startTick).ToTimeSpan().GetNanoSeconds();
932     m_LoadMeterDescription.cumulativeTime +=
933         static_cast<float>(span) * LoadMeterDescription::NANO_TO_MILLI;
934     m_CumulativeLoadMeterDescription.cumulativeTime +=
935         static_cast<float>(span) * LoadMeterDescription::NANO_TO_MILLI;
936 
937     m_LoadMeterDescription.startTick = 0;
938 }
939 
940 //----------------------------------------
941 void
ResumeLoadMeter()942 RenderSystem::ResumeLoadMeter()
943 {
944     m_LoadMeterDescription.startTick = nn::os::Tick::GetSystemCurrent();
945 }
946 
947 //----------------------------------------
948 void
DrawLoadMeter(GraphicsDrawing & graphicsDrawing,s32 positionX,s32 positionY)949 RenderSystem::DrawLoadMeter(
950     GraphicsDrawing& graphicsDrawing,
951     s32 positionX,
952     s32 positionY
953 )
954 {
955     float loadRate =
956         this->m_LoadMeterDescription.loadTime * LoadMeterDescription::MILLI_TO_RATE;
957     graphicsDrawing.DrawString(positionX, positionY, "      CPU :%5.2f ms/f (%6.2f %%)\n",
958         this->m_LoadMeterDescription.loadTime, loadRate * 100.0f);
959 
960     float loadGpuRate =
961         m_LoadMeterDescription.loadGpuTime * LoadMeterDescription::MILLI_TO_RATE;
962     graphicsDrawing.DrawString(positionX, positionY + 22, "      GPU :%5.2f ms/f (%6.2f %%)\n",
963         this->m_LoadMeterDescription.loadGpuTime, loadGpuRate * 100.0f);
964 
965     graphicsDrawing.DrawString(positionX, positionY + 62, " Cmd size :% 8d bytes\n",
966         m_LoadMeterDescription.drawCommandBufferSize);
967 }
968 
969 //----------------------------------------
970 void
ResetCumulativeLoadMeter()971 RenderSystem::ResetCumulativeLoadMeter()
972 {
973     m_CumulativeLoadMeterDescription.cumulativeTime = 0.0f;
974     m_CumulativeLoadMeterDescription.cumulativeGpuTime = 0.0f;
975     m_CumulativeLoadMeterDescription.cumulativeWaitTime = 0.0f;
976     m_CumulativeLoadMeterDescription.callCount = 0;
977 }
978 
979 //----------------------------------------
980 void
LogLoadMeter()981 RenderSystem::LogLoadMeter()
982 {
983     float loadRate =
984         this->m_LoadMeterDescription.loadTime * LoadMeterDescription::MILLI_TO_RATE;
985     NW_DEV_LOG("      CPU :%5.2f ms/f (%6.2f %%)\n",
986         this->m_LoadMeterDescription.loadTime, loadRate * 100.0f);
987 
988     float loadGpuRate =
989         m_LoadMeterDescription.loadGpuTime * LoadMeterDescription::MILLI_TO_RATE;
990     NW_DEV_LOG("      GPU :%5.2f ms/f (%6.2f %%)\n",
991         this->m_LoadMeterDescription.loadGpuTime, loadGpuRate * 100.0f);
992 
993     NW_DEV_LOG(" Cmd size :% 8d bytes\n",
994         m_LoadMeterDescription.drawCommandBufferSize);
995 }
996 
997 
998 //----------------------------------------
999 SceneSystem*
Create(os::IAllocator * allocator,const Description & description)1000 SceneSystem::Create(
1001     os::IAllocator* allocator,
1002     const Description& description
1003 )
1004 {
1005     void* memory = allocator->Alloc(sizeof(SceneSystem));
1006     SceneSystem* sceneSystem = new(memory) SceneSystem();
1007 
1008     sceneSystem->m_SceneContext = gfx::SceneContext::Builder()
1009         .MaxSceneNodes(description.maxSceneNodes)
1010         .MaxModels(description.maxModels)
1011         .MaxSkeletalModels(description.maxSkeletalModels)
1012         .MaxCameras(description.maxCameras)
1013         .MaxLights(description.maxLights)
1014         .MaxFragmentLights(description.maxFragmentLights)
1015         .MaxVertexLights(description.maxVertexLights)
1016         .MaxHemiSphereLights(description.maxHemiSphereLights)
1017         .MaxAmbientLights(description.maxAmbientLights)
1018         .MaxFogs(description.maxFogs)
1019         .MaxParticleSets(description.maxParticleSets)
1020         .MaxParticleEmitters(description.maxParticleEmitters)
1021         .MaxParticleModels(description.maxParticleModels)
1022         .IsFixedSizeMemory(description.isFixedSizeMemory)
1023         .Create(allocator);
1024 
1025     sceneSystem->m_SceneTraverser = gfx::SceneTraverser::Builder()
1026         .Create(allocator);
1027 
1028     gfx::WorldMatrixUpdater* worldMatrixUpdater = gfx::WorldMatrixUpdater::Builder()
1029         .Create(allocator);
1030 
1031     gfx::SkeletonUpdater* skeletonUpdater = gfx::SkeletonUpdater::Builder()
1032         .Create(allocator);
1033 
1034     sceneSystem->m_SceneUpdater = gfx::SceneUpdater::Builder()
1035         .WorldMatrixUpdaterPtr(worldMatrixUpdater)
1036         .SkeletonUpdaterPtr(skeletonUpdater)
1037         .Create(allocator);
1038 
1039     nw::gfx::IMaterialIdGenerator* materialIdGenerator =
1040         nw::gfx::SortingMaterialIdGenerator::Builder()
1041         .IsFixedSizeMemory(description.isFixedSizeMemory)
1042         .MaxMaterials(description.maxMaterials)
1043         .Create(allocator);
1044 
1045     sceneSystem->m_SceneInitializer = gfx::SceneInitializer::Builder()
1046         .MaterialIdGenerator(materialIdGenerator)
1047         .Create(allocator);
1048 
1049     sceneSystem->m_CameraController = CameraController::Builder()
1050         .Create(allocator);
1051 
1052     NW_GL_ASSERT();
1053 
1054     sceneSystem->m_Allocator = allocator;
1055 
1056     return sceneSystem;
1057 }
1058 
1059 //----------------------------------------
1060 void
Destroy()1061 SceneSystem::Destroy()
1062 {
1063     gfx::SafeDestroy(this->m_SceneInitializer);
1064     gfx::SafeDestroy(this->m_SceneTraverser);
1065     gfx::SafeDestroy(this->m_SceneUpdater);
1066     gfx::SafeDestroy(this->m_SceneContext);
1067 
1068     gfx::SafeDestroy(this->m_CameraController);
1069 
1070     os::IAllocator* allocator = this->m_Allocator;
1071 
1072     void* memory = static_cast<void*>(this);
1073     this->~SceneSystem();
1074     allocator->Free(memory);
1075 }
1076 
1077 //----------------------------------------
1078 void
InitializeScene(gfx::SceneNode * sceneRoot)1079 SceneSystem::InitializeScene(gfx::SceneNode* sceneRoot)
1080 {
1081     this->m_SceneInitializer->Begin();
1082     sceneRoot->Accept(this->m_SceneInitializer);
1083     this->m_SceneInitializer->End();
1084 
1085     TraverseScene(sceneRoot);
1086 }
1087 
1088 //----------------------------------------
1089 void
TraverseScene(gfx::SceneNode * sceneRoot)1090 SceneSystem::TraverseScene(gfx::SceneNode* sceneRoot)
1091 {
1092     this->m_SceneTraverser->Begin(this->m_SceneContext);
1093     sceneRoot->Accept(this->m_SceneTraverser);
1094     this->m_SceneTraverser->End();
1095 
1096     // パーティクルセットの依存順でソートする
1097     std::sort(
1098         this->m_SceneContext->GetParticleSetBegin(),
1099         this->m_SceneContext->GetParticleSetEnd(),
1100         gfx::ParticleSetCompare());
1101 }
1102 
1103 //----------------------------------------
1104 void
UpdateScene()1105 SceneSystem::UpdateScene()
1106 {
1107     NW_PROFILE("SceneSystem::UpdateScene");
1108 
1109     this->m_SceneUpdater->UpdateAll(this->m_SceneContext);
1110 }
1111 
1112 
1113 //----------------------------------------
1114 void
FinalizeGraphicsSystem()1115 FinalizeGraphicsSystem()
1116 {
1117     s_GraphicsMemoryAllocator.Finalize();
1118 
1119     nw::demo::FinalizeDemoAllocator(s_DeviceAllocator);
1120 }
1121 
1122 //----------------------------------------
1123 void
InitializeGraphicsSystem(nw::demo::DemoAllocator * deviceAllocator)1124 InitializeGraphicsSystem(nw::demo::DemoAllocator* deviceAllocator)
1125 {
1126     s_DeviceAllocator = deviceAllocator;
1127 
1128     nn::os::Initialize();
1129     nn::fs::Initialize();
1130 
1131     // メインメモリとデバイスメモリを初期化します。
1132     // メインメモリはユーザーが自由にアクセスできる領域となり、
1133     // デバイスメモリはGPUがアクセスするFCRAM上の領域となります。
1134     nw::demo::InitializeDemoMemory();
1135 
1136     nw::demo::InitializeDemoAllocator(s_DeviceAllocator, nw::demo::DEMO_MEMORY_SIZE, nn::os::ALLOCATE_OPTION_LINEAR);
1137 
1138     // デバイスメモリを用いるグラフィックスメモリアロケータを初期化します。
1139     s_GraphicsMemoryAllocator.Initialize(s_DeviceAllocator);
1140 
1141     const int MAX_FILE = 256;
1142     const int MAX_DIRECTORY = 16;
1143 
1144     s32 workingMemorySize = nn::fs::GetRomRequiredMemorySize(MAX_FILE, MAX_DIRECTORY);
1145     void* workingMemory = Alloc(workingMemorySize);
1146     nn::Result result = nn::fs::MountRom(MAX_FILE, MAX_DIRECTORY, workingMemory, workingMemorySize);
1147 
1148     NW_ASSERT(result.IsSuccess());
1149 }
1150 
1151 } // namespace demo
1152 } // namespace nw
1153