Flint Engine
Modular C++20 2D game engine powered by SDL3
Loading...
Searching...
No Matches
shaders.h
1// SPDX-License-Identifier: MIT
2// SPDX-FileCopyrightText: 2025-2026 Manuel Osorio
3#pragma once
4#include "flint/core/config.h"
5#include "flint/core/log.h"
6#include "flint/graphics/gpu/gpu-tracker.h"
7
8#include <SDL3/SDL_gpu.h>
9
10#include <memory>
11
12namespace Flint {
16 enum class ShaderStage {
17 Vertex,
18 Fragment
19 };
20
24 enum class ShaderFormat {
25 SPIRV,
26 MSL,
27 DXIL
28 };
29
39 inline ShaderFormat getShaderFormat(GRAPHICS_BACKEND backend) {
40 switch (backend) {
41 case GRAPHICS_BACKEND::METAL:
42 {
43 return ShaderFormat::MSL;
44 }
45 case GRAPHICS_BACKEND::D3D12:
46 {
47 return ShaderFormat::DXIL;
48 }
49 case GRAPHICS_BACKEND::VULKAN:
50 case GRAPHICS_BACKEND::AUTO:
51 default:
52 {
53 return ShaderFormat::SPIRV;
54 }
55 }
56 }
57
66 inline const char *getShaderExtension(ShaderFormat format) {
67 if (format == ShaderFormat::SPIRV) {
68 return ".spv";
69 }
70 if (format == ShaderFormat::MSL) {
71 return ".msl";
72 }
73 if (format == ShaderFormat::DXIL) {
74 return ".dxil";
75 }
76 return ".spv";
77 }
78
86 class Shader {
87 public:
94 Shader(SDL_GPUDevice *device, ShaderFormat format)
95 : m_device(device),
96 m_format(format) {
97 }
98
99 virtual ~Shader() {
100 FLINT_ENGINE_INFO("Shader class destroyed");
101 cleanup();
102 }
103
119 bool loadFromMemory(ShaderStage stage, const void *data, size_t size, Uint32 numSamplers = 0,
120 Uint32 numStorageTextures = 0, Uint32 numStorageBuffers = 0,
121 Uint32 numUniformBuffers = 0) {
122 SDL_GPUShaderCreateInfo createInfo{};
123 createInfo.code = static_cast<const Uint8 *>(data);
124 createInfo.code_size = size;
125 createInfo.entrypoint = (m_format == ShaderFormat::MSL) ? "main0" : "main";
126
127 switch (m_format) {
128 case ShaderFormat::SPIRV:
129 createInfo.format = SDL_GPU_SHADERFORMAT_SPIRV;
130 break;
131 case ShaderFormat::MSL:
132 createInfo.format = SDL_GPU_SHADERFORMAT_MSL;
133 break;
134 case ShaderFormat::DXIL:
135 createInfo.format = SDL_GPU_SHADERFORMAT_DXIL;
136 break;
137 default:
138 return false;
139 }
140
141 createInfo.stage = (stage == ShaderStage::Vertex) ? SDL_GPU_SHADERSTAGE_VERTEX
142 : SDL_GPU_SHADERSTAGE_FRAGMENT;
143
144 createInfo.num_samplers = numSamplers;
145 createInfo.num_storage_textures = numStorageTextures;
146 createInfo.num_storage_buffers = numStorageBuffers;
147 createInfo.num_uniform_buffers = numUniformBuffers;
148
149 SDL_GPUShader *shader = SDL_CreateGPUShader(m_device, &createInfo);
150 if (!shader) {
151 FLINT_ENGINE_ERROR("Failed to create shader: {}", SDL_GetError());
152 return false;
153 }
154
155 if (stage == ShaderStage::Vertex) {
156 if (m_vertexShader) {
157 GPU_RELEASE_SHADER(m_device, m_vertexShader);
158 }
159 m_vertexShader = shader;
160 GPU_TRACK_SHADER(m_vertexShader, "Vertex Shader in Shader class");
161 } else {
162 if (m_fragmentShader) {
163 GPU_RELEASE_SHADER(m_device, m_fragmentShader);
164 }
165 m_fragmentShader = shader;
166 GPU_TRACK_SHADER(m_fragmentShader, "Fragment Shader in Shader class");
167 }
168
169 return true;
170 }
171
173 SDL_GPUShader *getVertexShader() const {
174 return m_vertexShader;
175 }
176
178 SDL_GPUShader *getFragmentShader() const {
179 return m_fragmentShader;
180 }
181
183 SDL_GPUDevice *getDevice() const {
184 return m_device;
185 }
186
192 void cleanup() {
193 FLINT_ENGINE_INFO("Releasing Shader resources");
194 if (m_vertexShader) {
195 FLINT_ENGINE_INFO("Releasing vertex shader");
196 GPU_RELEASE_SHADER(m_device, m_vertexShader);
197 m_vertexShader = nullptr;
198 }
199 if (m_fragmentShader) {
200 FLINT_ENGINE_INFO("Releasing fragment shader");
201 GPU_RELEASE_SHADER(m_device, m_fragmentShader);
202 m_fragmentShader = nullptr;
203 }
204 FLINT_ENGINE_INFO("Shader resources released");
205 }
206
207 protected:
208 SDL_GPUDevice *m_device = nullptr;
209 SDL_GPUShader *m_vertexShader = nullptr;
210 SDL_GPUShader *m_fragmentShader = nullptr;
211 ShaderFormat m_format;
212 };
213}
SDL_GPUShader * getVertexShader() const
Return the vertex shader handle tracked by this object (nullptr if absent).
Definition shaders.h:173
Shader(SDL_GPUDevice *device, ShaderFormat format)
Construct a Shader bound to device and configured for format binaries.
Definition shaders.h:94
void cleanup()
Release both shader stages, if present, and reset their handles to nullptr.
Definition shaders.h:192
bool loadFromMemory(ShaderStage stage, const void *data, size_t size, Uint32 numSamplers=0, Uint32 numStorageTextures=0, Uint32 numStorageBuffers=0, Uint32 numUniformBuffers=0)
Compile and upload a shader stage from bytecode already present in memory.
Definition shaders.h:119
SDL_GPUDevice * getDevice() const
Return the SDL GPU device that owns these shaders (may be nullptr before init).
Definition shaders.h:183
SDL_GPUShader * getFragmentShader() const
Return the fragment shader handle tracked by this object (nullptr if absent).
Definition shaders.h:178
Owns a pair of SDL GPU shaders (vertex + fragment) tied to one SDL_GPUDevice.
Definition shaders.h:86