#pragma once #include #include #include #include #include #include #include "core/rendering/shader_backend.hpp" #include "utils/defaults.hpp" class Screen { public: // Configuració typada de Screen. L'omple el Director des d'Options, // de manera que Screen no coneix YAML ni la capa Options directament. struct Config { int zoom{Defaults::Window::ZOOM}; bool fullscreen{Defaults::Window::FULLSCREEN}; bool vsync{Defaults::Video::VSYNC}; Defaults::ScalingMode scaling_mode{Defaults::Video::SCALING_MODE}; Defaults::TextureFilter texture_filter{Defaults::Video::TEXTURE_FILTER}; bool gpu_acceleration{Defaults::Video::GPU_ACCELERATION}; std::string preferred_driver{Defaults::Video::GPU_PREFERRED_DRIVER}; }; // Meyers singleton només per a l'accés (get()). El cicle de vida de SDL // el porta el Director: configure() i shutdown() s'han de cridar // explícitament, NO el destructor estàtic. El patró atexit + destructor // estàtic provoca segfaults a vkDestroyInstance en Wayland/Vulkan perquè // alguns recursos de SDL ja són inconsistents quan corre el destructor. static auto get() -> Screen&; Screen(const Screen&) = delete; Screen(Screen&&) = delete; auto operator=(const Screen&) -> Screen& = delete; auto operator=(Screen&&) -> Screen& = delete; // Inicialitza SDL_VIDEO i crea finestra + renderer o shader backend GPU // segons la config. Cal cridar-lo una vegada abans de qualsevol clear()/present(). void configure(const Config& config); // Destrueix tots els recursos SDL (finestra, GPU device, renderer, textura) // i tanca el subsistema de vídeo. El Director el crida ABANS de SDL_Quit. void shutdown(); // Reenvia paràmetres PostFX al backend GPU. No-op si estem al path // SDL_Renderer (fallback) o si encara no hi ha backend. void applyPostFXParams(const Rendering::PostFXParams& params); // Selecciona el shader actiu (PostFX o CrtPi). No-op al path fallback. void setActiveShader(Rendering::ShaderType type); // Reenvia paràmetres CrtPi al backend GPU. No-op al path fallback. void applyCrtPiParams(const Rendering::CrtPiParams& params); // Canvis lleugers que NO destrueixen la finestra. Zoom i fullscreen // redimensionen SDL_Window i mantenen la finestra centrada respecte a // l'anterior posició; VSync s'envia directament al backend GPU. void setWindowZoom(int zoom); void setFullscreen(bool fullscreen); void setVSync(bool vsync); // Mode d'escalat del path GPU (letterbox/integer/stretch/...). Al // fallback SDL_Renderer es propaga via SDL_SetRenderLogicalPresentation. void setScalingMode(Defaults::ScalingMode mode); void clear(std::uint8_t r = 0, std::uint8_t g = 0, std::uint8_t b = 0); // Pinta un rectangle opac al canvas ARGB del joc. Pensat per a HUDs // simples (caixes de notificacions, barres d'estat); no gasta paleta. void fillRect(int x, int y, int w, int h, std::uint32_t argb); // Alpha-blend sobre el canvas: `alpha=0` no fa res, `alpha=255` equival // a fillRect. El color entra com a ARGB little-endian; `alpha` és el // factor de mescla amb el que ja hi ha al canvas. Pensat per a overlays // (menú, pauses) que volen veure's a través el joc atenuat. void blendRect(int x, int y, int w, int h, std::uint32_t argb, std::uint8_t alpha); // Hook invocat al principi de `present()`, abans de pujar els píxels al // backend. Serveix per a pintar overlays que han de quedar per damunt // de qualsevol escena (p.ex. el menú de servei). El Director el registra // un sol cop; Screen no coneix el tipus concret del callback. void setOverlayRenderer(std::function callback); void present(); [[nodiscard]] auto getRenderer() const -> SDL_Renderer* { return renderer_; } [[nodiscard]] auto getCanvasArgb() -> std::uint32_t* { return canvas_.data(); } [[nodiscard]] auto getCanvasWidth() const -> int { return canvas_width_; } [[nodiscard]] auto getCanvasHeight() const -> int { return canvas_height_; } // Mètriques per a overlays de debug (RenderInfo). [[nodiscard]] auto getLastFPS() const -> int { return fps_.last_value; } [[nodiscard]] auto getGPUDriver() const -> const std::string& { return gpu_driver_; } private: Screen() = default; ~Screen() = default; void destroySdl(); void createSdl(); // Redimensiona la finestra existent i la recenta sense tancar-la; al // fallback SDL_Renderer també ajusta la presentació lògica. void adjustWindowSize(); // Comptador intern de frames per segon. S'incrementa a cada present() i // es recalcula quan ha passat un segon complet (SDL_GetTicks en ms). struct FPS { std::uint32_t ticks{0}; int frame_count{0}; int last_value{0}; void increment() { ++frame_count; } void calculate(std::uint32_t current_ticks) { if (current_ticks - ticks >= 1000) { last_value = frame_count; frame_count = 0; ticks = current_ticks; } } }; bool subsystem_initialized_{false}; Config config_{}; SDL_Window* window_{nullptr}; // Camí SDL_Renderer (fallback quan gpu_acceleration = false). SDL_Renderer* renderer_{nullptr}; SDL_Texture* game_texture_{nullptr}; // Camí SDL3 GPU (prioritari quan gpu_acceleration = true). std::unique_ptr shader_backend_; int canvas_width_{0}; int canvas_height_{0}; std::vector canvas_; FPS fps_{}; std::string gpu_driver_; // Callback cridat al principi de present() per pintar overlays per // damunt de l'escena. Viu en Screen perquè la seqüència correcta // (dibuix al canvas → overlay → upload al backend → present) queda // centralitzada en un sol lloc, siga quina siga l'escena activa. std::function overlay_renderer_; };