1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2024 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#include "SDL_sysgpu.h"
23
24// FIXME: This could probably use SDL_ObjectValid
25#define CHECK_DEVICE_MAGIC(device, retval) \
26 if (device == NULL) { \
27 SDL_SetError("Invalid GPU device"); \
28 return retval; \
29 }
30
31#define CHECK_COMMAND_BUFFER \
32 if (((CommandBufferCommonHeader *)command_buffer)->submitted) { \
33 SDL_assert_release(!"Command buffer already submitted!"); \
34 return; \
35 }
36
37#define CHECK_COMMAND_BUFFER_RETURN_FALSE \
38 if (((CommandBufferCommonHeader *)command_buffer)->submitted) { \
39 SDL_assert_release(!"Command buffer already submitted!"); \
40 return false; \
41 }
42
43#define CHECK_COMMAND_BUFFER_RETURN_NULL \
44 if (((CommandBufferCommonHeader *)command_buffer)->submitted) { \
45 SDL_assert_release(!"Command buffer already submitted!"); \
46 return NULL; \
47 }
48
49#define CHECK_ANY_PASS_IN_PROGRESS(msg, retval) \
50 if ( \
51 ((CommandBufferCommonHeader *)command_buffer)->render_pass.in_progress || \
52 ((CommandBufferCommonHeader *)command_buffer)->compute_pass.in_progress || \
53 ((CommandBufferCommonHeader *)command_buffer)->copy_pass.in_progress) { \
54 SDL_assert_release(!msg); \
55 return retval; \
56 }
57
58#define CHECK_RENDERPASS \
59 if (!((Pass *)render_pass)->in_progress) { \
60 SDL_assert_release(!"Render pass not in progress!"); \
61 return; \
62 }
63
64#define CHECK_GRAPHICS_PIPELINE_BOUND \
65 if (!((CommandBufferCommonHeader *)RENDERPASS_COMMAND_BUFFER)->graphics_pipeline_bound) { \
66 SDL_assert_release(!"Graphics pipeline not bound!"); \
67 return; \
68 }
69
70#define CHECK_COMPUTEPASS \
71 if (!((Pass *)compute_pass)->in_progress) { \
72 SDL_assert_release(!"Compute pass not in progress!"); \
73 return; \
74 }
75
76#define CHECK_COMPUTE_PIPELINE_BOUND \
77 if (!((CommandBufferCommonHeader *)COMPUTEPASS_COMMAND_BUFFER)->compute_pipeline_bound) { \
78 SDL_assert_release(!"Compute pipeline not bound!"); \
79 return; \
80 }
81
82#define CHECK_COPYPASS \
83 if (!((Pass *)copy_pass)->in_progress) { \
84 SDL_assert_release(!"Copy pass not in progress!"); \
85 return; \
86 }
87
88#define CHECK_TEXTUREFORMAT_ENUM_INVALID(enumval, retval) \
89 if (enumval <= SDL_GPU_TEXTUREFORMAT_INVALID || enumval >= SDL_GPU_TEXTUREFORMAT_MAX_ENUM_VALUE) { \
90 SDL_assert_release(!"Invalid texture format enum!"); \
91 return retval; \
92 }
93
94#define CHECK_VERTEXELEMENTFORMAT_ENUM_INVALID(enumval, retval) \
95 if (enumval <= SDL_GPU_VERTEXELEMENTFORMAT_INVALID || enumval >= SDL_GPU_VERTEXELEMENTFORMAT_MAX_ENUM_VALUE) { \
96 SDL_assert_release(!"Invalid vertex format enum!"); \
97 return retval; \
98 }
99
100#define CHECK_COMPAREOP_ENUM_INVALID(enumval, retval) \
101 if (enumval <= SDL_GPU_COMPAREOP_INVALID || enumval >= SDL_GPU_COMPAREOP_MAX_ENUM_VALUE) { \
102 SDL_assert_release(!"Invalid compare op enum!"); \
103 return retval; \
104 }
105
106#define CHECK_STENCILOP_ENUM_INVALID(enumval, retval) \
107 if (enumval <= SDL_GPU_STENCILOP_INVALID || enumval >= SDL_GPU_STENCILOP_MAX_ENUM_VALUE) { \
108 SDL_assert_release(!"Invalid stencil op enum!"); \
109 return retval; \
110 }
111
112#define CHECK_BLENDOP_ENUM_INVALID(enumval, retval) \
113 if (enumval <= SDL_GPU_BLENDOP_INVALID || enumval >= SDL_GPU_BLENDOP_MAX_ENUM_VALUE) { \
114 SDL_assert_release(!"Invalid blend op enum!"); \
115 return retval; \
116 }
117
118#define CHECK_BLENDFACTOR_ENUM_INVALID(enumval, retval) \
119 if (enumval <= SDL_GPU_BLENDFACTOR_INVALID || enumval >= SDL_GPU_BLENDFACTOR_MAX_ENUM_VALUE) { \
120 SDL_assert_release(!"Invalid blend factor enum!"); \
121 return retval; \
122 }
123
124#define CHECK_SWAPCHAINCOMPOSITION_ENUM_INVALID(enumval, retval) \
125 if (enumval < 0 || enumval >= SDL_GPU_SWAPCHAINCOMPOSITION_MAX_ENUM_VALUE) { \
126 SDL_assert_release(!"Invalid swapchain composition enum!"); \
127 return retval; \
128 }
129
130#define CHECK_PRESENTMODE_ENUM_INVALID(enumval, retval) \
131 if (enumval < 0 || enumval >= SDL_GPU_PRESENTMODE_MAX_ENUM_VALUE) { \
132 SDL_assert_release(!"Invalid present mode enum!"); \
133 return retval; \
134 }
135
136#define COMMAND_BUFFER_DEVICE \
137 ((CommandBufferCommonHeader *)command_buffer)->device
138
139#define RENDERPASS_COMMAND_BUFFER \
140 ((Pass *)render_pass)->command_buffer
141
142#define RENDERPASS_DEVICE \
143 ((CommandBufferCommonHeader *)RENDERPASS_COMMAND_BUFFER)->device
144
145#define COMPUTEPASS_COMMAND_BUFFER \
146 ((Pass *)compute_pass)->command_buffer
147
148#define COMPUTEPASS_DEVICE \
149 ((CommandBufferCommonHeader *)COMPUTEPASS_COMMAND_BUFFER)->device
150
151#define COPYPASS_COMMAND_BUFFER \
152 ((Pass *)copy_pass)->command_buffer
153
154#define COPYPASS_DEVICE \
155 ((CommandBufferCommonHeader *)COPYPASS_COMMAND_BUFFER)->device
156
157// Drivers
158
159#ifndef SDL_GPU_DISABLED
160static const SDL_GPUBootstrap *backends[] = {
161#ifdef SDL_GPU_METAL
162 &MetalDriver,
163#endif
164#ifdef SDL_GPU_VULKAN
165 &VulkanDriver,
166#endif
167#ifdef SDL_GPU_D3D12
168 &D3D12Driver,
169#endif
170 NULL
171};
172#endif // !SDL_GPU_DISABLED
173
174// Internal Utility Functions
175
176SDL_GPUGraphicsPipeline *SDL_GPU_FetchBlitPipeline(
177 SDL_GPUDevice *device,
178 SDL_GPUTextureType source_texture_type,
179 SDL_GPUTextureFormat destination_format,
180 SDL_GPUShader *blit_vertex_shader,
181 SDL_GPUShader *blit_from_2d_shader,
182 SDL_GPUShader *blit_from_2d_array_shader,
183 SDL_GPUShader *blit_from_3d_shader,
184 SDL_GPUShader *blit_from_cube_shader,
185 SDL_GPUShader *blit_from_cube_array_shader,
186 BlitPipelineCacheEntry **blit_pipelines,
187 Uint32 *blit_pipeline_count,
188 Uint32 *blit_pipeline_capacity)
189{
190 SDL_GPUGraphicsPipelineCreateInfo blit_pipeline_create_info;
191 SDL_GPUColorTargetDescription color_target_desc;
192 SDL_GPUGraphicsPipeline *pipeline;
193
194 if (blit_pipeline_count == NULL) {
195 // use pre-created, format-agnostic pipelines
196 return (*blit_pipelines)[source_texture_type].pipeline;
197 }
198
199 for (Uint32 i = 0; i < *blit_pipeline_count; i += 1) {
200 if ((*blit_pipelines)[i].type == source_texture_type && (*blit_pipelines)[i].format == destination_format) {
201 return (*blit_pipelines)[i].pipeline;
202 }
203 }
204
205 // No pipeline found, we'll need to make one!
206 SDL_zero(blit_pipeline_create_info);
207
208 SDL_zero(color_target_desc);
209 color_target_desc.blend_state.color_write_mask = 0xF;
210 color_target_desc.format = destination_format;
211
212 blit_pipeline_create_info.target_info.color_target_descriptions = &color_target_desc;
213 blit_pipeline_create_info.target_info.num_color_targets = 1;
214 blit_pipeline_create_info.target_info.depth_stencil_format = SDL_GPU_TEXTUREFORMAT_D16_UNORM; // arbitrary
215 blit_pipeline_create_info.target_info.has_depth_stencil_target = false;
216
217 blit_pipeline_create_info.vertex_shader = blit_vertex_shader;
218 if (source_texture_type == SDL_GPU_TEXTURETYPE_CUBE) {
219 blit_pipeline_create_info.fragment_shader = blit_from_cube_shader;
220 } else if (source_texture_type == SDL_GPU_TEXTURETYPE_CUBE_ARRAY) {
221 blit_pipeline_create_info.fragment_shader = blit_from_cube_array_shader;
222 } else if (source_texture_type == SDL_GPU_TEXTURETYPE_2D_ARRAY) {
223 blit_pipeline_create_info.fragment_shader = blit_from_2d_array_shader;
224 } else if (source_texture_type == SDL_GPU_TEXTURETYPE_3D) {
225 blit_pipeline_create_info.fragment_shader = blit_from_3d_shader;
226 } else {
227 blit_pipeline_create_info.fragment_shader = blit_from_2d_shader;
228 }
229
230 blit_pipeline_create_info.multisample_state.sample_count = SDL_GPU_SAMPLECOUNT_1;
231 blit_pipeline_create_info.multisample_state.enable_mask = false;
232
233 blit_pipeline_create_info.primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST;
234
235 pipeline = SDL_CreateGPUGraphicsPipeline(
236 device,
237 &blit_pipeline_create_info);
238
239 if (pipeline == NULL) {
240 SDL_SetError("Failed to create GPU pipeline for blit");
241 return NULL;
242 }
243
244 // Cache the new pipeline
245 EXPAND_ARRAY_IF_NEEDED(
246 (*blit_pipelines),
247 BlitPipelineCacheEntry,
248 *blit_pipeline_count + 1,
249 *blit_pipeline_capacity,
250 *blit_pipeline_capacity * 2);
251
252 (*blit_pipelines)[*blit_pipeline_count].pipeline = pipeline;
253 (*blit_pipelines)[*blit_pipeline_count].type = source_texture_type;
254 (*blit_pipelines)[*blit_pipeline_count].format = destination_format;
255 *blit_pipeline_count += 1;
256
257 return pipeline;
258}
259
260void SDL_GPU_BlitCommon(
261 SDL_GPUCommandBuffer *command_buffer,
262 const SDL_GPUBlitInfo *info,
263 SDL_GPUSampler *blit_linear_sampler,
264 SDL_GPUSampler *blit_nearest_sampler,
265 SDL_GPUShader *blit_vertex_shader,
266 SDL_GPUShader *blit_from_2d_shader,
267 SDL_GPUShader *blit_from_2d_array_shader,
268 SDL_GPUShader *blit_from_3d_shader,
269 SDL_GPUShader *blit_from_cube_shader,
270 SDL_GPUShader *blit_from_cube_array_shader,
271 BlitPipelineCacheEntry **blit_pipelines,
272 Uint32 *blit_pipeline_count,
273 Uint32 *blit_pipeline_capacity)
274{
275 CommandBufferCommonHeader *cmdbufHeader = (CommandBufferCommonHeader *)command_buffer;
276 SDL_GPURenderPass *render_pass;
277 TextureCommonHeader *src_header = (TextureCommonHeader *)info->source.texture;
278 TextureCommonHeader *dst_header = (TextureCommonHeader *)info->destination.texture;
279 SDL_GPUGraphicsPipeline *blit_pipeline;
280 SDL_GPUColorTargetInfo color_target_info;
281 SDL_GPUViewport viewport;
282 SDL_GPUTextureSamplerBinding texture_sampler_binding;
283 BlitFragmentUniforms blit_fragment_uniforms;
284 Uint32 layer_divisor;
285
286 blit_pipeline = SDL_GPU_FetchBlitPipeline(
287 cmdbufHeader->device,
288 src_header->info.type,
289 dst_header->info.format,
290 blit_vertex_shader,
291 blit_from_2d_shader,
292 blit_from_2d_array_shader,
293 blit_from_3d_shader,
294 blit_from_cube_shader,
295 blit_from_cube_array_shader,
296 blit_pipelines,
297 blit_pipeline_count,
298 blit_pipeline_capacity);
299
300 SDL_assert(blit_pipeline != NULL);
301
302 color_target_info.load_op = info->load_op;
303 color_target_info.clear_color = info->clear_color;
304 color_target_info.store_op = SDL_GPU_STOREOP_STORE;
305
306 color_target_info.texture = info->destination.texture;
307 color_target_info.mip_level = info->destination.mip_level;
308 color_target_info.layer_or_depth_plane = info->destination.layer_or_depth_plane;
309 color_target_info.cycle = info->cycle;
310
311 render_pass = SDL_BeginGPURenderPass(
312 command_buffer,
313 &color_target_info,
314 1,
315 NULL);
316
317 viewport.x = (float)info->destination.x;
318 viewport.y = (float)info->destination.y;
319 viewport.w = (float)info->destination.w;
320 viewport.h = (float)info->destination.h;
321 viewport.min_depth = 0;
322 viewport.max_depth = 1;
323
324 SDL_SetGPUViewport(
325 render_pass,
326 &viewport);
327
328 SDL_BindGPUGraphicsPipeline(
329 render_pass,
330 blit_pipeline);
331
332 texture_sampler_binding.texture = info->source.texture;
333 texture_sampler_binding.sampler =
334 info->filter == SDL_GPU_FILTER_NEAREST ? blit_nearest_sampler : blit_linear_sampler;
335
336 SDL_BindGPUFragmentSamplers(
337 render_pass,
338 0,
339 &texture_sampler_binding,
340 1);
341
342 blit_fragment_uniforms.left = (float)info->source.x / (src_header->info.width >> info->source.mip_level);
343 blit_fragment_uniforms.top = (float)info->source.y / (src_header->info.height >> info->source.mip_level);
344 blit_fragment_uniforms.width = (float)info->source.w / (src_header->info.width >> info->source.mip_level);
345 blit_fragment_uniforms.height = (float)info->source.h / (src_header->info.height >> info->source.mip_level);
346 blit_fragment_uniforms.mip_level = info->source.mip_level;
347
348 layer_divisor = (src_header->info.type == SDL_GPU_TEXTURETYPE_3D) ? src_header->info.layer_count_or_depth : 1;
349 blit_fragment_uniforms.layer_or_depth = (float)info->source.layer_or_depth_plane / layer_divisor;
350
351 if (info->flip_mode & SDL_FLIP_HORIZONTAL) {
352 blit_fragment_uniforms.left += blit_fragment_uniforms.width;
353 blit_fragment_uniforms.width *= -1;
354 }
355
356 if (info->flip_mode & SDL_FLIP_VERTICAL) {
357 blit_fragment_uniforms.top += blit_fragment_uniforms.height;
358 blit_fragment_uniforms.height *= -1;
359 }
360
361 SDL_PushGPUFragmentUniformData(
362 command_buffer,
363 0,
364 &blit_fragment_uniforms,
365 sizeof(blit_fragment_uniforms));
366
367 SDL_DrawGPUPrimitives(render_pass, 3, 1, 0, 0);
368 SDL_EndGPURenderPass(render_pass);
369}
370
371// Driver Functions
372
373#ifndef SDL_GPU_DISABLED
374static const SDL_GPUBootstrap * SDL_GPUSelectBackend(SDL_PropertiesID props)
375{
376 Uint32 i;
377 SDL_GPUShaderFormat format_flags = 0;
378 const char *gpudriver;
379 SDL_VideoDevice *_this = SDL_GetVideoDevice();
380
381 if (_this == NULL) {
382 SDL_SetError("Video subsystem not initialized");
383 return NULL;
384 }
385
386 if (SDL_GetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_PRIVATE_BOOLEAN, false)) {
387 format_flags |= SDL_GPU_SHADERFORMAT_PRIVATE;
388 }
389 if (SDL_GetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_SPIRV_BOOLEAN, false)) {
390 format_flags |= SDL_GPU_SHADERFORMAT_SPIRV;
391 }
392 if (SDL_GetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_DXBC_BOOLEAN, false)) {
393 format_flags |= SDL_GPU_SHADERFORMAT_DXBC;
394 }
395 if (SDL_GetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_DXIL_BOOLEAN, false)) {
396 format_flags |= SDL_GPU_SHADERFORMAT_DXIL;
397 }
398 if (SDL_GetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_MSL_BOOLEAN, false)) {
399 format_flags |= SDL_GPU_SHADERFORMAT_MSL;
400 }
401 if (SDL_GetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_METALLIB_BOOLEAN, false)) {
402 format_flags |= SDL_GPU_SHADERFORMAT_METALLIB;
403 }
404
405 gpudriver = SDL_GetHint(SDL_HINT_GPU_DRIVER);
406 if (gpudriver == NULL) {
407 gpudriver = SDL_GetStringProperty(props, SDL_PROP_GPU_DEVICE_CREATE_NAME_STRING, NULL);
408 }
409
410 // Environment/Properties override...
411 if (gpudriver != NULL) {
412 for (i = 0; backends[i]; i += 1) {
413 if (SDL_strcasecmp(gpudriver, backends[i]->name) == 0) {
414 if (!(backends[i]->shader_formats & format_flags)) {
415 SDL_SetError("Required shader format for backend %s not provided!", gpudriver);
416 return NULL;
417 }
418 if (backends[i]->PrepareDriver(_this)) {
419 return backends[i];
420 }
421 }
422 }
423
424 SDL_SetError("SDL_HINT_GPU_DRIVER %s unsupported!", gpudriver);
425 return NULL;
426 }
427
428 for (i = 0; backends[i]; i += 1) {
429 if ((backends[i]->shader_formats & format_flags) == 0) {
430 // Don't select a backend which doesn't support the app's shaders.
431 continue;
432 }
433 if (backends[i]->PrepareDriver(_this)) {
434 return backends[i];
435 }
436 }
437
438 SDL_SetError("No supported SDL_GPU backend found!");
439 return NULL;
440}
441
442static void SDL_GPU_FillProperties(
443 SDL_PropertiesID props,
444 SDL_GPUShaderFormat format_flags,
445 bool debug_mode,
446 const char *name)
447{
448 if (format_flags & SDL_GPU_SHADERFORMAT_PRIVATE) {
449 SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_PRIVATE_BOOLEAN, true);
450 }
451 if (format_flags & SDL_GPU_SHADERFORMAT_SPIRV) {
452 SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_SPIRV_BOOLEAN, true);
453 }
454 if (format_flags & SDL_GPU_SHADERFORMAT_DXBC) {
455 SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_DXBC_BOOLEAN, true);
456 }
457 if (format_flags & SDL_GPU_SHADERFORMAT_DXIL) {
458 SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_DXIL_BOOLEAN, true);
459 }
460 if (format_flags & SDL_GPU_SHADERFORMAT_MSL) {
461 SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_MSL_BOOLEAN, true);
462 }
463 if (format_flags & SDL_GPU_SHADERFORMAT_METALLIB) {
464 SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_METALLIB_BOOLEAN, true);
465 }
466 SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_DEBUGMODE_BOOLEAN, debug_mode);
467 SDL_SetStringProperty(props, SDL_PROP_GPU_DEVICE_CREATE_NAME_STRING, name);
468}
469#endif // SDL_GPU_DISABLED
470
471bool SDL_GPUSupportsShaderFormats(
472 SDL_GPUShaderFormat format_flags,
473 const char *name)
474{
475#ifndef SDL_GPU_DISABLED
476 bool result;
477 SDL_PropertiesID props = SDL_CreateProperties();
478 SDL_GPU_FillProperties(props, format_flags, false, name);
479 result = SDL_GPUSupportsProperties(props);
480 SDL_DestroyProperties(props);
481 return result;
482#else
483 SDL_SetError("SDL not built with GPU support");
484 return false;
485#endif
486}
487
488bool SDL_GPUSupportsProperties(SDL_PropertiesID props)
489{
490#ifndef SDL_GPU_DISABLED
491 return (SDL_GPUSelectBackend(props) != NULL);
492#else
493 SDL_SetError("SDL not built with GPU support");
494 return false;
495#endif
496}
497
498SDL_GPUDevice *SDL_CreateGPUDevice(
499 SDL_GPUShaderFormat format_flags,
500 bool debug_mode,
501 const char *name)
502{
503#ifndef SDL_GPU_DISABLED
504 SDL_GPUDevice *result;
505 SDL_PropertiesID props = SDL_CreateProperties();
506 SDL_GPU_FillProperties(props, format_flags, debug_mode, name);
507 result = SDL_CreateGPUDeviceWithProperties(props);
508 SDL_DestroyProperties(props);
509 return result;
510#else
511 SDL_SetError("SDL not built with GPU support");
512 return NULL;
513#endif // SDL_GPU_DISABLED
514}
515
516SDL_GPUDevice *SDL_CreateGPUDeviceWithProperties(SDL_PropertiesID props)
517{
518#ifndef SDL_GPU_DISABLED
519 bool debug_mode;
520 bool preferLowPower;
521 SDL_GPUDevice *result = NULL;
522 const SDL_GPUBootstrap *selectedBackend;
523
524 selectedBackend = SDL_GPUSelectBackend(props);
525 if (selectedBackend != NULL) {
526 debug_mode = SDL_GetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_DEBUGMODE_BOOLEAN, true);
527 preferLowPower = SDL_GetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_PREFERLOWPOWER_BOOLEAN, false);
528
529 result = selectedBackend->CreateDevice(debug_mode, preferLowPower, props);
530 if (result != NULL) {
531 result->backend = selectedBackend->name;
532 result->shader_formats = selectedBackend->shader_formats;
533 result->debug_mode = debug_mode;
534 }
535 }
536 return result;
537#else
538 SDL_SetError("SDL not built with GPU support");
539 return NULL;
540#endif // SDL_GPU_DISABLED
541}
542
543void SDL_DestroyGPUDevice(SDL_GPUDevice *device)
544{
545 CHECK_DEVICE_MAGIC(device, );
546
547 device->DestroyDevice(device);
548}
549
550int SDL_GetNumGPUDrivers(void)
551{
552#ifndef SDL_GPU_DISABLED
553 return SDL_arraysize(backends) - 1;
554#else
555 return 0;
556#endif
557}
558
559const char * SDL_GetGPUDriver(int index)
560{
561 if (index < 0 || index >= SDL_GetNumGPUDrivers()) {
562 SDL_InvalidParamError("index");
563 return NULL;
564 }
565#ifndef SDL_GPU_DISABLED
566 return backends[index]->name;
567#else
568 return NULL;
569#endif
570}
571
572const char * SDL_GetGPUDeviceDriver(SDL_GPUDevice *device)
573{
574 CHECK_DEVICE_MAGIC(device, NULL);
575
576 return device->backend;
577}
578
579SDL_GPUShaderFormat SDL_GetGPUShaderFormats(SDL_GPUDevice *device)
580{
581 CHECK_DEVICE_MAGIC(device, SDL_GPU_SHADERFORMAT_INVALID);
582
583 return device->shader_formats;
584}
585
586Uint32 SDL_GPUTextureFormatTexelBlockSize(
587 SDL_GPUTextureFormat format)
588{
589 switch (format) {
590 case SDL_GPU_TEXTUREFORMAT_BC1_RGBA_UNORM:
591 case SDL_GPU_TEXTUREFORMAT_BC1_RGBA_UNORM_SRGB:
592 case SDL_GPU_TEXTUREFORMAT_BC4_R_UNORM:
593 return 8;
594 case SDL_GPU_TEXTUREFORMAT_BC2_RGBA_UNORM:
595 case SDL_GPU_TEXTUREFORMAT_BC3_RGBA_UNORM:
596 case SDL_GPU_TEXTUREFORMAT_BC5_RG_UNORM:
597 case SDL_GPU_TEXTUREFORMAT_BC7_RGBA_UNORM:
598 case SDL_GPU_TEXTUREFORMAT_BC6H_RGB_FLOAT:
599 case SDL_GPU_TEXTUREFORMAT_BC6H_RGB_UFLOAT:
600 case SDL_GPU_TEXTUREFORMAT_BC2_RGBA_UNORM_SRGB:
601 case SDL_GPU_TEXTUREFORMAT_BC3_RGBA_UNORM_SRGB:
602 case SDL_GPU_TEXTUREFORMAT_BC7_RGBA_UNORM_SRGB:
603 return 16;
604 case SDL_GPU_TEXTUREFORMAT_R8_UNORM:
605 case SDL_GPU_TEXTUREFORMAT_R8_SNORM:
606 case SDL_GPU_TEXTUREFORMAT_A8_UNORM:
607 case SDL_GPU_TEXTUREFORMAT_R8_UINT:
608 return 1;
609 case SDL_GPU_TEXTUREFORMAT_B5G6R5_UNORM:
610 case SDL_GPU_TEXTUREFORMAT_B4G4R4A4_UNORM:
611 case SDL_GPU_TEXTUREFORMAT_B5G5R5A1_UNORM:
612 case SDL_GPU_TEXTUREFORMAT_R16_FLOAT:
613 case SDL_GPU_TEXTUREFORMAT_R8G8_SNORM:
614 case SDL_GPU_TEXTUREFORMAT_R8G8_UNORM:
615 case SDL_GPU_TEXTUREFORMAT_R8G8_UINT:
616 case SDL_GPU_TEXTUREFORMAT_R16_UNORM:
617 case SDL_GPU_TEXTUREFORMAT_R16_SNORM:
618 case SDL_GPU_TEXTUREFORMAT_R16_UINT:
619 case SDL_GPU_TEXTUREFORMAT_D16_UNORM:
620 return 2;
621 case SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM:
622 case SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM:
623 case SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM_SRGB:
624 case SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM_SRGB:
625 case SDL_GPU_TEXTUREFORMAT_R32_FLOAT:
626 case SDL_GPU_TEXTUREFORMAT_R16G16_FLOAT:
627 case SDL_GPU_TEXTUREFORMAT_R11G11B10_UFLOAT:
628 case SDL_GPU_TEXTUREFORMAT_R8G8B8A8_SNORM:
629 case SDL_GPU_TEXTUREFORMAT_R10G10B10A2_UNORM:
630 case SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UINT:
631 case SDL_GPU_TEXTUREFORMAT_R16G16_UINT:
632 case SDL_GPU_TEXTUREFORMAT_R16G16_UNORM:
633 case SDL_GPU_TEXTUREFORMAT_R16G16_SNORM:
634 case SDL_GPU_TEXTUREFORMAT_D24_UNORM:
635 case SDL_GPU_TEXTUREFORMAT_D32_FLOAT:
636 case SDL_GPU_TEXTUREFORMAT_D24_UNORM_S8_UINT:
637 return 4;
638 case SDL_GPU_TEXTUREFORMAT_D32_FLOAT_S8_UINT:
639 return 5;
640 case SDL_GPU_TEXTUREFORMAT_R16G16B16A16_FLOAT:
641 case SDL_GPU_TEXTUREFORMAT_R16G16B16A16_UNORM:
642 case SDL_GPU_TEXTUREFORMAT_R16G16B16A16_SNORM:
643 case SDL_GPU_TEXTUREFORMAT_R32G32_FLOAT:
644 case SDL_GPU_TEXTUREFORMAT_R16G16B16A16_UINT:
645 return 8;
646 case SDL_GPU_TEXTUREFORMAT_R32G32B32A32_FLOAT:
647 return 16;
648 case SDL_GPU_TEXTUREFORMAT_ASTC_4x4_UNORM:
649 case SDL_GPU_TEXTUREFORMAT_ASTC_5x4_UNORM:
650 case SDL_GPU_TEXTUREFORMAT_ASTC_5x5_UNORM:
651 case SDL_GPU_TEXTUREFORMAT_ASTC_6x5_UNORM:
652 case SDL_GPU_TEXTUREFORMAT_ASTC_6x6_UNORM:
653 case SDL_GPU_TEXTUREFORMAT_ASTC_8x5_UNORM:
654 case SDL_GPU_TEXTUREFORMAT_ASTC_8x6_UNORM:
655 case SDL_GPU_TEXTUREFORMAT_ASTC_8x8_UNORM:
656 case SDL_GPU_TEXTUREFORMAT_ASTC_10x5_UNORM:
657 case SDL_GPU_TEXTUREFORMAT_ASTC_10x6_UNORM:
658 case SDL_GPU_TEXTUREFORMAT_ASTC_10x8_UNORM:
659 case SDL_GPU_TEXTUREFORMAT_ASTC_10x10_UNORM:
660 case SDL_GPU_TEXTUREFORMAT_ASTC_12x10_UNORM:
661 case SDL_GPU_TEXTUREFORMAT_ASTC_12x12_UNORM:
662 case SDL_GPU_TEXTUREFORMAT_ASTC_4x4_UNORM_SRGB:
663 case SDL_GPU_TEXTUREFORMAT_ASTC_5x4_UNORM_SRGB:
664 case SDL_GPU_TEXTUREFORMAT_ASTC_5x5_UNORM_SRGB:
665 case SDL_GPU_TEXTUREFORMAT_ASTC_6x5_UNORM_SRGB:
666 case SDL_GPU_TEXTUREFORMAT_ASTC_6x6_UNORM_SRGB:
667 case SDL_GPU_TEXTUREFORMAT_ASTC_8x5_UNORM_SRGB:
668 case SDL_GPU_TEXTUREFORMAT_ASTC_8x6_UNORM_SRGB:
669 case SDL_GPU_TEXTUREFORMAT_ASTC_8x8_UNORM_SRGB:
670 case SDL_GPU_TEXTUREFORMAT_ASTC_10x5_UNORM_SRGB:
671 case SDL_GPU_TEXTUREFORMAT_ASTC_10x6_UNORM_SRGB:
672 case SDL_GPU_TEXTUREFORMAT_ASTC_10x8_UNORM_SRGB:
673 case SDL_GPU_TEXTUREFORMAT_ASTC_10x10_UNORM_SRGB:
674 case SDL_GPU_TEXTUREFORMAT_ASTC_12x10_UNORM_SRGB:
675 case SDL_GPU_TEXTUREFORMAT_ASTC_12x12_UNORM_SRGB:
676 case SDL_GPU_TEXTUREFORMAT_ASTC_4x4_FLOAT:
677 case SDL_GPU_TEXTUREFORMAT_ASTC_5x4_FLOAT:
678 case SDL_GPU_TEXTUREFORMAT_ASTC_5x5_FLOAT:
679 case SDL_GPU_TEXTUREFORMAT_ASTC_6x5_FLOAT:
680 case SDL_GPU_TEXTUREFORMAT_ASTC_6x6_FLOAT:
681 case SDL_GPU_TEXTUREFORMAT_ASTC_8x5_FLOAT:
682 case SDL_GPU_TEXTUREFORMAT_ASTC_8x6_FLOAT:
683 case SDL_GPU_TEXTUREFORMAT_ASTC_8x8_FLOAT:
684 case SDL_GPU_TEXTUREFORMAT_ASTC_10x5_FLOAT:
685 case SDL_GPU_TEXTUREFORMAT_ASTC_10x6_FLOAT:
686 case SDL_GPU_TEXTUREFORMAT_ASTC_10x8_FLOAT:
687 case SDL_GPU_TEXTUREFORMAT_ASTC_10x10_FLOAT:
688 case SDL_GPU_TEXTUREFORMAT_ASTC_12x10_FLOAT:
689 case SDL_GPU_TEXTUREFORMAT_ASTC_12x12_FLOAT:
690 return 16;
691 default:
692 SDL_assert_release(!"Unrecognized TextureFormat!");
693 return 0;
694 }
695}
696
697bool SDL_GPUTextureSupportsFormat(
698 SDL_GPUDevice *device,
699 SDL_GPUTextureFormat format,
700 SDL_GPUTextureType type,
701 SDL_GPUTextureUsageFlags usage)
702{
703 CHECK_DEVICE_MAGIC(device, false);
704
705 if (device->debug_mode) {
706 CHECK_TEXTUREFORMAT_ENUM_INVALID(format, false)
707 }
708
709 return device->SupportsTextureFormat(
710 device->driverData,
711 format,
712 type,
713 usage);
714}
715
716bool SDL_GPUTextureSupportsSampleCount(
717 SDL_GPUDevice *device,
718 SDL_GPUTextureFormat format,
719 SDL_GPUSampleCount sample_count)
720{
721 CHECK_DEVICE_MAGIC(device, 0);
722
723 if (device->debug_mode) {
724 CHECK_TEXTUREFORMAT_ENUM_INVALID(format, 0)
725 }
726
727 return device->SupportsSampleCount(
728 device->driverData,
729 format,
730 sample_count);
731}
732
733// State Creation
734
735SDL_GPUComputePipeline *SDL_CreateGPUComputePipeline(
736 SDL_GPUDevice *device,
737 const SDL_GPUComputePipelineCreateInfo *createinfo)
738{
739 CHECK_DEVICE_MAGIC(device, NULL);
740 if (createinfo == NULL) {
741 SDL_InvalidParamError("createinfo");
742 return NULL;
743 }
744
745 if (device->debug_mode) {
746 if (createinfo->format == SDL_GPU_SHADERFORMAT_INVALID) {
747 SDL_assert_release(!"Shader format cannot be INVALID!");
748 return NULL;
749 }
750 if (!(createinfo->format & device->shader_formats)) {
751 SDL_assert_release(!"Incompatible shader format for GPU backend");
752 return NULL;
753 }
754 if (createinfo->num_readwrite_storage_textures > MAX_COMPUTE_WRITE_TEXTURES) {
755 SDL_assert_release(!"Compute pipeline write-only texture count cannot be higher than 8!");
756 return NULL;
757 }
758 if (createinfo->num_readwrite_storage_buffers > MAX_COMPUTE_WRITE_BUFFERS) {
759 SDL_assert_release(!"Compute pipeline write-only buffer count cannot be higher than 8!");
760 return NULL;
761 }
762 if (createinfo->threadcount_x == 0 ||
763 createinfo->threadcount_y == 0 ||
764 createinfo->threadcount_z == 0) {
765 SDL_assert_release(!"Compute pipeline threadCount dimensions must be at least 1!");
766 return NULL;
767 }
768 }
769
770 return device->CreateComputePipeline(
771 device->driverData,
772 createinfo);
773}
774
775SDL_GPUGraphicsPipeline *SDL_CreateGPUGraphicsPipeline(
776 SDL_GPUDevice *device,
777 const SDL_GPUGraphicsPipelineCreateInfo *graphicsPipelineCreateInfo)
778{
779 CHECK_DEVICE_MAGIC(device, NULL);
780 if (graphicsPipelineCreateInfo == NULL) {
781 SDL_InvalidParamError("graphicsPipelineCreateInfo");
782 return NULL;
783 }
784
785 if (device->debug_mode) {
786 if (graphicsPipelineCreateInfo->vertex_shader == NULL) {
787 SDL_assert_release(!"Vertex shader cannot be NULL!");
788 return NULL;
789 }
790 if (graphicsPipelineCreateInfo->fragment_shader == NULL) {
791 SDL_assert_release(!"Fragment shader cannot be NULL!");
792 return NULL;
793 }
794 if (graphicsPipelineCreateInfo->target_info.num_color_targets > 0 && graphicsPipelineCreateInfo->target_info.color_target_descriptions == NULL) {
795 SDL_assert_release(!"Color target descriptions array pointer cannot be NULL if num_color_targets is greater than zero!");
796 return NULL;
797 }
798 for (Uint32 i = 0; i < graphicsPipelineCreateInfo->target_info.num_color_targets; i += 1) {
799 CHECK_TEXTUREFORMAT_ENUM_INVALID(graphicsPipelineCreateInfo->target_info.color_target_descriptions[i].format, NULL);
800 if (IsDepthFormat(graphicsPipelineCreateInfo->target_info.color_target_descriptions[i].format)) {
801 SDL_assert_release(!"Color target formats cannot be a depth format!");
802 return NULL;
803 }
804 if (graphicsPipelineCreateInfo->target_info.color_target_descriptions[i].blend_state.enable_blend) {
805 const SDL_GPUColorTargetBlendState *blend_state = &graphicsPipelineCreateInfo->target_info.color_target_descriptions[i].blend_state;
806 CHECK_BLENDFACTOR_ENUM_INVALID(blend_state->src_color_blendfactor, NULL)
807 CHECK_BLENDFACTOR_ENUM_INVALID(blend_state->dst_color_blendfactor, NULL)
808 CHECK_BLENDOP_ENUM_INVALID(blend_state->color_blend_op, NULL)
809 CHECK_BLENDFACTOR_ENUM_INVALID(blend_state->src_alpha_blendfactor, NULL)
810 CHECK_BLENDFACTOR_ENUM_INVALID(blend_state->dst_alpha_blendfactor, NULL)
811 CHECK_BLENDOP_ENUM_INVALID(blend_state->alpha_blend_op, NULL)
812 }
813 }
814 if (graphicsPipelineCreateInfo->target_info.has_depth_stencil_target) {
815 CHECK_TEXTUREFORMAT_ENUM_INVALID(graphicsPipelineCreateInfo->target_info.depth_stencil_format, NULL);
816 if (!IsDepthFormat(graphicsPipelineCreateInfo->target_info.depth_stencil_format)) {
817 SDL_assert_release(!"Depth-stencil target format must be a depth format!");
818 return NULL;
819 }
820 }
821 if (graphicsPipelineCreateInfo->vertex_input_state.num_vertex_buffers > 0 && graphicsPipelineCreateInfo->vertex_input_state.vertex_buffer_descriptions == NULL) {
822 SDL_assert_release(!"Vertex buffer descriptions array pointer cannot be NULL!");
823 return NULL;
824 }
825 if (graphicsPipelineCreateInfo->vertex_input_state.num_vertex_buffers > MAX_VERTEX_BUFFERS) {
826 SDL_assert_release(!"The number of vertex buffer descriptions in a vertex input state must not exceed 16!");
827 return NULL;
828 }
829 if (graphicsPipelineCreateInfo->vertex_input_state.num_vertex_attributes > 0 && graphicsPipelineCreateInfo->vertex_input_state.vertex_attributes == NULL) {
830 SDL_assert_release(!"Vertex attributes array pointer cannot be NULL!");
831 return NULL;
832 }
833 if (graphicsPipelineCreateInfo->vertex_input_state.num_vertex_attributes > MAX_VERTEX_ATTRIBUTES) {
834 SDL_assert_release(!"The number of vertex attributes in a vertex input state must not exceed 16!");
835 return NULL;
836 }
837 Uint32 locations[MAX_VERTEX_ATTRIBUTES];
838 for (Uint32 i = 0; i < graphicsPipelineCreateInfo->vertex_input_state.num_vertex_attributes; i += 1) {
839 CHECK_VERTEXELEMENTFORMAT_ENUM_INVALID(graphicsPipelineCreateInfo->vertex_input_state.vertex_attributes[i].format, NULL);
840
841 locations[i] = graphicsPipelineCreateInfo->vertex_input_state.vertex_attributes[i].location;
842 for (Uint32 j = 0; j < i; j += 1) {
843 if (locations[j] == locations[i]) {
844 SDL_assert_release(!"Each vertex attribute location in a vertex input state must be unique!");
845 }
846 }
847 }
848 if (graphicsPipelineCreateInfo->depth_stencil_state.enable_depth_test) {
849 CHECK_COMPAREOP_ENUM_INVALID(graphicsPipelineCreateInfo->depth_stencil_state.compare_op, NULL)
850 }
851 if (graphicsPipelineCreateInfo->depth_stencil_state.enable_stencil_test) {
852 const SDL_GPUStencilOpState *stencil_state = &graphicsPipelineCreateInfo->depth_stencil_state.back_stencil_state;
853 CHECK_COMPAREOP_ENUM_INVALID(stencil_state->compare_op, NULL)
854 CHECK_STENCILOP_ENUM_INVALID(stencil_state->fail_op, NULL)
855 CHECK_STENCILOP_ENUM_INVALID(stencil_state->pass_op, NULL)
856 CHECK_STENCILOP_ENUM_INVALID(stencil_state->depth_fail_op, NULL)
857 }
858 }
859
860 return device->CreateGraphicsPipeline(
861 device->driverData,
862 graphicsPipelineCreateInfo);
863}
864
865SDL_GPUSampler *SDL_CreateGPUSampler(
866 SDL_GPUDevice *device,
867 const SDL_GPUSamplerCreateInfo *createinfo)
868{
869 CHECK_DEVICE_MAGIC(device, NULL);
870 if (createinfo == NULL) {
871 SDL_InvalidParamError("createinfo");
872 return NULL;
873 }
874
875 return device->CreateSampler(
876 device->driverData,
877 createinfo);
878}
879
880SDL_GPUShader *SDL_CreateGPUShader(
881 SDL_GPUDevice *device,
882 const SDL_GPUShaderCreateInfo *createinfo)
883{
884 CHECK_DEVICE_MAGIC(device, NULL);
885 if (createinfo == NULL) {
886 SDL_InvalidParamError("createinfo");
887 return NULL;
888 }
889
890 if (device->debug_mode) {
891 if (createinfo->format == SDL_GPU_SHADERFORMAT_INVALID) {
892 SDL_assert_release(!"Shader format cannot be INVALID!");
893 return NULL;
894 }
895 if (!(createinfo->format & device->shader_formats)) {
896 SDL_assert_release(!"Incompatible shader format for GPU backend");
897 return NULL;
898 }
899 }
900
901 return device->CreateShader(
902 device->driverData,
903 createinfo);
904}
905
906SDL_GPUTexture *SDL_CreateGPUTexture(
907 SDL_GPUDevice *device,
908 const SDL_GPUTextureCreateInfo *createinfo)
909{
910 CHECK_DEVICE_MAGIC(device, NULL);
911 if (createinfo == NULL) {
912 SDL_InvalidParamError("createinfo");
913 return NULL;
914 }
915
916 if (device->debug_mode) {
917 bool failed = false;
918
919 const Uint32 MAX_2D_DIMENSION = 16384;
920 const Uint32 MAX_3D_DIMENSION = 2048;
921
922 // Common checks for all texture types
923 CHECK_TEXTUREFORMAT_ENUM_INVALID(createinfo->format, NULL)
924
925 if (createinfo->width <= 0 || createinfo->height <= 0 || createinfo->layer_count_or_depth <= 0) {
926 SDL_assert_release(!"For any texture: width, height, and layer_count_or_depth must be >= 1");
927 failed = true;
928 }
929 if (createinfo->num_levels <= 0) {
930 SDL_assert_release(!"For any texture: num_levels must be >= 1");
931 failed = true;
932 }
933 if ((createinfo->usage & SDL_GPU_TEXTUREUSAGE_GRAPHICS_STORAGE_READ) && (createinfo->usage & SDL_GPU_TEXTUREUSAGE_SAMPLER)) {
934 SDL_assert_release(!"For any texture: usage cannot contain both GRAPHICS_STORAGE_READ and SAMPLER");
935 failed = true;
936 }
937 if (createinfo->sample_count > SDL_GPU_SAMPLECOUNT_1 &&
938 (createinfo->usage & (SDL_GPU_TEXTUREUSAGE_SAMPLER |
939 SDL_GPU_TEXTUREUSAGE_GRAPHICS_STORAGE_READ |
940 SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ |
941 SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE))) {
942 SDL_assert_release(!"For multisample textures: usage cannot contain SAMPLER or STORAGE flags");
943 failed = true;
944 }
945 if (IsDepthFormat(createinfo->format) && (createinfo->usage & ~(SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET | SDL_GPU_TEXTUREUSAGE_SAMPLER))) {
946 SDL_assert_release(!"For depth textures: usage cannot contain any flags except for DEPTH_STENCIL_TARGET and SAMPLER");
947 failed = true;
948 }
949 if (IsIntegerFormat(createinfo->format) && (createinfo->usage & SDL_GPU_TEXTUREUSAGE_SAMPLER)) {
950 SDL_assert_release(!"For any texture: usage cannot contain SAMPLER for textures with an integer format");
951 failed = true;
952 }
953
954 if (createinfo->type == SDL_GPU_TEXTURETYPE_CUBE) {
955 // Cubemap validation
956 if (createinfo->width != createinfo->height) {
957 SDL_assert_release(!"For cube textures: width and height must be identical");
958 failed = true;
959 }
960 if (createinfo->width > MAX_2D_DIMENSION || createinfo->height > MAX_2D_DIMENSION) {
961 SDL_assert_release(!"For cube textures: width and height must be <= 16384");
962 failed = true;
963 }
964 if (createinfo->layer_count_or_depth != 6) {
965 SDL_assert_release(!"For cube textures: layer_count_or_depth must be 6");
966 failed = true;
967 }
968 if (createinfo->sample_count > SDL_GPU_SAMPLECOUNT_1) {
969 SDL_assert_release(!"For cube textures: sample_count must be SDL_GPU_SAMPLECOUNT_1");
970 failed = true;
971 }
972 if (!SDL_GPUTextureSupportsFormat(device, createinfo->format, SDL_GPU_TEXTURETYPE_CUBE, createinfo->usage)) {
973 SDL_assert_release(!"For cube textures: the format is unsupported for the given usage");
974 failed = true;
975 }
976 } else if (createinfo->type == SDL_GPU_TEXTURETYPE_CUBE_ARRAY) {
977 // Cubemap array validation
978 if (createinfo->width != createinfo->height) {
979 SDL_assert_release(!"For cube array textures: width and height must be identical");
980 failed = true;
981 }
982 if (createinfo->width > MAX_2D_DIMENSION || createinfo->height > MAX_2D_DIMENSION) {
983 SDL_assert_release(!"For cube array textures: width and height must be <= 16384");
984 failed = true;
985 }
986 if (createinfo->layer_count_or_depth % 6 != 0) {
987 SDL_assert_release(!"For cube array textures: layer_count_or_depth must be a multiple of 6");
988 failed = true;
989 }
990 if (createinfo->sample_count > SDL_GPU_SAMPLECOUNT_1) {
991 SDL_assert_release(!"For cube array textures: sample_count must be SDL_GPU_SAMPLECOUNT_1");
992 failed = true;
993 }
994 if (!SDL_GPUTextureSupportsFormat(device, createinfo->format, SDL_GPU_TEXTURETYPE_CUBE_ARRAY, createinfo->usage)) {
995 SDL_assert_release(!"For cube array textures: the format is unsupported for the given usage");
996 failed = true;
997 }
998 } else if (createinfo->type == SDL_GPU_TEXTURETYPE_3D) {
999 // 3D Texture Validation
1000 if (createinfo->width > MAX_3D_DIMENSION || createinfo->height > MAX_3D_DIMENSION || createinfo->layer_count_or_depth > MAX_3D_DIMENSION) {
1001 SDL_assert_release(!"For 3D textures: width, height, and layer_count_or_depth must be <= 2048");
1002 failed = true;
1003 }
1004 if (createinfo->usage & SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET) {
1005 SDL_assert_release(!"For 3D textures: usage must not contain DEPTH_STENCIL_TARGET");
1006 failed = true;
1007 }
1008 if (createinfo->sample_count > SDL_GPU_SAMPLECOUNT_1) {
1009 SDL_assert_release(!"For 3D textures: sample_count must be SDL_GPU_SAMPLECOUNT_1");
1010 failed = true;
1011 }
1012 if (!SDL_GPUTextureSupportsFormat(device, createinfo->format, SDL_GPU_TEXTURETYPE_3D, createinfo->usage)) {
1013 SDL_assert_release(!"For 3D textures: the format is unsupported for the given usage");
1014 failed = true;
1015 }
1016 } else {
1017 if (createinfo->type == SDL_GPU_TEXTURETYPE_2D_ARRAY) {
1018 // Array Texture Validation
1019 if (createinfo->usage & SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET) {
1020 SDL_assert_release(!"For array textures: usage must not contain DEPTH_STENCIL_TARGET");
1021 failed = true;
1022 }
1023 if (createinfo->sample_count > SDL_GPU_SAMPLECOUNT_1) {
1024 SDL_assert_release(!"For array textures: sample_count must be SDL_GPU_SAMPLECOUNT_1");
1025 failed = true;
1026 }
1027 }
1028 if (createinfo->sample_count > SDL_GPU_SAMPLECOUNT_1 && createinfo->num_levels > 1) {
1029 SDL_assert_release(!"For 2D multisample textures: num_levels must be 1");
1030 failed = true;
1031 }
1032 if (!SDL_GPUTextureSupportsFormat(device, createinfo->format, SDL_GPU_TEXTURETYPE_2D, createinfo->usage)) {
1033 SDL_assert_release(!"For 2D textures: the format is unsupported for the given usage");
1034 failed = true;
1035 }
1036 }
1037
1038 if (failed) {
1039 return NULL;
1040 }
1041 }
1042
1043 return device->CreateTexture(
1044 device->driverData,
1045 createinfo);
1046}
1047
1048SDL_GPUBuffer *SDL_CreateGPUBuffer(
1049 SDL_GPUDevice *device,
1050 const SDL_GPUBufferCreateInfo *createinfo)
1051{
1052 CHECK_DEVICE_MAGIC(device, NULL);
1053 if (createinfo == NULL) {
1054 SDL_InvalidParamError("createinfo");
1055 return NULL;
1056 }
1057
1058 return device->CreateBuffer(
1059 device->driverData,
1060 createinfo->usage,
1061 createinfo->size);
1062}
1063
1064SDL_GPUTransferBuffer *SDL_CreateGPUTransferBuffer(
1065 SDL_GPUDevice *device,
1066 const SDL_GPUTransferBufferCreateInfo *createinfo)
1067{
1068 CHECK_DEVICE_MAGIC(device, NULL);
1069 if (createinfo == NULL) {
1070 SDL_InvalidParamError("createinfo");
1071 return NULL;
1072 }
1073
1074 return device->CreateTransferBuffer(
1075 device->driverData,
1076 createinfo->usage,
1077 createinfo->size);
1078}
1079
1080// Debug Naming
1081
1082void SDL_SetGPUBufferName(
1083 SDL_GPUDevice *device,
1084 SDL_GPUBuffer *buffer,
1085 const char *text)
1086{
1087 CHECK_DEVICE_MAGIC(device, );
1088 if (buffer == NULL) {
1089 SDL_InvalidParamError("buffer");
1090 return;
1091 }
1092 if (text == NULL) {
1093 SDL_InvalidParamError("text");
1094 }
1095
1096 device->SetBufferName(
1097 device->driverData,
1098 buffer,
1099 text);
1100}
1101
1102void SDL_SetGPUTextureName(
1103 SDL_GPUDevice *device,
1104 SDL_GPUTexture *texture,
1105 const char *text)
1106{
1107 CHECK_DEVICE_MAGIC(device, );
1108 if (texture == NULL) {
1109 SDL_InvalidParamError("texture");
1110 return;
1111 }
1112 if (text == NULL) {
1113 SDL_InvalidParamError("text");
1114 }
1115
1116 device->SetTextureName(
1117 device->driverData,
1118 texture,
1119 text);
1120}
1121
1122void SDL_InsertGPUDebugLabel(
1123 SDL_GPUCommandBuffer *command_buffer,
1124 const char *text)
1125{
1126 if (command_buffer == NULL) {
1127 SDL_InvalidParamError("command_buffer");
1128 return;
1129 }
1130 if (text == NULL) {
1131 SDL_InvalidParamError("text");
1132 return;
1133 }
1134
1135 if (COMMAND_BUFFER_DEVICE->debug_mode) {
1136 CHECK_COMMAND_BUFFER
1137 }
1138
1139 COMMAND_BUFFER_DEVICE->InsertDebugLabel(
1140 command_buffer,
1141 text);
1142}
1143
1144void SDL_PushGPUDebugGroup(
1145 SDL_GPUCommandBuffer *command_buffer,
1146 const char *name)
1147{
1148 if (command_buffer == NULL) {
1149 SDL_InvalidParamError("command_buffer");
1150 return;
1151 }
1152 if (name == NULL) {
1153 SDL_InvalidParamError("name");
1154 return;
1155 }
1156
1157 if (COMMAND_BUFFER_DEVICE->debug_mode) {
1158 CHECK_COMMAND_BUFFER
1159 }
1160
1161 COMMAND_BUFFER_DEVICE->PushDebugGroup(
1162 command_buffer,
1163 name);
1164}
1165
1166void SDL_PopGPUDebugGroup(
1167 SDL_GPUCommandBuffer *command_buffer)
1168{
1169 if (command_buffer == NULL) {
1170 SDL_InvalidParamError("command_buffer");
1171 return;
1172 }
1173
1174 if (COMMAND_BUFFER_DEVICE->debug_mode) {
1175 CHECK_COMMAND_BUFFER
1176 }
1177
1178 COMMAND_BUFFER_DEVICE->PopDebugGroup(
1179 command_buffer);
1180}
1181
1182// Disposal
1183
1184void SDL_ReleaseGPUTexture(
1185 SDL_GPUDevice *device,
1186 SDL_GPUTexture *texture)
1187{
1188 CHECK_DEVICE_MAGIC(device, );
1189 if (texture == NULL) {
1190 return;
1191 }
1192
1193 device->ReleaseTexture(
1194 device->driverData,
1195 texture);
1196}
1197
1198void SDL_ReleaseGPUSampler(
1199 SDL_GPUDevice *device,
1200 SDL_GPUSampler *sampler)
1201{
1202 CHECK_DEVICE_MAGIC(device, );
1203 if (sampler == NULL) {
1204 return;
1205 }
1206
1207 device->ReleaseSampler(
1208 device->driverData,
1209 sampler);
1210}
1211
1212void SDL_ReleaseGPUBuffer(
1213 SDL_GPUDevice *device,
1214 SDL_GPUBuffer *buffer)
1215{
1216 CHECK_DEVICE_MAGIC(device, );
1217 if (buffer == NULL) {
1218 return;
1219 }
1220
1221 device->ReleaseBuffer(
1222 device->driverData,
1223 buffer);
1224}
1225
1226void SDL_ReleaseGPUTransferBuffer(
1227 SDL_GPUDevice *device,
1228 SDL_GPUTransferBuffer *transfer_buffer)
1229{
1230 CHECK_DEVICE_MAGIC(device, );
1231 if (transfer_buffer == NULL) {
1232 return;
1233 }
1234
1235 device->ReleaseTransferBuffer(
1236 device->driverData,
1237 transfer_buffer);
1238}
1239
1240void SDL_ReleaseGPUShader(
1241 SDL_GPUDevice *device,
1242 SDL_GPUShader *shader)
1243{
1244 CHECK_DEVICE_MAGIC(device, );
1245 if (shader == NULL) {
1246 return;
1247 }
1248
1249 device->ReleaseShader(
1250 device->driverData,
1251 shader);
1252}
1253
1254void SDL_ReleaseGPUComputePipeline(
1255 SDL_GPUDevice *device,
1256 SDL_GPUComputePipeline *compute_pipeline)
1257{
1258 CHECK_DEVICE_MAGIC(device, );
1259 if (compute_pipeline == NULL) {
1260 return;
1261 }
1262
1263 device->ReleaseComputePipeline(
1264 device->driverData,
1265 compute_pipeline);
1266}
1267
1268void SDL_ReleaseGPUGraphicsPipeline(
1269 SDL_GPUDevice *device,
1270 SDL_GPUGraphicsPipeline *graphics_pipeline)
1271{
1272 CHECK_DEVICE_MAGIC(device, );
1273 if (graphics_pipeline == NULL) {
1274 return;
1275 }
1276
1277 device->ReleaseGraphicsPipeline(
1278 device->driverData,
1279 graphics_pipeline);
1280}
1281
1282// Command Buffer
1283
1284SDL_GPUCommandBuffer *SDL_AcquireGPUCommandBuffer(
1285 SDL_GPUDevice *device)
1286{
1287 SDL_GPUCommandBuffer *command_buffer;
1288 CommandBufferCommonHeader *commandBufferHeader;
1289
1290 CHECK_DEVICE_MAGIC(device, NULL);
1291
1292 command_buffer = device->AcquireCommandBuffer(
1293 device->driverData);
1294
1295 if (command_buffer == NULL) {
1296 return NULL;
1297 }
1298
1299 commandBufferHeader = (CommandBufferCommonHeader *)command_buffer;
1300 commandBufferHeader->device = device;
1301 commandBufferHeader->render_pass.command_buffer = command_buffer;
1302 commandBufferHeader->render_pass.in_progress = false;
1303 commandBufferHeader->graphics_pipeline_bound = false;
1304 commandBufferHeader->compute_pass.command_buffer = command_buffer;
1305 commandBufferHeader->compute_pass.in_progress = false;
1306 commandBufferHeader->compute_pipeline_bound = false;
1307 commandBufferHeader->copy_pass.command_buffer = command_buffer;
1308 commandBufferHeader->copy_pass.in_progress = false;
1309 commandBufferHeader->swapchain_texture_acquired = false;
1310 commandBufferHeader->submitted = false;
1311
1312 return command_buffer;
1313}
1314
1315// Uniforms
1316
1317void SDL_PushGPUVertexUniformData(
1318 SDL_GPUCommandBuffer *command_buffer,
1319 Uint32 slot_index,
1320 const void *data,
1321 Uint32 length)
1322{
1323 if (command_buffer == NULL) {
1324 SDL_InvalidParamError("command_buffer");
1325 return;
1326 }
1327 if (data == NULL) {
1328 SDL_InvalidParamError("data");
1329 return;
1330 }
1331
1332 if (COMMAND_BUFFER_DEVICE->debug_mode) {
1333 CHECK_COMMAND_BUFFER
1334 }
1335
1336 COMMAND_BUFFER_DEVICE->PushVertexUniformData(
1337 command_buffer,
1338 slot_index,
1339 data,
1340 length);
1341}
1342
1343void SDL_PushGPUFragmentUniformData(
1344 SDL_GPUCommandBuffer *command_buffer,
1345 Uint32 slot_index,
1346 const void *data,
1347 Uint32 length)
1348{
1349 if (command_buffer == NULL) {
1350 SDL_InvalidParamError("command_buffer");
1351 return;
1352 }
1353 if (data == NULL) {
1354 SDL_InvalidParamError("data");
1355 return;
1356 }
1357
1358 if (COMMAND_BUFFER_DEVICE->debug_mode) {
1359 CHECK_COMMAND_BUFFER
1360 }
1361
1362 COMMAND_BUFFER_DEVICE->PushFragmentUniformData(
1363 command_buffer,
1364 slot_index,
1365 data,
1366 length);
1367}
1368
1369void SDL_PushGPUComputeUniformData(
1370 SDL_GPUCommandBuffer *command_buffer,
1371 Uint32 slot_index,
1372 const void *data,
1373 Uint32 length)
1374{
1375 if (command_buffer == NULL) {
1376 SDL_InvalidParamError("command_buffer");
1377 return;
1378 }
1379 if (data == NULL) {
1380 SDL_InvalidParamError("data");
1381 return;
1382 }
1383
1384 if (COMMAND_BUFFER_DEVICE->debug_mode) {
1385 CHECK_COMMAND_BUFFER
1386 }
1387
1388 COMMAND_BUFFER_DEVICE->PushComputeUniformData(
1389 command_buffer,
1390 slot_index,
1391 data,
1392 length);
1393}
1394
1395// Render Pass
1396
1397SDL_GPURenderPass *SDL_BeginGPURenderPass(
1398 SDL_GPUCommandBuffer *command_buffer,
1399 const SDL_GPUColorTargetInfo *color_target_infos,
1400 Uint32 num_color_targets,
1401 const SDL_GPUDepthStencilTargetInfo *depth_stencil_target_info)
1402{
1403 CommandBufferCommonHeader *commandBufferHeader;
1404
1405 if (command_buffer == NULL) {
1406 SDL_InvalidParamError("command_buffer");
1407 return NULL;
1408 }
1409 if (color_target_infos == NULL && num_color_targets > 0) {
1410 SDL_InvalidParamError("color_target_infos");
1411 return NULL;
1412 }
1413
1414 if (num_color_targets > MAX_COLOR_TARGET_BINDINGS) {
1415 SDL_SetError("num_color_targets exceeds MAX_COLOR_TARGET_BINDINGS");
1416 return NULL;
1417 }
1418
1419 if (COMMAND_BUFFER_DEVICE->debug_mode) {
1420 CHECK_COMMAND_BUFFER_RETURN_NULL
1421 CHECK_ANY_PASS_IN_PROGRESS("Cannot begin render pass during another pass!", NULL)
1422
1423 for (Uint32 i = 0; i < num_color_targets; i += 1) {
1424 TextureCommonHeader *textureHeader = (TextureCommonHeader *)color_target_infos[i].texture;
1425
1426 if (color_target_infos[i].cycle && color_target_infos[i].load_op == SDL_GPU_LOADOP_LOAD) {
1427 SDL_assert_release(!"Cannot cycle color target when load op is LOAD!");
1428 }
1429
1430 if (color_target_infos[i].store_op == SDL_GPU_STOREOP_RESOLVE || color_target_infos[i].store_op == SDL_GPU_STOREOP_RESOLVE_AND_STORE) {
1431 if (color_target_infos[i].resolve_texture == NULL) {
1432 SDL_assert_release(!"Store op is RESOLVE or RESOLVE_AND_STORE but resolve_texture is NULL!");
1433 } else {
1434 TextureCommonHeader *resolveTextureHeader = (TextureCommonHeader *)color_target_infos[i].resolve_texture;
1435 if (textureHeader->info.sample_count == SDL_GPU_SAMPLECOUNT_1) {
1436 SDL_assert_release(!"Store op is RESOLVE or RESOLVE_AND_STORE but texture is not multisample!");
1437 }
1438 if (resolveTextureHeader->info.sample_count != SDL_GPU_SAMPLECOUNT_1) {
1439 SDL_assert_release(!"Resolve texture must have a sample count of 1!");
1440 }
1441 if (resolveTextureHeader->info.format != textureHeader->info.format) {
1442 SDL_assert_release(!"Resolve texture must have the same format as its corresponding color target!");
1443 }
1444 if (resolveTextureHeader->info.type == SDL_GPU_TEXTURETYPE_3D) {
1445 SDL_assert_release(!"Resolve texture must not be of TEXTURETYPE_3D!");
1446 }
1447 if (!(resolveTextureHeader->info.usage & SDL_GPU_TEXTUREUSAGE_COLOR_TARGET)) {
1448 SDL_assert_release(!"Resolve texture usage must include COLOR_TARGET!");
1449 }
1450 }
1451 }
1452 }
1453
1454 if (depth_stencil_target_info != NULL) {
1455
1456 TextureCommonHeader *textureHeader = (TextureCommonHeader *)depth_stencil_target_info->texture;
1457 if (!(textureHeader->info.usage & SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET)) {
1458 SDL_assert_release(!"Depth target must have been created with the DEPTH_STENCIL_TARGET usage flag!");
1459 }
1460
1461 if (depth_stencil_target_info->cycle && (depth_stencil_target_info->load_op == SDL_GPU_LOADOP_LOAD || depth_stencil_target_info->stencil_load_op == SDL_GPU_LOADOP_LOAD)) {
1462 SDL_assert_release(!"Cannot cycle depth target when load op or stencil load op is LOAD!");
1463 }
1464
1465 if (depth_stencil_target_info->store_op == SDL_GPU_STOREOP_RESOLVE ||
1466 depth_stencil_target_info->stencil_store_op == SDL_GPU_STOREOP_RESOLVE ||
1467 depth_stencil_target_info->store_op == SDL_GPU_STOREOP_RESOLVE_AND_STORE ||
1468 depth_stencil_target_info->stencil_store_op == SDL_GPU_STOREOP_RESOLVE_AND_STORE) {
1469 SDL_assert_release(!"RESOLVE store ops are not supported for depth-stencil targets!");
1470 }
1471 }
1472 }
1473
1474 COMMAND_BUFFER_DEVICE->BeginRenderPass(
1475 command_buffer,
1476 color_target_infos,
1477 num_color_targets,
1478 depth_stencil_target_info);
1479
1480 commandBufferHeader = (CommandBufferCommonHeader *)command_buffer;
1481 commandBufferHeader->render_pass.in_progress = true;
1482 return (SDL_GPURenderPass *)&(commandBufferHeader->render_pass);
1483}
1484
1485void SDL_BindGPUGraphicsPipeline(
1486 SDL_GPURenderPass *render_pass,
1487 SDL_GPUGraphicsPipeline *graphics_pipeline)
1488{
1489 CommandBufferCommonHeader *commandBufferHeader;
1490
1491 if (render_pass == NULL) {
1492 SDL_InvalidParamError("render_pass");
1493 return;
1494 }
1495 if (graphics_pipeline == NULL) {
1496 SDL_InvalidParamError("graphics_pipeline");
1497 return;
1498 }
1499
1500 RENDERPASS_DEVICE->BindGraphicsPipeline(
1501 RENDERPASS_COMMAND_BUFFER,
1502 graphics_pipeline);
1503
1504 commandBufferHeader = (CommandBufferCommonHeader *)RENDERPASS_COMMAND_BUFFER;
1505 commandBufferHeader->graphics_pipeline_bound = true;
1506}
1507
1508void SDL_SetGPUViewport(
1509 SDL_GPURenderPass *render_pass,
1510 const SDL_GPUViewport *viewport)
1511{
1512 if (render_pass == NULL) {
1513 SDL_InvalidParamError("render_pass");
1514 return;
1515 }
1516 if (viewport == NULL) {
1517 SDL_InvalidParamError("viewport");
1518 return;
1519 }
1520
1521 if (RENDERPASS_DEVICE->debug_mode) {
1522 CHECK_RENDERPASS
1523 }
1524
1525 RENDERPASS_DEVICE->SetViewport(
1526 RENDERPASS_COMMAND_BUFFER,
1527 viewport);
1528}
1529
1530void SDL_SetGPUScissor(
1531 SDL_GPURenderPass *render_pass,
1532 const SDL_Rect *scissor)
1533{
1534 if (render_pass == NULL) {
1535 SDL_InvalidParamError("render_pass");
1536 return;
1537 }
1538 if (scissor == NULL) {
1539 SDL_InvalidParamError("scissor");
1540 return;
1541 }
1542
1543 if (RENDERPASS_DEVICE->debug_mode) {
1544 CHECK_RENDERPASS
1545 }
1546
1547 RENDERPASS_DEVICE->SetScissor(
1548 RENDERPASS_COMMAND_BUFFER,
1549 scissor);
1550}
1551
1552void SDL_SetGPUBlendConstants(
1553 SDL_GPURenderPass *render_pass,
1554 SDL_FColor blend_constants)
1555{
1556 if (render_pass == NULL) {
1557 SDL_InvalidParamError("render_pass");
1558 return;
1559 }
1560
1561 if (RENDERPASS_DEVICE->debug_mode) {
1562 CHECK_RENDERPASS
1563 }
1564
1565 RENDERPASS_DEVICE->SetBlendConstants(
1566 RENDERPASS_COMMAND_BUFFER,
1567 blend_constants);
1568}
1569
1570void SDL_SetGPUStencilReference(
1571 SDL_GPURenderPass *render_pass,
1572 Uint8 reference)
1573{
1574 if (render_pass == NULL) {
1575 SDL_InvalidParamError("render_pass");
1576 return;
1577 }
1578
1579 if (RENDERPASS_DEVICE->debug_mode) {
1580 CHECK_RENDERPASS
1581 }
1582
1583 RENDERPASS_DEVICE->SetStencilReference(
1584 RENDERPASS_COMMAND_BUFFER,
1585 reference);
1586}
1587
1588void SDL_BindGPUVertexBuffers(
1589 SDL_GPURenderPass *render_pass,
1590 Uint32 first_binding,
1591 const SDL_GPUBufferBinding *bindings,
1592 Uint32 num_bindings)
1593{
1594 if (render_pass == NULL) {
1595 SDL_InvalidParamError("render_pass");
1596 return;
1597 }
1598 if (bindings == NULL && num_bindings > 0) {
1599 SDL_InvalidParamError("bindings");
1600 return;
1601 }
1602
1603 if (RENDERPASS_DEVICE->debug_mode) {
1604 CHECK_RENDERPASS
1605 }
1606
1607 RENDERPASS_DEVICE->BindVertexBuffers(
1608 RENDERPASS_COMMAND_BUFFER,
1609 first_binding,
1610 bindings,
1611 num_bindings);
1612}
1613
1614void SDL_BindGPUIndexBuffer(
1615 SDL_GPURenderPass *render_pass,
1616 const SDL_GPUBufferBinding *binding,
1617 SDL_GPUIndexElementSize index_element_size)
1618{
1619 if (render_pass == NULL) {
1620 SDL_InvalidParamError("render_pass");
1621 return;
1622 }
1623 if (binding == NULL) {
1624 SDL_InvalidParamError("binding");
1625 return;
1626 }
1627
1628 if (RENDERPASS_DEVICE->debug_mode) {
1629 CHECK_RENDERPASS
1630 }
1631
1632 RENDERPASS_DEVICE->BindIndexBuffer(
1633 RENDERPASS_COMMAND_BUFFER,
1634 binding,
1635 index_element_size);
1636}
1637
1638void SDL_BindGPUVertexSamplers(
1639 SDL_GPURenderPass *render_pass,
1640 Uint32 first_slot,
1641 const SDL_GPUTextureSamplerBinding *texture_sampler_bindings,
1642 Uint32 num_bindings)
1643{
1644 if (render_pass == NULL) {
1645 SDL_InvalidParamError("render_pass");
1646 return;
1647 }
1648 if (texture_sampler_bindings == NULL && num_bindings > 0) {
1649 SDL_InvalidParamError("texture_sampler_bindings");
1650 return;
1651 }
1652
1653 if (RENDERPASS_DEVICE->debug_mode) {
1654 CHECK_RENDERPASS
1655 }
1656
1657 RENDERPASS_DEVICE->BindVertexSamplers(
1658 RENDERPASS_COMMAND_BUFFER,
1659 first_slot,
1660 texture_sampler_bindings,
1661 num_bindings);
1662}
1663
1664void SDL_BindGPUVertexStorageTextures(
1665 SDL_GPURenderPass *render_pass,
1666 Uint32 first_slot,
1667 SDL_GPUTexture *const *storage_textures,
1668 Uint32 num_bindings)
1669{
1670 if (render_pass == NULL) {
1671 SDL_InvalidParamError("render_pass");
1672 return;
1673 }
1674 if (storage_textures == NULL && num_bindings > 0) {
1675 SDL_InvalidParamError("storage_textures");
1676 return;
1677 }
1678
1679 if (RENDERPASS_DEVICE->debug_mode) {
1680 CHECK_RENDERPASS
1681 }
1682
1683 RENDERPASS_DEVICE->BindVertexStorageTextures(
1684 RENDERPASS_COMMAND_BUFFER,
1685 first_slot,
1686 storage_textures,
1687 num_bindings);
1688}
1689
1690void SDL_BindGPUVertexStorageBuffers(
1691 SDL_GPURenderPass *render_pass,
1692 Uint32 first_slot,
1693 SDL_GPUBuffer *const *storage_buffers,
1694 Uint32 num_bindings)
1695{
1696 if (render_pass == NULL) {
1697 SDL_InvalidParamError("render_pass");
1698 return;
1699 }
1700 if (storage_buffers == NULL && num_bindings > 0) {
1701 SDL_InvalidParamError("storage_buffers");
1702 return;
1703 }
1704
1705 if (RENDERPASS_DEVICE->debug_mode) {
1706 CHECK_RENDERPASS
1707 }
1708
1709 RENDERPASS_DEVICE->BindVertexStorageBuffers(
1710 RENDERPASS_COMMAND_BUFFER,
1711 first_slot,
1712 storage_buffers,
1713 num_bindings);
1714}
1715
1716void SDL_BindGPUFragmentSamplers(
1717 SDL_GPURenderPass *render_pass,
1718 Uint32 first_slot,
1719 const SDL_GPUTextureSamplerBinding *texture_sampler_bindings,
1720 Uint32 num_bindings)
1721{
1722 if (render_pass == NULL) {
1723 SDL_InvalidParamError("render_pass");
1724 return;
1725 }
1726 if (texture_sampler_bindings == NULL && num_bindings > 0) {
1727 SDL_InvalidParamError("texture_sampler_bindings");
1728 return;
1729 }
1730
1731 if (RENDERPASS_DEVICE->debug_mode) {
1732 CHECK_RENDERPASS
1733 }
1734
1735 RENDERPASS_DEVICE->BindFragmentSamplers(
1736 RENDERPASS_COMMAND_BUFFER,
1737 first_slot,
1738 texture_sampler_bindings,
1739 num_bindings);
1740}
1741
1742void SDL_BindGPUFragmentStorageTextures(
1743 SDL_GPURenderPass *render_pass,
1744 Uint32 first_slot,
1745 SDL_GPUTexture *const *storage_textures,
1746 Uint32 num_bindings)
1747{
1748 if (render_pass == NULL) {
1749 SDL_InvalidParamError("render_pass");
1750 return;
1751 }
1752 if (storage_textures == NULL && num_bindings > 0) {
1753 SDL_InvalidParamError("storage_textures");
1754 return;
1755 }
1756
1757 if (RENDERPASS_DEVICE->debug_mode) {
1758 CHECK_RENDERPASS
1759 }
1760
1761 RENDERPASS_DEVICE->BindFragmentStorageTextures(
1762 RENDERPASS_COMMAND_BUFFER,
1763 first_slot,
1764 storage_textures,
1765 num_bindings);
1766}
1767
1768void SDL_BindGPUFragmentStorageBuffers(
1769 SDL_GPURenderPass *render_pass,
1770 Uint32 first_slot,
1771 SDL_GPUBuffer *const *storage_buffers,
1772 Uint32 num_bindings)
1773{
1774 if (render_pass == NULL) {
1775 SDL_InvalidParamError("render_pass");
1776 return;
1777 }
1778 if (storage_buffers == NULL && num_bindings > 0) {
1779 SDL_InvalidParamError("storage_buffers");
1780 return;
1781 }
1782
1783 if (RENDERPASS_DEVICE->debug_mode) {
1784 CHECK_RENDERPASS
1785 }
1786
1787 RENDERPASS_DEVICE->BindFragmentStorageBuffers(
1788 RENDERPASS_COMMAND_BUFFER,
1789 first_slot,
1790 storage_buffers,
1791 num_bindings);
1792}
1793
1794void SDL_DrawGPUIndexedPrimitives(
1795 SDL_GPURenderPass *render_pass,
1796 Uint32 num_indices,
1797 Uint32 num_instances,
1798 Uint32 first_index,
1799 Sint32 vertex_offset,
1800 Uint32 first_instance)
1801{
1802 if (render_pass == NULL) {
1803 SDL_InvalidParamError("render_pass");
1804 return;
1805 }
1806
1807 if (RENDERPASS_DEVICE->debug_mode) {
1808 CHECK_RENDERPASS
1809 CHECK_GRAPHICS_PIPELINE_BOUND
1810 }
1811
1812 RENDERPASS_DEVICE->DrawIndexedPrimitives(
1813 RENDERPASS_COMMAND_BUFFER,
1814 num_indices,
1815 num_instances,
1816 first_index,
1817 vertex_offset,
1818 first_instance);
1819}
1820
1821void SDL_DrawGPUPrimitives(
1822 SDL_GPURenderPass *render_pass,
1823 Uint32 num_vertices,
1824 Uint32 num_instances,
1825 Uint32 first_vertex,
1826 Uint32 first_instance)
1827{
1828 if (render_pass == NULL) {
1829 SDL_InvalidParamError("render_pass");
1830 return;
1831 }
1832
1833 if (RENDERPASS_DEVICE->debug_mode) {
1834 CHECK_RENDERPASS
1835 CHECK_GRAPHICS_PIPELINE_BOUND
1836 }
1837
1838 RENDERPASS_DEVICE->DrawPrimitives(
1839 RENDERPASS_COMMAND_BUFFER,
1840 num_vertices,
1841 num_instances,
1842 first_vertex,
1843 first_instance);
1844}
1845
1846void SDL_DrawGPUPrimitivesIndirect(
1847 SDL_GPURenderPass *render_pass,
1848 SDL_GPUBuffer *buffer,
1849 Uint32 offset,
1850 Uint32 draw_count)
1851{
1852 if (render_pass == NULL) {
1853 SDL_InvalidParamError("render_pass");
1854 return;
1855 }
1856 if (buffer == NULL) {
1857 SDL_InvalidParamError("buffer");
1858 return;
1859 }
1860
1861 if (RENDERPASS_DEVICE->debug_mode) {
1862 CHECK_RENDERPASS
1863 CHECK_GRAPHICS_PIPELINE_BOUND
1864 }
1865
1866 RENDERPASS_DEVICE->DrawPrimitivesIndirect(
1867 RENDERPASS_COMMAND_BUFFER,
1868 buffer,
1869 offset,
1870 draw_count);
1871}
1872
1873void SDL_DrawGPUIndexedPrimitivesIndirect(
1874 SDL_GPURenderPass *render_pass,
1875 SDL_GPUBuffer *buffer,
1876 Uint32 offset,
1877 Uint32 draw_count)
1878{
1879 if (render_pass == NULL) {
1880 SDL_InvalidParamError("render_pass");
1881 return;
1882 }
1883 if (buffer == NULL) {
1884 SDL_InvalidParamError("buffer");
1885 return;
1886 }
1887
1888 if (RENDERPASS_DEVICE->debug_mode) {
1889 CHECK_RENDERPASS
1890 CHECK_GRAPHICS_PIPELINE_BOUND
1891 }
1892
1893 RENDERPASS_DEVICE->DrawIndexedPrimitivesIndirect(
1894 RENDERPASS_COMMAND_BUFFER,
1895 buffer,
1896 offset,
1897 draw_count);
1898}
1899
1900void SDL_EndGPURenderPass(
1901 SDL_GPURenderPass *render_pass)
1902{
1903 CommandBufferCommonHeader *commandBufferCommonHeader;
1904
1905 if (render_pass == NULL) {
1906 SDL_InvalidParamError("render_pass");
1907 return;
1908 }
1909
1910 if (RENDERPASS_DEVICE->debug_mode) {
1911 CHECK_RENDERPASS
1912 }
1913
1914 RENDERPASS_DEVICE->EndRenderPass(
1915 RENDERPASS_COMMAND_BUFFER);
1916
1917 commandBufferCommonHeader = (CommandBufferCommonHeader *)RENDERPASS_COMMAND_BUFFER;
1918 commandBufferCommonHeader->render_pass.in_progress = false;
1919 commandBufferCommonHeader->graphics_pipeline_bound = false;
1920}
1921
1922// Compute Pass
1923
1924SDL_GPUComputePass *SDL_BeginGPUComputePass(
1925 SDL_GPUCommandBuffer *command_buffer,
1926 const SDL_GPUStorageTextureReadWriteBinding *storage_texture_bindings,
1927 Uint32 num_storage_texture_bindings,
1928 const SDL_GPUStorageBufferReadWriteBinding *storage_buffer_bindings,
1929 Uint32 num_storage_buffer_bindings)
1930{
1931 CommandBufferCommonHeader *commandBufferHeader;
1932
1933 if (command_buffer == NULL) {
1934 SDL_InvalidParamError("command_buffer");
1935 return NULL;
1936 }
1937 if (storage_texture_bindings == NULL && num_storage_texture_bindings > 0) {
1938 SDL_InvalidParamError("storage_texture_bindings");
1939 return NULL;
1940 }
1941 if (storage_buffer_bindings == NULL && num_storage_buffer_bindings > 0) {
1942 SDL_InvalidParamError("storage_buffer_bindings");
1943 return NULL;
1944 }
1945 if (num_storage_texture_bindings > MAX_COMPUTE_WRITE_TEXTURES) {
1946 SDL_InvalidParamError("num_storage_texture_bindings");
1947 return NULL;
1948 }
1949 if (num_storage_buffer_bindings > MAX_COMPUTE_WRITE_BUFFERS) {
1950 SDL_InvalidParamError("num_storage_buffer_bindings");
1951 return NULL;
1952 }
1953 if (COMMAND_BUFFER_DEVICE->debug_mode) {
1954 CHECK_COMMAND_BUFFER_RETURN_NULL
1955 CHECK_ANY_PASS_IN_PROGRESS("Cannot begin compute pass during another pass!", NULL)
1956
1957 for (Uint32 i = 0; i < num_storage_texture_bindings; i += 1) {
1958 TextureCommonHeader *header = (TextureCommonHeader *)storage_texture_bindings[i].texture;
1959 if (!(header->info.usage & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE) && !(header->info.usage & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_SIMULTANEOUS_READ_WRITE)) {
1960 SDL_assert_release(!"Texture must be created with COMPUTE_STORAGE_WRITE or COMPUTE_STORAGE_SIMULTANEOUS_READ_WRITE flag");
1961 return NULL;
1962 }
1963 }
1964
1965 // TODO: validate buffer usage?
1966 }
1967
1968 COMMAND_BUFFER_DEVICE->BeginComputePass(
1969 command_buffer,
1970 storage_texture_bindings,
1971 num_storage_texture_bindings,
1972 storage_buffer_bindings,
1973 num_storage_buffer_bindings);
1974
1975 commandBufferHeader = (CommandBufferCommonHeader *)command_buffer;
1976 commandBufferHeader->compute_pass.in_progress = true;
1977 return (SDL_GPUComputePass *)&(commandBufferHeader->compute_pass);
1978}
1979
1980void SDL_BindGPUComputePipeline(
1981 SDL_GPUComputePass *compute_pass,
1982 SDL_GPUComputePipeline *compute_pipeline)
1983{
1984 CommandBufferCommonHeader *commandBufferHeader;
1985
1986 if (compute_pass == NULL) {
1987 SDL_InvalidParamError("compute_pass");
1988 return;
1989 }
1990 if (compute_pipeline == NULL) {
1991 SDL_InvalidParamError("compute_pipeline");
1992 return;
1993 }
1994
1995 if (COMPUTEPASS_DEVICE->debug_mode) {
1996 CHECK_COMPUTEPASS
1997 }
1998
1999 COMPUTEPASS_DEVICE->BindComputePipeline(
2000 COMPUTEPASS_COMMAND_BUFFER,
2001 compute_pipeline);
2002
2003 commandBufferHeader = (CommandBufferCommonHeader *)COMPUTEPASS_COMMAND_BUFFER;
2004 commandBufferHeader->compute_pipeline_bound = true;
2005}
2006
2007void SDL_BindGPUComputeSamplers(
2008 SDL_GPUComputePass *compute_pass,
2009 Uint32 first_slot,
2010 const SDL_GPUTextureSamplerBinding *texture_sampler_bindings,
2011 Uint32 num_bindings)
2012{
2013 if (compute_pass == NULL) {
2014 SDL_InvalidParamError("compute_pass");
2015 return;
2016 }
2017 if (texture_sampler_bindings == NULL && num_bindings > 0) {
2018 SDL_InvalidParamError("texture_sampler_bindings");
2019 return;
2020 }
2021
2022 if (COMPUTEPASS_DEVICE->debug_mode) {
2023 CHECK_COMPUTEPASS
2024 }
2025
2026 COMPUTEPASS_DEVICE->BindComputeSamplers(
2027 COMPUTEPASS_COMMAND_BUFFER,
2028 first_slot,
2029 texture_sampler_bindings,
2030 num_bindings);
2031}
2032
2033void SDL_BindGPUComputeStorageTextures(
2034 SDL_GPUComputePass *compute_pass,
2035 Uint32 first_slot,
2036 SDL_GPUTexture *const *storage_textures,
2037 Uint32 num_bindings)
2038{
2039 if (compute_pass == NULL) {
2040 SDL_InvalidParamError("compute_pass");
2041 return;
2042 }
2043 if (storage_textures == NULL && num_bindings > 0) {
2044 SDL_InvalidParamError("storage_textures");
2045 return;
2046 }
2047
2048 if (COMPUTEPASS_DEVICE->debug_mode) {
2049 CHECK_COMPUTEPASS
2050 }
2051
2052 COMPUTEPASS_DEVICE->BindComputeStorageTextures(
2053 COMPUTEPASS_COMMAND_BUFFER,
2054 first_slot,
2055 storage_textures,
2056 num_bindings);
2057}
2058
2059void SDL_BindGPUComputeStorageBuffers(
2060 SDL_GPUComputePass *compute_pass,
2061 Uint32 first_slot,
2062 SDL_GPUBuffer *const *storage_buffers,
2063 Uint32 num_bindings)
2064{
2065 if (compute_pass == NULL) {
2066 SDL_InvalidParamError("compute_pass");
2067 return;
2068 }
2069 if (storage_buffers == NULL && num_bindings > 0) {
2070 SDL_InvalidParamError("storage_buffers");
2071 return;
2072 }
2073
2074 if (COMPUTEPASS_DEVICE->debug_mode) {
2075 CHECK_COMPUTEPASS
2076 }
2077
2078 COMPUTEPASS_DEVICE->BindComputeStorageBuffers(
2079 COMPUTEPASS_COMMAND_BUFFER,
2080 first_slot,
2081 storage_buffers,
2082 num_bindings);
2083}
2084
2085void SDL_DispatchGPUCompute(
2086 SDL_GPUComputePass *compute_pass,
2087 Uint32 groupcount_x,
2088 Uint32 groupcount_y,
2089 Uint32 groupcount_z)
2090{
2091 if (compute_pass == NULL) {
2092 SDL_InvalidParamError("compute_pass");
2093 return;
2094 }
2095
2096 if (COMPUTEPASS_DEVICE->debug_mode) {
2097 CHECK_COMPUTEPASS
2098 CHECK_COMPUTE_PIPELINE_BOUND
2099 }
2100
2101 COMPUTEPASS_DEVICE->DispatchCompute(
2102 COMPUTEPASS_COMMAND_BUFFER,
2103 groupcount_x,
2104 groupcount_y,
2105 groupcount_z);
2106}
2107
2108void SDL_DispatchGPUComputeIndirect(
2109 SDL_GPUComputePass *compute_pass,
2110 SDL_GPUBuffer *buffer,
2111 Uint32 offset)
2112{
2113 if (compute_pass == NULL) {
2114 SDL_InvalidParamError("compute_pass");
2115 return;
2116 }
2117
2118 if (COMPUTEPASS_DEVICE->debug_mode) {
2119 CHECK_COMPUTEPASS
2120 CHECK_COMPUTE_PIPELINE_BOUND
2121 }
2122
2123 COMPUTEPASS_DEVICE->DispatchComputeIndirect(
2124 COMPUTEPASS_COMMAND_BUFFER,
2125 buffer,
2126 offset);
2127}
2128
2129void SDL_EndGPUComputePass(
2130 SDL_GPUComputePass *compute_pass)
2131{
2132 CommandBufferCommonHeader *commandBufferCommonHeader;
2133
2134 if (compute_pass == NULL) {
2135 SDL_InvalidParamError("compute_pass");
2136 return;
2137 }
2138
2139 if (COMPUTEPASS_DEVICE->debug_mode) {
2140 CHECK_COMPUTEPASS
2141 }
2142
2143 COMPUTEPASS_DEVICE->EndComputePass(
2144 COMPUTEPASS_COMMAND_BUFFER);
2145
2146 commandBufferCommonHeader = (CommandBufferCommonHeader *)COMPUTEPASS_COMMAND_BUFFER;
2147 commandBufferCommonHeader->compute_pass.in_progress = false;
2148 commandBufferCommonHeader->compute_pipeline_bound = false;
2149}
2150
2151// TransferBuffer Data
2152
2153void *SDL_MapGPUTransferBuffer(
2154 SDL_GPUDevice *device,
2155 SDL_GPUTransferBuffer *transfer_buffer,
2156 bool cycle)
2157{
2158 CHECK_DEVICE_MAGIC(device, NULL);
2159 if (transfer_buffer == NULL) {
2160 SDL_InvalidParamError("transfer_buffer");
2161 return NULL;
2162 }
2163
2164 return device->MapTransferBuffer(
2165 device->driverData,
2166 transfer_buffer,
2167 cycle);
2168}
2169
2170void SDL_UnmapGPUTransferBuffer(
2171 SDL_GPUDevice *device,
2172 SDL_GPUTransferBuffer *transfer_buffer)
2173{
2174 CHECK_DEVICE_MAGIC(device, );
2175 if (transfer_buffer == NULL) {
2176 SDL_InvalidParamError("transfer_buffer");
2177 return;
2178 }
2179
2180 device->UnmapTransferBuffer(
2181 device->driverData,
2182 transfer_buffer);
2183}
2184
2185// Copy Pass
2186
2187SDL_GPUCopyPass *SDL_BeginGPUCopyPass(
2188 SDL_GPUCommandBuffer *command_buffer)
2189{
2190 CommandBufferCommonHeader *commandBufferHeader;
2191
2192 if (command_buffer == NULL) {
2193 SDL_InvalidParamError("command_buffer");
2194 return NULL;
2195 }
2196
2197 if (COMMAND_BUFFER_DEVICE->debug_mode) {
2198 CHECK_COMMAND_BUFFER_RETURN_NULL
2199 CHECK_ANY_PASS_IN_PROGRESS("Cannot begin copy pass during another pass!", NULL)
2200 }
2201
2202 COMMAND_BUFFER_DEVICE->BeginCopyPass(
2203 command_buffer);
2204
2205 commandBufferHeader = (CommandBufferCommonHeader *)command_buffer;
2206 commandBufferHeader->copy_pass.in_progress = true;
2207 return (SDL_GPUCopyPass *)&(commandBufferHeader->copy_pass);
2208}
2209
2210void SDL_UploadToGPUTexture(
2211 SDL_GPUCopyPass *copy_pass,
2212 const SDL_GPUTextureTransferInfo *source,
2213 const SDL_GPUTextureRegion *destination,
2214 bool cycle)
2215{
2216 if (copy_pass == NULL) {
2217 SDL_InvalidParamError("copy_pass");
2218 return;
2219 }
2220 if (source == NULL) {
2221 SDL_InvalidParamError("source");
2222 return;
2223 }
2224 if (destination == NULL) {
2225 SDL_InvalidParamError("destination");
2226 return;
2227 }
2228
2229 if (COPYPASS_DEVICE->debug_mode) {
2230 CHECK_COPYPASS
2231 if (source->transfer_buffer == NULL) {
2232 SDL_assert_release(!"Source transfer buffer cannot be NULL!");
2233 return;
2234 }
2235 if (destination->texture == NULL) {
2236 SDL_assert_release(!"Destination texture cannot be NULL!");
2237 return;
2238 }
2239 }
2240
2241 COPYPASS_DEVICE->UploadToTexture(
2242 COPYPASS_COMMAND_BUFFER,
2243 source,
2244 destination,
2245 cycle);
2246}
2247
2248void SDL_UploadToGPUBuffer(
2249 SDL_GPUCopyPass *copy_pass,
2250 const SDL_GPUTransferBufferLocation *source,
2251 const SDL_GPUBufferRegion *destination,
2252 bool cycle)
2253{
2254 if (copy_pass == NULL) {
2255 SDL_InvalidParamError("copy_pass");
2256 return;
2257 }
2258 if (source == NULL) {
2259 SDL_InvalidParamError("source");
2260 return;
2261 }
2262 if (destination == NULL) {
2263 SDL_InvalidParamError("destination");
2264 return;
2265 }
2266
2267 if (COPYPASS_DEVICE->debug_mode) {
2268 CHECK_COPYPASS
2269 if (source->transfer_buffer == NULL) {
2270 SDL_assert_release(!"Source transfer buffer cannot be NULL!");
2271 return;
2272 }
2273 if (destination->buffer == NULL) {
2274 SDL_assert_release(!"Destination buffer cannot be NULL!");
2275 return;
2276 }
2277 }
2278
2279 COPYPASS_DEVICE->UploadToBuffer(
2280 COPYPASS_COMMAND_BUFFER,
2281 source,
2282 destination,
2283 cycle);
2284}
2285
2286void SDL_CopyGPUTextureToTexture(
2287 SDL_GPUCopyPass *copy_pass,
2288 const SDL_GPUTextureLocation *source,
2289 const SDL_GPUTextureLocation *destination,
2290 Uint32 w,
2291 Uint32 h,
2292 Uint32 d,
2293 bool cycle)
2294{
2295 if (copy_pass == NULL) {
2296 SDL_InvalidParamError("copy_pass");
2297 return;
2298 }
2299 if (source == NULL) {
2300 SDL_InvalidParamError("source");
2301 return;
2302 }
2303 if (destination == NULL) {
2304 SDL_InvalidParamError("destination");
2305 return;
2306 }
2307
2308 if (COPYPASS_DEVICE->debug_mode) {
2309 CHECK_COPYPASS
2310 if (source->texture == NULL) {
2311 SDL_assert_release(!"Source texture cannot be NULL!");
2312 return;
2313 }
2314 if (destination->texture == NULL) {
2315 SDL_assert_release(!"Destination texture cannot be NULL!");
2316 return;
2317 }
2318 }
2319
2320 COPYPASS_DEVICE->CopyTextureToTexture(
2321 COPYPASS_COMMAND_BUFFER,
2322 source,
2323 destination,
2324 w,
2325 h,
2326 d,
2327 cycle);
2328}
2329
2330void SDL_CopyGPUBufferToBuffer(
2331 SDL_GPUCopyPass *copy_pass,
2332 const SDL_GPUBufferLocation *source,
2333 const SDL_GPUBufferLocation *destination,
2334 Uint32 size,
2335 bool cycle)
2336{
2337 if (copy_pass == NULL) {
2338 SDL_InvalidParamError("copy_pass");
2339 return;
2340 }
2341 if (source == NULL) {
2342 SDL_InvalidParamError("source");
2343 return;
2344 }
2345 if (destination == NULL) {
2346 SDL_InvalidParamError("destination");
2347 return;
2348 }
2349
2350 if (COPYPASS_DEVICE->debug_mode) {
2351 CHECK_COPYPASS
2352 if (source->buffer == NULL) {
2353 SDL_assert_release(!"Source buffer cannot be NULL!");
2354 return;
2355 }
2356 if (destination->buffer == NULL) {
2357 SDL_assert_release(!"Destination buffer cannot be NULL!");
2358 return;
2359 }
2360 }
2361
2362 COPYPASS_DEVICE->CopyBufferToBuffer(
2363 COPYPASS_COMMAND_BUFFER,
2364 source,
2365 destination,
2366 size,
2367 cycle);
2368}
2369
2370void SDL_DownloadFromGPUTexture(
2371 SDL_GPUCopyPass *copy_pass,
2372 const SDL_GPUTextureRegion *source,
2373 const SDL_GPUTextureTransferInfo *destination)
2374{
2375 if (copy_pass == NULL) {
2376 SDL_InvalidParamError("copy_pass");
2377 return;
2378 }
2379 if (source == NULL) {
2380 SDL_InvalidParamError("source");
2381 return;
2382 }
2383 if (destination == NULL) {
2384 SDL_InvalidParamError("destination");
2385 return;
2386 }
2387
2388 if (COPYPASS_DEVICE->debug_mode) {
2389 CHECK_COPYPASS
2390 if (source->texture == NULL) {
2391 SDL_assert_release(!"Source texture cannot be NULL!");
2392 return;
2393 }
2394 if (destination->transfer_buffer == NULL) {
2395 SDL_assert_release(!"Destination transfer buffer cannot be NULL!");
2396 return;
2397 }
2398 }
2399
2400 COPYPASS_DEVICE->DownloadFromTexture(
2401 COPYPASS_COMMAND_BUFFER,
2402 source,
2403 destination);
2404}
2405
2406void SDL_DownloadFromGPUBuffer(
2407 SDL_GPUCopyPass *copy_pass,
2408 const SDL_GPUBufferRegion *source,
2409 const SDL_GPUTransferBufferLocation *destination)
2410{
2411 if (copy_pass == NULL) {
2412 SDL_InvalidParamError("copy_pass");
2413 return;
2414 }
2415 if (source == NULL) {
2416 SDL_InvalidParamError("source");
2417 return;
2418 }
2419 if (destination == NULL) {
2420 SDL_InvalidParamError("destination");
2421 return;
2422 }
2423
2424 if (COPYPASS_DEVICE->debug_mode) {
2425 CHECK_COPYPASS
2426 if (source->buffer == NULL) {
2427 SDL_assert_release(!"Source buffer cannot be NULL!");
2428 return;
2429 }
2430 if (destination->transfer_buffer == NULL) {
2431 SDL_assert_release(!"Destination transfer buffer cannot be NULL!");
2432 return;
2433 }
2434 }
2435
2436 COPYPASS_DEVICE->DownloadFromBuffer(
2437 COPYPASS_COMMAND_BUFFER,
2438 source,
2439 destination);
2440}
2441
2442void SDL_EndGPUCopyPass(
2443 SDL_GPUCopyPass *copy_pass)
2444{
2445 if (copy_pass == NULL) {
2446 SDL_InvalidParamError("copy_pass");
2447 return;
2448 }
2449
2450 if (COPYPASS_DEVICE->debug_mode) {
2451 CHECK_COPYPASS
2452 }
2453
2454 COPYPASS_DEVICE->EndCopyPass(
2455 COPYPASS_COMMAND_BUFFER);
2456
2457 ((CommandBufferCommonHeader *)COPYPASS_COMMAND_BUFFER)->copy_pass.in_progress = false;
2458}
2459
2460void SDL_GenerateMipmapsForGPUTexture(
2461 SDL_GPUCommandBuffer *command_buffer,
2462 SDL_GPUTexture *texture)
2463{
2464 if (command_buffer == NULL) {
2465 SDL_InvalidParamError("command_buffer");
2466 return;
2467 }
2468 if (texture == NULL) {
2469 SDL_InvalidParamError("texture");
2470 return;
2471 }
2472
2473 if (COMMAND_BUFFER_DEVICE->debug_mode) {
2474 CHECK_COMMAND_BUFFER
2475 CHECK_ANY_PASS_IN_PROGRESS("Cannot generate mipmaps during a pass!", )
2476
2477 TextureCommonHeader *header = (TextureCommonHeader *)texture;
2478 if (header->info.num_levels <= 1) {
2479 SDL_assert_release(!"Cannot generate mipmaps for texture with num_levels <= 1!");
2480 return;
2481 }
2482
2483 if (!(header->info.usage & SDL_GPU_TEXTUREUSAGE_SAMPLER) || !(header->info.usage & SDL_GPU_TEXTUREUSAGE_COLOR_TARGET)) {
2484 SDL_assert_release(!"GenerateMipmaps texture must be created with SAMPLER and COLOR_TARGET usage flags!");
2485 return;
2486 }
2487 }
2488
2489 COMMAND_BUFFER_DEVICE->GenerateMipmaps(
2490 command_buffer,
2491 texture);
2492}
2493
2494void SDL_BlitGPUTexture(
2495 SDL_GPUCommandBuffer *command_buffer,
2496 const SDL_GPUBlitInfo *info)
2497{
2498 if (command_buffer == NULL) {
2499 SDL_InvalidParamError("command_buffer");
2500 return;
2501 }
2502 if (info == NULL) {
2503 SDL_InvalidParamError("info");
2504 return;
2505 }
2506
2507 if (COMMAND_BUFFER_DEVICE->debug_mode) {
2508 CHECK_COMMAND_BUFFER
2509 CHECK_ANY_PASS_IN_PROGRESS("Cannot blit during a pass!", )
2510
2511 // Validation
2512 bool failed = false;
2513 TextureCommonHeader *srcHeader = (TextureCommonHeader *)info->source.texture;
2514 TextureCommonHeader *dstHeader = (TextureCommonHeader *)info->destination.texture;
2515
2516 if (srcHeader == NULL) {
2517 SDL_assert_release(!"Blit source texture must be non-NULL");
2518 return; // attempting to proceed will crash
2519 }
2520 if (dstHeader == NULL) {
2521 SDL_assert_release(!"Blit destination texture must be non-NULL");
2522 return; // attempting to proceed will crash
2523 }
2524 if (srcHeader->info.sample_count != SDL_GPU_SAMPLECOUNT_1) {
2525 SDL_assert_release(!"Blit source texture must have a sample count of 1");
2526 failed = true;
2527 }
2528 if ((srcHeader->info.usage & SDL_GPU_TEXTUREUSAGE_SAMPLER) == 0) {
2529 SDL_assert_release(!"Blit source texture must be created with the SAMPLER usage flag");
2530 failed = true;
2531 }
2532 if ((dstHeader->info.usage & SDL_GPU_TEXTUREUSAGE_COLOR_TARGET) == 0) {
2533 SDL_assert_release(!"Blit destination texture must be created with the COLOR_TARGET usage flag");
2534 failed = true;
2535 }
2536 if (IsDepthFormat(srcHeader->info.format)) {
2537 SDL_assert_release(!"Blit source texture cannot have a depth format");
2538 failed = true;
2539 }
2540 if (info->source.w == 0 || info->source.h == 0 || info->destination.w == 0 || info->destination.h == 0) {
2541 SDL_assert_release(!"Blit source/destination regions must have non-zero width, height, and depth");
2542 failed = true;
2543 }
2544
2545 if (failed) {
2546 return;
2547 }
2548 }
2549
2550 COMMAND_BUFFER_DEVICE->Blit(
2551 command_buffer,
2552 info);
2553}
2554
2555// Submission/Presentation
2556
2557bool SDL_WindowSupportsGPUSwapchainComposition(
2558 SDL_GPUDevice *device,
2559 SDL_Window *window,
2560 SDL_GPUSwapchainComposition swapchain_composition)
2561{
2562 CHECK_DEVICE_MAGIC(device, false);
2563 if (window == NULL) {
2564 SDL_InvalidParamError("window");
2565 return false;
2566 }
2567
2568 if (device->debug_mode) {
2569 CHECK_SWAPCHAINCOMPOSITION_ENUM_INVALID(swapchain_composition, false)
2570 }
2571
2572 return device->SupportsSwapchainComposition(
2573 device->driverData,
2574 window,
2575 swapchain_composition);
2576}
2577
2578bool SDL_WindowSupportsGPUPresentMode(
2579 SDL_GPUDevice *device,
2580 SDL_Window *window,
2581 SDL_GPUPresentMode present_mode)
2582{
2583 CHECK_DEVICE_MAGIC(device, false);
2584 if (window == NULL) {
2585 SDL_InvalidParamError("window");
2586 return false;
2587 }
2588
2589 if (device->debug_mode) {
2590 CHECK_PRESENTMODE_ENUM_INVALID(present_mode, false)
2591 }
2592
2593 return device->SupportsPresentMode(
2594 device->driverData,
2595 window,
2596 present_mode);
2597}
2598
2599bool SDL_ClaimWindowForGPUDevice(
2600 SDL_GPUDevice *device,
2601 SDL_Window *window)
2602{
2603 CHECK_DEVICE_MAGIC(device, false);
2604 if (window == NULL) {
2605 SDL_InvalidParamError("window");
2606 return false;
2607 }
2608
2609 return device->ClaimWindow(
2610 device->driverData,
2611 window);
2612}
2613
2614void SDL_ReleaseWindowFromGPUDevice(
2615 SDL_GPUDevice *device,
2616 SDL_Window *window)
2617{
2618 CHECK_DEVICE_MAGIC(device, );
2619 if (window == NULL) {
2620 SDL_InvalidParamError("window");
2621 return;
2622 }
2623
2624 device->ReleaseWindow(
2625 device->driverData,
2626 window);
2627}
2628
2629bool SDL_SetGPUSwapchainParameters(
2630 SDL_GPUDevice *device,
2631 SDL_Window *window,
2632 SDL_GPUSwapchainComposition swapchain_composition,
2633 SDL_GPUPresentMode present_mode)
2634{
2635 CHECK_DEVICE_MAGIC(device, false);
2636 if (window == NULL) {
2637 SDL_InvalidParamError("window");
2638 return false;
2639 }
2640
2641 if (device->debug_mode) {
2642 CHECK_SWAPCHAINCOMPOSITION_ENUM_INVALID(swapchain_composition, false)
2643 CHECK_PRESENTMODE_ENUM_INVALID(present_mode, false)
2644 }
2645
2646 return device->SetSwapchainParameters(
2647 device->driverData,
2648 window,
2649 swapchain_composition,
2650 present_mode);
2651}
2652
2653bool SDL_SetGPUAllowedFramesInFlight(
2654 SDL_GPUDevice *device,
2655 Uint32 allowed_frames_in_flight)
2656{
2657 CHECK_DEVICE_MAGIC(device, false);
2658
2659 if (device->debug_mode) {
2660 if (allowed_frames_in_flight < 1 || allowed_frames_in_flight > 3)
2661 {
2662 SDL_assert_release(!"allowed_frames_in_flight value must be between 1 and 3!");
2663 }
2664 }
2665
2666 allowed_frames_in_flight = SDL_clamp(allowed_frames_in_flight, 1, 3);
2667 return device->SetAllowedFramesInFlight(
2668 device->driverData,
2669 allowed_frames_in_flight);
2670}
2671
2672SDL_GPUTextureFormat SDL_GetGPUSwapchainTextureFormat(
2673 SDL_GPUDevice *device,
2674 SDL_Window *window)
2675{
2676 CHECK_DEVICE_MAGIC(device, SDL_GPU_TEXTUREFORMAT_INVALID);
2677 if (window == NULL) {
2678 SDL_InvalidParamError("window");
2679 return SDL_GPU_TEXTUREFORMAT_INVALID;
2680 }
2681
2682 return device->GetSwapchainTextureFormat(
2683 device->driverData,
2684 window);
2685}
2686
2687bool SDL_AcquireGPUSwapchainTexture(
2688 SDL_GPUCommandBuffer *command_buffer,
2689 SDL_Window *window,
2690 SDL_GPUTexture **swapchain_texture,
2691 Uint32 *swapchain_texture_width,
2692 Uint32 *swapchain_texture_height)
2693{
2694 CommandBufferCommonHeader *commandBufferHeader = (CommandBufferCommonHeader *)command_buffer;
2695
2696 if (command_buffer == NULL) {
2697 return SDL_InvalidParamError("command_buffer");
2698 }
2699 if (window == NULL) {
2700 return SDL_InvalidParamError("window");
2701 }
2702 if (swapchain_texture == NULL) {
2703 return SDL_InvalidParamError("swapchain_texture");
2704 }
2705
2706 if (COMMAND_BUFFER_DEVICE->debug_mode) {
2707 CHECK_COMMAND_BUFFER_RETURN_FALSE
2708 CHECK_ANY_PASS_IN_PROGRESS("Cannot acquire a swapchain texture during a pass!", false)
2709 }
2710
2711 bool result = COMMAND_BUFFER_DEVICE->AcquireSwapchainTexture(
2712 command_buffer,
2713 window,
2714 swapchain_texture,
2715 swapchain_texture_width,
2716 swapchain_texture_height);
2717
2718 if (*swapchain_texture != NULL){
2719 commandBufferHeader->swapchain_texture_acquired = true;
2720 }
2721
2722 return result;
2723}
2724
2725bool SDL_WaitForGPUSwapchain(
2726 SDL_GPUDevice *device,
2727 SDL_Window *window)
2728{
2729 CHECK_DEVICE_MAGIC(device, false);
2730
2731 if (window == NULL) {
2732 return SDL_InvalidParamError("window");
2733 }
2734
2735 return device->WaitForSwapchain(
2736 device->driverData,
2737 window);
2738}
2739
2740bool SDL_WaitAndAcquireGPUSwapchainTexture(
2741 SDL_GPUCommandBuffer *command_buffer,
2742 SDL_Window *window,
2743 SDL_GPUTexture **swapchain_texture,
2744 Uint32 *swapchain_texture_width,
2745 Uint32 *swapchain_texture_height)
2746{
2747 CommandBufferCommonHeader *commandBufferHeader = (CommandBufferCommonHeader *)command_buffer;
2748
2749 if (command_buffer == NULL) {
2750 return SDL_InvalidParamError("command_buffer");
2751 }
2752 if (window == NULL) {
2753 return SDL_InvalidParamError("window");
2754 }
2755 if (swapchain_texture == NULL) {
2756 return SDL_InvalidParamError("swapchain_texture");
2757 }
2758
2759 if (COMMAND_BUFFER_DEVICE->debug_mode) {
2760 CHECK_COMMAND_BUFFER_RETURN_FALSE
2761 CHECK_ANY_PASS_IN_PROGRESS("Cannot acquire a swapchain texture during a pass!", false)
2762 }
2763
2764 bool result = COMMAND_BUFFER_DEVICE->WaitAndAcquireSwapchainTexture(
2765 command_buffer,
2766 window,
2767 swapchain_texture,
2768 swapchain_texture_width,
2769 swapchain_texture_height);
2770
2771 if (*swapchain_texture != NULL){
2772 commandBufferHeader->swapchain_texture_acquired = true;
2773 }
2774
2775 return result;
2776}
2777
2778bool SDL_SubmitGPUCommandBuffer(
2779 SDL_GPUCommandBuffer *command_buffer)
2780{
2781 CommandBufferCommonHeader *commandBufferHeader = (CommandBufferCommonHeader *)command_buffer;
2782
2783 if (command_buffer == NULL) {
2784 SDL_InvalidParamError("command_buffer");
2785 return false;
2786 }
2787
2788 if (COMMAND_BUFFER_DEVICE->debug_mode) {
2789 CHECK_COMMAND_BUFFER_RETURN_FALSE
2790 if (
2791 commandBufferHeader->render_pass.in_progress ||
2792 commandBufferHeader->compute_pass.in_progress ||
2793 commandBufferHeader->copy_pass.in_progress) {
2794 SDL_assert_release(!"Cannot submit command buffer while a pass is in progress!");
2795 return false;
2796 }
2797 }
2798
2799 commandBufferHeader->submitted = true;
2800
2801 return COMMAND_BUFFER_DEVICE->Submit(
2802 command_buffer);
2803}
2804
2805SDL_GPUFence *SDL_SubmitGPUCommandBufferAndAcquireFence(
2806 SDL_GPUCommandBuffer *command_buffer)
2807{
2808 CommandBufferCommonHeader *commandBufferHeader = (CommandBufferCommonHeader *)command_buffer;
2809
2810 if (command_buffer == NULL) {
2811 SDL_InvalidParamError("command_buffer");
2812 return NULL;
2813 }
2814
2815 if (COMMAND_BUFFER_DEVICE->debug_mode) {
2816 CHECK_COMMAND_BUFFER_RETURN_NULL
2817 if (
2818 commandBufferHeader->render_pass.in_progress ||
2819 commandBufferHeader->compute_pass.in_progress ||
2820 commandBufferHeader->copy_pass.in_progress) {
2821 SDL_assert_release(!"Cannot submit command buffer while a pass is in progress!");
2822 return NULL;
2823 }
2824 }
2825
2826 commandBufferHeader->submitted = true;
2827
2828 return COMMAND_BUFFER_DEVICE->SubmitAndAcquireFence(
2829 command_buffer);
2830}
2831
2832bool SDL_CancelGPUCommandBuffer(
2833 SDL_GPUCommandBuffer *command_buffer)
2834{
2835 CommandBufferCommonHeader *commandBufferHeader = (CommandBufferCommonHeader *)command_buffer;
2836
2837 if (command_buffer == NULL) {
2838 SDL_InvalidParamError("command_buffer");
2839 return false;
2840 }
2841
2842 if (COMMAND_BUFFER_DEVICE->debug_mode) {
2843 if (commandBufferHeader->swapchain_texture_acquired) {
2844 SDL_assert_release(!"Cannot cancel command buffer after a swapchain texture has been acquired!");
2845 return false;
2846 }
2847 }
2848
2849 return COMMAND_BUFFER_DEVICE->Cancel(
2850 command_buffer);
2851}
2852
2853bool SDL_WaitForGPUIdle(
2854 SDL_GPUDevice *device)
2855{
2856 CHECK_DEVICE_MAGIC(device, false);
2857
2858 return device->Wait(
2859 device->driverData);
2860}
2861
2862bool SDL_WaitForGPUFences(
2863 SDL_GPUDevice *device,
2864 bool wait_all,
2865 SDL_GPUFence *const *fences,
2866 Uint32 num_fences)
2867{
2868 CHECK_DEVICE_MAGIC(device, false);
2869 if (fences == NULL && num_fences > 0) {
2870 SDL_InvalidParamError("fences");
2871 return false;
2872 }
2873
2874 return device->WaitForFences(
2875 device->driverData,
2876 wait_all,
2877 fences,
2878 num_fences);
2879}
2880
2881bool SDL_QueryGPUFence(
2882 SDL_GPUDevice *device,
2883 SDL_GPUFence *fence)
2884{
2885 CHECK_DEVICE_MAGIC(device, false);
2886 if (fence == NULL) {
2887 SDL_InvalidParamError("fence");
2888 return false;
2889 }
2890
2891 return device->QueryFence(
2892 device->driverData,
2893 fence);
2894}
2895
2896void SDL_ReleaseGPUFence(
2897 SDL_GPUDevice *device,
2898 SDL_GPUFence *fence)
2899{
2900 CHECK_DEVICE_MAGIC(device, );
2901 if (fence == NULL) {
2902 return;
2903 }
2904
2905 device->ReleaseFence(
2906 device->driverData,
2907 fence);
2908}
2909
2910Uint32 SDL_CalculateGPUTextureFormatSize(
2911 SDL_GPUTextureFormat format,
2912 Uint32 width,
2913 Uint32 height,
2914 Uint32 depth_or_layer_count)
2915{
2916 Uint32 blockWidth = SDL_max(Texture_GetBlockWidth(format), 1);
2917 Uint32 blockHeight = SDL_max(Texture_GetBlockHeight(format), 1);
2918 Uint32 blocksPerRow = (width + blockWidth - 1) / blockWidth;
2919 Uint32 blocksPerColumn = (height + blockHeight - 1) / blockHeight;
2920 return depth_or_layer_count * blocksPerRow * blocksPerColumn * SDL_GPUTextureFormatTexelBlockSize(format);
2921}