diff --git a/source/external/jail_shader.cpp b/source/external/jail_shader.cpp index 4a76be4..4bc4bc0 100644 --- a/source/external/jail_shader.cpp +++ b/source/external/jail_shader.cpp @@ -19,6 +19,12 @@ namespace shader { + // Constantes + const GLuint INVALID_SHADER_ID = 0; + const GLuint INVALID_PROGRAM_ID = 0; + const GLuint DEFAULT_TEXTURE_ID = 1; + + // Variables globales SDL_Window *win = nullptr; SDL_Renderer *renderer = nullptr; GLuint programId = 0; @@ -42,6 +48,7 @@ namespace shader PFNGLGETPROGRAMIVPROC glGetProgramiv; PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog; PFNGLUSEPROGRAMPROC glUseProgram; + PFNGLDELETEPROGRAMPROC glDeleteProgram; bool initGLExtensions() { @@ -58,14 +65,26 @@ namespace shader glGetProgramiv = (PFNGLGETPROGRAMIVPROC)SDL_GL_GetProcAddress("glGetProgramiv"); glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)SDL_GL_GetProcAddress("glGetProgramInfoLog"); glUseProgram = (PFNGLUSEPROGRAMPROC)SDL_GL_GetProcAddress("glUseProgram"); + glDeleteProgram = (PFNGLDELETEPROGRAMPROC)SDL_GL_GetProcAddress("glDeleteProgram"); return glCreateShader && glShaderSource && glCompileShader && glGetShaderiv && glGetShaderInfoLog && glDeleteShader && glAttachShader && glCreateProgram && glLinkProgram && glValidateProgram && glGetProgramiv && glGetProgramInfoLog && - glUseProgram; + glUseProgram && glDeleteProgram; } #endif + // Función para verificar errores de OpenGL + void checkGLError(const char* operation) + { + GLenum error = glGetError(); + if (error != GL_NO_ERROR) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, + "Error OpenGL en %s: 0x%x", operation, error); + } + } + // Función para compilar un shader a partir de un std::string GLuint compileShader(const std::string &source, GLuint shader_type) { @@ -77,6 +96,12 @@ namespace shader // Crear identificador del shader GLuint shader_id = glCreateShader(shader_type); + if (shader_id == INVALID_SHADER_ID) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error al crear el shader."); + checkGLError("glCreateShader"); + return INVALID_SHADER_ID; + } // Agregar una directiva según el tipo de shader std::string directive = (shader_type == GL_VERTEX_SHADER) @@ -87,9 +112,11 @@ namespace shader // Especificar el código fuente del shader glShaderSource(shader_id, 2, sources, nullptr); + checkGLError("glShaderSource"); // Compilar el shader glCompileShader(shader_id); + checkGLError("glCompileShader"); // Verificar si la compilación fue exitosa GLint compiled_ok = GL_FALSE; @@ -106,7 +133,7 @@ namespace shader SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Registro de compilación del shader: %s", log.data()); } glDeleteShader(shader_id); - shader_id = 0; + return INVALID_SHADER_ID; } return shader_id; } @@ -115,50 +142,134 @@ namespace shader GLuint compileProgram(const std::string &vertex_shader_source, const std::string &fragment_shader_source) { GLuint program_id = glCreateProgram(); + if (program_id == INVALID_PROGRAM_ID) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error al crear el programa de shaders."); + checkGLError("glCreateProgram"); + return INVALID_PROGRAM_ID; + } // Si el fragment shader está vacío, reutilizamos el código del vertex shader GLuint vertex_shader_id = compileShader(vertex_shader_source, GL_VERTEX_SHADER); GLuint fragment_shader_id = compileShader(fragment_shader_source.empty() ? vertex_shader_source : fragment_shader_source, GL_FRAGMENT_SHADER); - if (vertex_shader_id && fragment_shader_id) + if (vertex_shader_id != INVALID_SHADER_ID && fragment_shader_id != INVALID_SHADER_ID) { // Asociar los shaders al programa glAttachShader(program_id, vertex_shader_id); + checkGLError("glAttachShader vertex"); glAttachShader(program_id, fragment_shader_id); + checkGLError("glAttachShader fragment"); + glLinkProgram(program_id); - glValidateProgram(program_id); + checkGLError("glLinkProgram"); // Verificar el estado del enlace - GLint log_length; - glGetProgramiv(program_id, GL_INFO_LOG_LENGTH, &log_length); - if (log_length > 0) + GLint isLinked = GL_FALSE; + glGetProgramiv(program_id, GL_LINK_STATUS, &isLinked); + if (isLinked == GL_FALSE) { - std::vector log(log_length); - glGetProgramInfoLog(program_id, log_length, &log_length, log.data()); - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Registro de información del programa:\n%s", log.data()); + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error al enlazar el programa de shaders."); + GLint log_length; + glGetProgramiv(program_id, GL_INFO_LOG_LENGTH, &log_length); + if (log_length > 0) + { + std::vector log(log_length); + glGetProgramInfoLog(program_id, log_length, &log_length, log.data()); + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Registro de enlace del programa: %s", log.data()); + } + glDeleteProgram(program_id); + program_id = INVALID_PROGRAM_ID; + } + else + { + glValidateProgram(program_id); + checkGLError("glValidateProgram"); + + // Log de información del programa (solo si hay información) + GLint log_length; + glGetProgramiv(program_id, GL_INFO_LOG_LENGTH, &log_length); + if (log_length > 1) // > 1 porque algunos drivers devuelven 1 para cadena vacía + { + std::vector log(log_length); + glGetProgramInfoLog(program_id, log_length, &log_length, log.data()); + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Registro de información del programa:\n%s", log.data()); + } } } - if (vertex_shader_id) + else + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: No se pudieron compilar los shaders."); + glDeleteProgram(program_id); + program_id = INVALID_PROGRAM_ID; + } + + // Limpiar los shaders (ya no son necesarios después del enlace) + if (vertex_shader_id != INVALID_SHADER_ID) { glDeleteShader(vertex_shader_id); } - if (fragment_shader_id) + if (fragment_shader_id != INVALID_SHADER_ID) { glDeleteShader(fragment_shader_id); } + return program_id; } + // Función para obtener el ID de textura OpenGL desde SDL3 + GLuint getTextureID(SDL_Texture* texture) + { + if (!texture) return DEFAULT_TEXTURE_ID; + + // Intentar obtener el ID de textura OpenGL desde las propiedades de SDL3 + SDL_PropertiesID props = SDL_GetTextureProperties(texture); + GLuint textureId = 0; + + // Intentar diferentes nombres de propiedades según la versión de SDL3 + textureId = (GLuint)(uintptr_t)SDL_GetPointerProperty(props, "SDL.texture.opengl.texture", nullptr); + + // Si la primera no funciona, intentar con el nombre alternativo + if (textureId == 0) { + textureId = (GLuint)(uintptr_t)SDL_GetPointerProperty(props, "texture.opengl.texture", nullptr); + } + + // Si aún no funciona, intentar obtener como número + if (textureId == 0) { + textureId = (GLuint)SDL_GetNumberProperty(props, "SDL.texture.opengl.texture", DEFAULT_TEXTURE_ID); + } + + // Si ninguna funciona, usar el método manual de bindeo de textura + if (textureId == 0) { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, + "No se pudo obtener el ID de textura OpenGL, usando ID por defecto (%d)", DEFAULT_TEXTURE_ID); + textureId = DEFAULT_TEXTURE_ID; + } + + return textureId; + } + bool init(SDL_Window *window, SDL_Texture *back_buffer_texture, const std::string &vertex_shader, const std::string &fragment_shader) { shader::win = window; shader::renderer = SDL_GetRenderer(window); shader::backBuffer = back_buffer_texture; - SDL_GetWindowSize(window, &win_size.x, &win_size.y); + + if (!shader::renderer) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: No se pudo obtener el renderer de la ventana."); + return false; + } + SDL_GetWindowSize(window, &win_size.x, &win_size.y); SDL_GetTextureSize(back_buffer_texture, &tex_size.x, &tex_size.y); const auto render_name = SDL_GetRendererName(renderer); + if (!render_name) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: No se pudo obtener el nombre del renderer."); + return false; + } // Verificar que el renderer sea OpenGL if (!strncmp(render_name, "opengl", 6)) @@ -166,41 +277,61 @@ namespace shader #ifndef __APPLE__ if (!initGLExtensions()) { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "ADVERTENCIA: No se han podido inicializar las extensiones de OpenGL."); + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "ERROR: No se han podido inicializar las extensiones de OpenGL."); usingOpenGL = false; return false; } #endif // Compilar el programa de shaders utilizando std::string programId = compileProgram(vertex_shader, fragment_shader); + if (programId == INVALID_PROGRAM_ID) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "ERROR: No se pudo compilar el programa de shaders."); + usingOpenGL = false; + return false; + } } else { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "ADVERTENCIA: El driver del renderer no es OpenGL."); + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "ADVERTENCIA: El driver del renderer no es OpenGL (%s).", render_name); usingOpenGL = false; return false; } + usingOpenGL = true; + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Sistema de shaders inicializado correctamente."); return true; } void render() { - GLint oldProgramId; // Establece el color de fondo SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); SDL_SetRenderTarget(renderer, nullptr); SDL_RenderClear(renderer); - if (usingOpenGL) + if (usingOpenGL && programId != INVALID_PROGRAM_ID) { - SDL_GetTextureProperties(backBuffer); - glBindTexture(GL_TEXTURE_2D, 1); - if (programId != 0) - { - glGetIntegerv(GL_CURRENT_PROGRAM, &oldProgramId); - glUseProgram(programId); - } + // Guardar estados de OpenGL + GLint oldProgramId; + glGetIntegerv(GL_CURRENT_PROGRAM, &oldProgramId); + + GLint oldViewport[4]; + glGetIntegerv(GL_VIEWPORT, oldViewport); + + GLboolean wasTextureEnabled = glIsEnabled(GL_TEXTURE_2D); + GLint oldTextureId; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldTextureId); + + // Obtener y bindear la textura + GLuint textureId = getTextureID(backBuffer); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, textureId); + checkGLError("glBindTexture"); + + // Usar nuestro programa de shaders + glUseProgram(programId); + checkGLError("glUseProgram"); // Recupera el tamaño lógico configurado con SDL_RenderSetLogicalSize int logicalW, logicalH; @@ -247,6 +378,7 @@ namespace shader } } glViewport(viewportX, viewportY, viewportW, viewportH); + checkGLError("glViewport"); // Configurar la proyección ortográfica usando el espacio lógico glMatrixMode(GL_PROJECTION); @@ -274,18 +406,50 @@ namespace shader glTexCoord2f(1.0f, 0.0f); glVertex2f(static_cast(logicalW), static_cast(logicalH)); glEnd(); + checkGLError("render quad"); SDL_GL_SwapWindow(win); - if (programId != 0) + // Restaurar estados de OpenGL + glUseProgram(oldProgramId); + glBindTexture(GL_TEXTURE_2D, oldTextureId); + if (!wasTextureEnabled) { - glUseProgram(oldProgramId); + glDisable(GL_TEXTURE_2D); } + glViewport(oldViewport[0], oldViewport[1], oldViewport[2], oldViewport[3]); } else { + // Fallback a renderizado normal de SDL SDL_RenderTexture(renderer, backBuffer, nullptr, nullptr); SDL_RenderPresent(renderer); } } + + void cleanup() + { + if (programId != INVALID_PROGRAM_ID) + { + glDeleteProgram(programId); + programId = INVALID_PROGRAM_ID; + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Programa de shaders liberado."); + } + + // Reinicializar variables + win = nullptr; + renderer = nullptr; + backBuffer = nullptr; + usingOpenGL = false; + } + + bool isUsingOpenGL() + { + return usingOpenGL; + } + + GLuint getProgramId() + { + return programId; + } } \ No newline at end of file diff --git a/source/resource.cpp b/source/resource.cpp index 96ce9a4..a93ddf3 100644 --- a/source/resource.cpp +++ b/source/resource.cpp @@ -49,7 +49,6 @@ void Resource::load() // Muerstra la ventana y desactiva el sincronismo vertical auto screen = Screen::get(); auto vsync = screen->getVSync(); - screen->show(); screen->setVSync(false); SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n** LOADING RESOURCES"); diff --git a/source/screen.cpp b/source/screen.cpp index 73b09c8..6458523 100644 --- a/source/screen.cpp +++ b/source/screen.cpp @@ -50,9 +50,6 @@ Screen::Screen() game_canvas_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height); SDL_SetTextureScaleMode(game_canvas_, SDL_SCALEMODE_NEAREST); - // Inicializa variables - adjustRenderLogicalSize(); - // Crea el objeto de texto createText(); @@ -62,8 +59,13 @@ Screen::Screen() #endif // Inicializa los shaders - show(); initShaders(); + + // Al parecer los shaders en macos y linux no funcionan si no se ha renderizado almenos una vez + auto temp = Options::video.shaders; + Options::video.shaders = false; + renderScreen(); + Options::video.shaders = temp; } // Destructor @@ -275,23 +277,26 @@ void Screen::initShaders() // Calcula el tamaño de la ventana void Screen::adjustWindowSize() { - // Establece el nuevo tamaño - const int WIDTH = param.game.width * Options::window.size; - const int HEIGHT = param.game.height * Options::window.size; + // if (!Options::video.fullscreen) + { + // Establece el nuevo tamaño + const int WIDTH = param.game.width * Options::window.size; + const int HEIGHT = param.game.height * Options::window.size; - int old_width, old_height; - SDL_GetWindowSize(window_, &old_width, &old_height); + int old_width, old_height; + SDL_GetWindowSize(window_, &old_width, &old_height); - int old_pos_x, old_pos_y; - SDL_GetWindowPosition(window_, &old_pos_x, &old_pos_y); + int old_pos_x, old_pos_y; + SDL_GetWindowPosition(window_, &old_pos_x, &old_pos_y); - const int NEW_POS_X = old_pos_x + (old_width - WIDTH) / 2; - const int NEW_POS_Y = old_pos_y + (old_height - HEIGHT) / 2; + const int NEW_POS_X = old_pos_x + (old_width - WIDTH) / 2; + const int NEW_POS_Y = old_pos_y + (old_height - HEIGHT) / 2; - SDL_SetWindowPosition(window_, std::max(NEW_POS_X, WINDOWS_DECORATIONS_), std::max(NEW_POS_Y, 0)); - SDL_SetWindowSize(window_, WIDTH, HEIGHT); + SDL_SetWindowPosition(window_, std::max(NEW_POS_X, WINDOWS_DECORATIONS_), std::max(NEW_POS_Y, 0)); + SDL_SetWindowSize(window_, WIDTH, HEIGHT); - initShaders(); + initShaders(); + } } // Renderiza todos los overlays y efectos @@ -360,10 +365,10 @@ bool Screen::initSDLVideo() else { SDL_SetRenderDrawColor(renderer_, 0x00, 0x00, 0x00, 0xFF); - SDL_SetRenderLogicalPresentation(renderer_, param.game.width, param.game.height, SDL_LOGICAL_PRESENTATION_INTEGER_SCALE); - SDL_SetWindowFullscreen(window_, Options::video.fullscreen); + SDL_SetRenderLogicalPresentation(renderer_, param.game.width, param.game.height, Options::video.integer_scale ? SDL_LOGICAL_PRESENTATION_INTEGER_SCALE : SDL_LOGICAL_PRESENTATION_LETTERBOX); SDL_SetRenderDrawBlendMode(renderer_, SDL_BLENDMODE_BLEND); SDL_SetRenderVSync(renderer_, Options::video.v_sync ? 1 : SDL_RENDERER_VSYNC_DISABLED); + SDL_SetWindowFullscreen(window_, Options::video.fullscreen); } } } @@ -414,11 +419,17 @@ void Screen::getDisplayInfo() } } +// Alterna entre activar y desactivar los shaders +void Screen::toggleShaders() +{ + Options::video.shaders = !Options::video.shaders; +} + // Alterna entre activar y desactivar el escalado entero void Screen::toggleIntegerScale() { Options::video.integer_scale = !Options::video.integer_scale; - SDL_SetRenderLogicalPresentation(Screen::get()->getRenderer(), param.game.width, param.game.height, Options::video.integer_scale ? SDL_LOGICAL_PRESENTATION_INTEGER_SCALE : SDL_LOGICAL_PRESENTATION_LETTERBOX); + SDL_SetRenderLogicalPresentation(renderer_, param.game.width, param.game.height, Options::video.integer_scale ? SDL_LOGICAL_PRESENTATION_INTEGER_SCALE : SDL_LOGICAL_PRESENTATION_LETTERBOX); } // Alterna entre activar y desactivar el V-Sync @@ -455,5 +466,5 @@ void Screen::applySettings() void Screen::createText() { auto texture = std::make_shared(getRenderer(), Asset::get()->get("aseprite.png")); - text_ = std::make_unique(texture, Asset::get()->get("aseprite.txt")); + text_ = std::make_shared(texture, Asset::get()->get("aseprite.txt")); } \ No newline at end of file diff --git a/source/screen.h b/source/screen.h index 8de09cb..530b7c0 100644 --- a/source/screen.h +++ b/source/screen.h @@ -34,16 +34,16 @@ public: // --- Configuración de ventana y render --- void setFullscreenMode(bool mode = Options::video.fullscreen); // Establece el modo de video - void toggleFullscreen(); // Cambia entre pantalla completa y ventana - void setWindowZoom(int size); // Cambia el tamaño de la ventana - bool decWindowSize(); // Reduce el tamaño de la ventana - bool incWindowSize(); // Aumenta el tamaño de la ventana - void applySettings(); // Aplica los valores de las opciones + void toggleFullscreen(); // Cambia entre pantalla completa y ventana + void setWindowZoom(int size); // Cambia el tamaño de la ventana + bool decWindowSize(); // Reduce el tamaño de la ventana + bool incWindowSize(); // Aumenta el tamaño de la ventana + void applySettings(); // Aplica los valores de las opciones // --- Efectos visuales --- void shake() { shake_effect_.enable(src_rect_, dst_rect_); } // Agita la pantalla void flash(Color color, int lenght = 10, int delay = 0) { flash_effect_ = FlashEffect(true, lenght, delay, color); } // Pone la pantalla de color - void toggleShaders() { Options::video.shaders = !Options::video.shaders; } // Alterna entre activar y desactivar los shaders + void toggleShaders(); // Alterna entre activar y desactivar los shaders void toggleIntegerScale(); // Alterna entre activar y desactivar el escalado entero void toggleVSync(); // Alterna entre activar y desactivar el V-Sync void setVSync(bool enabled); // Establece el estado del V-Sync @@ -54,7 +54,7 @@ public: void show() { SDL_ShowWindow(window_); } // Muestra la ventana void hide() { SDL_HideWindow(window_); } // Oculta la ventana void getSingletons(); // Obtiene los punteros a los singletones - bool getVSync() const { return Options::video.v_sync; } // Obtiene el valor de V-Sync + bool getVSync() const { return Options::video.v_sync; } // Obtiene el valor de V-Sync std::shared_ptr getText() const { return text_; } // Obtiene el puntero al texto de Screen #ifdef DEBUG @@ -197,7 +197,7 @@ private: std::shared_ptr text_; // Objeto para escribir texto en pantalla // --- Métodos internos --- - bool initSDLVideo(); // Arranca SDL VIDEO y crea la ventana + bool initSDLVideo(); // Arranca SDL VIDEO y crea la ventana void renderFlash(); // Dibuja el efecto de flash en la pantalla void renderShake(); // Aplica el efecto de agitar la pantalla void renderInfo(); // Muestra información por pantalla @@ -205,7 +205,6 @@ private: void loadShaders(); // Carga el contenido del archivo GLSL void initShaders(); // Inicializa los shaders void adjustWindowSize(); // Calcula el tamaño de la ventana - void adjustRenderLogicalSize() { SDL_SetRenderLogicalPresentation(renderer_, param.game.width, param.game.height, SDL_LOGICAL_PRESENTATION_INTEGER_SCALE); } void getDisplayInfo(); // Obtiene información sobre la pantalla void renderOverlays(); // Renderiza todos los overlays y efectos void renderAttenuate(); // Atenúa la pantalla