1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifdef SDL_VIDEO_RENDER_GPU
24
25#include "SDL_shaders_gpu.h"
26
27// SDL_GPU shader implementation
28
29typedef struct GPU_ShaderModuleSource
30{
31 const unsigned char *code;
32 unsigned int code_len;
33 SDL_GPUShaderFormat format;
34} GPU_ShaderModuleSource;
35
36#if defined(SDL_GPU_VULKAN) && SDL_GPU_VULKAN
37#define IF_VULKAN(...) __VA_ARGS__
38#define HAVE_SPIRV_SHADERS 1
39#include "shaders/spir-v.h"
40#else
41#define IF_VULKAN(...)
42#define HAVE_SPIRV_SHADERS 0
43#endif
44
45#ifdef SDL_GPU_D3D12
46#define IF_D3D12(...) __VA_ARGS__
47#define HAVE_DXIL60_SHADERS 1
48#include "shaders/dxil.h"
49#else
50#define IF_D3D12(...)
51#define HAVE_DXIL60_SHADERS 0
52#endif
53
54#ifdef SDL_GPU_METAL
55#define IF_METAL(...) __VA_ARGS__
56#define HAVE_METAL_SHADERS 1
57#include "shaders/msl.h"
58#else
59#define IF_METAL(...)
60#define HAVE_METAL_SHADERS 0
61#endif
62
63typedef struct GPU_ShaderSources
64{
65 IF_VULKAN(GPU_ShaderModuleSource spirv;)
66 IF_D3D12(GPU_ShaderModuleSource dxil60;)
67 IF_METAL(GPU_ShaderModuleSource msl;)
68 unsigned int num_samplers;
69 unsigned int num_uniform_buffers;
70} GPU_ShaderSources;
71
72#define SHADER_SPIRV(code) \
73 IF_VULKAN(.spirv = { code, sizeof(code), SDL_GPU_SHADERFORMAT_SPIRV }, )
74
75#define SHADER_DXIL60(code) \
76 IF_D3D12(.dxil60 = { code, sizeof(code), SDL_GPU_SHADERFORMAT_DXIL }, )
77
78#define SHADER_METAL(code) \
79 IF_METAL(.msl = { code, sizeof(code), SDL_GPU_SHADERFORMAT_MSL }, )
80
81// clang-format off
82static const GPU_ShaderSources vert_shader_sources[NUM_VERT_SHADERS] = {
83 [VERT_SHADER_LINEPOINT] = {
84 .num_samplers = 0,
85 .num_uniform_buffers = 1,
86 SHADER_SPIRV(linepoint_vert_spv)
87 SHADER_DXIL60(linepoint_vert_dxil)
88 SHADER_METAL(linepoint_vert_msl)
89 },
90 [VERT_SHADER_TRI_COLOR] = {
91 .num_samplers = 0,
92 .num_uniform_buffers = 1,
93 SHADER_SPIRV(tri_color_vert_spv)
94 SHADER_DXIL60(tri_color_vert_dxil)
95 SHADER_METAL(tri_color_vert_msl)
96 },
97 [VERT_SHADER_TRI_TEXTURE] = {
98 .num_samplers = 0,
99 .num_uniform_buffers = 1,
100 SHADER_SPIRV(tri_texture_vert_spv)
101 SHADER_DXIL60(tri_texture_vert_dxil)
102 SHADER_METAL(tri_texture_vert_msl)
103 },
104};
105
106static const GPU_ShaderSources frag_shader_sources[NUM_FRAG_SHADERS] = {
107 [FRAG_SHADER_COLOR] = {
108 .num_samplers = 0,
109 .num_uniform_buffers = 0,
110 SHADER_SPIRV(color_frag_spv)
111 SHADER_DXIL60(color_frag_dxil)
112 SHADER_METAL(color_frag_msl)
113 },
114 [FRAG_SHADER_TEXTURE_RGB] = {
115 .num_samplers = 1,
116 .num_uniform_buffers = 0,
117 SHADER_SPIRV(texture_rgb_frag_spv)
118 SHADER_DXIL60(texture_rgb_frag_dxil)
119 SHADER_METAL(texture_rgb_frag_msl)
120 },
121 [FRAG_SHADER_TEXTURE_RGBA] = {
122 .num_samplers = 1,
123 .num_uniform_buffers = 0,
124 SHADER_SPIRV(texture_rgba_frag_spv)
125 SHADER_DXIL60(texture_rgba_frag_dxil)
126 SHADER_METAL(texture_rgba_frag_msl)
127 },
128 [FRAG_SHADER_TEXTURE_RGB_PIXELART] = {
129 .num_samplers = 1,
130 .num_uniform_buffers = 1,
131 SHADER_SPIRV(texture_rgb_pixelart_frag_spv)
132 SHADER_DXIL60(texture_rgb_pixelart_frag_dxil)
133 SHADER_METAL(texture_rgb_pixelart_frag_msl)
134 },
135 [FRAG_SHADER_TEXTURE_RGBA_PIXELART] = {
136 .num_samplers = 1,
137 .num_uniform_buffers = 1,
138 SHADER_SPIRV(texture_rgba_pixelart_frag_spv)
139 SHADER_DXIL60(texture_rgba_pixelart_frag_dxil)
140 SHADER_METAL(texture_rgba_pixelart_frag_msl)
141 },
142};
143// clang-format on
144
145static SDL_GPUShader *CompileShader(const GPU_ShaderSources *sources, SDL_GPUDevice *device, SDL_GPUShaderStage stage)
146{
147 const GPU_ShaderModuleSource *sms = NULL;
148 SDL_GPUShaderFormat formats = SDL_GetGPUShaderFormats(device);
149
150 if (formats == SDL_GPU_SHADERFORMAT_INVALID) {
151 // SDL_GetGPUShaderFormats already set the error
152 return NULL;
153#if HAVE_SPIRV_SHADERS
154 } else if (formats & SDL_GPU_SHADERFORMAT_SPIRV) {
155 sms = &sources->spirv;
156#endif // HAVE_SPIRV_SHADERS
157#if HAVE_DXIL60_SHADERS
158 } else if (formats & SDL_GPU_SHADERFORMAT_DXIL) {
159 sms = &sources->dxil60;
160#endif // HAVE_DXIL60_SHADERS
161#if HAVE_METAL_SHADERS
162 } else if (formats & SDL_GPU_SHADERFORMAT_MSL) {
163 sms = &sources->msl;
164#endif // HAVE_METAL_SHADERS
165 } else {
166 SDL_SetError("Unsupported GPU backend");
167 return NULL;
168 }
169
170 SDL_GPUShaderCreateInfo sci = { 0 };
171 sci.code = sms->code;
172 sci.code_size = sms->code_len;
173 sci.format = sms->format;
174 // FIXME not sure if this is correct
175 sci.entrypoint =
176#if HAVE_METAL_SHADERS
177 (sms == &sources->msl) ? "main0" :
178#endif // HAVE_METAL_SHADERS
179 "main";
180 sci.num_samplers = sources->num_samplers;
181 sci.num_uniform_buffers = sources->num_uniform_buffers;
182 sci.stage = stage;
183
184 return SDL_CreateGPUShader(device, &sci);
185}
186
187bool GPU_InitShaders(GPU_Shaders *shaders, SDL_GPUDevice *device)
188{
189 for (int i = 0; i < SDL_arraysize(vert_shader_sources); ++i) {
190 shaders->vert_shaders[i] = CompileShader(
191 &vert_shader_sources[i], device, SDL_GPU_SHADERSTAGE_VERTEX);
192 if (shaders->vert_shaders[i] == NULL) {
193 GPU_ReleaseShaders(shaders, device);
194 return false;
195 }
196 }
197
198 for (int i = 0; i < SDL_arraysize(frag_shader_sources); ++i) {
199 if (i == FRAG_SHADER_TEXTURE_CUSTOM) {
200 continue;
201 }
202 shaders->frag_shaders[i] = CompileShader(
203 &frag_shader_sources[i], device, SDL_GPU_SHADERSTAGE_FRAGMENT);
204 if (shaders->frag_shaders[i] == NULL) {
205 GPU_ReleaseShaders(shaders, device);
206 return false;
207 }
208 }
209
210 return true;
211}
212
213void GPU_ReleaseShaders(GPU_Shaders *shaders, SDL_GPUDevice *device)
214{
215 for (int i = 0; i < SDL_arraysize(shaders->vert_shaders); ++i) {
216 SDL_ReleaseGPUShader(device, shaders->vert_shaders[i]);
217 shaders->vert_shaders[i] = NULL;
218 }
219
220 for (int i = 0; i < SDL_arraysize(shaders->frag_shaders); ++i) {
221 if (i == FRAG_SHADER_TEXTURE_CUSTOM) {
222 continue;
223 }
224 SDL_ReleaseGPUShader(device, shaders->frag_shaders[i]);
225 shaders->frag_shaders[i] = NULL;
226 }
227}
228
229SDL_GPUShader *GPU_GetVertexShader(GPU_Shaders *shaders, GPU_VertexShaderID id)
230{
231 SDL_assert((unsigned int)id < SDL_arraysize(shaders->vert_shaders));
232 SDL_GPUShader *shader = shaders->vert_shaders[id];
233 SDL_assert(shader != NULL);
234 return shader;
235}
236
237SDL_GPUShader *GPU_GetFragmentShader(GPU_Shaders *shaders, GPU_FragmentShaderID id)
238{
239 SDL_assert((unsigned int)id < SDL_arraysize(shaders->frag_shaders));
240 SDL_GPUShader *shader = shaders->frag_shaders[id];
241 SDL_assert(shader != NULL);
242 return shader;
243}
244
245void GPU_FillSupportedShaderFormats(SDL_PropertiesID props)
246{
247 SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_SPIRV_BOOLEAN, HAVE_SPIRV_SHADERS);
248 SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_DXIL_BOOLEAN, HAVE_DXIL60_SHADERS);
249 SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_MSL_BOOLEAN, HAVE_METAL_SHADERS);
250}
251
252#endif // SDL_VIDEO_RENDER_GPU
253