107 lines
4.7 KiB
C++
107 lines
4.7 KiB
C++
#pragma once
|
||
|
||
#include <SDL3/SDL.h>
|
||
#include <SDL3/SDL_gpu.h>
|
||
|
||
#include "rendering/shader_backend.hpp"
|
||
|
||
// PostFX uniforms pushed to fragment stage each frame.
|
||
// Must match the MSL struct and GLSL uniform block layout.
|
||
// 12 floats = 48 bytes — meets Metal/Vulkan 16-byte alignment requirement.
|
||
struct PostFXUniforms {
|
||
float vignette_strength; // 0 = none, ~0.8 = subtle
|
||
float chroma_strength; // 0 = off, ~0.2 = subtle chromatic aberration
|
||
float scanline_strength; // 0 = off, 1 = full
|
||
float screen_height; // logical height in pixels (used by bleeding effect)
|
||
float mask_strength; // 0 = off, 1 = full phosphor dot mask
|
||
float gamma_strength; // 0 = off, 1 = full gamma 2.4/2.2 correction
|
||
float curvature; // 0 = flat, 1 = max barrel distortion
|
||
float bleeding; // 0 = off, 1 = max NTSC chrominance bleeding
|
||
float pixel_scale; // physical pixels per logical pixel (vh / tex_height_)
|
||
float time; // seconds since SDL init (SDL_GetTicks() / 1000.0f)
|
||
float oversample; // supersampling factor (1.0 = off, 3.0 = 3×SS)
|
||
float flicker; // 0 = off, 1 = phosphor flicker ~50 Hz — keep struct at 48 bytes (3 × 16)
|
||
};
|
||
|
||
namespace Rendering {
|
||
|
||
/**
|
||
* @brief Backend de shaders usando SDL3 GPU API (Metal en macOS, Vulkan/SPIR-V en Win/Linux)
|
||
*
|
||
* Reemplaza el backend OpenGL para que los shaders PostFX funcionen en macOS.
|
||
* Pipeline: Surface pixels (CPU) → SDL_GPUTransferBuffer → SDL_GPUTexture (scene)
|
||
* → PostFX render pass → swapchain → present
|
||
*/
|
||
class SDL3GPUShader : public ShaderBackend {
|
||
public:
|
||
SDL3GPUShader() = default;
|
||
~SDL3GPUShader() override;
|
||
|
||
auto init(SDL_Window* window,
|
||
SDL_Texture* texture,
|
||
const std::string& vertex_source,
|
||
const std::string& fragment_source) -> bool override;
|
||
|
||
void render() override;
|
||
void setTextureSize(float width, float height) override {}
|
||
void cleanup() final; // Libera pipeline/texturas pero mantiene el device vivo
|
||
void destroy(); // Limpieza completa (device + swapchain); llamar solo al cerrar
|
||
[[nodiscard]] auto isHardwareAccelerated() const -> bool override { return is_initialized_; }
|
||
|
||
// Sube píxeles ARGB8888 desde CPU; llamado antes de render()
|
||
void uploadPixels(const Uint32* pixels, int width, int height) override;
|
||
|
||
// Actualiza los parámetros de intensidad de los efectos PostFX
|
||
void setPostFXParams(const PostFXParams& p) override;
|
||
|
||
// Activa/desactiva VSync en el swapchain
|
||
void setVSync(bool vsync) override;
|
||
|
||
// Activa/desactiva escalado entero (integer scale)
|
||
void setScaleMode(bool integer_scale) override;
|
||
|
||
// Establece factor de supersampling (1 = off, 3 = 3×SS)
|
||
void setOversample(int factor) override;
|
||
|
||
private:
|
||
static auto createShaderMSL(SDL_GPUDevice* device,
|
||
const char* msl_source,
|
||
const char* entrypoint,
|
||
SDL_GPUShaderStage stage,
|
||
Uint32 num_samplers,
|
||
Uint32 num_uniform_buffers) -> SDL_GPUShader*;
|
||
|
||
static auto createShaderSPIRV(SDL_GPUDevice* device,
|
||
const uint8_t* spv_code,
|
||
size_t spv_size,
|
||
const char* entrypoint,
|
||
SDL_GPUShaderStage stage,
|
||
Uint32 num_samplers,
|
||
Uint32 num_uniform_buffers) -> SDL_GPUShader*;
|
||
|
||
auto createPipeline() -> bool;
|
||
auto reinitTexturesAndBuffer() -> bool; // Recrea textura y buffer con oversample actual
|
||
|
||
SDL_Window* window_ = nullptr;
|
||
SDL_GPUDevice* device_ = nullptr;
|
||
SDL_GPUGraphicsPipeline* pipeline_ = nullptr;
|
||
SDL_GPUTexture* scene_texture_ = nullptr;
|
||
SDL_GPUTransferBuffer* upload_buffer_ = nullptr;
|
||
SDL_GPUSampler* sampler_ = nullptr; // NEAREST — para path sin supersampling
|
||
SDL_GPUSampler* linear_sampler_ = nullptr; // LINEAR — para path con supersampling
|
||
|
||
PostFXUniforms uniforms_{.vignette_strength = 0.6F, .chroma_strength = 0.15F, .scanline_strength = 0.7F, .screen_height = 192.0F, .pixel_scale = 1.0F, .oversample = 1.0F};
|
||
|
||
int game_width_ = 0; // Dimensiones originales del canvas (sin SS)
|
||
int game_height_ = 0;
|
||
int tex_width_ = 0; // Dimensiones de la textura GPU (game × oversample_)
|
||
int tex_height_ = 0;
|
||
int oversample_ = 1; // Factor SS actual (1 o 3)
|
||
float baked_scanline_strength_ = 0.0F; // Guardado para hornear en CPU
|
||
bool is_initialized_ = false;
|
||
bool vsync_ = true;
|
||
bool integer_scale_ = false;
|
||
};
|
||
|
||
} // namespace Rendering
|