#pragma once #include #include #include #include #include #include "gpu_texture.hpp" // ============================================================================ // NTSCParams — uniform buffer for NTSC shader passes (set=3, binding=0) // Matches the layout in pass0_encode.frag and pass1_decode.frag. // Pushed via SDL_PushGPUFragmentUniformData(cmd, 0, &ntsc, sizeof(NTSCParams)). // ============================================================================ struct NTSCParams { float source_width; float source_height; float a_value; float b_value; float cc_value; float scan_time; float notch_width; float y_freq; float i_freq; float q_freq; float _pad[2]; }; static_assert(sizeof(NTSCParams) == 48, "NTSCParams must be 48 bytes"); // ============================================================================ // ShaderPass — one render pass in a multi-pass shader preset // ============================================================================ struct ShaderPass { SDL_GPUGraphicsPipeline* pipeline = nullptr; GpuTexture* target = nullptr; // null = swapchain (last pass) }; // ============================================================================ // GpuShaderPreset — loads and owns a multi-pass shader preset from disk. // // Directory layout: // /preset.ini — descriptor // /pass0_xxx.vert — GLSL 4.50 vertex shader source // /pass0_xxx.frag — GLSL 4.50 fragment shader source // /pass0_xxx.vert.spv — compiled SPIRV (by CMake/glslc at build time) // /pass0_xxx.frag.spv — compiled SPIRV // ... // ============================================================================ class GpuShaderPreset { public: // Load preset from directory. swapchain_fmt is the target format for the // last pass; intermediate passes use R16G16B16A16_FLOAT. bool load(SDL_GPUDevice* device, const std::string& dir, SDL_GPUTextureFormat swapchain_fmt, int w, int h); void destroy(SDL_GPUDevice* device); // Recreate intermediate render targets on resolution change. void recreateTargets(SDL_GPUDevice* device, int w, int h); int passCount() const { return static_cast(passes_.size()); } ShaderPass& pass(int i) { return passes_[i]; } const std::string& name() const { return name_; } // Read a float parameter parsed from preset.ini (returns default_val if absent). float param(const std::string& key, float default_val) const; private: std::vector passes_; std::vector> targets_; // intermediate render targets std::string name_; std::string dir_; std::unordered_map params_; SDL_GPUTextureFormat swapchain_fmt_ = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM; // Entries read from preset.ini for each pass struct PassDesc { std::string vert_name; // e.g. "pass0_encode.vert" std::string frag_name; // e.g. "pass0_encode.frag" }; std::vector descs_; bool parseIni(const std::string& ini_path); // Build a full-screen-triangle pipeline from two on-disk SPV files. SDL_GPUGraphicsPipeline* buildPassPipeline(SDL_GPUDevice* device, const std::string& vert_spv_path, const std::string& frag_spv_path, SDL_GPUTextureFormat target_fmt); };