/** * 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, GL_RGBA,(texturedata_t){ .rgba = { .colors = 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; #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; #endif } void frameBufferBind(const framebuffer_t *framebuffer) { if(framebuffer == NULL) { #if DISPLAY_SDL2 frameBufferBind(&FRAMEBUFFER_BACKBUFFER); #endif 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) ); #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); #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 }