#pragma once #include #include #include #include #include "core/rendering/shader_backend.hpp" // Uniforms enviats al fragment stage cada frame. // Han de coincidir amb l'estructura MSL/GLSL (48 bytes, alineats a 16). struct PostFXUniforms { float vignette_strength; float chroma_strength; float scanline_strength; float screen_height; float mask_strength; float gamma_strength; float curvature; float bleeding; float pixel_scale; float time; float oversample; float flicker; }; namespace Rendering { // Backend GPU basat en SDL3 GPU API (Vulkan/SPIR-V a Linux i Windows, // Metal/MSL a macOS). Pipeline únic: CPU framebuffer → transfer buffer → // scene texture → PostFX pass → swapchain. Sense supersampling, sense // CrtPi i sense Lanczos (reservats per a fases següents). // // Marcat `final` perquè el destructor crida cleanup() (que és virtual); // sense final, clang-tidy avisa de dispatch virtual durant destrucció. class SDL3GPUShader final : public ShaderBackend { public: SDL3GPUShader() = default; ~SDL3GPUShader() override; SDL3GPUShader(const SDL3GPUShader&) = delete; SDL3GPUShader(SDL3GPUShader&&) = delete; auto operator=(const SDL3GPUShader&) -> SDL3GPUShader& = delete; auto operator=(SDL3GPUShader&&) -> SDL3GPUShader& = delete; auto init(SDL_Window* window, int game_width, int game_height) -> bool override; void uploadPixels(const std::uint32_t* pixels, int width, int height) override; void render() override; void setPostFXParams(const PostFXParams& params) override; void setVSync(bool vsync) override; void setPreferredDriver(const std::string& driver) override { preferred_driver_ = driver; } void cleanup() override; // Allibera la finestra del device sense destruir el device. Cal cridar-ho // abans de SDL_DestroyWindow perquè el VkSurface tancat no sobrevisca la // finestra de Wayland (vkDestroyInstance cridaria Wayland amb surface invàlida). void releaseWindow(); [[nodiscard]] auto isReady() const -> bool override { return is_ready_; } [[nodiscard]] auto getDriverName() const -> std::string override { return driver_name_; } private: auto createDevice() -> bool; auto createShaders() -> bool; auto createPipeline() -> bool; auto createResources() -> bool; [[nodiscard]] auto bestPresentMode(bool vsync) const -> SDL_GPUPresentMode; SDL_Window* window_{nullptr}; SDL_GPUDevice* device_{nullptr}; SDL_GPUShader* vertex_shader_{nullptr}; SDL_GPUShader* fragment_shader_{nullptr}; SDL_GPUGraphicsPipeline* pipeline_{nullptr}; SDL_GPUTexture* scene_texture_{nullptr}; SDL_GPUTransferBuffer* upload_buffer_{nullptr}; SDL_GPUSampler* sampler_{nullptr}; PostFXUniforms uniforms_{ .vignette_strength = 0.0F, .chroma_strength = 0.0F, .scanline_strength = 0.0F, .screen_height = 0.0F, .mask_strength = 0.0F, .gamma_strength = 0.0F, .curvature = 0.0F, .bleeding = 0.0F, .pixel_scale = 1.0F, .time = 0.0F, .oversample = 1.0F, .flicker = 0.0F, }; int game_width_{0}; int game_height_{0}; std::string preferred_driver_; std::string driver_name_; bool vsync_{true}; bool is_ready_{false}; }; } // namespace Rendering