155 lines
5.7 KiB
C++
155 lines
5.7 KiB
C++
#pragma once
|
||
|
||
#include <SDL3/SDL.h>
|
||
#include <SDL3/SDL_gpu.h>
|
||
|
||
#include <string>
|
||
#include <utility>
|
||
|
||
#include "rendering/shader_backend.hpp"
|
||
|
||
// PostFX uniforms pushed to fragment stage each frame.
|
||
// 12 floats = 48 bytes — meets Metal/Vulkan 16-byte alignment requirement.
|
||
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;
|
||
};
|
||
|
||
// CrtPi uniforms pushed to fragment stage each frame.
|
||
// 16 fields = 64 bytes — 4 × 16-byte alignment.
|
||
struct CrtPiUniforms {
|
||
float scanline_weight;
|
||
float scanline_gap_brightness;
|
||
float bloom_factor;
|
||
float input_gamma;
|
||
float output_gamma;
|
||
float mask_brightness;
|
||
float curvature_x;
|
||
float curvature_y;
|
||
int mask_type;
|
||
int enable_scanlines;
|
||
int enable_multisample;
|
||
int enable_gamma;
|
||
int enable_curvature;
|
||
int enable_sharper;
|
||
float texture_width;
|
||
float texture_height;
|
||
};
|
||
|
||
// Downscale uniforms for Lanczos downscale fragment stage.
|
||
// 1 int + 3 floats = 16 bytes.
|
||
struct DownscaleUniforms {
|
||
int algorithm;
|
||
float pad0;
|
||
float pad1;
|
||
float pad2;
|
||
};
|
||
|
||
namespace Rendering {
|
||
|
||
/**
|
||
* @brief Backend de shaders usando SDL3 GPU API (Metal en macOS, Vulkan/SPIR-V en Win/Linux)
|
||
*
|
||
* Pipeline: Surface pixels (CPU) → SDL_GPUTransferBuffer → SDL_GPUTexture (scene)
|
||
* → [Upscale →] PostFX/CrtPi render pass → [Lanczos downscale →] 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;
|
||
void destroy();
|
||
[[nodiscard]] auto isHardwareAccelerated() const -> bool override { return is_initialized_; }
|
||
[[nodiscard]] auto getDriverName() const -> std::string override { return driver_name_; }
|
||
|
||
void setPreferredDriver(const std::string& driver) override { preferred_driver_ = driver; }
|
||
void uploadPixels(const Uint32* pixels, int width, int height) override;
|
||
void setPostFXParams(const PostFXParams& p) override;
|
||
void setVSync(bool vsync) override;
|
||
void setScaleMode(bool integer_scale) override;
|
||
void setOversample(int factor) override;
|
||
|
||
void setLinearUpscale(bool linear) override;
|
||
[[nodiscard]] auto isLinearUpscale() const -> bool override { return linear_upscale_; }
|
||
void setDownscaleAlgo(int algo) override;
|
||
[[nodiscard]] auto getDownscaleAlgo() const -> int override { return downscale_algo_; }
|
||
[[nodiscard]] auto getSsTextureSize() const -> std::pair<int, int> override;
|
||
|
||
void setActiveShader(ShaderType type) override;
|
||
void setCrtPiParams(const CrtPiParams& p) override;
|
||
[[nodiscard]] auto getActiveShader() const -> ShaderType override { return active_shader_; }
|
||
|
||
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 createCrtPiPipeline() -> bool;
|
||
auto reinitTexturesAndBuffer() -> bool;
|
||
auto recreateScaledTexture(int factor) -> bool;
|
||
static auto calcSsFactor(float zoom) -> int;
|
||
[[nodiscard]] auto bestPresentMode(bool vsync) const -> SDL_GPUPresentMode;
|
||
|
||
SDL_Window* window_ = nullptr;
|
||
SDL_GPUDevice* device_ = nullptr;
|
||
SDL_GPUGraphicsPipeline* pipeline_ = nullptr;
|
||
SDL_GPUGraphicsPipeline* crtpi_pipeline_ = nullptr;
|
||
SDL_GPUGraphicsPipeline* postfx_offscreen_pipeline_ = nullptr;
|
||
SDL_GPUGraphicsPipeline* upscale_pipeline_ = nullptr;
|
||
SDL_GPUGraphicsPipeline* downscale_pipeline_ = nullptr;
|
||
SDL_GPUTexture* scene_texture_ = nullptr;
|
||
SDL_GPUTexture* scaled_texture_ = nullptr;
|
||
SDL_GPUTexture* postfx_texture_ = nullptr;
|
||
SDL_GPUTransferBuffer* upload_buffer_ = nullptr;
|
||
SDL_GPUSampler* sampler_ = nullptr;
|
||
SDL_GPUSampler* linear_sampler_ = nullptr;
|
||
|
||
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};
|
||
CrtPiUniforms crtpi_uniforms_{.scanline_weight = 6.0F, .scanline_gap_brightness = 0.12F, .bloom_factor = 3.5F, .input_gamma = 2.4F, .output_gamma = 2.2F, .mask_brightness = 0.80F, .curvature_x = 0.05F, .curvature_y = 0.10F, .mask_type = 2, .enable_scanlines = 1, .enable_multisample = 1, .enable_gamma = 1};
|
||
ShaderType active_shader_ = ShaderType::POSTFX;
|
||
|
||
int game_width_ = 0;
|
||
int game_height_ = 0;
|
||
int ss_factor_ = 0;
|
||
int oversample_ = 1;
|
||
int downscale_algo_ = 1;
|
||
std::string driver_name_;
|
||
std::string preferred_driver_;
|
||
bool is_initialized_ = false;
|
||
bool vsync_ = true;
|
||
bool integer_scale_ = false;
|
||
bool linear_upscale_ = false;
|
||
};
|
||
|
||
} // namespace Rendering
|