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