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 | |
29 | typedef 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 | |
63 | typedef 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 |
82 | static 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 | |
106 | static 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 | |
145 | static 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 | |
187 | bool 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 | |
213 | void 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 | |
229 | SDL_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 | |
237 | SDL_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 | |
245 | void 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 | |