Reestructura carpetes: src->source, third_party->source/external, shaders->data/shaders
This commit is contained in:
238
source/rendering/sdl3gpu/sdl3gpu_shader_backend.cpp
Normal file
238
source/rendering/sdl3gpu/sdl3gpu_shader_backend.cpp
Normal file
@@ -0,0 +1,238 @@
|
||||
#include "rendering/sdl3gpu/sdl3gpu_shader_backend.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "rendering/sdl3gpu/shader_factory.hpp"
|
||||
|
||||
namespace Rendering {
|
||||
|
||||
namespace {
|
||||
|
||||
void logInfo(const std::string& msg) { std::cout << "[INFO] " << msg << '\n'; }
|
||||
void logError(const std::string& msg) { std::cerr << "[ERROR] " << msg << '\n'; }
|
||||
|
||||
#ifdef __APPLE__
|
||||
constexpr SDL_GPUShaderFormat SHADER_FORMAT = SDL_GPU_SHADERFORMAT_MSL;
|
||||
constexpr const char* VERTEX_ENTRY = "passthrough_vs";
|
||||
constexpr const char* FRAGMENT_ENTRY = "test_fs"; // overridden per-shader (see loadShader)
|
||||
constexpr const char* VERTEX_SUFFIX = ".vert.msl";
|
||||
constexpr const char* FRAGMENT_SUFFIX = ".frag.msl";
|
||||
#else
|
||||
constexpr SDL_GPUShaderFormat SHADER_FORMAT = SDL_GPU_SHADERFORMAT_SPIRV;
|
||||
constexpr const char* VERTEX_ENTRY = "main";
|
||||
constexpr const char* FRAGMENT_ENTRY = "main";
|
||||
constexpr const char* VERTEX_SUFFIX = ".vert.spv";
|
||||
constexpr const char* FRAGMENT_SUFFIX = ".frag.spv";
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
Sdl3GpuShaderBackend::~Sdl3GpuShaderBackend() { cleanup(); }
|
||||
|
||||
auto Sdl3GpuShaderBackend::init(SDL_Window* window) -> bool {
|
||||
window_ = window;
|
||||
return createDevice();
|
||||
}
|
||||
|
||||
auto Sdl3GpuShaderBackend::createDevice() -> bool {
|
||||
device_ = SDL_CreateGPUDevice(SHADER_FORMAT, false, nullptr);
|
||||
if (device_ == nullptr) {
|
||||
logError(std::string("SDL_CreateGPUDevice failed: ") + SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
if (!SDL_ClaimWindowForGPUDevice(device_, window_)) {
|
||||
logError(std::string("SDL_ClaimWindowForGPUDevice failed: ") + SDL_GetError());
|
||||
SDL_DestroyGPUDevice(device_);
|
||||
device_ = nullptr;
|
||||
return false;
|
||||
}
|
||||
SDL_SetGPUSwapchainParameters(device_, window_, SDL_GPU_SWAPCHAINCOMPOSITION_SDR, bestPresentMode());
|
||||
|
||||
const char* name = SDL_GetGPUDeviceDriver(device_);
|
||||
const std::string raw = (name != nullptr) ? name : "GPU";
|
||||
if (raw == "vulkan") { driver_name_ = "Vulkan"; }
|
||||
else if (raw == "metal") { driver_name_ = "Metal"; }
|
||||
else if (raw == "d3d12") { driver_name_ = "D3D12"; }
|
||||
else if (!raw.empty()) {
|
||||
driver_name_ = raw;
|
||||
driver_name_[0] = static_cast<char>(std::toupper(static_cast<unsigned char>(driver_name_[0])));
|
||||
} else {
|
||||
driver_name_ = "GPU";
|
||||
}
|
||||
logInfo("GPU driver: " + driver_name_);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto Sdl3GpuShaderBackend::loadVertexShaderFor(const ShaderProgramSpec& spec) -> bool {
|
||||
if (vertex_shader_ != nullptr) { return true; }
|
||||
|
||||
const std::filesystem::path common_dir = spec.folder.parent_path() / "_common";
|
||||
const std::filesystem::path vertex_path = common_dir / (std::string("passthrough") + VERTEX_SUFFIX);
|
||||
|
||||
vertex_shader_ = Sdl3Gpu::loadShaderFromFile(device_, vertex_path, SHADER_FORMAT,
|
||||
VERTEX_ENTRY, SDL_GPU_SHADERSTAGE_VERTEX, 0, 0);
|
||||
if (vertex_shader_ == nullptr) {
|
||||
logError("Failed to load shared vertex shader: " + vertex_path.string() + " (" + SDL_GetError() + ")");
|
||||
return false;
|
||||
}
|
||||
logInfo("Loaded shared vertex shader: " + vertex_path.filename().string());
|
||||
return true;
|
||||
}
|
||||
|
||||
auto Sdl3GpuShaderBackend::buildPipeline(SDL_GPUShader* fragment) -> SDL_GPUGraphicsPipeline* {
|
||||
const SDL_GPUTextureFormat SWAPCHAIN_FORMAT = SDL_GetGPUSwapchainTextureFormat(device_, window_);
|
||||
|
||||
SDL_GPUColorTargetBlendState no_blend{};
|
||||
SDL_GPUColorTargetDescription color_target{};
|
||||
color_target.format = SWAPCHAIN_FORMAT;
|
||||
color_target.blend_state = no_blend;
|
||||
|
||||
SDL_GPUGraphicsPipelineCreateInfo info{};
|
||||
info.vertex_shader = vertex_shader_;
|
||||
info.fragment_shader = fragment;
|
||||
info.primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST;
|
||||
info.target_info.num_color_targets = 1;
|
||||
info.target_info.color_target_descriptions = &color_target;
|
||||
|
||||
SDL_GPUGraphicsPipeline* pipeline = SDL_CreateGPUGraphicsPipeline(device_, &info);
|
||||
if (pipeline == nullptr) {
|
||||
logError(std::string("SDL_CreateGPUGraphicsPipeline failed: ") + SDL_GetError());
|
||||
}
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
auto Sdl3GpuShaderBackend::loadShader(const ShaderProgramSpec& spec) -> bool {
|
||||
if (device_ == nullptr) { return false; }
|
||||
|
||||
if (!loadVertexShaderFor(spec)) { return false; }
|
||||
|
||||
const std::filesystem::path frag_path = spec.folder / (spec.base_name + FRAGMENT_SUFFIX);
|
||||
|
||||
#ifdef __APPLE__
|
||||
const std::string entry = spec.base_name + "_fs";
|
||||
const char* fragment_entry = entry.c_str();
|
||||
#else
|
||||
const char* fragment_entry = FRAGMENT_ENTRY;
|
||||
#endif
|
||||
|
||||
SDL_GPUShader* new_fragment = Sdl3Gpu::loadShaderFromFile(device_, frag_path, SHADER_FORMAT,
|
||||
fragment_entry, SDL_GPU_SHADERSTAGE_FRAGMENT, 0, 1);
|
||||
if (new_fragment == nullptr) {
|
||||
logError("Failed to load fragment shader: " + frag_path.string() + " (" + SDL_GetError() + ")");
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_GPUGraphicsPipeline* new_pipeline = buildPipeline(new_fragment);
|
||||
if (new_pipeline == nullptr) {
|
||||
SDL_ReleaseGPUShader(device_, new_fragment);
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_WaitForGPUIdle(device_);
|
||||
if (pipeline_ != nullptr) { SDL_ReleaseGPUGraphicsPipeline(device_, pipeline_); }
|
||||
if (fragment_shader_ != nullptr) { SDL_ReleaseGPUShader(device_, fragment_shader_); }
|
||||
|
||||
pipeline_ = new_pipeline;
|
||||
fragment_shader_ = new_fragment;
|
||||
logInfo("Shader loaded successfully: " + spec.base_name);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Sdl3GpuShaderBackend::render(const ShaderUniforms& uniforms) {
|
||||
if (device_ == nullptr || pipeline_ == nullptr) { return; }
|
||||
|
||||
SDL_GPUCommandBuffer* cmd = SDL_AcquireGPUCommandBuffer(device_);
|
||||
if (cmd == nullptr) { return; }
|
||||
|
||||
SDL_GPUTexture* swapchain = nullptr;
|
||||
Uint32 sw = 0;
|
||||
Uint32 sh = 0;
|
||||
if (!SDL_AcquireGPUSwapchainTexture(cmd, window_, &swapchain, &sw, &sh) || swapchain == nullptr) {
|
||||
SDL_SubmitGPUCommandBuffer(cmd);
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_GPUColorTargetInfo color_target{};
|
||||
color_target.texture = swapchain;
|
||||
color_target.load_op = SDL_GPU_LOADOP_CLEAR;
|
||||
color_target.store_op = SDL_GPU_STOREOP_STORE;
|
||||
color_target.clear_color = {.r = 0.0f, .g = 0.0f, .b = 0.0f, .a = 1.0f};
|
||||
|
||||
SDL_GPURenderPass* pass = SDL_BeginGPURenderPass(cmd, &color_target, 1, nullptr);
|
||||
if (pass != nullptr) {
|
||||
SDL_GPUViewport vp{};
|
||||
vp.x = 0.0f;
|
||||
vp.y = 0.0f;
|
||||
vp.w = static_cast<float>(sw);
|
||||
vp.h = static_cast<float>(sh);
|
||||
vp.min_depth = 0.0f;
|
||||
vp.max_depth = 1.0f;
|
||||
SDL_SetGPUViewport(pass, &vp);
|
||||
|
||||
SDL_BindGPUGraphicsPipeline(pass, pipeline_);
|
||||
|
||||
UniformsStd140 ubo{};
|
||||
ubo.iTime = uniforms.iTime;
|
||||
ubo.iResolutionX = uniforms.iResolutionX;
|
||||
ubo.iResolutionY = uniforms.iResolutionY;
|
||||
SDL_PushGPUFragmentUniformData(cmd, 0, &ubo, sizeof(ubo));
|
||||
|
||||
SDL_DrawGPUPrimitives(pass, 3, 1, 0, 0);
|
||||
SDL_EndGPURenderPass(pass);
|
||||
}
|
||||
|
||||
SDL_SubmitGPUCommandBuffer(cmd);
|
||||
}
|
||||
|
||||
void Sdl3GpuShaderBackend::setVSync(bool vsync) {
|
||||
vsync_ = vsync;
|
||||
if (device_ != nullptr && window_ != nullptr) {
|
||||
SDL_SetGPUSwapchainParameters(device_, window_, SDL_GPU_SWAPCHAINCOMPOSITION_SDR, bestPresentMode());
|
||||
logInfo(vsync ? "VSync enabled" : "VSync disabled");
|
||||
}
|
||||
}
|
||||
|
||||
auto Sdl3GpuShaderBackend::bestPresentMode() const -> SDL_GPUPresentMode {
|
||||
if (vsync_) { return SDL_GPU_PRESENTMODE_VSYNC; }
|
||||
if (device_ != nullptr && window_ != nullptr) {
|
||||
if (SDL_WindowSupportsGPUPresentMode(device_, window_, SDL_GPU_PRESENTMODE_IMMEDIATE)) {
|
||||
return SDL_GPU_PRESENTMODE_IMMEDIATE;
|
||||
}
|
||||
if (SDL_WindowSupportsGPUPresentMode(device_, window_, SDL_GPU_PRESENTMODE_MAILBOX)) {
|
||||
return SDL_GPU_PRESENTMODE_MAILBOX;
|
||||
}
|
||||
}
|
||||
return SDL_GPU_PRESENTMODE_VSYNC;
|
||||
}
|
||||
|
||||
void Sdl3GpuShaderBackend::cleanup() {
|
||||
if (device_ == nullptr) { return; }
|
||||
|
||||
SDL_WaitForGPUIdle(device_);
|
||||
|
||||
if (pipeline_ != nullptr) {
|
||||
SDL_ReleaseGPUGraphicsPipeline(device_, pipeline_);
|
||||
pipeline_ = nullptr;
|
||||
}
|
||||
if (fragment_shader_ != nullptr) {
|
||||
SDL_ReleaseGPUShader(device_, fragment_shader_);
|
||||
fragment_shader_ = nullptr;
|
||||
}
|
||||
if (vertex_shader_ != nullptr) {
|
||||
SDL_ReleaseGPUShader(device_, vertex_shader_);
|
||||
vertex_shader_ = nullptr;
|
||||
}
|
||||
|
||||
if (window_ != nullptr) {
|
||||
SDL_ReleaseWindowFromGPUDevice(device_, window_);
|
||||
}
|
||||
SDL_DestroyGPUDevice(device_);
|
||||
device_ = nullptr;
|
||||
window_ = nullptr;
|
||||
}
|
||||
|
||||
auto makeSdl3GpuBackend() -> std::unique_ptr<IShaderBackend> {
|
||||
return std::make_unique<Sdl3GpuShaderBackend>();
|
||||
}
|
||||
|
||||
} // namespace Rendering
|
||||
44
source/rendering/sdl3gpu/sdl3gpu_shader_backend.hpp
Normal file
44
source/rendering/sdl3gpu/sdl3gpu_shader_backend.hpp
Normal file
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include "rendering/shader_backend.hpp"
|
||||
|
||||
namespace Rendering {
|
||||
|
||||
class Sdl3GpuShaderBackend final : public IShaderBackend {
|
||||
public:
|
||||
Sdl3GpuShaderBackend() = default;
|
||||
~Sdl3GpuShaderBackend() override;
|
||||
|
||||
auto init(SDL_Window* window) -> bool override;
|
||||
auto loadShader(const ShaderProgramSpec& spec) -> bool override;
|
||||
void render(const ShaderUniforms& uniforms) override;
|
||||
void setVSync(bool vsync) override;
|
||||
void cleanup() override;
|
||||
[[nodiscard]] auto driverName() const -> std::string override { return driver_name_; }
|
||||
|
||||
private:
|
||||
struct UniformsStd140 {
|
||||
float iTime{0.0f};
|
||||
float pad0{0.0f};
|
||||
float iResolutionX{0.0f};
|
||||
float iResolutionY{0.0f};
|
||||
};
|
||||
|
||||
auto createDevice() -> bool;
|
||||
auto loadVertexShaderFor(const ShaderProgramSpec& spec) -> bool;
|
||||
auto buildPipeline(SDL_GPUShader* fragment) -> SDL_GPUGraphicsPipeline*;
|
||||
[[nodiscard]] auto bestPresentMode() 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};
|
||||
|
||||
bool vsync_{true};
|
||||
std::string driver_name_;
|
||||
};
|
||||
|
||||
} // namespace Rendering
|
||||
41
source/rendering/sdl3gpu/shader_factory.hpp
Normal file
41
source/rendering/sdl3gpu/shader_factory.hpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
namespace Rendering::Sdl3Gpu {
|
||||
|
||||
// Loads a compiled shader binary or source from disk and creates an SDL_GPUShader.
|
||||
// For SPIR-V: pass the .spv path with format = SDL_GPU_SHADERFORMAT_SPIRV.
|
||||
// For MSL: pass the .msl text path with format = SDL_GPU_SHADERFORMAT_MSL.
|
||||
inline auto loadShaderFromFile(SDL_GPUDevice* device,
|
||||
const std::filesystem::path& path,
|
||||
SDL_GPUShaderFormat format,
|
||||
const char* entrypoint,
|
||||
SDL_GPUShaderStage stage,
|
||||
Uint32 num_samplers,
|
||||
Uint32 num_uniform_buffers) -> SDL_GPUShader* {
|
||||
std::size_t size = 0;
|
||||
void* data = SDL_LoadFile(path.string().c_str(), &size);
|
||||
if (data == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SDL_GPUShaderCreateInfo info{};
|
||||
info.code_size = size;
|
||||
info.code = static_cast<Uint8*>(data);
|
||||
info.entrypoint = entrypoint;
|
||||
info.format = format;
|
||||
info.stage = stage;
|
||||
info.num_samplers = num_samplers;
|
||||
info.num_storage_textures = 0;
|
||||
info.num_storage_buffers = 0;
|
||||
info.num_uniform_buffers = num_uniform_buffers;
|
||||
|
||||
SDL_GPUShader* shader = SDL_CreateGPUShader(device, &info);
|
||||
SDL_free(data);
|
||||
return shader;
|
||||
}
|
||||
|
||||
} // namespace Rendering::Sdl3Gpu
|
||||
Reference in New Issue
Block a user