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