| 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 | |