/*---------------------------------------------------------------------------* Project: Horizon File: gx_CommandListDouble.cpp Copyright (C)2009-2012 Nintendo Co., Ltd. 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. $Rev: 47228 $ *---------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include "demo.h" #define USE_COMMAND_LIST_DOUBLE namespace { GLuint s_ProgramId = 0; GLuint s_ShaderId = 0; nn::fnd::ExpHeap s_AppHeap; uptr s_AddrForGxHeap; const u32 s_GxHeapSize = 0x400000; nn::os::Tick s_FrameStartTick; nn::os::Tick s_FrameEndTick; nn::os::Tick s_StoreTick; nn::os::Tick s_RenderTick; nn::math::Vector3 s_CameraPosition(0.0f, 1.0f, 5.0f); nn::math::Vector3 s_CameraUp(0.0f, 1.0f, 0.0f); nn::math::Vector3 s_CameraTarget(0.0f, 0.0f, 0.0f); nn::math::Matrix34 s_ViewMatrix; nn::math::Matrix44 s_Display0ProjectionMatrix; nn::math::Matrix44 s_Display1ProjectionMatrix; f32 s_GlobalAmbientLight[] = { 0.2f, 0.2f, 0.2f, 1.0f}; f32 s_Light0Ambient[] = { 0.2f, 0.2f, 0.2f, 1.0f}; f32 s_Light0Diffuse[] = { 0.7f, 0.7f, 0.7f, 1.0f}; f32 s_Light0Position[] = {10.0f, 10.0f, 10.0f, 0.0f}; f32 s_MaterialAmbient[] = {0.3f, 0.3f, 0.3f, 1.0f}; f32 s_MaterialDiffuse[] = {0.7f, 0.7f, 0.7f, 1.0f}; const u32 s_RowCubeNum = 20; const u32 s_ColumnCubeNum = 10; demo::Cube s_Cube[s_RowCubeNum][s_ColumnCubeNum]; const u32 s_MaxCount = 120; } namespace demo { // // Class that double buffers the render command list // class RenderSystemDouble : public demo::RenderSystem { public: RenderSystemDouble(void); virtual ~RenderSystemDouble(); public: virtual void Initialize(const uptr fcramAddress, const size_t memorySize, const u32 commandBufferSize = 0x40000, const u32 requestNum = 128, const bool serialRunMode = true, const demo::DisplayBuffersDescription& displayBuffers0Desc = DisplayBuffersDescription::GetDefaultDisplay0Description(), const demo::DisplayBuffersDescription& displayBuffers1Desc = DisplayBuffersDescription::GetDefaultDisplay1Description(), const demo::FrameBufferDescription& frameBuffer0Desc = FrameBufferDescription::GetDefaultDisplay0FrameBufferDescription(), const demo::DisplayBuffersDescription& displayBuffers0ExtDesc = DisplayBuffersDescription::GetDefaultDisplay0ExtDescription() ); virtual void Finalize(void); protected: void CreateCommandListDouble(GLsizei bufSize, GLsizei reqCountNum); void RunCommandListDouble(void); public: virtual void SwapBuffers(void); protected: virtual void SwapBuffers0(void); virtual void SwapBuffers1(void); protected: GLuint m_CurrentDisplay0BufferIndex; GLuint m_CurrentDisplay1BufferIndex; GLuint m_Display0BufferIdArray[2]; GLuint m_Display1BufferIdArray[2]; GLuint m_CurrentCommandListIndex; GLuint m_CommandListIdArray[2]; }; RenderSystemDouble::RenderSystemDouble() : demo::RenderSystem(), m_CurrentDisplay0BufferIndex(0), m_CurrentDisplay1BufferIndex(0), m_CurrentCommandListIndex(0) { for (u32 index = 0; index < 2; index++) { m_Display0BufferIdArray[index] = 0; m_Display1BufferIdArray[index] = 0; m_CommandListIdArray[index] = 0; } } RenderSystemDouble::~RenderSystemDouble() { } void RenderSystemDouble::Initialize(const uptr fcramAddress, const size_t memorySize, const u32 commandBufferSize, const u32 requestNum, const bool serialRunMode, const demo::DisplayBuffersDescription& displayBuffers0Desc, const demo::DisplayBuffersDescription& displayBuffers1Desc, const demo::FrameBufferDescription& frameBuffer0Desc, const demo::DisplayBuffersDescription& displayBuffers0ExtDesc) { NN_UNUSED_VAR(serialRunMode); NN_UNUSED_VAR(displayBuffers0ExtDesc); #ifdef USE_COMMAND_LIST_DOUBLE demo::InitializeMemoryManager(fcramAddress, memorySize); if ( nngxInitialize(demo::GetAllocator, demo::GetDeallocator) == GL_FALSE ) { NN_TPANIC_("nngxInitialize() failed.\n"); } // Double buffer the command list CreateCommandListDouble(commandBufferSize, requestNum); DisplayBuffers::Create(displayBuffers0Desc, m_DisplayBuffers0); DEMO_ASSERT_GL_ERROR(); DisplayBuffers::Create(displayBuffers1Desc, m_DisplayBuffers1); DEMO_ASSERT_GL_ERROR(); m_CurrentDisplay0BufferIndex = 0; m_CurrentDisplay1BufferIndex = 0; for (u32 index = 0; index < 2; index++) { m_Display0BufferIdArray[index] = m_DisplayBuffers0.GetDisplayBufferId(index); m_Display1BufferIdArray[index] = m_DisplayBuffers1.GetDisplayBufferId(index); } FrameBuffer::Create(frameBuffer0Desc, m_FrameBuffer0); DEMO_ASSERT_GL_ERROR(); m_InitializeFlag = true; InitializeLcdDisplay(); DEMO_ASSERT_GL_ERROR(); #else RenderSystem::Initialize(fcramAddress, memorySize, commandBufferSize, requestNum, serialRunMode, displayBuffers0Desc, displayBuffers1Desc, frameBuffer0Desc); #endif } void RenderSystemDouble::CreateCommandListDouble(GLsizei bufSize, GLsizei reqCountNum) { nngxGenCmdlists(2, &m_CommandListIdArray[0]); for (u32 index = 0; index < 2; index++) { nngxBindCmdlist(m_CommandListIdArray[index]); nngxCmdlistStorage(bufSize, reqCountNum); nngxSetCmdlistParameteri(NN_GX_CMDLIST_RUN_MODE, NN_GX_CMDLIST_SERIAL_RUN); nngxClearCmdlist(); } m_CurrentCommandListIndex = 0; nngxBindCmdlist( m_CommandListIdArray[m_CurrentCommandListIndex] ); } void RenderSystemDouble::Finalize(void) { #ifdef USE_COMMAND_LIST_DOUBLE nngxDeleteCmdlists(2, &m_CommandListIdArray[0]); #endif RenderSystem::Finalize(); } void RenderSystemDouble::SwapBuffers(void) { #ifdef USE_COMMAND_LIST_DOUBLE if ( m_TargetDisplay == NN_GX_DISPLAY0 ) { SwapBuffers0(); } else if ( m_TargetDisplay == NN_GX_DISPLAY1 ) { SwapBuffers1(); } else { NN_TPANIC_("Invalid display.\n"); } #else RenderSystem::SwapBuffers(); s_RenderTick = nn::os::Tick::GetSystemCurrent(); #endif } void RenderSystemDouble::SwapBuffers0(void) { if (! m_InitializeFlag ) { return; } // Sends the color buffer contents for each screen to display buffers. nngxTransferRenderImage(m_Display0BufferIdArray[m_CurrentDisplay0BufferIndex], NN_GX_ANTIALIASE_NOT_USED, GL_FALSE, 0, 0); // Binds the display buffers to each display // However, the buffers for the previous screens are bound nngxActiveDisplay(NN_GX_DISPLAY0); m_CurrentDisplay0BufferIndex = 1 - m_CurrentDisplay0BufferIndex; nngxBindDisplaybuffer(m_Display0BufferIdArray[m_CurrentDisplay0BufferIndex]); // Waits for the color buffer to finish sending. RunCommandListDouble(); // Measures the time it takes to finish rendering to the upper screen s_RenderTick = nn::os::Tick::GetSystemCurrent(); nngxSwapBuffers(m_TargetDisplay); // Binds the accumulated command list and executes it nngxBindCmdlist( m_CommandListIdArray[m_CurrentCommandListIndex] ); nngxRunCmdlist(); // Switches the command list to start accumulating m_CurrentCommandListIndex = 1 - m_CurrentCommandListIndex; // Binds the command list to start accumulating nngxBindCmdlist( m_CommandListIdArray[m_CurrentCommandListIndex] ); } void RenderSystemDouble::SwapBuffers1(void) { if (! m_InitializeFlag ) { return; } // Sends the color buffer contents for each screen to display buffers. nngxTransferRenderImage(m_Display1BufferIdArray[m_CurrentDisplay1BufferIndex], NN_GX_ANTIALIASE_NOT_USED, GL_FALSE, 0, 0); // Binds the display buffers to each display // However, the buffers for the previous screens are bound nngxActiveDisplay(NN_GX_DISPLAY1); m_CurrentDisplay0BufferIndex = 1 - m_CurrentDisplay1BufferIndex; nngxBindDisplaybuffer(m_Display1BufferIdArray[m_CurrentDisplay1BufferIndex]); // Waits for the color buffer to finish sending. RunCommandListDouble(); s_RenderTick = nn::os::Tick::GetSystemCurrent(); nngxSwapBuffers(m_TargetDisplay); // Binds the accumulated command list and executes it nngxBindCmdlist( m_CommandListIdArray[m_CurrentCommandListIndex] ); nngxRunCmdlist(); // Switches the command list to start accumulating m_CurrentCommandListIndex = 1 - m_CurrentCommandListIndex; // Binds the command list to start accumulating nngxBindCmdlist( m_CommandListIdArray[m_CurrentCommandListIndex] ); } void RenderSystemDouble::RunCommandListDouble(void) { // Splits the 3D command buffer of the accumulating command list nngxSplitDrawCmdlist(); // Waits for the executing command list to finish nngxBindCmdlist( m_CommandListIdArray[1 - m_CurrentCommandListIndex] ); nngxWaitCmdlistDone(); // Clears the command list that has finished execution nngxClearCmdlist(); // Binds the accumulating command list nngxBindCmdlist( m_CommandListIdArray[m_CurrentCommandListIndex] ); } } namespace { demo::RenderSystemDouble s_RenderSystem; } void InitializeGraphics(void); void InitializeShader(void); void UseShader(void); void UpdateCamera(void); void DrawDisplay0(void); void DrawDisplay1(void); void DrawBody(demo::Body& body); void Initialize(void) { // fs initialization nn::fs::Initialize(); const size_t ROMFS_BUFFER_SIZE = 1024 * 64; static char buffer[ROMFS_BUFFER_SIZE]; NN_UTIL_PANIC_IF_FAILED( nn::fs::MountRom(16, 16, buffer, ROMFS_BUFFER_SIZE)); InitializeGraphics(); } void InitializeGraphics(void) { s_AppHeap.Initialize(nn::os::GetDeviceMemoryAddress(), nn::os::GetDeviceMemorySize() ); s_AddrForGxHeap = reinterpret_cast(s_AppHeap.Allocate(s_GxHeapSize)); s_RenderSystem.Initialize(s_AddrForGxHeap, s_GxHeapSize); glClearColor(0.3f, 0.3f, 0.3f, 1.0f); glClearDepthf(1.0f); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); glEnable(GL_CULL_FACE); glFrontFace(GL_CCW); glCullFace(GL_BACK); InitializeShader(); // Initialize projection matrix nn::math::MTX44PerspectivePivotDeg(&s_Display0ProjectionMatrix, 45.0f, demo::DISPLAY0_ASPECT, 0.2f, 100.0f, nn::math::PIVOT_UPSIDE_TO_TOP); nn::math::MTX44PerspectivePivotDeg(&s_Display1ProjectionMatrix, 45.0f, demo::DISPLAY1_ASPECT, 0.2f, 100.0f, nn::math::PIVOT_UPSIDE_TO_TOP); // Initialize cubes u32 vertexAttributes = demo::VERTEX_POSITION_ATTRIBUTE | demo::VERTEX_COLOR_ATTRIBUTE | demo::VERTEX_NORMAL_ATTRIBUTE; f32 s_CubeHalfEdges[3]; s_CubeHalfEdges[0] = 0.1f; s_CubeHalfEdges[1] = 0.1f; s_CubeHalfEdges[2] = 0.1f; f32 s_CubeOffsetSpace = 0.025f; f32 s_CubeOffsetX = - (s_CubeHalfEdges[0] + s_CubeOffsetSpace) * s_RowCubeNum; f32 s_CubeOffsetY = - (s_CubeHalfEdges[1] + s_CubeOffsetSpace) * s_ColumnCubeNum; f32 z = 0.0f; for (u32 j = 0; j < s_ColumnCubeNum; j++) { f32 y = s_CubeOffsetY + 2.0f * (s_CubeHalfEdges[1] + s_CubeOffsetSpace) * j; for (u32 i = 0; i < s_RowCubeNum; i++) { f32 x = s_CubeOffsetX + 2.0f * (s_CubeHalfEdges[0] + s_CubeOffsetSpace) * i; s_Cube[i][j].InitializeCube(vertexAttributes, s_CubeHalfEdges[0], s_CubeHalfEdges[1], s_CubeHalfEdges[2]); s_Cube[i][j].SetWorldPosition(x, y, z); f32 color[3]; demo::GetRandomColor(color[0], color[1], color[2]); s_Cube[i][j].SetColor(color[0], color[1], color[2]); } } #ifdef USE_COMMAND_LIST_DOUBLE NN_LOG("Use double-buffered command list\n"); #else NN_LOG("Use single command list\n"); #endif NN_LOG(" Total number of cubes = %d\n", s_ColumnCubeNum * s_RowCubeNum); NN_LOG(" Total number of vertices to draw = %d\n", s_ColumnCubeNum * s_RowCubeNum * 24); NN_LOG(" Total number of triangles to draw = %d\n", s_ColumnCubeNum * s_RowCubeNum * 12); } void InitializeShader(void) { s_ProgramId = glCreateProgram(); s_ShaderId = glCreateShader(GL_VERTEX_SHADER); nn::fs::FileReader file(L"rom:/shader.shbin"); size_t fileSize = file.GetSize(); void* buf = s_AppHeap.Allocate(fileSize); s32 read = file.Read(buf, fileSize); glShaderBinary(1, &s_ShaderId, GL_PLATFORM_BINARY_DMP, buf, read); file.Finalize(); s_AppHeap.Free(buf); glAttachShader(s_ProgramId, s_ShaderId); glAttachShader(s_ProgramId, GL_DMP_FRAGMENT_SHADER_DMP); glBindAttribLocation(s_ProgramId, 0, "aPosition"); glBindAttribLocation(s_ProgramId, 1, "aColor"); glBindAttribLocation(s_ProgramId, 2, "aNormal"); glLinkProgram(s_ProgramId); glValidateProgram(s_ProgramId); demo::InitializeUniforms(s_ProgramId); } void Finalize(void) { for (u32 j = 0; j < s_ColumnCubeNum; j++) { for (u32 i = 0; i < s_RowCubeNum; i++) { s_Cube[i][j].Finalize(); } } s_RenderSystem.Finalize(); s_AppHeap.Free(reinterpret_cast(s_AddrForGxHeap)); s_AppHeap.Finalize(); } bool DrawFrame(void) { static f32 totalStoreTime = 0.0f; static f32 totalRenderTime = 0.0f; static f32 totalFrameTime = 0.0f; static u32 count = 0; s_FrameStartTick = nn::os::Tick::GetSystemCurrent(); UpdateCamera(); DrawDisplay0(); DrawDisplay1(); s_RenderSystem.WaitVsync(NN_GX_DISPLAY_BOTH); s_FrameEndTick = nn::os::Tick::GetSystemCurrent(); nn::fnd::TimeSpan storeTimeSpan = s_StoreTick - s_FrameStartTick; totalStoreTime += storeTimeSpan.GetMicroSeconds(); nn::fnd::TimeSpan renderTimeSpan = s_RenderTick - s_StoreTick; totalRenderTime += renderTimeSpan.GetMicroSeconds(); nn::fnd::TimeSpan frameTimeSpan = s_FrameEndTick - s_FrameStartTick; totalFrameTime += frameTimeSpan.GetMicroSeconds(); if ( count >= s_MaxCount ) { NN_UNUSED_VAR(totalStoreTime); NN_UNUSED_VAR(totalRenderTime); NN_UNUSED_VAR(totalFrameTime); f32 storeTime = totalStoreTime / static_cast( s_MaxCount ); f32 renderTime = totalRenderTime / static_cast( s_MaxCount ); f32 frameTime = totalFrameTime / static_cast( s_MaxCount ); NN_UNUSED_VAR(storeTime); NN_UNUSED_VAR(renderTime); NN_UNUSED_VAR(frameTime); NN_LOG("\n"); NN_LOG("Average storeTime = %f (usec)\n", storeTime); NN_LOG("Average renderTime = %f (usec)\n", renderTime); NN_LOG("Average frameTime = %f (usec)\n", frameTime); count = 0; totalStoreTime = 0.0f; totalRenderTime = 0.0f; totalFrameTime = 0.0f; } else { count += 1; } return true; } void UseShader(void) { glUseProgram(s_ProgramId); // Fragment uniform glUniform1i(demo::s_UniformLocations[demo::FRAGMENT_UNIFORM_ALPHA_TEST], GL_FALSE); // Fragment uniform : Texture samplerType glUniform1i(demo::s_UniformLocations[demo::FRAGMENT_UNIFORM_TEXTURE0_SAMPLER_TYPE], GL_FALSE); glUniform1i(demo::s_UniformLocations[demo::FRAGMENT_UNIFORM_TEXTURE1_SAMPLER_TYPE], GL_FALSE); glUniform1i(demo::s_UniformLocations[demo::FRAGMENT_UNIFORM_TEXTURE2_SAMPLER_TYPE], GL_FALSE); glUniform1i(demo::s_UniformLocations[demo::FRAGMENT_UNIFORM_TEXTURE3_SAMPLER_TYPE], GL_FALSE); // Fragment uniform : Fragment lighting glUniform1i(demo::s_UniformLocations[demo::FRAGMENT_UNIFORM_FRAGMENT_LIGHTING_ENABLED], GL_TRUE); glUniform4fv(demo::s_UniformLocations[demo::FRAGMENT_UNIFORM_FRAGMENT_LIGHTING_AMBIENT], 1, s_GlobalAmbientLight); glUniform1i(demo::s_UniformLocations[demo::FRAGMENT_UNIFORM_FRAGMENT_LIGHT_SOURCE0_ENABLED], GL_TRUE); glUniform4fv(demo::s_UniformLocations[demo::FRAGMENT_UNIFORM_FRAGMENT_LIGHT_SOURCE0_AMBIENT], 1, s_Light0Ambient); glUniform4fv(demo::s_UniformLocations[demo::FRAGMENT_UNIFORM_FRAGMENT_LIGHT_SOURCE0_DIFFUSE], 1, s_Light0Diffuse); glUniform4fv(demo::s_UniformLocations[demo::FRAGMENT_UNIFORM_FRAGMENT_LIGHT_SOURCE0_POSITION], 1, s_Light0Position); // Fragment uniform : Material glUniform4fv(demo::s_UniformLocations[demo::FRAGMENT_UNIFORM_FRAGMENT_MATERIAL_AMBIENT], 1, s_MaterialAmbient); glUniform4fv(demo::s_UniformLocations[demo::FRAGMENT_UNIFORM_FRAGMENT_MATERIAL_DIFFUSE], 1, s_MaterialDiffuse); // Fragment uniform : Texture combiner // Modulate fragment primary color and vertex color. glUniform3i(demo::s_UniformLocations[demo::FRAGMENT_UNIFORM_TEXENV2_SRC_RGB], GL_FRAGMENT_PRIMARY_COLOR_DMP, GL_PRIMARY_COLOR, GL_PREVIOUS); glUniform3i(demo::s_UniformLocations[demo::FRAGMENT_UNIFORM_TEXENV2_SRC_ALPHA], GL_PRIMARY_COLOR, GL_PRIMARY_COLOR, GL_PREVIOUS); glUniform3i(demo::s_UniformLocations[demo::FRAGMENT_UNIFORM_TEXENV2_OPERAND_RGB], GL_SRC_COLOR, GL_SRC_COLOR, GL_SRC_COLOR); glUniform3i(demo::s_UniformLocations[demo::FRAGMENT_UNIFORM_TEXENV2_OPERAND_ALPHA], GL_SRC_ALPHA, GL_SRC_ALPHA, GL_SRC_ALPHA); glUniform1i(demo::s_UniformLocations[demo::FRAGMENT_UNIFORM_TEXENV2_COMBINE_RGB], GL_MODULATE); glUniform1i(demo::s_UniformLocations[demo::FRAGMENT_UNIFORM_TEXENV2_COMBINE_ALPHA], GL_MODULATE); } void UpdateCamera(void) { nn::math::MTX34LookAt(&s_ViewMatrix, &s_CameraPosition, &s_CameraUp, &s_CameraTarget); } void DrawDisplay0(void) { s_RenderSystem.SetRenderTarget(NN_GX_DISPLAY0); s_RenderSystem.Clear(); UseShader(); glUniformMatrix4fv(demo::s_UniformLocations[demo::VERTEX_UNIFORM_PROJECTION], 1, GL_TRUE, static_cast(s_Display0ProjectionMatrix)); for (u32 i = 0; i < s_RowCubeNum; i++) { for (u32 j = 0; j < s_ColumnCubeNum; j++) { DrawBody(s_Cube[i][j]); } } s_StoreTick = nn::os::Tick::GetSystemCurrent(); s_RenderSystem.SwapBuffers(); } void DrawDisplay1(void) { s_RenderSystem.SetRenderTarget(NN_GX_DISPLAY1); s_RenderSystem.Clear(); s_RenderSystem.SwapBuffers(); } void DrawBody(demo::Body& body) { nn::math::MTX44 modelViewMatrix(s_ViewMatrix); nn::math::Matrix44 worldMatrix = body.GetWorldMatrix(); nn::math::MTX44Mult(&modelViewMatrix, &modelViewMatrix, &worldMatrix); glUniformMatrix4fv(demo::s_UniformLocations[demo::VERTEX_UNIFORM_MODELVIEW], 1, GL_TRUE, static_cast(modelViewMatrix)); body.Draw(); } void nnMain(void) { // Call only nn::applet::Enable to also allow execution from the HOME Menu // HOME Menu transitions, POWER Button presses, and sleep are all unsupported nn::applet::Enable(); Initialize(); bool flag = true; while ( flag ) { flag = DrawFrame(); } Finalize(); }