#pragma once #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); void clear(std::uint8_t r = 0, std::uint8_t g = 0, std::uint8_t b = 0); 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(); // 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_; };