diff --git a/archive/platform/sdl2.c b/archive/platform/sdl2.c index 3cbc3e6..f1e0963 100644 --- a/archive/platform/sdl2.c +++ b/archive/platform/sdl2.c @@ -7,99 +7,7 @@ #include "sdl2.h" -void displaySDL2Init(void) { - 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"); - } - - #if PSP - displayInitPSP(); - #else - - #endif -} - void displaySDL2Update(void) { - 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); } void displaySDL2Swap(void) { diff --git a/cmake/targets/linux.cmake b/cmake/targets/linux.cmake index bc78a13..3bb1a48 100644 --- a/cmake/targets/linux.cmake +++ b/cmake/targets/linux.cmake @@ -14,4 +14,7 @@ target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC DUSK_SDL2 DUSK_OPENGL DUSK_LINUX + DUSK_DISPLAY_SIZE_DYNAMIC + DUSK_DISPLAY_WIDTH_DEFAULT=640 + DUSK_DISPLAY_HEIGHT_DEFAULT=480 ) \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 15eaeca..00cde61 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -8,12 +8,12 @@ add_subdirectory(dusk) if(DUSK_TARGET_SYSTEM STREQUAL "linux") add_subdirectory(dusklinux) add_subdirectory(dusksdl2) - add_subdirectory(duskopengl) + add_subdirectory(duskgl) elseif(DUSK_TARGET_SYSTEM STREQUAL "psp") add_subdirectory(duskpsp) add_subdirectory(dusksdl2) - add_subdirectory(duskopengl) + add_subdirectory(duskgl) elseif(DUSK_TARGET_SYSTEM STREQUAL "gamecube" OR DUSK_TARGET_SYSTEM STREQUAL "wii") add_subdirectory(duskdolphin) diff --git a/src/dusk/display/CMakeLists.txt b/src/dusk/display/CMakeLists.txt index 08af9fc..fcad3fd 100644 --- a/src/dusk/display/CMakeLists.txt +++ b/src/dusk/display/CMakeLists.txt @@ -7,9 +7,6 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME} PUBLIC display.c - framebuffer.c - screen.c - spritebatch.c text.c ) diff --git a/src/dusk/display/camera/camera.c b/src/dusk/display/camera/camera.c index 059a2d4..6dd0c21 100644 --- a/src/dusk/display/camera/camera.c +++ b/src/dusk/display/camera/camera.c @@ -8,8 +8,8 @@ #include "camera.h" #include "display/display.h" #include "assert/assert.h" -#include "display/framebuffer.h" -#include "display/screen.h" +#include "display/framebuffer/framebuffer.h" +#include "display/screen/screen.h" void cameraInit(camera_t *camera) { cameraInitPerspective(camera); diff --git a/src/dusk/display/display.c b/src/dusk/display/display.c index a61d6a6..1c925dd 100644 --- a/src/dusk/display/display.c +++ b/src/dusk/display/display.c @@ -7,11 +7,11 @@ #include "display/display.h" #include "engine/engine.h" -#include "display/framebuffer.h" +#include "display/framebuffer/framebuffer.h" #include "scene/scene.h" -#include "display/spritebatch.h" +#include "display/spritebatch/spritebatch.h" #include "display/mesh/quad.h" -#include "display/screen.h" +#include "display/screen/screen.h" #include "ui/ui.h" #include "debug/debug.h" #include "display/text.h" @@ -25,10 +25,8 @@ display_t DISPLAY = { 0 }; errorret_t displayInit(void) { memoryZero(&DISPLAY, sizeof(DISPLAY)); - #if DISPLAY_SDL2 - displaySDL2Init(); - #elif DOLPHIN - displayDolphinInit(); + #ifdef displayPlatformInit + errorChain(displayPlatformInit()); #endif quadInit(); @@ -42,8 +40,8 @@ errorret_t displayInit(void) { } errorret_t displayUpdate(void) { - #if DISPLAY_SDL2 - displaySDL2Update(); + #ifdef displayPlatformUpdate + errorChain(displayPlatformUpdate()); #endif // Reset state @@ -60,19 +58,16 @@ errorret_t displayUpdate(void) { errorChain(sceneRender()); // Render UI - // uiRender(); + uiRender(); // Finish up screenUnbind(); screenRender(); - #if DISPLAY_SDL2 - displaySDL2Swap(); - #elif DOLPHIN - displayDolphinSwap(); + // Swap and return. + #ifdef displayPlatformSwap + errorChain(displayPlatformSwap()); #endif - - // For now, we just return an OK error. errorOk(); } @@ -80,9 +75,9 @@ errorret_t displayDispose(void) { spriteBatchDispose(); screenDispose(); textDispose(); - - #if DISPLAY_SDL2 - displaySDL2Dispose(); + + #ifdef displayPlatformDispose + displayPlatformDispose(); #endif // For now, we just return an OK error. diff --git a/src/dusk/display/display.h b/src/dusk/display/display.h index 0407fff..d6775f7 100644 --- a/src/dusk/display/display.h +++ b/src/dusk/display/display.h @@ -6,12 +6,35 @@ */ #pragma once -#include "displaydefs.h" -#include "error/error.h" -#include "display/camera/camera.h" -#include "display/framebuffer.h" #include "display/displayplatform.h" +#include "display/camera/camera.h" +// Expecting some definitions to be provided +#ifndef DUSK_DISPLAY_SIZE_DYNAMIC + #ifndef DUSK_DISPLAY_WIDTH + #error "DUSK_DISPLAY_WIDTH must be defined." + #endif + #ifndef DUSK_DISPLAY_HEIGHT + #error "DUSK_DISPLAY_HEIGHT must be defined" + #endif + #define DISPLAY_WINDOW_WIDTH_DEFAULT DUSK_DISPLAY_WIDTH + #define DISPLAY_WINDOW_HEIGHT_DEFAULT DUSK_DISPLAY_HEIGHT +#else + #ifndef DUSK_DISPLAY_WIDTH_DEFAULT + #error "DUSK_DISPLAY_WIDTH_DEFAULT must be defined." + #endif + #ifndef DUSK_DISPLAY_HEIGHT_DEFAULT + #error "DUSK_DISPLAY_HEIGHT_DEFAULT must be defined." + #endif + #ifdef DUSK_DISPLAY_WIDTH + #error "DUSK_DISPLAY_WIDTH should not be defined." + #endif + #ifdef DUSK_DISPLAY_HEIGHT + #error "DUSK_DISPLAY_HEIGHT should not be defined." + #endif +#endif + +// Main Display Struct, platform-speicifc typedef displayplatform_t display_t; extern display_t DISPLAY; diff --git a/src/dusk/display/displaydefs.h b/src/dusk/display/displaydefs.h deleted file mode 100644 index fdb4358..0000000 --- a/src/dusk/display/displaydefs.h +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright (c) 2025 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "dusk.h" - -#if DISPLAY_SDL2 - #include - - #define GL_GLEXT_PROTOTYPES - #include - #include - - #ifndef DISPLAY_SIZE_DYNAMIC - #define DISPLAY_SIZE_DYNAMIC 1 - #endif -#elif DOLPHIN - // Dolphin. - #define DISPLAY_FIFO_SIZE (256*1024) -#else - #error "Need to specify display backend." -#endif - -#if DISPLAY_SIZE_DYNAMIC == 0 - #ifndef DISPLAY_WIDTH - #error "DISPLAY_WIDTH must be defined when DISPLAY_SIZE_DYNAMIC is 0." - #endif - #ifndef DISPLAY_HEIGHT - #error "DISPLAY_HEIGHT must be defined when DISPLAY_SIZE_DYNAMIC is 0." - #endif -#endif - -#ifndef DISPLAY_WINDOW_WIDTH_DEFAULT - #error "DISPLAY_WINDOW_WIDTH_DEFAULT must be defined." -#endif -#ifndef DISPLAY_WINDOW_HEIGHT_DEFAULT - #define DISPLAY_WINDOW_HEIGHT_DEFAULT DISPLAY_HEIGHT -#endif \ No newline at end of file diff --git a/src/dusk/display/framebuffer.c b/src/dusk/display/framebuffer.c deleted file mode 100644 index 6ec197a..0000000 --- a/src/dusk/display/framebuffer.c +++ /dev/null @@ -1,192 +0,0 @@ -/** - * Copyright (c) 2025 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "framebuffer.h" -#include "display/display.h" -#include "assert/assert.h" -#include "util/memory.h" - -framebuffer_t FRAMEBUFFER_BACKBUFFER = {0}; -const framebuffer_t *FRAMEBUFFER_BOUND = &FRAMEBUFFER_BACKBUFFER; - -void frameBufferInitBackbuffer() { - memoryZero(&FRAMEBUFFER_BACKBUFFER, sizeof(framebuffer_t)); - - FRAMEBUFFER_BACKBUFFER.id = -1; - FRAMEBUFFER_BOUND = &FRAMEBUFFER_BACKBUFFER; -} - -#if DISPLAY_SIZE_DYNAMIC == 1 - void frameBufferInit( - framebuffer_t *framebuffer, - const uint32_t width, - const uint32_t height - ) { - #if DISPLAY_SDL2 == 1 - assertNotNull(framebuffer, "Framebuffer cannot be NULL"); - assertTrue(width > 0 && height > 0, "W/H must be greater than 0"); - - memoryZero(framebuffer, sizeof(framebuffer_t)); - textureInit(&framebuffer->texture, width, height, TEXTURE_FORMAT_RGBA,(texturedata_t){ - .rgbaColors = NULL - }); - - glGenFramebuffersEXT(1, &framebuffer->id); - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer->id); - - glFramebufferTexture2DEXT( - GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, - GL_TEXTURE_2D, framebuffer->texture.id, 0 - ); - - if( - glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) != - GL_FRAMEBUFFER_COMPLETE_EXT - ) { - assertUnreachable("Framebuffer is not complete"); - } - - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); - #endif - } -#endif - -int32_t frameBufferGetWidth(const framebuffer_t *framebuffer) { - #if DISPLAY_SDL2 - if(framebuffer == &FRAMEBUFFER_BACKBUFFER) { - #if DISPLAY_SIZE_DYNAMIC == 0 - return DISPLAY_WIDTH; - #else - int32_t windowWidth, windowHeight; - SDL_GetWindowSize(DISPLAY.window, &windowWidth, &windowHeight); - return windowWidth; - #endif - } - - return framebuffer->texture.width; - - #elif DOLPHIN - return DISPLAY.screenMode->fbWidth; - - #else - #error "Unsupported DISPLAY_TYPE." - - #endif -} - -int32_t frameBufferGetHeight(const framebuffer_t *framebuffer) { - #if DISPLAY_SDL2 - if(framebuffer == &FRAMEBUFFER_BACKBUFFER) { - #if DISPLAY_SIZE_DYNAMIC == 0 - return DISPLAY_HEIGHT; - #else - int32_t windowWidth, windowHeight; - SDL_GetWindowSize(DISPLAY.window, &windowWidth, &windowHeight); - return windowHeight; - #endif - } - - return framebuffer->texture.height; - - #elif DOLPHIN - return DISPLAY.screenMode->efbHeight; - - #else - #error "Unsupported DISPLAY_TYPE." - - #endif -} - -void frameBufferBind(const framebuffer_t *framebuffer) { - if(framebuffer == NULL) { - frameBufferBind(&FRAMEBUFFER_BACKBUFFER); - FRAMEBUFFER_BOUND = &FRAMEBUFFER_BACKBUFFER; - return; - } - - // Bind the framebuffer for rendering - #if DISPLAY_SDL2 - if(framebuffer == &FRAMEBUFFER_BACKBUFFER) { - #if PSP - - #else - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); - #endif - } else { - #if PSP - assertUnreachable("Framebuffers not supported on PSP"); - #else - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer->id); - #endif - } - - glViewport( - 0, 0, - frameBufferGetWidth(framebuffer), frameBufferGetHeight(framebuffer) - ); - - #elif DOLPHIN - GX_InvVtxCache(); - GX_InvalidateTexAll(); - GX_SetZMode(GX_FALSE, GX_ALWAYS, GX_FALSE); - - GX_SetViewport( - 0, 0, - frameBufferGetWidth(framebuffer), - frameBufferGetHeight(framebuffer), - 0, 1 - ); - - #endif - - FRAMEBUFFER_BOUND = framebuffer; -} - -void frameBufferClear(uint8_t flags, color_t color) { - #if DISPLAY_SDL2 - GLbitfield glFlags = 0; - - if(flags & FRAMEBUFFER_CLEAR_COLOR) { - glFlags |= GL_COLOR_BUFFER_BIT; - glClearColor( - color.r / 255.0f, - color.g / 255.0f, - color.b / 255.0f, - color.a / 255.0f - ); - } - - if(flags & FRAMEBUFFER_CLEAR_DEPTH) { - glFlags |= GL_DEPTH_BUFFER_BIT; - } - - glClear(glFlags); - #elif DOLPHIN - GX_SetCopyClear( - (GXColor){ color.r, color.g, color.b, color.a }, - GX_MAX_Z24 - ); - - #endif -} - -void frameBufferDispose(framebuffer_t *framebuffer) { - assertNotNull(framebuffer, "Framebuffer cannot be NULL"); - - #if DISPLAY_SDL2 - if(framebuffer == &FRAMEBUFFER_BACKBUFFER) { - assertUnreachable("Cannot dispose of backbuffer"); - } - - #if DISPLAY_SIZE_DYNAMIC == 0 - assertUnreachable("Dynamic size framebuffers not supported"); - #else - textureDispose(&framebuffer->texture); - glDeleteFramebuffersEXT(1, &framebuffer->id); - #endif - #endif -} \ No newline at end of file diff --git a/src/dusk/display/framebuffer/framebuffer.c b/src/dusk/display/framebuffer/framebuffer.c new file mode 100644 index 0000000..4c1487e --- /dev/null +++ b/src/dusk/display/framebuffer/framebuffer.c @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "framebuffer.h" +#include "display/display.h" +#include "assert/assert.h" +#include "util/memory.h" + +framebuffer_t FRAMEBUFFER_BACKBUFFER = {0}; +const framebuffer_t *FRAMEBUFFER_BOUND = &FRAMEBUFFER_BACKBUFFER; + +errorret_t frameBufferInitBackbuffer() { + memoryZero(&FRAMEBUFFER_BACKBUFFER, sizeof(framebuffer_t)); + errorChain(frameBufferPlatformInitBackbuffer()); + FRAMEBUFFER_BOUND = &FRAMEBUFFER_BACKBUFFER; +} + +void frameBufferBind(framebuffer_t *framebuffer) { + if(framebuffer == NULL) { + frameBufferBind(&FRAMEBUFFER_BACKBUFFER); + FRAMEBUFFER_BOUND = &FRAMEBUFFER_BACKBUFFER; + return; + } + + errorChain(frameBufferPlatformBind(framebuffer)); + FRAMEBUFFER_BOUND = framebuffer; + errorOk(); +} + +void frameBufferDispose(framebuffer_t *framebuffer) { + assertNotNull(framebuffer, "Framebuffer cannot be NULL"); + + #if DISPLAY_SDL2 + if(framebuffer == &FRAMEBUFFER_BACKBUFFER) { + assertUnreachable("Cannot dispose of backbuffer"); + } + + #if DISPLAY_SIZE_DYNAMIC == 0 + assertUnreachable("Dynamic size framebuffers not supported"); + #else + textureDispose(&framebuffer->texture); + glDeleteFramebuffersEXT(1, &framebuffer->id); + #endif + #endif +} + +#ifdef DUSK_DISPLAY_SIZE_DYNAMIC + errorret_t frameBufferInit( + framebuffer_t *fb, + const uint32_t width, + const uint32_t height + ) { + assertNotNull(fb, "Framebuffer cannot be NULL"); + assertTrue(width > 0 && height > 0, "W/H must be greater than 0"); + + memoryZero(fb, sizeof(framebuffer_t)); + errorChain(frameBufferPlatformInit(fb, width, height)); + errorOk(); + } +#endif \ No newline at end of file diff --git a/src/dusk/display/framebuffer.h b/src/dusk/display/framebuffer/framebuffer.h similarity index 54% rename from src/dusk/display/framebuffer.h rename to src/dusk/display/framebuffer/framebuffer.h index 3c7fd44..0a33ee7 100644 --- a/src/dusk/display/framebuffer.h +++ b/src/dusk/display/framebuffer/framebuffer.h @@ -6,46 +6,32 @@ */ #pragma once +#include "error/error.h" +#include "display/framebuffer/framebufferplatform.h" #include "display/texture/texture.h" +// Expected defs. +#ifndef frameBufferPlatformInitBackBuffer + #error "frameBufferPlatformInitBackBuffer not defined for this platform" +#endif +#ifndef frameBufferPlatformBind + #error "frameBufferPlatformBind not defined for this platform" +#endif + #define FRAMEBUFFER_CLEAR_COLOR (1 << 0) #define FRAMEBUFFER_CLEAR_DEPTH (1 << 1) -typedef struct { - #if DISPLAY_SDL2 == 1 - // OpenGL Framebuffer Object ID - GLuint id; - texture_t texture; - #elif DOLPHIN - // --- IGNORE --- - uint8_t id; - #else - #error "Framebuffers not implemented on this platform." - #endif -} framebuffer_t; +typedef framebufferplatform_t framebuffer_t; extern framebuffer_t FRAMEBUFFER_BACKBUFFER; extern const framebuffer_t *FRAMEBUFFER_BOUND; /** * Initializes the backbuffer framebuffer. - */ -void frameBufferInitBackbuffer(void); - -/** - * Initializes a framebuffer. * - * @param framebuffer The framebuffer to initialize. - * @param width The width of the framebuffer. - * @param height The height of the framebuffer. + * @return Error for initialization of the backbuffer. */ -#if DISPLAY_SIZE_DYNAMIC == 1 - void frameBufferInit( - framebuffer_t *framebuffer, - const uint32_t width, - const uint32_t height - ); -#endif +errorret_t frameBufferInitBackBuffer(void); /** * Gets the width of the framebuffer. @@ -53,7 +39,7 @@ void frameBufferInitBackbuffer(void); * @param framebuffer The framebuffer to get the width of. * @return The width of the framebuffer, or 0 if the framebuffer is NULL. */ -int32_t frameBufferGetWidth(const framebuffer_t *framebuffer); +uint32_t frameBufferGetWidth(const framebuffer_t *framebuffer); /** * Gets the height of the framebuffer. @@ -61,7 +47,7 @@ int32_t frameBufferGetWidth(const framebuffer_t *framebuffer); * @param framebuffer The framebuffer to get the height of. * @return The height of the framebuffer, or 0 if the framebuffer is NULL. */ -int32_t frameBufferGetHeight(const framebuffer_t *framebuffer); +uint32_t frameBufferGetHeight(const framebuffer_t *framebuffer); /** * Binds the framebuffer for rendering, or the backbuffer if the framebuffer @@ -80,30 +66,29 @@ void frameBufferBind(const framebuffer_t *framebuffer); void frameBufferClear(uint8_t flags, color_t color); /** - * Disposes of the framebuffer using EXT methods. + * Disposes of the framebuffer. Will also be used for request disposing of the + * backbuffer. * * @param framebuffer The framebuffer to dispose of. */ void frameBufferDispose(framebuffer_t *framebuffer); -// #if RENDER_USE_FRAMEBUFFER -// typedef struct { -// GLuint id; -// texture_t texture; -// } framebuffer_t; +#ifdef DUSK_DISPLAY_SIZE_DYNAMIC + #ifndef frameBufferPlatformInit + #error "frameBufferPlatformInit not defined for this platform" + #endif -// /** -// * Initializes a framebuffer using EXT methods. -// * -// * @param framebuffer The framebuffer to initialize. -// * @param width The width of the framebuffer. -// * @param height The height of the framebuffer. -// * @return An error code indicating success or failure. -// */ -// void frameBufferInit( -// framebuffer_t *framebuffer, -// const uint32_t width, -// const uint32_t height -// ); - -// #endif \ No newline at end of file + /** + * Initializes a framebuffer. + * + * @param framebuffer The framebuffer to initialize. + * @param width The width of the framebuffer. + * @param height The height of the framebuffer. + * @return Error for initialization of the framebuffer. + */ + errorret_t frameBufferInit( + framebuffer_t *framebuffer, + const uint32_t width, + const uint32_t height + ); +#endif \ No newline at end of file diff --git a/src/dusk/display/screen.c b/src/dusk/display/screen/screen.c similarity index 100% rename from src/dusk/display/screen.c rename to src/dusk/display/screen/screen.c diff --git a/src/dusk/display/screen.h b/src/dusk/display/screen/screen.h similarity index 100% rename from src/dusk/display/screen.h rename to src/dusk/display/screen/screen.h diff --git a/src/dusk/display/spritebatch.c b/src/dusk/display/spritebatch/spritebatch.c similarity index 100% rename from src/dusk/display/spritebatch.c rename to src/dusk/display/spritebatch/spritebatch.c diff --git a/src/dusk/display/spritebatch.h b/src/dusk/display/spritebatch/spritebatch.h similarity index 100% rename from src/dusk/display/spritebatch.h rename to src/dusk/display/spritebatch/spritebatch.h diff --git a/src/dusk/display/texture/texture.c b/src/dusk/display/texture/texture.c index 3bcc750..7f1fdf1 100644 --- a/src/dusk/display/texture/texture.c +++ b/src/dusk/display/texture/texture.c @@ -11,9 +11,9 @@ #include "util/math.h" #include "display/display.h" -const texture_t *TEXTURE_BOUND = NULL; +texture_t *TEXTURE_BOUND = NULL; -void textureInit( +errorret_t textureInit( texture_t *texture, const int32_t width, const int32_t height, @@ -22,401 +22,29 @@ void textureInit( ) { assertNotNull(texture, "Texture cannot be NULL"); assertTrue(width > 0 && height > 0, "width/height must be greater than 0"); - memoryZero(texture, sizeof(texture_t)); texture->width = width; texture->height = height; texture->format = format; - assertTrue(width == mathNextPowTwo(width), "Width must be a power of 2."); assertTrue(height == mathNextPowTwo(height), "Height must be a power of 2."); - #if DISPLAY_SDL2 - glGenTextures(1, &texture->id); - glBindTexture(GL_TEXTURE_2D, texture->id); - - switch(format) { - case TEXTURE_FORMAT_RGBA: - glTexImage2D( - GL_TEXTURE_2D, 0, format, width, height, 0, - format, GL_UNSIGNED_BYTE, (void*)data.rgbaColors - ); - break; - - case TEXTURE_FORMAT_PALETTE: - assertNotNull(data.paletteData, "Palette texture data cannot be NULL"); - - if(DISPLAY.usingShaderedPalettes) { - // Palette textures not supported, convert to GL_RED style texture - // so shader can perform the lookup. - uint8_t formatted[width * height]; - for(int32_t i = 0; i < width * height; i++) { - uint8_t index = data.paletteData[i]; - formatted[i] = index * 128; - } - glTexImage2D( - GL_TEXTURE_2D, 0, GL_R8, width, height, 0, - GL_RED, GL_UNSIGNED_BYTE, (void*)formatted - ); - - } else { - glTexImage2D( - GL_TEXTURE_2D, - 0, GL_COLOR_INDEX8_EXT, - width, height, - 0, GL_COLOR_INDEX8_EXT, - GL_UNSIGNED_BYTE, (void*)data.paletteData - ); - // glColorTableEXT( - // GL_TEXTURE_2D, GL_RGBA, data.palette.palette->colorCount, GL_RGBA, - // GL_UNSIGNED_BYTE, (const void*)data.palette.palette->colors - // ); - } - - GLenum err = glGetError(); - if(err != GL_NO_ERROR) { - printf("GL Error uploading palette texture: %d\n", err); - assertUnreachable("GL error uploading palette texture"); - } - break; - - default: - assertUnreachable("Unknown texture format"); - break; - } - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - - glBindTexture(GL_TEXTURE_2D, 0); - texture->ready = true; - - #elif DOLPHIN - - switch(format) { - case TEXTURE_FORMAT_RGBA: - assertTrue( - (width % 4) == 0 && (height % 4) == 0, - "RGB5A3 requires w/h multiple of 4 (or pad)" - ); - - // Convert to RGB5A3 format - size_t rgbaSize = width * height * sizeof(u16); - texture->rgba = (u16*)memalign(32, rgbaSize); - assertNotNull(texture->rgba, "Failed to allocate texture RGBA data"); - - for(uint32_t y = 0; y < height; ++y) { - for(uint32_t x = 0; x < width; ++x) { - const int src = y * width + x; - - const int tileX = x >> 2; - const int tileY = y >> 2; - const int tilesPerRow = width >> 2; - const int tileIndex = tileY * tilesPerRow + tileX; - const int tileBaseWords = tileIndex * 16; - const int inTile = ((y & 3) << 2) + (x & 3); - const int dest = tileBaseWords + inTile; - - color4b_t col = data.rgba.colors[src]; - - u16 outCol; - if(col.a < 255) { - // 0AAA RRRR GGGG BBBB - outCol = ( - (0u << 15) | - ((u16)(col.a >> 5) << 12) | - ((u16)(col.r >> 4) << 8) | - ((u16)(col.g >> 4) << 4) | - ((u16)(col.b >> 4) << 0) - ); - } else { - // 1RRRR RRGG GGGB BBBB - outCol = ( - (1u << 15) | - ((u16)(col.r >> 3) << 10) | - ((u16)(col.g >> 3) << 5) | - ((u16)(col.b >> 3) << 0) - ); - } - texture->rgba[dest] = outCol; - } - } - - DCFlushRange(texture->rgba, rgbaSize); - GX_InitTexObj( - &texture->texObj, - texture->rgba, - width, height, - GX_TF_RGB5A3, - GX_REPEAT, GX_REPEAT, - GX_FALSE - ); - - DCFlushRange(texture->rgba, rgbaSize); - GX_InvalidateTexAll(); - - GX_InitTexObjLOD( - &texture->texObj, - GX_NEAR, GX_NEAR, - 0.0f, 0.0f, 0.0f, - GX_FALSE, - GX_FALSE, - GX_ANISO_1 - ); - break; - - case TEXTURE_FORMAT_ALPHA: { - assertTrue( - (width % 4) == 0 && (height % 4) == 0, - "GX_TF_I8 requires w/h multiple of 4 (or pad)" - ); - - // 1 byte per pixel (I8), GX expects 4x4 tiled layout - const size_t alphaSize = (size_t)width * (size_t)height; - - texture->alpha = (u8*)memalign(32, alphaSize); - assertNotNull(texture->alpha, "Failed to allocate alpha texture data"); - - const u32 tilesPerRow = ((u32)width) >> 3; // /8 - - for (u32 y = 0; y < (u32)height; ++y) { - const u32 tileY = y >> 2; // /4 - const u32 inTileY = (y & 3) << 3; // (y%4)*8 - - for (u32 x = 0; x < (u32)width; ++x) { - const u32 srcI = y * (u32)width + x; - const u8 srcA = data.alpha.data[srcI]; // linear input - - const u32 tileX = x >> 3; // /8 - const u32 tileIndex = tileY * tilesPerRow + tileX; - - const u32 tileBase = tileIndex * 32; // 8*4*1 = 32 bytes per tile - const u32 inTile = inTileY + (x & 7); // (y%4)*8 + (x%8) - - texture->alpha[tileBase + inTile] = 0xFF - srcA;// Fixes inverted alpha. - } - } - - // Flush CPU cache so GX sees the swizzled I8 texture data - DCFlushRange(texture->alpha, alphaSize); - - // Initialize GX texture object with swizzled data - GX_InitTexObj( - &texture->texObj, - texture->alpha, - width, height, - GX_TF_I8, - GX_REPEAT, GX_REPEAT, - GX_FALSE - ); - - GX_InitTexObjLOD( - &texture->texObj, - GX_NEAR, GX_NEAR, - 0.0f, 0.0f, 0.0f, - GX_FALSE, - GX_FALSE, - GX_ANISO_1 - ); - break; - } - - case TEXTURE_FORMAT_PALETTE: { - // Not supported, convert to RGBA using lookup - color_t* formatted = memoryAllocate(width * height * sizeof(color_t)); - for(int32_t i = 0; i < width * height; i++) { - uint8_t index = data.palette.data[i]; - assertTrue( - index < data.palette.palette->colorCount, - "Palette index out of range" - ); - formatted[i] = data.palette.palette->colors[index]; - } - - textureInit( - texture, width, height, TEXTURE_FORMAT_RGBA, - (texturedata_t){ - .rgba = { .colors = formatted } - } - ); - memoryFree(formatted); - break; - } - - default: - assertUnreachable("Unsupported texture format for Dolphin"); - break; - } - - texture->ready = true; - #endif + errorChain(textureInitPlatform(texture, width, height, format, data)); + errorOk(); } -void textureBind(texture_t *texture) { - if(TEXTURE_BOUND == texture) return; - - if(texture == NULL) { - #if DISPLAY_SDL2 - glDisable(GL_TEXTURE_2D); - #elif DOLPHIN - GX_SetNumChans(0); - #endif - TEXTURE_BOUND = NULL; - return; - } - - assertTrue(texture->ready, "Texture ID must be ready"); - assertTrue( - texture->width > 0 && texture->height > 0, - "Texture width and height must be greater than 0" - ); - - #if DISPLAY_SDL2 - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, texture->id); - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - #elif DOLPHIN - GX_SetNumChans(1); - GX_LoadTexObj(&texture->texObj, GX_TEXMAP0); - #endif - TEXTURE_BOUND = texture; +errorret_t textureBind(texture_t *texture) { + errorChain(textureBindPlatform(texture)); + errorOk(); } -void textureDispose(texture_t *texture) { +errorret_t textureDispose(texture_t *texture) { assertNotNull(texture, "Texture cannot be NULL"); - assertTrue(texture->ready, "Texture ID must be ready"); if(TEXTURE_BOUND == texture) { textureBind(NULL); } - #if DISPLAY_SDL2 - glDeleteTextures(1, &texture->id); - #elif DOLPHIN - switch(texture->format) { - case TEXTURE_FORMAT_RGBA: - free(texture->rgba); - break; - - case TEXTURE_FORMAT_ALPHA: - free(texture->alpha); - break; - - default: - break; - } - - memoryZero(texture, sizeof(texture_t)); - #endif -} - -#if DOLPHIN - void textureDolphinUploadTEV() { - if(TEXTURE_BOUND == NULL) { - GX_SetNumTexGens(0); - GX_SetNumTevStages(1); - GX_SetTevOp(GX_TEVSTAGE0, GX_PASSCLR); - return; - } - - // Add channel for vertex color - GX_SetNumChans(1); - GX_SetChanCtrl( - GX_COLOR0A0,// Store in color channel 0 - GX_DISABLE,// Lighting disabled - GX_SRC_REG,// Ambient color? - GX_SRC_VTX,// Material color? - GX_LIGHTNULL,// Light Mask - GX_DF_NONE,// Diffuse function - GX_AF_NONE// Attenuation function - ); - - // One set of UVs - GX_SetNumTexGens(1); - GX_SetTexCoordGen( - GX_TEXCOORD0, - GX_TG_MTX2x4, - GX_TG_TEX0, - GX_IDENTITY - ); - - // Basically the shader setup - switch(TEXTURE_BOUND->format) { - case TEXTURE_FORMAT_RGBA: - // One TEV stage: vertex color * texture color - GX_SetNumTevStages(1); - GX_SetTevOp(GX_TEVSTAGE0, GX_MODULATE); - GX_SetTevOrder( - GX_TEVSTAGE0, - GX_TEXCOORD0, - GX_TEXMAP0, - GX_COLOR0A0 - ); - GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR); - GX_SetAlphaCompare(GX_ALWAYS, 0, GX_AOP_AND, GX_ALWAYS, 0); - break; - - case TEXTURE_FORMAT_ALPHA: - // One TEV stage: vertex color * texture color - GX_SetNumTevStages(1); - GX_SetTevOrder( - GX_TEVSTAGE0, - GX_TEXCOORD0, - GX_TEXMAP0, - GX_COLOR0A0 - ); - - // Color = vertex color - GX_SetTevColorIn( - GX_TEVSTAGE0, - GX_CC_RASC, - GX_CC_ZERO, - GX_CC_ZERO, - GX_CC_ZERO - ); - GX_SetTevColorOp( - GX_TEVSTAGE0, - GX_TEV_ADD, - GX_TB_ZERO, - GX_CS_SCALE_1, - GX_TRUE, - GX_TEVPREV - ); - - // Alpha = vertex alpha * I8 intensity - GX_SetTevAlphaIn( - GX_TEVSTAGE0, - GX_CA_RASA, - GX_CA_ZERO, - GX_CA_TEXA, - GX_CA_ZERO - ); - GX_SetTevAlphaOp( - GX_TEVSTAGE0, - GX_TEV_ADD, - GX_TB_ZERO, - GX_CS_SCALE_1, - GX_TRUE, - GX_TEVPREV - ); - - GX_SetBlendMode( - GX_BM_BLEND, - GX_BL_SRCALPHA, - GX_BL_INVSRCALPHA, - GX_LO_CLEAR - ); - - GX_SetColorUpdate(GX_TRUE); - GX_SetAlphaUpdate(GX_TRUE); - - break; - - default: - assertUnreachable("Unknown texture format in meshDraw"); - break; - } - } -#endif \ No newline at end of file + errorChain(textureDisposePlatform(texture)); + errorOk(); +} \ No newline at end of file diff --git a/src/dusk/display/texture/texture.h b/src/dusk/display/texture/texture.h index b10b676..02e8bec 100644 --- a/src/dusk/display/texture/texture.h +++ b/src/dusk/display/texture/texture.h @@ -6,44 +6,30 @@ */ #pragma once +#include "error/error.h" #include "display/color.h" -#include "display/displaydefs.h" #include "display/texture/palette.h" +#include "display/texture/textureplatform.h" -typedef enum { - #if DISPLAY_SDL2 - TEXTURE_FORMAT_RGBA = GL_RGBA, - TEXTURE_FORMAT_PALETTE = GL_COLOR_INDEX8_EXT, - #elif DOLPHIN - // TEXTURE_FORMAT_RGBA = GX_TF_RGBA8, - // TEXTURE_FORMAT_ALPHA = GX_TF_A8, - TEXTURE_FORMAT_PALETTE = GX_TF_CI8, - #endif -} textureformat_t; +#ifndef textureInitPlatform + #error "textureInitPlatform should not be defined." +#endif +#ifndef textureBindPlatform + #error "textureBindPlatform should not be defined." +#endif +#ifndef textureDisposePlatform + #error "textureDisposePlatform should not be defined." +#endif -typedef struct { - #if DISPLAY_SDL2 - GLuint id; - #elif DOLPHIN - GXTexObj texObj; - union { - u16 *rgba; - u8 *alpha; - }; - #endif - - textureformat_t format; - bool_t ready; - int32_t width; - int32_t height; -} texture_t; +typedef textureformatplatform_t textureformat_t; +typedef textureplatform_t texture_t; -typedef union { +typedef union texturedata_u { uint8_t *paletteData; color_t *rgbaColors; } texturedata_t; -extern const texture_t *TEXTURE_BOUND; +extern texture_t *TEXTURE_BOUND; /** * Initializes a texture. @@ -54,7 +40,7 @@ extern const texture_t *TEXTURE_BOUND; * @param format The format of the texture (e.g., GL_RGBA, GL_ALPHA). * @param data The data for the texture, the format changes per format. */ -void textureInit( +errorret_t textureInit( texture_t *texture, const int32_t width, const int32_t height, @@ -67,18 +53,11 @@ void textureInit( * * @param texture The texture to bind. */ -void textureBind(texture_t *texture); +errorret_t textureBind(texture_t *texture); /** * Disposes a texture. * * @param texture The texture to dispose. */ -void textureDispose(texture_t *texture); - -#if DOLPHIN - /** - * Uploads the TEV settings for the currently bound texture. - */ - void textureDolphinUploadTEV(); -#endif \ No newline at end of file +errorret_t textureDispose(texture_t *texture); \ No newline at end of file diff --git a/src/duskopengl/CMakeLists.txt b/src/duskgl/CMakeLists.txt similarity index 80% rename from src/duskopengl/CMakeLists.txt rename to src/duskgl/CMakeLists.txt index ea21ad6..d7391b8 100644 --- a/src/duskopengl/CMakeLists.txt +++ b/src/duskgl/CMakeLists.txt @@ -9,4 +9,6 @@ target_include_directories(${DUSK_LIBRARY_TARGET_NAME} ${CMAKE_CURRENT_LIST_DIR} ) -# Subdirs \ No newline at end of file +# Subdirs +add_subdirectory(display) +add_subdirectory(error) \ No newline at end of file diff --git a/src/duskgl/display/CMakeLists.txt b/src/duskgl/display/CMakeLists.txt new file mode 100644 index 0000000..36983e2 --- /dev/null +++ b/src/duskgl/display/CMakeLists.txt @@ -0,0 +1,14 @@ +# Copyright (c) 2026 Dominic Masters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +# Sources +target_sources(${DUSK_LIBRARY_TARGET_NAME} + PUBLIC + displaygl.c +) + +# Subdirs +add_subdirectory(framebuffer) +add_subdirectory(texture) \ No newline at end of file diff --git a/src/duskgl/display/displaygl.c b/src/duskgl/display/displaygl.c new file mode 100644 index 0000000..212accc --- /dev/null +++ b/src/duskgl/display/displaygl.c @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "displaygl.h" + +errorret_t displayOpenGLInit(void) { + glDisable(GL_CULL_FACE); + glDisable(GL_LIGHTING);// PSP defaults this on? + glShadeModel(GL_SMOOTH); // Fixes color on PSP? + errorChain(errorGLCheck()); + + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LEQUAL); + glClearDepth(1.0f); + errorChain(errorGLCheck()); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + errorChain(errorGLCheck()); + + glEnableClientState(GL_COLOR_ARRAY);// To confirm: every frame on PSP? + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_VERTEX_ARRAY); + errorChain(errorGLCheck()); +} \ No newline at end of file diff --git a/src/duskgl/display/displaygl.h b/src/duskgl/display/displaygl.h new file mode 100644 index 0000000..1f2d59a --- /dev/null +++ b/src/duskgl/display/displaygl.h @@ -0,0 +1,14 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "error/errorgl.h" + +/** + * Initializes the OpenGL specific contexts for rendering. + */ +errorret_t displayOpenGLInit(void); \ No newline at end of file diff --git a/src/duskgl/display/framebuffer/CMakeLists.txt b/src/duskgl/display/framebuffer/CMakeLists.txt new file mode 100644 index 0000000..8f09211 --- /dev/null +++ b/src/duskgl/display/framebuffer/CMakeLists.txt @@ -0,0 +1,9 @@ +# Copyright (c) 2026 Dominic Masters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +# Sources +target_sources(${DUSK_LIBRARY_TARGET_NAME} + PUBLIC +) \ No newline at end of file diff --git a/src/duskgl/display/framebuffer/framebuffergl.c b/src/duskgl/display/framebuffer/framebuffergl.c new file mode 100644 index 0000000..5e275f4 --- /dev/null +++ b/src/duskgl/display/framebuffer/framebuffergl.c @@ -0,0 +1,133 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "display/display.h" +#include "display/framebuffer/framebuffer.h" +#include "assert/assert.h" + +errorret_t frameBufferGLInitBackBuffer(void) { + errorOk(); +} + +uint32_t frameBufferGetWidth(const framebuffer_t *framebuffer) { + if(framebuffer == NULL) { + return 0; + } + + if(framebuffer == &FRAMEBUFFER_BACKBUFFER) { + #ifdef DUSK_DISPLAY_SIZE_DYNAMIC + int32_t windowWidth, windowHeight; + SDL_GetWindowSize(DISPLAY.window, &windowWidth, &windowHeight); + return windowWidth; + #else + return DUSK_DISPLAY_WIDTH; + #endif + } + + return framebuffer->texture.width; +} + +uint32_t frameBufferGLGetHeight(const framebuffer_t *framebuffer) { + if(framebuffer == NULL) { + return 0; + } + + if(framebuffer == &FRAMEBUFFER_BACKBUFFER) { + #ifdef DUSK_DISPLAY_SIZE_DYNAMIC + int32_t windowWidth, windowHeight; + SDL_GetWindowSize(DISPLAY.window, &windowWidth, &windowHeight); + return windowHeight; + #else + return DUSK_DISPLAY_HEIGHT; + #endif + } + + return framebuffer->texture.height; +} + +errorret_t frameBufferGLBind(framebuffer_t *framebuffer) { + assertNotNull(framebuffer, "Framebuffer cannot be NULL"); + + #ifdef DUSK_DISPLAY_SIZE_DYNAMIC + if(framebuffer == &FRAMEBUFFER_BACKBUFFER) { + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + } else { + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer->id); + } + + glViewport( + 0, 0, + frameBufferGetWidth(framebuffer), frameBufferGetHeight(framebuffer) + ); + #else + glViewport( + 0, 0, + DUSK_DISPLAY_WIDTH, DUSK_DISPLAY_HEIGHT + ); + #endif + + errorChain(errorGLCheck()); + errorOk(); +} + +void frameBufferClear(const uint8_t flags, const color_t color) { + GLbitfield glFlags = 0; + + if(flags & FRAMEBUFFER_CLEAR_COLOR) { + glFlags |= GL_COLOR_BUFFER_BIT; + glClearColor( + color.r / 255.0f, + color.g / 255.0f, + color.b / 255.0f, + color.a / 255.0f + ); + } + + if(flags & FRAMEBUFFER_CLEAR_DEPTH) { + glFlags |= GL_DEPTH_BUFFER_BIT; + } + + glClear(glFlags); +} + +#ifdef DUSK_DISPLAY_SIZE_DYNAMIC + errorret_t frameBufferGLInit( + framebuffer_t *fb, + const uint32_t width, + const uint32_t height + ) { + assertNotNull(fb, "Framebuffer cannot be NULL"); + assertTrue(width > 0 && height > 0, "W/H must be greater than 0"); + + memoryZero(fb, sizeof(framebuffer_t)); + textureInit(&fb->texture, width, height, TEXTURE_FORMAT_RGBA,(texturedata_t){ + .rgbaColors = NULL + }); + errorChain(errorGLCheck()); + + glGenFramebuffersEXT(1, &fb->id); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb->id); + errorChain(errorGLCheck()); + + glFramebufferTexture2DEXT( + GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, + GL_TEXTURE_2D, fb->texture.id, 0 + ); + errorChain(errorGLCheck()); + + if( + glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) != + GL_FRAMEBUFFER_COMPLETE_EXT + ) { + assertUnreachable("Framebuffer is not complete"); + } + + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + errorChain(errorGLCheck()); + errorOk(); + } +#endif \ No newline at end of file diff --git a/src/duskgl/display/framebuffer/framebuffergl.h b/src/duskgl/display/framebuffer/framebuffergl.h new file mode 100644 index 0000000..df765d6 --- /dev/null +++ b/src/duskgl/display/framebuffer/framebuffergl.h @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "display/texture/texture.h" +#include "error/errorgl.h" + +typedef struct { + GLuint id; + texture_t texture; +} framebuffergl_t; + +/** + * Initializes the backbuffer framebuffer. (OpenGL implementation). + */ +errorret_t frameBufferGLInitBackBuffer(void); + +#ifdef DUSK_DISPLAY_SIZE_DYNAMIC + /** + * Initializes an OpenGL style framebuffer. + * + * @param fb The framebuffer to initialize. + * @param width The width of the framebuffer. + * @param height The height of the framebuffer. + * @return Either error or not. + */ + errorret_t frameBufferGLInit( + framebuffergl_t *fb, + const uint32_t width, + const uint32_t height + ); +#endif \ No newline at end of file diff --git a/src/duskgl/display/framebuffer/framebufferplatform.h b/src/duskgl/display/framebuffer/framebufferplatform.h new file mode 100644 index 0000000..6c693bf --- /dev/null +++ b/src/duskgl/display/framebuffer/framebufferplatform.h @@ -0,0 +1,14 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "display/framebuffer/framebuffergl.h" +typedef framebuffergl_t framebufferplatform_t; + +#define frameBufferPlatformInitBackBuffer frameBufferGLInitBackBuffer +#define frameBufferPlatformInit frameBufferGLInit +#define frameBufferPlatformBind frameBufferGLBind \ No newline at end of file diff --git a/src/duskgl/display/texture/CMakeLists.txt b/src/duskgl/display/texture/CMakeLists.txt new file mode 100644 index 0000000..b8a5af7 --- /dev/null +++ b/src/duskgl/display/texture/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright (c) 2026 Dominic Masters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +# Sources +target_sources(${DUSK_LIBRARY_TARGET_NAME} + PUBLIC + texturegl.c +) \ No newline at end of file diff --git a/src/duskgl/display/texture/texturegl.c b/src/duskgl/display/texture/texturegl.c new file mode 100644 index 0000000..6a7ff46 --- /dev/null +++ b/src/duskgl/display/texture/texturegl.c @@ -0,0 +1,86 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "display/texture/texture.h" +#include "assert/assert.h" +#include "error/errorgl.h" + +errorret_t textureInitGL( + texturegl_t *texture, + const int32_t width, + const int32_t height, + const textureformatgl_t format, + const texturedata_t data +) { + glGenTextures(1, &texture->id); + glBindTexture(GL_TEXTURE_2D, texture->id); + errorChain(errorGLCheck()); + + switch(format) { + case TEXTURE_FORMAT_RGBA: + glTexImage2D( + GL_TEXTURE_2D, 0, format, width, height, 0, + format, GL_UNSIGNED_BYTE, (void*)data.rgbaColors + ); + break; + + case TEXTURE_FORMAT_PALETTE: + assertNotNull(data.paletteData, "Palette texture data cannot be NULL"); + glTexImage2D( + GL_TEXTURE_2D, + 0, GL_COLOR_INDEX8_EXT, + width, height, + 0, GL_COLOR_INDEX8_EXT, + GL_UNSIGNED_BYTE, (void*)data.paletteData + ); + errorChain(errorGLCheck()); + + glColorTableEXT( + GL_TEXTURE_2D, GL_RGBA, data.palette.palette->colorCount, GL_RGBA, + GL_UNSIGNED_BYTE, (const void*)data.palette.palette->colors + ); + break; + + default: + assertUnreachable("Unknown texture format"); + break; + } + errorChain(errorGLCheck()); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + errorChain(errorGLCheck()); + + glBindTexture(GL_TEXTURE_2D, 0); + errorChain(errorGLCheck()); + + errorOk(); +} + +errorret_t textureBindGL(texturegl_t *texture) { + if(texture == NULL) { + glBindTexture(GL_TEXTURE_2D, 0); + errorChain(errorGLCheck()); + errorOk(); + } + + assertTrue(texture->id != 0, "Texture ID must be valid"); + glBindTexture(GL_TEXTURE_2D, texture->id); + errorChain(errorGLCheck()); + errorOk(); +} + +errorret_t textureDisposeGL(texturegl_t *texture) { + assertNotNull(texture, "Texture cannot be NULL"); + assertTrue(texture->id != 0, "Texture ID must be valid"); + + glDeleteTextures(1, &texture->id); + errorChain(errorGLCheck()); + errorOk(); +} \ No newline at end of file diff --git a/src/duskgl/display/texture/texturegl.h b/src/duskgl/display/texture/texturegl.h new file mode 100644 index 0000000..7d620d5 --- /dev/null +++ b/src/duskgl/display/texture/texturegl.h @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "dusk.h" + +typedef union texturedata_u texturedata_t; + +typedef enum { + TEXTURE_FORMAT_RGBA = GL_RGBA, + TEXTURE_FORMAT_PALETTE = GL_COLOR_INDEX8_EXT, +} textureformatgl_t; + +typedef struct { + GLuint id; + textureformatgl_t format; + int32_t width; + int32_t height; +} texturegl_t; + +/** + * Initializes a texture. + * + * @param texture The texture to initialize. + * @param width The width of the texture. + * @param height The height of the texture. + * @param format The format of the texture (e.g., GL_RGBA, GL_ALPHA). + * @param data The data for the texture, the format changes per format. + * @return An error if the texture failed to initialize, otherwise success. + */ +errorret_t textureInitGL( + texturegl_t *texture, + const int32_t width, + const int32_t height, + const textureformatgl_t format, + const texturedata_t data +); + +/** + * Binds a texture for rendering. Providing NULL will unbind any texture. + * + * @param texture The texture to bind. + * @return An error if the texture failed to bind, otherwise success. + */ +errorret_t textureBindGL(texturegl_t *texture); + +/** + * Disposes a texture. + * + * @param texture The texture to dispose. + * @return An error if the texture failed to dispose, otherwise success. + */ +errorret_t textureDisposeGL(texturegl_t *texture); \ No newline at end of file diff --git a/src/duskgl/display/texture/textureplatform.h b/src/duskgl/display/texture/textureplatform.h new file mode 100644 index 0000000..c540531 --- /dev/null +++ b/src/duskgl/display/texture/textureplatform.h @@ -0,0 +1,16 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "display/texture/texturegl.h" + +typedef textureformatgl_t textureformatplatform_t; +typedef texturegl_t textureplatform_t; + +#define textureInitPlatform textureInitGL +#define textureBindPlatform textureBindGL +#define textureDisposePlatform textureDisposeGL \ No newline at end of file diff --git a/src/duskdolphin/display/displayplatform.h b/src/duskgl/duskgl.h similarity index 69% rename from src/duskdolphin/display/displayplatform.h rename to src/duskgl/duskgl.h index f2d7268..58940a2 100644 --- a/src/duskdolphin/display/displayplatform.h +++ b/src/duskgl/duskgl.h @@ -6,4 +6,6 @@ */ #pragma once -#include "display/displaysdl2.h" \ No newline at end of file +#define GL_GLEXT_PROTOTYPES +#include +#include \ No newline at end of file diff --git a/src/duskgl/error/CMakeLists.txt b/src/duskgl/error/CMakeLists.txt new file mode 100644 index 0000000..9a52452 --- /dev/null +++ b/src/duskgl/error/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright (c) 2026 Dominic Masters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +# Sources +target_sources(${DUSK_LIBRARY_TARGET_NAME} + PUBLIC + errorgl.c +) \ No newline at end of file diff --git a/src/duskgl/error/errorgl.c b/src/duskgl/error/errorgl.c new file mode 100644 index 0000000..af9ceb3 --- /dev/null +++ b/src/duskgl/error/errorgl.c @@ -0,0 +1,17 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "errorgl.h" + +errorret_t errorGLCheck(void) { + GLenum err = glGetError(); + if(err != GL_NO_ERROR) { + errorThrow("GL Error: %d", err); + } + + errorOk(); +} \ No newline at end of file diff --git a/src/duskgl/error/errorgl.h b/src/duskgl/error/errorgl.h new file mode 100644 index 0000000..d73d3f8 --- /dev/null +++ b/src/duskgl/error/errorgl.h @@ -0,0 +1,16 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "error/error.h" + +/** + * Gets the GL error (if any) and throws it as a dusk style error. + * + * @return Error code, if GL error present. + */ +errorret_t errorGLCheck(void); \ No newline at end of file diff --git a/src/dusklinux/display/displayplatform.h b/src/dusklinux/display/displayplatform.h new file mode 100644 index 0000000..b193667 --- /dev/null +++ b/src/dusklinux/display/displayplatform.h @@ -0,0 +1,16 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "display/displaysdl2.h" + +typedef displaysdl2_t displayplatform_t; + +#define displayPlatformInit displaySDL2Init +#define displayPlatformUpdate displaySDL2Update +#define displayPlatformSwap displaySDL2Swap +#define displayPlatformDispose displaySDL2Dispose \ No newline at end of file diff --git a/src/duskopengl/duskopengl.h b/src/duskopengl/duskopengl.h deleted file mode 100644 index 127e1eb..0000000 --- a/src/duskopengl/duskopengl.h +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once \ No newline at end of file diff --git a/src/dusksdl2/CMakeLists.txt b/src/dusksdl2/CMakeLists.txt index ea21ad6..a968cc8 100644 --- a/src/dusksdl2/CMakeLists.txt +++ b/src/dusksdl2/CMakeLists.txt @@ -9,4 +9,5 @@ target_include_directories(${DUSK_LIBRARY_TARGET_NAME} ${CMAKE_CURRENT_LIST_DIR} ) -# Subdirs \ No newline at end of file +# Subdirs +add_subdirectory(display) \ No newline at end of file diff --git a/src/dusksdl2/display/CMakeLists.txt b/src/dusksdl2/display/CMakeLists.txt new file mode 100644 index 0000000..8db4dd7 --- /dev/null +++ b/src/dusksdl2/display/CMakeLists.txt @@ -0,0 +1,12 @@ +# Copyright (c) 2026 Dominic Masters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +# Sources +target_sources(${DUSK_LIBRARY_TARGET_NAME} + PUBLIC + displaysdl2.c +) + +# Subdirs \ No newline at end of file diff --git a/src/dusksdl2/display/displaysdl2.c b/src/dusksdl2/display/displaysdl2.c new file mode 100644 index 0000000..2d9ff5e --- /dev/null +++ b/src/dusksdl2/display/displaysdl2.c @@ -0,0 +1,111 @@ +/** + * Copyright (c) 2026 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/displaygl.h" + +erroret_t displaySDL2Init(void) { + 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, + DUSK_WINDOW_WIDTH_DEFAULT, + DUSK_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()); + } + errorChain(errorGLCheck()); + + SDL_GL_SetSwapInterval(1); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + errorChain(errorGLCheck()); + + errorChain(displayOpenGLInit()); + + + // #if DUSK_PSP + // errorChain(displayPSPInit()); + // #endif + + errorChain(errorGLCheck()); +} + +errorret_t displaySDL2Update(void) { + 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); + errorOk(); +} + +errorret_t displaySDL2Swap(void) { + SDL_GL_SwapWindow(DISPLAY.window); + + GLenum err; + while((err = glGetError()) != GL_NO_ERROR) { + debugPrint("GL Error: %d\n", err); + } + errorOk(); +} + +void displaySDL2Dispose(void) { + if(DISPLAY.glContext) { + SDL_GL_DeleteContext(DISPLAY.glContext); + DISPLAY.glContext = NULL; + } + if(DISPLAY.window) { + SDL_DestroyWindow(DISPLAY.window); + DISPLAY.window = NULL; + } + SDL_Quit(); +} \ No newline at end of file diff --git a/src/dusksdl2/display/displaysdl2.h b/src/dusksdl2/display/displaysdl2.h index bc53421..ebee170 100644 --- a/src/dusksdl2/display/displaysdl2.h +++ b/src/dusksdl2/display/displaysdl2.h @@ -6,10 +6,30 @@ */ #pragma once -#include "dusk.h" +#include "error/error.h" typedef struct { SDL_Window *window; SDL_GLContext glContext; bool_t usingShaderedPalettes; -} displayplatform_t; \ No newline at end of file +} displaysdl2_t; + +/** + * Initializes the SDL2 display system. + */ +errorret_t displaySDL2Init(void); + +/** + * Handles SDL2 Updating (pre-render context initialization) + */ +errorret_t displaySDL2Update(void); + +/** + * Handles SDL2 Display Swapping (to switch from frontbuffer to backbuffer). + */ +errorret_t displaySDL2Swap(void); + +/** + * Disposes/Cleans up the SDL2 Render system. + */ +void displaySDL2Dispose(void); \ No newline at end of file diff --git a/src/dusksdl2/dusksdl2.h b/src/dusksdl2/dusksdl2.h index c92f39e..44a23c2 100644 --- a/src/dusksdl2/dusksdl2.h +++ b/src/dusksdl2/dusksdl2.h @@ -6,4 +6,5 @@ */ #pragma once -#include "duskopengl.h" \ No newline at end of file +#include +#include "duskgl.h" \ No newline at end of file