/*---------------------------------------------------------------------------* Project: Horizon File: EarlyDepthTestSample.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 "Loader.h" #include "Util.h" #include #include "Memory.h" #include "object.h" #include "MemoryManager.h" // Does not wait for VSync in order to make early depth test effects more easily perceptible. //#define WAIT_VSYNC #define USE_EARLY_DEPTH_TEST // Displays the number of fragments input into the per-fragment operation module if enabled. //#define GET_PROFILING_RESULT // Display alpha static u32 s_Displaybuffer0[2]; static u32 s_Displaybuffer1[2]; static s32 s_CurrentDisplaybuffer0 = 0; static s32 s_CurrentDisplaybuffer1 = 0; // Framebuffer and render buffer static u32 s_Framebuffer = 0; static u32 s_Renderbuffer[2]; // Block 32 mode must be set when using an early depth test. // A buffer having the same size as the screen cannot be used for this reason. #ifdef USE_EARLY_DEPTH_TEST static u32 s_Disp0Width = 256; static u32 s_Disp0Height = 416; static u32 s_Disp1Width = 256; static u32 s_Disp1Height = 320; #else static u32 s_Disp0Width = 240; static u32 s_Disp0Height = 400; static u32 s_Disp1Width = 240; static u32 s_Disp1Height = 320; #endif // USE_EARLY_DEPTH_TEST // Command list static u32 s_CommandList = 0; // Vertex buffer static u32 s_CubeArrayBuffer; static u32 s_CubeElementArrayBuffer; // Texture static u32 s_Texture; // Shader static u32 s_Program; static u32 s_Shader; // Uniform location static s32 s_UniformLocationProj; static s32 s_UniformLocationView; // Matrix static nn::math::MTX44 s_ProjMtx; static nn::math::MTX34 s_ViewMtx; // FPS display #define FPS_AVG_FRAME_COUNT 100 static u32 s_CurrentFpsCount = 0; static f64 s_TmpFpsSum = 0.0; // ExpHeap for app. nn::fnd::ExpHeap s_AppHeap; uptr s_HeapForGx; uptr s_HeapForMalloc; const u32 s_GxHeapSize = 0x800000; const u32 s_HeapSize = 0x200000; #define PRINT(__msg) NN_LOG __msg static void DrawCube(void) { glBindBuffer(GL_ARRAY_BUFFER, s_CubeArrayBuffer); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0) ; glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (void*)sizeof(s_CubePos)); glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, (void*)(sizeof(s_CubePos) + sizeof(s_CubeNormal))) ; glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s_CubeElementArrayBuffer); glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_SHORT, 0); } static void LoadObjects(void) { // Cube glGenBuffers(1, &s_CubeArrayBuffer); glBindBuffer(GL_ARRAY_BUFFER, s_CubeArrayBuffer); glBufferData(GL_ARRAY_BUFFER, sizeof(s_CubePos) + sizeof(s_CubeNormal) + sizeof(s_CubeTexCoord), 0, GL_STATIC_DRAW); glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(s_CubePos), s_CubePos); glBufferSubData(GL_ARRAY_BUFFER, sizeof(s_CubePos), sizeof(s_CubeNormal), s_CubeNormal); glBufferSubData(GL_ARRAY_BUFFER, sizeof(s_CubePos) + sizeof(s_CubeNormal), sizeof(s_CubeTexCoord), s_CubeTexCoord); glGenBuffers(1, &s_CubeElementArrayBuffer); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s_CubeElementArrayBuffer); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(s_CubeIdx), s_CubeIdx, GL_STATIC_DRAW); glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); // Load textures bool IsAlpha; glGenTextures(1, &s_Texture); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, s_Texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, 0); loadTexture( "rom:/resources/knight.tga", GL_TEXTURE_2D, 0, IsAlpha, true); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } s64 GetTimeMicroSeconds(void) { nn::os::Tick ticks = nn::os::Tick::GetSystemCurrent(); nn::fnd::TimeSpan TimeSpan = ticks; return TimeSpan.GetMicroSeconds(); } s32 DrawFrame(void) { static s32 f = 0; s64 startTime = GetTimeMicroSeconds(); // Bind the framebuffer glBindFramebuffer(GL_FRAMEBUFFER, s_Framebuffer); // Viewport settings glViewport(0, 0, nn::gx::DISPLAY0_WIDTH * 2, nn::gx::DISPLAY0_HEIGHT * 2); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); #ifdef USE_EARLY_DEPTH_TEST glClear( GL_EARLY_DEPTH_BUFFER_BIT_DMP ); #endif // USE_EARLY_DEPTH_TEST // Model view matrix nn::math::Vector3 camPos( 0.f, 0.f, 14.f ); nn::math::Vector3 camUp( 0.f, 1.f, 0.f ); nn::math::Vector3 target( 0.f, 0.f, 0.f ); nn::math::MTX34LookAt( &s_ViewMtx, &camPos, &camUp, &target ); static f32 frontPos = 12.0f; if( frontPos <= -60.0f )frontPos = 12.0f; // Object in screen foreground. for(int i = 0; i < 16; i++) { // Model view matrix nn::math::Matrix34 trans, vTmp; nn::math::Vector3 transVec( 0.f, frontPos + ( i * 3.0f ), 7.2f ); nn::math::MTX34Translate( &trans, &transVec ); nn::math::MTX34Mult(&vTmp, &s_ViewMtx, &trans); nn::math::Matrix44 tmp(vTmp); glUniformMatrix4fv(s_UniformLocationView, 1, GL_TRUE, static_cast(tmp)); // Rendering a cube DrawCube(); } frontPos -= 0.05f; // The fragment load has been increased to the extreme in order to clearly show the effect of the early depth test. glUniform1i(glGetUniformLocation(s_Program, "dmp_FragmentLightSource[1].enabled"), GL_TRUE); glUniform1i(glGetUniformLocation(s_Program, "dmp_FragmentLightSource[2].enabled"), GL_TRUE); glUniform1i(glGetUniformLocation(s_Program, "dmp_FragmentLightSource[3].enabled"), GL_TRUE); glUniform1i(glGetUniformLocation(s_Program, "dmp_LightEnv.config"), GL_LIGHT_ENV_LAYER_CONFIG7_DMP); glDisable(GL_CULL_FACE); static f32 backPos = -12.0f; if( backPos >= -3.0f )backPos = -12.0f; // Object in screen background. for(int i = 0; i < 8; i++) { // Model view matrix nn::math::Matrix34 trans, vTmp; nn::math::Vector3 transVec( 0.f, backPos + ( i * 3.0f ), 5.2f ); nn::math::MTX34Translate( &trans, &transVec ); nn::math::MTX34Mult(&vTmp, &s_ViewMtx, &trans); nn::math::Matrix44 tmp(vTmp); glUniformMatrix4fv(s_UniformLocationView, 1, GL_TRUE, static_cast(tmp)); // Rendering a cube DrawCube(); } backPos += 0.05f; glUniform1i(glGetUniformLocation(s_Program, "dmp_FragmentLightSource[1].enabled"), GL_FALSE); glUniform1i(glGetUniformLocation(s_Program, "dmp_FragmentLightSource[2].enabled"), GL_FALSE); glUniform1i(glGetUniformLocation(s_Program, "dmp_FragmentLightSource[3].enabled"), GL_FALSE); glUniform1i(glGetUniformLocation(s_Program, "dmp_LightEnv.config"), GL_LIGHT_ENV_LAYER_CONFIG0_DMP); glEnable(GL_CULL_FACE); glFinish(); nngxActiveDisplay(NN_GX_DISPLAY0); nngxBindDisplaybuffer(s_Displaybuffer0[s_CurrentDisplaybuffer0]); nngxTransferRenderImage(s_Displaybuffer0[s_CurrentDisplaybuffer0], NN_GX_ANTIALIASE_2x2, GL_FALSE, 0, 0); nngxActiveDisplay(NN_GX_DISPLAY1); nngxBindDisplaybuffer(s_Displaybuffer1[s_CurrentDisplaybuffer1]); // Swap buffers nngxSwapBuffers(NN_GX_DISPLAY_BOTH); s_CurrentDisplaybuffer0 = (s_CurrentDisplaybuffer0 == 0 ? 1 : 0); s_CurrentDisplaybuffer1 = (s_CurrentDisplaybuffer1 == 0 ? 1 : 0); nngxWaitCmdlistDone(); nngxClearCmdlist(); #ifdef WAIT_VSYNC nngxWaitVSync(NN_GX_DISPLAY_BOTH); #endif #ifdef GET_PROFILING_RESULT static u32 resFragment = 0, oldResFragment = 0; nngxGetProfilingResult( NN_GX_PROFILING_FRAGMENT, &resFragment ); NN_LOG("Fragment Count: %d pixels\n", resFragment - oldResFragment); oldResFragment = resFragment; #endif nngxRunCmdlist(); if ( f == 0 ) { nngxStartLcdDisplay(); } // FPS display s_TmpFpsSum += GetTimeMicroSeconds() - startTime; if (++s_CurrentFpsCount == FPS_AVG_FRAME_COUNT) { f64 avgtime = s_TmpFpsSum / FPS_AVG_FRAME_COUNT; PRINT(("AVG Time: %0.5f (fps: %0.3f)\n", avgtime, (1. / (avgtime/1000000.0)))); NN_UNUSED_VAR(avgtime); s_CurrentFpsCount = 0; s_TmpFpsSum = 0.0; } f++; return !glGetError(); } static void SetRenderState(void) { f32 diffuse0[] = {0.5f,0.5f,0.5f,1.f} ; f32 diffuse1[] = {0.15f,0.85f,0.15f,1.f} ; f32 diffuse2[] = {0.15f,0.15f,0.85f,1.f} ; f32 diffuse3[] = {0.85f,0.15f,0.15f,1.f} ; // Lighting glUniform1i(glGetUniformLocation(s_Program, "dmp_FragmentLighting.enabled"), GL_TRUE); glUniform1i(glGetUniformLocation(s_Program, "dmp_FragmentLightSource[0].enabled"), GL_TRUE); glUniform1i(glGetUniformLocation(s_Program, "dmp_FragmentLightSource[1].enabled"), GL_TRUE); glUniform1i(glGetUniformLocation(s_Program, "dmp_FragmentLightSource[2].enabled"), GL_TRUE); glUniform1i(glGetUniformLocation(s_Program, "dmp_FragmentLightSource[3].enabled"), GL_TRUE); glUniform1i(glGetUniformLocation(s_Program, "dmp_LightEnv.config"), GL_LIGHT_ENV_LAYER_CONFIG7_DMP); // Color glUniform4fv(glGetUniformLocation(s_Program, "dmp_FragmentLightSource[0].diffuse"), 1, diffuse0); glUniform4fv(glGetUniformLocation(s_Program, "dmp_FragmentLightSource[1].diffuse"), 1, diffuse1); glUniform4fv(glGetUniformLocation(s_Program, "dmp_FragmentLightSource[2].diffuse"), 1, diffuse2); glUniform4fv(glGetUniformLocation(s_Program, "dmp_FragmentLightSource[3].diffuse"), 1, diffuse3); // Light position nn::math::Vector4 lightPos0(130.f, 10.f, 100.f, 1.f); nn::math::Vector4 lightPos1(50.f, 90.f, 60.f,1.f); nn::math::Vector4 lightPos2(-60.f, -120.f, 20.f,1.f); nn::math::Vector4 lightPos3(0.f, 0.f, -80.f,1.f); glUniform4fv(glGetUniformLocation(s_Program, "dmp_FragmentLightSource[0].position"), 1, static_cast(lightPos0)); glUniform4fv(glGetUniformLocation(s_Program, "dmp_FragmentLightSource[1].position"), 1, static_cast(lightPos1)); glUniform4fv(glGetUniformLocation(s_Program, "dmp_FragmentLightSource[2].position"), 1, static_cast(lightPos2)); glUniform4fv(glGetUniformLocation(s_Program, "dmp_FragmentLightSource[3].position"), 1, static_cast(lightPos3)); // Texture settings glUniform1i(glGetUniformLocation(s_Program, "dmp_Texture[0].samplerType"), GL_TEXTURE_2D); glUniform1i(glGetUniformLocation(s_Program, "dmp_TexEnv[0].combineRgb"), GL_MODULATE); glUniform1i(glGetUniformLocation(s_Program, "dmp_TexEnv[0].combineAlpha"), GL_REPLACE); glUniform3i(glGetUniformLocation(s_Program, "dmp_TexEnv[0].operandRgb"), GL_SRC_COLOR, GL_SRC_COLOR, GL_SRC_COLOR); glUniform3i(glGetUniformLocation(s_Program, "dmp_TexEnv[0].operandAlpha"), GL_SRC_ALPHA, GL_SRC_ALPHA, GL_SRC_ALPHA); glUniform3i(glGetUniformLocation(s_Program, "dmp_TexEnv[0].srcRgb"), GL_TEXTURE0, GL_FRAGMENT_PRIMARY_COLOR_DMP, GL_CONSTANT); glUniform3i(glGetUniformLocation(s_Program, "dmp_TexEnv[0].srcAlpha"), GL_CONSTANT, GL_CONSTANT, GL_CONSTANT); // The projection matrix f32 aspect = static_cast(nn::gx::DISPLAY0_HEIGHT) / static_cast(nn::gx::DISPLAY0_WIDTH); nn::math::MTX44Frustum(&s_ProjMtx, -0.04f, 0.04f, -0.04f * aspect, 0.04f * aspect, 0.22, 100.0f); glUniformMatrix4fv(s_UniformLocationProj, 1, GL_TRUE, static_cast(s_ProjMtx)); } static void InitShader(void) { // Create a shader program s_Program = glCreateProgram(); s_Shader = glCreateShader(GL_VERTEX_SHADER); // Load the 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_Shader, GL_PLATFORM_BINARY_DMP, buf, read); file.Finalize(); s_AppHeap.Free(buf); // Attach a shader glAttachShader(s_Program, s_Shader); glAttachShader(s_Program, GL_DMP_FRAGMENT_SHADER_DMP); glBindAttribLocation(s_Program, 0, "aPosition"); glBindAttribLocation(s_Program, 1, "aNormal"); glBindAttribLocation(s_Program, 2, "aTexCoord"); // Link the shader glLinkProgram(s_Program); glValidateProgram(s_Program); glUseProgram(s_Program); // Get the uniform location s_UniformLocationView = glGetUniformLocation(s_Program, "uModelView"); s_UniformLocationProj = glGetUniformLocation(s_Program, "uProjection"); } static s32 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)); s_AppHeap.Initialize(nn::os::GetDeviceMemoryAddress(), nn::os::GetDeviceMemorySize() ); s_HeapForGx = reinterpret_cast(s_AppHeap.Allocate(s_GxHeapSize)); demo::memory_manager::InitializeMemoryManager(s_HeapForGx, s_GxHeapSize); nngxInitialize(demo::memory_manager::GetAllocator, demo::memory_manager::GetDeallocator); s_HeapForMalloc = reinterpret_cast(s_AppHeap.Allocate(s_HeapSize)); setMemoryHeap(s_HeapForMalloc, s_HeapSize); // Initialize the framebuffer object and render buffer glGenFramebuffers(1, &s_Framebuffer); glBindFramebuffer(GL_FRAMEBUFFER, s_Framebuffer); glGenRenderbuffers(2, s_Renderbuffer); glBindRenderbuffer(GL_RENDERBUFFER, s_Renderbuffer[0]); glRenderbufferStorage(GL_RENDERBUFFER | NN_GX_MEM_VRAMA, GL_RGBA8_OES, s_Disp0Width * 2, s_Disp0Height * 2); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, s_Renderbuffer[0]); glBindRenderbuffer(GL_RENDERBUFFER, s_Renderbuffer[1]); glRenderbufferStorage(GL_RENDERBUFFER | NN_GX_MEM_VRAMB, GL_DEPTH24_STENCIL8_EXT, s_Disp0Width * 2, s_Disp0Height * 2); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, s_Renderbuffer[1]); // Initialize display buffer nngxGenDisplaybuffers(2, s_Displaybuffer0); nngxActiveDisplay(NN_GX_DISPLAY0); nngxDisplayEnv(0, 0); nngxBindDisplaybuffer(s_Displaybuffer0[0]); nngxDisplaybufferStorage(GL_RGB8_OES, s_Disp0Width, s_Disp0Height, NN_GX_MEM_FCRAM); nngxBindDisplaybuffer(s_Displaybuffer0[1]); nngxDisplaybufferStorage(GL_RGB8_OES, s_Disp0Width, s_Disp0Height, NN_GX_MEM_FCRAM); nngxGenDisplaybuffers(2, s_Displaybuffer1); nngxActiveDisplay(NN_GX_DISPLAY1); nngxDisplayEnv(0, 0); nngxBindDisplaybuffer(s_Displaybuffer1[0]); nngxDisplaybufferStorage(GL_RGB8_OES, s_Disp1Width, s_Disp1Height, NN_GX_MEM_FCRAM); nngxBindDisplaybuffer(s_Displaybuffer1[1]); nngxDisplaybufferStorage(GL_RGB8_OES, s_Disp1Width, s_Disp1Height, NN_GX_MEM_FCRAM); // Initialize the command list object nngxGenCmdlists(1, &s_CommandList); nngxBindCmdlist(s_CommandList); nngxCmdlistStorage(0x40000, 128); nngxRunCmdlist(); glClearColor(0.31f, 0.40f, 0.5f, 1.0f); glClearDepthf(1.f); // DepthTest glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); // Culling glEnable(GL_CULL_FACE); glFrontFace(GL_CCW); glCullFace(GL_BACK); #ifdef USE_EARLY_DEPTH_TEST // Set an early depth test glEnable(GL_EARLY_DEPTH_TEST_DMP); glEarlyDepthFuncDMP(GL_LESS); glClearEarlyDepthDMP(0xFFFFFF); // Change the block mode glRenderBlockModeDMP(GL_RENDER_BLOCK32_MODE_DMP); #endif // USE_EARLY_DEPTH_TEST // Initialize the shader InitShader(); // Set objects LoadObjects(); // Enable the vertex attribute array glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); glEnableVertexAttribArray(2); // Set render parameters SetRenderState(); return 0; } static void Finalize() { // Release the display buffer nngxBindDisplaybuffer(0); nngxDeleteDisplaybuffers(2, s_Displaybuffer0); nngxDeleteDisplaybuffers(2, s_Displaybuffer1); s_Displaybuffer0[0] = s_Displaybuffer0[1] = 0; s_Displaybuffer1[0] = s_Displaybuffer1[1] = 0; // Delete the command list nngxDeleteCmdlists(1, &s_CommandList); nngxFinalize(); } void nnMain(void) { // Call only the nn::applet::Enable function to also allow execution from the HOME Menu // HOME Menu transitions, POWER Button presses, and sleep are all unsupported nn::applet::Enable(); if (Initialize() >= 0) { while (1) { (void)DrawFrame(); } } Finalize(); /* shutdown_display */ s_AppHeap.Free(reinterpret_cast(s_HeapForGx)); s_AppHeap.Finalize(); }