/** * Copyright (c) 2025 Dominic Masters * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ #include "display/display.h" #include "engine/engine.h" #include "display/framebuffer.h" #include "scene/scene.h" #include "display/spritebatch.h" #include "display/mesh/quad.h" #include "display/screen.h" #include "ui/ui.h" #include "debug/debug.h" #include "display/text.h" #include "assert/assert.h" #include "util/memory.h" #include "util/string.h" #include "asset/asset.h" display_t DISPLAY = { 0 }; errorret_t displayInit(void) { memoryZero(&DISPLAY, sizeof(DISPLAY)); #if DISPLAY_SDL2 uint32_t flags = SDL_INIT_VIDEO; #if INPUT_GAMEPAD == 1 flags |= SDL_INIT_GAMECONTROLLER | SDL_INIT_JOYSTICK; #endif if(SDL_Init(flags) != 0) { errorThrow("SDL Failed to Initialize: %s", SDL_GetError()); } // Set OpenGL attributes (Needs to be done now or later?) SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); // Create window with OpenGL flag. DISPLAY.window = SDL_CreateWindow( "Dusk", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, DISPLAY_WINDOW_WIDTH_DEFAULT, DISPLAY_WINDOW_HEIGHT_DEFAULT, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_OPENGL ); if(!DISPLAY.window) { errorThrow("SDL_CreateWindow failed: %s", SDL_GetError()); } // Create OpenGL context DISPLAY.glContext = SDL_GL_CreateContext(DISPLAY.window); if(!DISPLAY.glContext) { errorThrow("SDL_GL_CreateContext failed: %s", SDL_GetError()); } SDL_GL_SetSwapInterval(1); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); glDisable(GL_CULL_FACE); glDisable(GL_LIGHTING);// PSP defaults this on? glShadeModel(GL_SMOOTH); // Fixes color on PSP? glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); glClearDepth(1.0f); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glPixelStorei(GL_PACK_ALIGNMENT, 1); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glEnableClientState(GL_COLOR_ARRAY);// To confirm: every frame on PSP? glEnableClientState(GL_TEXTURE_COORD_ARRAY); glEnableClientState(GL_VERTEX_ARRAY); // Get and validate GL state. GLenum err = glGetError(); if(err != GL_NO_ERROR) { assertUnreachable("GL Error before checking support"); } // Check if paletted textures are supported. #if PSP DISPLAY.usingShaderedPalettes = false; #else GLint mask = 0; glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &mask); DISPLAY.usingShaderedPalettes = true; if(mask & GL_CONTEXT_CORE_PROFILE_BIT) { GLint numExtens = 0; glGetIntegerv(GL_NUM_EXTENSIONS, &numExtens); for(GLint i = 0; i < numExtens; ++i) { const char* e = (const char*)glGetStringi(GL_EXTENSIONS, i); if(!e) continue; if(stringCompare(e, "GL_EXT_paletted_texture") != 0) continue; DISPLAY.usingShaderedPalettes = false; break; } } else { const char* ext = (const char*)glGetString(GL_EXTENSIONS); DISPLAY.usingShaderedPalettes = !( ext && strstr(ext, "GL_EXT_paletted_texture") ); } #endif #elif DOLPHIN VIDEO_Init(); DISPLAY.screenMode = VIDEO_GetPreferredMode(NULL); DISPLAY.frameBuffer[0] = MEM_K0_TO_K1( SYS_AllocateFramebuffer(DISPLAY.screenMode) ); DISPLAY.frameBuffer[1] = MEM_K0_TO_K1( SYS_AllocateFramebuffer(DISPLAY.screenMode) ); VIDEO_Configure(DISPLAY.screenMode); VIDEO_SetNextFramebuffer(DISPLAY.frameBuffer[DISPLAY.whichFrameBuffer]); // VIDEO_SetPostRetraceCallback(copy_buffers); VIDEO_SetBlack(FALSE); VIDEO_Flush(); VIDEO_WaitVSync(); if(DISPLAY.screenMode->viTVMode & VI_NON_INTERLACE) VIDEO_WaitVSync(); DISPLAY.fifoBuffer = memalign(32, DISPLAY_FIFO_SIZE); memoryZero(DISPLAY.fifoBuffer, DISPLAY_FIFO_SIZE); GX_Init(DISPLAY.fifoBuffer, DISPLAY_FIFO_SIZE); // This seems to be mostly related to interlacing vs progressive GX_SetViewport( 0, 0, DISPLAY.screenMode->fbWidth, DISPLAY.screenMode->efbHeight, 0, 1 ); float_t yscale = GX_GetYScaleFactor( DISPLAY.screenMode->efbHeight, DISPLAY.screenMode->xfbHeight ); uint32_t xfbHeight = GX_SetDispCopyYScale(yscale); GX_SetScissor( 0, 0, DISPLAY.screenMode->fbWidth, DISPLAY.screenMode->efbHeight ); GX_SetDispCopySrc( 0, 0, DISPLAY.screenMode->fbWidth, DISPLAY.screenMode->efbHeight ); GX_SetDispCopyDst(DISPLAY.screenMode->fbWidth, xfbHeight); GX_SetCopyFilter( DISPLAY.screenMode->aa, DISPLAY.screenMode->sample_pattern, GX_TRUE, DISPLAY.screenMode->vfilter ); GX_SetFieldMode( DISPLAY.screenMode->field_rendering, ( (DISPLAY.screenMode->viHeight == 2 * DISPLAY.screenMode->xfbHeight) ? GX_ENABLE : GX_DISABLE ) ); // Setup cull modes GX_SetCullMode(GX_CULL_NONE); GX_SetZMode(GX_FALSE, GX_ALWAYS, GX_FALSE); GX_CopyDisp(DISPLAY.frameBuffer[DISPLAY.whichFrameBuffer], GX_TRUE); GX_SetDispCopyGamma(GX_GM_1_0); GX_ClearVtxDesc(); GX_SetVtxDesc(GX_VA_POS, GX_INDEX16); GX_SetVtxDesc(GX_VA_CLR0, GX_INDEX16); GX_SetVtxDesc(GX_VA_TEX0, GX_INDEX16); GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0); GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_U8, 0); GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0); #endif quadInit(); frameBufferInitBackbuffer(); spriteBatchInit(); errorChain(textInit()); errorChain(assetLoad("main_palette.dpf", &PALETTES[0])); screenInit(); errorOk(); } errorret_t displayUpdate(void) { #if DISPLAY_SDL2 SDL_Event event; while(SDL_PollEvent(&event)) { switch(event.type) { case SDL_QUIT: { ENGINE.running = false; break; } case SDL_WINDOWEVENT: { switch(event.window.event) { case SDL_WINDOWEVENT_CLOSE: { ENGINE.running = false; break; } default: { break; } } } default: { break; } } } SDL_GL_MakeCurrent(DISPLAY.window, DISPLAY.glContext); #endif // Reset state spriteBatchClear(); frameBufferBind(NULL); // Bind screen and render scene screenBind(); frameBufferClear( FRAMEBUFFER_CLEAR_COLOR | FRAMEBUFFER_CLEAR_DEPTH, SCREEN.background ); errorChain(sceneRender()); // Render UI // uiRender(); // Finish up screenUnbind(); screenRender(); #if DISPLAY_SDL2 SDL_GL_SwapWindow(DISPLAY.window); GLenum err; while((err = glGetError()) != GL_NO_ERROR) { debugPrint("GL Error: %d\n", err); } #elif DOLPHIN GX_DrawDone(); DISPLAY.whichFrameBuffer ^= 1; GX_SetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE); GX_SetColorUpdate(GX_TRUE); GX_CopyDisp(DISPLAY.frameBuffer[DISPLAY.whichFrameBuffer], GX_TRUE); VIDEO_SetNextFramebuffer(DISPLAY.frameBuffer[DISPLAY.whichFrameBuffer]); VIDEO_Flush(); VIDEO_WaitVSync(); #endif // For now, we just return an OK error. errorOk(); } errorret_t displayDispose(void) { spriteBatchDispose(); screenDispose(); textDispose(); #if DISPLAY_SDL2 if(DISPLAY.glContext) { SDL_GL_DeleteContext(DISPLAY.glContext); DISPLAY.glContext = NULL; } if(DISPLAY.window) { SDL_DestroyWindow(DISPLAY.window); DISPLAY.window = NULL; } SDL_Quit(); #endif // For now, we just return an OK error. errorOk(); }