Atlas - SDL_render_gpu.c
Home / ext / SDL / src / render / gpu Lines: 1 | Size: 66277 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2025 Sam Lantinga <[email protected]> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21#include "SDL_internal.h" 22 23#ifdef SDL_VIDEO_RENDER_GPU 24 25#include "../../video/SDL_pixels_c.h" 26#include "../SDL_d3dmath.h" 27#include "../SDL_sysrender.h" 28#include "SDL_gpu_util.h" 29#include "SDL_pipeline_gpu.h" 30#include "SDL_shaders_gpu.h" 31 32typedef struct GPU_VertexShaderUniformData 33{ 34 Float4X4 mvp; 35} GPU_VertexShaderUniformData; 36 37typedef struct GPU_SimpleFragmentShaderUniformData 38{ 39 float color_scale; 40} GPU_SimpleFragmentShaderUniformData; 41 42typedef struct GPU_AdvancedFragmentShaderUniformData 43{ 44 float scRGB_output; 45 float texture_type; 46 float input_type; 47 float color_scale; 48 49 float texel_width; 50 float texel_height; 51 float texture_width; 52 float texture_height; 53 54 float tonemap_method; 55 float tonemap_factor1; 56 float tonemap_factor2; 57 float sdr_white_point; 58 59 float YCbCr_matrix[16]; 60} GPU_AdvancedFragmentShaderUniformData; 61 62// These should mirror the definitions in shaders/texture_advanced.frag.hlsl 63static const float TONEMAP_NONE = 0; 64//static const float TONEMAP_LINEAR = 1; 65static const float TONEMAP_CHROME = 2; 66 67//static const float TEXTURETYPE_NONE = 0; 68static const float TEXTURETYPE_RGB = 1; 69static const float TEXTURETYPE_RGB_PIXELART = 2; 70static const float TEXTURETYPE_RGBA = 3; 71static const float TEXTURETYPE_RGBA_PIXELART = 4; 72static const float TEXTURETYPE_PALETTE_NEAREST = 5; 73static const float TEXTURETYPE_PALETTE_LINEAR = 6; 74static const float TEXTURETYPE_PALETTE_PIXELART = 7; 75static const float TEXTURETYPE_NV12 = 8; 76static const float TEXTURETYPE_NV21 = 9; 77static const float TEXTURETYPE_YUV = 10; 78 79static const float INPUTTYPE_UNSPECIFIED = 0; 80static const float INPUTTYPE_SRGB = 1; 81static const float INPUTTYPE_SCRGB = 2; 82static const float INPUTTYPE_HDR10 = 3; 83 84typedef struct GPU_RenderData 85{ 86 bool external_device; 87 SDL_GPUDevice *device; 88 GPU_Shaders shaders; 89 GPU_PipelineCache pipeline_cache; 90 91 struct 92 { 93 SDL_GPUTexture *texture; 94 SDL_GPUTextureFormat format; 95 Uint32 width; 96 Uint32 height; 97 } backbuffer; 98 99 struct 100 { 101 SDL_GPUSwapchainComposition composition; 102 SDL_GPUPresentMode present_mode; 103 } swapchain; 104 105 struct 106 { 107 SDL_GPUTransferBuffer *transfer_buf; 108 SDL_GPUBuffer *buffer; 109 Uint32 buffer_size; 110 } vertices; 111 112 struct 113 { 114 SDL_GPURenderPass *render_pass; 115 SDL_Texture *render_target; 116 SDL_GPUCommandBuffer *command_buffer; 117 SDL_GPUColorTargetInfo color_attachment; 118 SDL_GPUViewport viewport; 119 SDL_Rect scissor; 120 bool scissor_enabled; 121 bool scissor_was_enabled; 122 } state; 123 124 SDL_GPUSampler *samplers[RENDER_SAMPLER_COUNT]; 125} GPU_RenderData; 126 127typedef struct GPU_PaletteData 128{ 129 SDL_GPUTexture *texture; 130} GPU_PaletteData; 131 132typedef struct GPU_TextureData 133{ 134 bool external_texture; 135 SDL_GPUTexture *texture; 136 SDL_GPUTextureFormat format; 137 void *pixels; 138 int pitch; 139 SDL_Rect locked_rect; 140 const float *YCbCr_matrix; 141#ifdef SDL_HAVE_YUV 142 // YV12 texture support 143 bool yuv; 144 bool external_texture_u; 145 bool external_texture_v; 146 SDL_GPUTexture *textureU; 147 SDL_GPUTexture *textureV; 148 149 // NV12 texture support 150 bool nv12; 151 bool external_texture_nv; 152 SDL_GPUTexture *textureNV; 153#endif 154} GPU_TextureData; 155 156static bool GPU_SupportsBlendMode(SDL_Renderer *renderer, SDL_BlendMode blendMode) 157{ 158 SDL_BlendFactor srcColorFactor = SDL_GetBlendModeSrcColorFactor(blendMode); 159 SDL_BlendFactor srcAlphaFactor = SDL_GetBlendModeSrcAlphaFactor(blendMode); 160 SDL_BlendOperation colorOperation = SDL_GetBlendModeColorOperation(blendMode); 161 SDL_BlendFactor dstColorFactor = SDL_GetBlendModeDstColorFactor(blendMode); 162 SDL_BlendFactor dstAlphaFactor = SDL_GetBlendModeDstAlphaFactor(blendMode); 163 SDL_BlendOperation alphaOperation = SDL_GetBlendModeAlphaOperation(blendMode); 164 165 if (GPU_ConvertBlendFactor(srcColorFactor) == SDL_GPU_BLENDFACTOR_INVALID || 166 GPU_ConvertBlendFactor(srcAlphaFactor) == SDL_GPU_BLENDFACTOR_INVALID || 167 GPU_ConvertBlendOperation(colorOperation) == SDL_GPU_BLENDOP_INVALID || 168 GPU_ConvertBlendFactor(dstColorFactor) == SDL_GPU_BLENDFACTOR_INVALID || 169 GPU_ConvertBlendFactor(dstAlphaFactor) == SDL_GPU_BLENDFACTOR_INVALID || 170 GPU_ConvertBlendOperation(alphaOperation) == SDL_GPU_BLENDOP_INVALID) { 171 return false; 172 } 173 174 return true; 175} 176 177static bool GPU_CreatePalette(SDL_Renderer *renderer, SDL_TexturePalette *palette) 178{ 179 GPU_RenderData *data = (GPU_RenderData *)renderer->internal; 180 GPU_PaletteData *palettedata = (GPU_PaletteData *)SDL_calloc(1, sizeof(*palettedata)); 181 if (!palettedata) { 182 return false; 183 } 184 palette->internal = palettedata; 185 186 SDL_GPUTextureCreateInfo tci; 187 SDL_zero(tci); 188 tci.format = SDL_GetGPUTextureFormatFromPixelFormat(SDL_PIXELFORMAT_RGBA32); 189 tci.layer_count_or_depth = 1; 190 tci.num_levels = 1; 191 tci.usage = SDL_GPU_TEXTUREUSAGE_SAMPLER; 192 tci.width = 256; 193 tci.height = 1; 194 tci.sample_count = SDL_GPU_SAMPLECOUNT_1; 195 196 palettedata->texture = SDL_CreateGPUTexture(data->device, &tci); 197 if (!palettedata->texture) { 198 return false; 199 } 200 return true; 201} 202 203static bool GPU_UpdatePalette(SDL_Renderer *renderer, SDL_TexturePalette *palette, int ncolors, SDL_Color *colors) 204{ 205 GPU_RenderData *data = (GPU_RenderData *)renderer->internal; 206 GPU_PaletteData *palettedata = (GPU_PaletteData *)palette->internal; 207 const Uint32 data_size = ncolors * sizeof(*colors); 208 209 SDL_GPUTransferBufferCreateInfo tbci; 210 SDL_zero(tbci); 211 tbci.size = data_size; 212 tbci.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD; 213 214 SDL_GPUTransferBuffer *tbuf = SDL_CreateGPUTransferBuffer(data->device, &tbci); 215 if (tbuf == NULL) { 216 return false; 217 } 218 219 Uint8 *output = SDL_MapGPUTransferBuffer(data->device, tbuf, false); 220 SDL_memcpy(output, colors, data_size); 221 SDL_UnmapGPUTransferBuffer(data->device, tbuf); 222 223 SDL_GPUCommandBuffer *cbuf = data->state.command_buffer; 224 SDL_GPUCopyPass *cpass = SDL_BeginGPUCopyPass(cbuf); 225 226 SDL_GPUTextureTransferInfo tex_src; 227 SDL_zero(tex_src); 228 tex_src.transfer_buffer = tbuf; 229 tex_src.rows_per_layer = 1; 230 tex_src.pixels_per_row = ncolors; 231 232 SDL_GPUTextureRegion tex_dst; 233 SDL_zero(tex_dst); 234 tex_dst.texture = palettedata->texture; 235 tex_dst.x = 0; 236 tex_dst.y = 0; 237 tex_dst.w = ncolors; 238 tex_dst.h = 1; 239 tex_dst.d = 1; 240 241 SDL_UploadToGPUTexture(cpass, &tex_src, &tex_dst, false); 242 SDL_EndGPUCopyPass(cpass); 243 SDL_ReleaseGPUTransferBuffer(data->device, tbuf); 244 245 return true; 246} 247 248static void GPU_DestroyPalette(SDL_Renderer *renderer, SDL_TexturePalette *palette) 249{ 250 GPU_RenderData *data = (GPU_RenderData *)renderer->internal; 251 GPU_PaletteData *palettedata = (GPU_PaletteData *)palette->internal; 252 253 if (palettedata) { 254 SDL_ReleaseGPUTexture(data->device, palettedata->texture); 255 SDL_free(palettedata); 256 } 257} 258 259static bool GPU_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props) 260{ 261 GPU_RenderData *renderdata = (GPU_RenderData *)renderer->internal; 262 GPU_TextureData *data; 263 SDL_GPUTextureFormat format; 264 SDL_GPUTextureUsageFlags usage = SDL_GPU_TEXTUREUSAGE_SAMPLER; 265 266 data = (GPU_TextureData *)SDL_calloc(1, sizeof(*data)); 267 if (!data) { 268 return false; 269 } 270 texture->internal = data; 271 272 switch (texture->format) { 273 case SDL_PIXELFORMAT_INDEX8: 274 case SDL_PIXELFORMAT_YV12: 275 case SDL_PIXELFORMAT_IYUV: 276 case SDL_PIXELFORMAT_NV12: 277 case SDL_PIXELFORMAT_NV21: 278 format = SDL_GPU_TEXTUREFORMAT_R8_UNORM; 279 break; 280 case SDL_PIXELFORMAT_P010: 281 format = SDL_GPU_TEXTUREFORMAT_R16_UNORM; 282 break; 283 default: 284 format = SDL_GetGPUTextureFormatFromPixelFormat(texture->format); 285 break; 286 } 287 if (renderer->output_colorspace == SDL_COLORSPACE_SRGB_LINEAR) { 288 switch (format) { 289 case SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM: 290 format = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM_SRGB; 291 break; 292 case SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM: 293 format = SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM_SRGB; 294 break; 295 default: 296 break; 297 } 298 } 299 if (format == SDL_GPU_TEXTUREFORMAT_INVALID) { 300 return SDL_SetError("Texture format %s not supported by SDL_GPU", 301 SDL_GetPixelFormatName(texture->format)); 302 } 303 data->format = format; 304 305 if (texture->access == SDL_TEXTUREACCESS_STREAMING) { 306 size_t size; 307 data->pitch = texture->w * SDL_BYTESPERPIXEL(texture->format); 308 size = (size_t)texture->h * data->pitch; 309 if (texture->format == SDL_PIXELFORMAT_YV12 || 310 texture->format == SDL_PIXELFORMAT_IYUV) { 311 // Need to add size for the U and V planes 312 size += 2 * ((texture->h + 1) / 2) * ((data->pitch + 1) / 2); 313 } 314 if (texture->format == SDL_PIXELFORMAT_NV12 || 315 texture->format == SDL_PIXELFORMAT_NV21 || 316 texture->format == SDL_PIXELFORMAT_P010) { 317 // Need to add size for the U/V plane 318 size += 2 * ((texture->h + 1) / 2) * ((data->pitch + 1) / 2); 319 } 320 data->pixels = SDL_calloc(1, size); 321 if (!data->pixels) { 322 SDL_free(data); 323 return false; 324 } 325 326 // TODO allocate a persistent transfer buffer 327 } 328 329 if (texture->access == SDL_TEXTUREACCESS_TARGET) { 330 usage |= SDL_GPU_TEXTUREUSAGE_COLOR_TARGET; 331 } 332 333 SDL_GPUTextureCreateInfo tci; 334 SDL_zero(tci); 335 tci.format = format; 336 tci.layer_count_or_depth = 1; 337 tci.num_levels = 1; 338 tci.usage = usage; 339 tci.width = texture->w; 340 tci.height = texture->h; 341 tci.sample_count = SDL_GPU_SAMPLECOUNT_1; 342 tci.props = create_props; 343 344 data->texture = SDL_GetPointerProperty(create_props, SDL_PROP_TEXTURE_CREATE_GPU_TEXTURE_POINTER, NULL); 345 if (data->texture) { 346 data->external_texture = true; 347 } else { 348 data->texture = SDL_CreateGPUTexture(renderdata->device, &tci); 349 if (!data->texture) { 350 return false; 351 } 352 } 353 354 SDL_PropertiesID props = SDL_GetTextureProperties(texture); 355 SDL_SetPointerProperty(props, SDL_PROP_TEXTURE_GPU_TEXTURE_POINTER, data->texture); 356 357#ifdef SDL_HAVE_YUV 358 if (texture->format == SDL_PIXELFORMAT_YV12 || 359 texture->format == SDL_PIXELFORMAT_IYUV) { 360 data->yuv = true; 361 362 tci.width = (tci.width + 1) / 2; 363 tci.height = (tci.height + 1) / 2; 364 365 data->textureU = SDL_GetPointerProperty(create_props, SDL_PROP_TEXTURE_CREATE_GPU_TEXTURE_U_POINTER, NULL); 366 if (data->textureU) { 367 data->external_texture_u = true; 368 } else { 369 data->textureU = SDL_CreateGPUTexture(renderdata->device, &tci); 370 if (!data->textureU) { 371 return false; 372 } 373 } 374 SDL_SetPointerProperty(props, SDL_PROP_TEXTURE_GPU_TEXTURE_U_POINTER, data->textureU); 375 376 data->textureV = SDL_GetPointerProperty(create_props, SDL_PROP_TEXTURE_CREATE_GPU_TEXTURE_V_POINTER, NULL); 377 if (data->textureV) { 378 data->external_texture_v = true; 379 } else { 380 data->textureV = SDL_CreateGPUTexture(renderdata->device, &tci); 381 if (!data->textureV) { 382 return false; 383 } 384 } 385 SDL_SetPointerProperty(props, SDL_PROP_TEXTURE_GPU_TEXTURE_V_POINTER, data->textureU); 386 387 data->YCbCr_matrix = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, 8); 388 if (!data->YCbCr_matrix) { 389 return SDL_SetError("Unsupported YUV colorspace"); 390 } 391 } 392 if (texture->format == SDL_PIXELFORMAT_NV12 || 393 texture->format == SDL_PIXELFORMAT_NV21 || 394 texture->format == SDL_PIXELFORMAT_P010) { 395 int bits_per_pixel; 396 397 data->nv12 = true; 398 399 data->textureNV = SDL_GetPointerProperty(create_props, SDL_PROP_TEXTURE_CREATE_GPU_TEXTURE_UV_POINTER, NULL); 400 if (data->textureNV) { 401 data->external_texture_nv = true; 402 } else { 403 tci.width = ((tci.width + 1) / 2); 404 tci.height = ((tci.height + 1) / 2); 405 if (texture->format == SDL_PIXELFORMAT_P010) { 406 tci.format = SDL_GPU_TEXTUREFORMAT_R16G16_UNORM; 407 } else { 408 tci.format = SDL_GPU_TEXTUREFORMAT_R8G8_UNORM; 409 } 410 411 data->textureNV = SDL_CreateGPUTexture(renderdata->device, &tci); 412 if (!data->textureNV) { 413 return false; 414 } 415 } 416 SDL_SetPointerProperty(props, SDL_PROP_TEXTURE_GPU_TEXTURE_UV_POINTER, data->textureNV); 417 418 switch (texture->format) { 419 case SDL_PIXELFORMAT_P010: 420 bits_per_pixel = 10; 421 break; 422 default: 423 bits_per_pixel = 8; 424 break; 425 } 426 data->YCbCr_matrix = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, bits_per_pixel); 427 if (!data->YCbCr_matrix) { 428 return SDL_SetError("Unsupported YUV colorspace"); 429 } 430 } 431#endif // SDL_HAVE_YUV 432 return true; 433} 434 435static bool GPU_UpdateTextureInternal(GPU_RenderData *renderdata, SDL_GPUCopyPass *cpass, SDL_GPUTexture *texture, int bpp, int x, int y, int w, int h, const void *pixels, int pitch) 436{ 437 size_t row_size, data_size; 438 if (!SDL_size_mul_check_overflow(w, bpp, &row_size) || 439 !SDL_size_mul_check_overflow(h, row_size, &data_size)) { 440 return SDL_SetError("update size overflow"); 441 } 442 443 SDL_GPUTransferBufferCreateInfo tbci; 444 SDL_zero(tbci); 445 tbci.size = (Uint32)data_size; 446 tbci.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD; 447 448 SDL_GPUTransferBuffer *tbuf = SDL_CreateGPUTransferBuffer(renderdata->device, &tbci); 449 if (tbuf == NULL) { 450 return false; 451 } 452 453 Uint8 *output = SDL_MapGPUTransferBuffer(renderdata->device, tbuf, false); 454 if (!output) { 455 return false; 456 } 457 if ((size_t)pitch == row_size) { 458 SDL_memcpy(output, pixels, data_size); 459 } else { 460 const Uint8 *input = pixels; 461 for (int i = 0; i < h; ++i) { 462 SDL_memcpy(output, input, row_size); 463 output += row_size; 464 input += pitch; 465 } 466 } 467 SDL_UnmapGPUTransferBuffer(renderdata->device, tbuf); 468 469 SDL_GPUTextureTransferInfo tex_src; 470 SDL_zero(tex_src); 471 tex_src.transfer_buffer = tbuf; 472 tex_src.rows_per_layer = h; 473 tex_src.pixels_per_row = w; 474 475 SDL_GPUTextureRegion tex_dst; 476 SDL_zero(tex_dst); 477 tex_dst.texture = texture; 478 tex_dst.x = x; 479 tex_dst.y = y; 480 tex_dst.w = w; 481 tex_dst.h = h; 482 tex_dst.d = 1; 483 484 SDL_UploadToGPUTexture(cpass, &tex_src, &tex_dst, false); 485 SDL_ReleaseGPUTransferBuffer(renderdata->device, tbuf); 486 487 return true; 488} 489 490#ifdef SDL_HAVE_YUV 491static bool GPU_UpdateTextureNV(SDL_Renderer *renderer, SDL_Texture *texture, 492 const SDL_Rect *rect, 493 const Uint8 *Yplane, int Ypitch, 494 const Uint8 *UVplane, int UVpitch); 495 496static bool GPU_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *texture, 497 const SDL_Rect *rect, 498 const Uint8 *Yplane, int Ypitch, 499 const Uint8 *Uplane, int Upitch, 500 const Uint8 *Vplane, int Vpitch); 501#endif 502 503static bool GPU_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect, const void *pixels, int pitch) 504{ 505 GPU_RenderData *renderdata = (GPU_RenderData *)renderer->internal; 506 GPU_TextureData *data = (GPU_TextureData *)texture->internal; 507 508 bool retval = true; 509 SDL_GPUCommandBuffer *cbuf = renderdata->state.command_buffer; 510 SDL_GPUCopyPass *cpass = SDL_BeginGPUCopyPass(cbuf); 511 int bpp = SDL_BYTESPERPIXEL(texture->format); 512 513 retval = GPU_UpdateTextureInternal(renderdata, cpass, data->texture, bpp, rect->x, rect->y, rect->w, rect->h, pixels, pitch); 514 515#ifdef SDL_HAVE_YUV 516 if (data->nv12) { 517 const Uint8 *Yplane = (const Uint8 *)pixels; 518 const Uint8 *UVplane = Yplane + rect->h * pitch; 519 int UVpitch; 520 521 bpp *= 2; 522 if (texture->format == SDL_PIXELFORMAT_P010) { 523 UVpitch = (pitch + 3) & ~3; 524 } else { 525 UVpitch = (pitch + 1) & ~1; 526 } 527 retval &= GPU_UpdateTextureInternal(renderdata, cpass, data->textureNV, bpp, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, UVplane, UVpitch); 528 529 } else if (data->yuv) { 530 int Ypitch = pitch; 531 int UVpitch = ((Ypitch + 1) / 2); 532 const Uint8 *Yplane = (const Uint8 *)pixels; 533 const Uint8 *Uplane = Yplane + rect->h * Ypitch; 534 const Uint8 *Vplane = Uplane + ((rect->h + 1) / 2) * UVpitch; 535 536 if (texture->format == SDL_PIXELFORMAT_YV12) { 537 retval &= GPU_UpdateTextureInternal(renderdata, cpass, data->textureV, bpp, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Uplane, UVpitch); 538 retval &= GPU_UpdateTextureInternal(renderdata, cpass, data->textureU, bpp, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Vplane, UVpitch); 539 } else { 540 retval &= GPU_UpdateTextureInternal(renderdata, cpass, data->textureU, bpp, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Uplane, UVpitch); 541 retval &= GPU_UpdateTextureInternal(renderdata, cpass, data->textureV, bpp, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Vplane, UVpitch); 542 } 543 } 544#endif 545 546 SDL_EndGPUCopyPass(cpass); 547 return retval; 548} 549 550#ifdef SDL_HAVE_YUV 551static bool GPU_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *texture, 552 const SDL_Rect *rect, 553 const Uint8 *Yplane, int Ypitch, 554 const Uint8 *Uplane, int Upitch, 555 const Uint8 *Vplane, int Vpitch) 556{ 557 GPU_RenderData *renderdata = (GPU_RenderData *)renderer->internal; 558 GPU_TextureData *data = (GPU_TextureData *)texture->internal; 559 int bpp = SDL_BYTESPERPIXEL(texture->format); 560 561 bool retval = true; 562 SDL_GPUCommandBuffer *cbuf = renderdata->state.command_buffer; 563 SDL_GPUCopyPass *cpass = SDL_BeginGPUCopyPass(cbuf); 564 retval &= GPU_UpdateTextureInternal(renderdata, cpass, data->texture, bpp, rect->x, rect->y, rect->w, rect->h, Yplane, Ypitch); 565 retval &= GPU_UpdateTextureInternal(renderdata, cpass, data->textureU, bpp, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Uplane, Upitch); 566 retval &= GPU_UpdateTextureInternal(renderdata, cpass, data->textureV, bpp, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Vplane, Vpitch); 567 SDL_EndGPUCopyPass(cpass); 568 return retval; 569} 570 571static bool GPU_UpdateTextureNV(SDL_Renderer *renderer, SDL_Texture *texture, 572 const SDL_Rect *rect, 573 const Uint8 *Yplane, int Ypitch, 574 const Uint8 *UVplane, int UVpitch) 575{ 576 GPU_RenderData *renderdata = (GPU_RenderData *)renderer->internal; 577 GPU_TextureData *data = (GPU_TextureData *)texture->internal; 578 int bpp = SDL_BYTESPERPIXEL(texture->format); 579 580 bool retval = true; 581 SDL_GPUCommandBuffer *cbuf = renderdata->state.command_buffer; 582 SDL_GPUCopyPass *cpass = SDL_BeginGPUCopyPass(cbuf); 583 retval &= GPU_UpdateTextureInternal(renderdata, cpass, data->texture, bpp, rect->x, rect->y, rect->w, rect->h, Yplane, Ypitch); 584 bpp *= 2; 585 retval &= GPU_UpdateTextureInternal(renderdata, cpass, data->textureNV, bpp, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, UVplane, UVpitch); 586 SDL_EndGPUCopyPass(cpass); 587 return retval; 588} 589#endif // SDL_HAVE_YUV 590 591static bool GPU_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture, 592 const SDL_Rect *rect, void **pixels, int *pitch) 593{ 594 GPU_TextureData *data = (GPU_TextureData *)texture->internal; 595 596 data->locked_rect = *rect; 597 *pixels = 598 (void *)((Uint8 *)data->pixels + rect->y * data->pitch + 599 rect->x * SDL_BYTESPERPIXEL(texture->format)); 600 *pitch = data->pitch; 601 return true; 602} 603 604static void GPU_UnlockTexture(SDL_Renderer *renderer, SDL_Texture *texture) 605{ 606 GPU_TextureData *data = (GPU_TextureData *)texture->internal; 607 const SDL_Rect *rect; 608 void *pixels; 609 610 rect = &data->locked_rect; 611 pixels = 612 (void *)((Uint8 *)data->pixels + rect->y * data->pitch + 613 rect->x * SDL_BYTESPERPIXEL(texture->format)); 614 GPU_UpdateTexture(renderer, texture, rect, pixels, data->pitch); 615} 616 617static bool GPU_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture) 618{ 619 GPU_RenderData *data = (GPU_RenderData *)renderer->internal; 620 621 data->state.render_target = texture; 622 623 return true; 624} 625 626static bool GPU_QueueNoOp(SDL_Renderer *renderer, SDL_RenderCommand *cmd) 627{ 628 return true; // nothing to do in this backend. 629} 630 631static bool GPU_QueueDrawPoints(SDL_Renderer *renderer, SDL_RenderCommand *cmd, const SDL_FPoint *points, int count) 632{ 633 float *verts; 634 size_t sz = 2 * sizeof(float) + 4 * sizeof(float); 635 SDL_FColor color = cmd->data.draw.color; 636 bool convert_color = SDL_RenderingLinearSpace(renderer); 637 638 verts = (float *)SDL_AllocateRenderVertices(renderer, count * sz, 0, &cmd->data.draw.first); 639 if (!verts) { 640 return false; 641 } 642 643 if (convert_color) { 644 SDL_ConvertToLinear(&color); 645 } 646 647 cmd->data.draw.count = count; 648 for (int i = 0; i < count; i++) { 649 *(verts++) = 0.5f + points[i].x; 650 *(verts++) = 0.5f + points[i].y; 651 652 *(verts++) = color.r; 653 *(verts++) = color.g; 654 *(verts++) = color.b; 655 *(verts++) = color.a; 656 } 657 return true; 658} 659 660static bool GPU_QueueGeometry(SDL_Renderer *renderer, SDL_RenderCommand *cmd, SDL_Texture *texture, 661 const float *xy, int xy_stride, const SDL_FColor *color, int color_stride, const float *uv, int uv_stride, 662 int num_vertices, const void *indices, int num_indices, int size_indices, 663 float scale_x, float scale_y) 664{ 665 int i; 666 int count = indices ? num_indices : num_vertices; 667 float *verts; 668 size_t sz = 2 * sizeof(float) + 4 * sizeof(float) + (texture ? 2 : 0) * sizeof(float); 669 bool convert_color = SDL_RenderingLinearSpace(renderer); 670 671 verts = (float *)SDL_AllocateRenderVertices(renderer, count * sz, 0, &cmd->data.draw.first); 672 if (!verts) { 673 return false; 674 } 675 676 cmd->data.draw.count = count; 677 size_indices = indices ? size_indices : 0; 678 679 for (i = 0; i < count; i++) { 680 int j; 681 float *xy_; 682 SDL_FColor col_; 683 if (size_indices == 4) { 684 j = ((const Uint32 *)indices)[i]; 685 } else if (size_indices == 2) { 686 j = ((const Uint16 *)indices)[i]; 687 } else if (size_indices == 1) { 688 j = ((const Uint8 *)indices)[i]; 689 } else { 690 j = i; 691 } 692 693 xy_ = (float *)((char *)xy + j * xy_stride); 694 695 *(verts++) = xy_[0] * scale_x; 696 *(verts++) = xy_[1] * scale_y; 697 698 col_ = *(SDL_FColor *)((char *)color + j * color_stride); 699 if (convert_color) { 700 SDL_ConvertToLinear(&col_); 701 } 702 703 *(verts++) = col_.r; 704 *(verts++) = col_.g; 705 *(verts++) = col_.b; 706 *(verts++) = col_.a; 707 708 if (texture) { 709 float *uv_ = (float *)((char *)uv + j * uv_stride); 710 *(verts++) = uv_[0]; 711 *(verts++) = uv_[1]; 712 } 713 } 714 return true; 715} 716 717static void GPU_InvalidateCachedState(SDL_Renderer *renderer) 718{ 719 GPU_RenderData *data = (GPU_RenderData *)renderer->internal; 720 721 data->state.scissor_enabled = false; 722} 723 724static SDL_GPURenderPass *RestartRenderPass(GPU_RenderData *data) 725{ 726 if (data->state.render_pass) { 727 SDL_EndGPURenderPass(data->state.render_pass); 728 } 729 730 data->state.render_pass = SDL_BeginGPURenderPass( 731 data->state.command_buffer, &data->state.color_attachment, 1, NULL); 732 733 // *** FIXME *** 734 // This is busted. We should be able to know which load op to use. 735 // LOAD is incorrect behavior most of the time, unless we had to break a render pass. 736 // -cosmonaut 737 data->state.color_attachment.load_op = SDL_GPU_LOADOP_LOAD; 738 data->state.scissor_was_enabled = false; 739 740 return data->state.render_pass; 741} 742 743static void PushVertexUniforms(GPU_RenderData *data, SDL_RenderCommand *cmd) 744{ 745 GPU_VertexShaderUniformData uniforms; 746 SDL_zero(uniforms); 747 uniforms.mvp.m[0][0] = 2.0f / data->state.viewport.w; 748 uniforms.mvp.m[1][1] = -2.0f / data->state.viewport.h; 749 uniforms.mvp.m[2][2] = 1.0f; 750 uniforms.mvp.m[3][0] = -1.0f; 751 uniforms.mvp.m[3][1] = 1.0f; 752 uniforms.mvp.m[3][3] = 1.0f; 753 754 SDL_PushGPUVertexUniformData(data->state.command_buffer, 0, &uniforms, sizeof(uniforms)); 755} 756 757static void SetViewportAndScissor(GPU_RenderData *data) 758{ 759 SDL_SetGPUViewport(data->state.render_pass, &data->state.viewport); 760 761 if (data->state.scissor_enabled) { 762 SDL_SetGPUScissor(data->state.render_pass, &data->state.scissor); 763 data->state.scissor_was_enabled = true; 764 } else if (data->state.scissor_was_enabled) { 765 SDL_Rect r; 766 r.x = (int)data->state.viewport.x; 767 r.y = (int)data->state.viewport.y; 768 r.w = (int)data->state.viewport.w; 769 r.h = (int)data->state.viewport.h; 770 SDL_SetGPUScissor(data->state.render_pass, &r); 771 data->state.scissor_was_enabled = false; 772 } 773} 774 775static SDL_GPUSampler *GetSampler(GPU_RenderData *data, SDL_PixelFormat format, SDL_ScaleMode scale_mode, SDL_TextureAddressMode address_u, SDL_TextureAddressMode address_v) 776{ 777 Uint32 key = RENDER_SAMPLER_HASHKEY(scale_mode, address_u, address_v); 778 SDL_assert(key < SDL_arraysize(data->samplers)); 779 if (!data->samplers[key]) { 780 SDL_GPUSamplerCreateInfo sci; 781 SDL_zero(sci); 782 switch (scale_mode) { 783 case SDL_SCALEMODE_NEAREST: 784 sci.min_filter = SDL_GPU_FILTER_NEAREST; 785 sci.mag_filter = SDL_GPU_FILTER_NEAREST; 786 sci.mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_NEAREST; 787 break; 788 case SDL_SCALEMODE_PIXELART: // Uses linear sampling 789 case SDL_SCALEMODE_LINEAR: 790 if (format == SDL_PIXELFORMAT_INDEX8) { 791 // We'll do linear sampling in the shader 792 sci.min_filter = SDL_GPU_FILTER_NEAREST; 793 sci.mag_filter = SDL_GPU_FILTER_NEAREST; 794 sci.mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_NEAREST; 795 } else { 796 sci.min_filter = SDL_GPU_FILTER_LINEAR; 797 sci.mag_filter = SDL_GPU_FILTER_LINEAR; 798 sci.mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_LINEAR; 799 } 800 break; 801 default: 802 SDL_SetError("Unknown scale mode: %d", scale_mode); 803 return NULL; 804 } 805 switch (address_u) { 806 case SDL_TEXTURE_ADDRESS_CLAMP: 807 sci.address_mode_u = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE; 808 break; 809 case SDL_TEXTURE_ADDRESS_WRAP: 810 sci.address_mode_u = SDL_GPU_SAMPLERADDRESSMODE_REPEAT; 811 break; 812 default: 813 SDL_SetError("Unknown texture address mode: %d", address_u); 814 return NULL; 815 } 816 switch (address_v) { 817 case SDL_TEXTURE_ADDRESS_CLAMP: 818 sci.address_mode_v = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE; 819 break; 820 case SDL_TEXTURE_ADDRESS_WRAP: 821 sci.address_mode_v = SDL_GPU_SAMPLERADDRESSMODE_REPEAT; 822 break; 823 default: 824 SDL_SetError("Unknown texture address mode: %d", address_v); 825 return NULL; 826 } 827 sci.address_mode_w = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE; 828 829 data->samplers[key] = SDL_CreateGPUSampler(data->device, &sci); 830 } 831 return data->samplers[key]; 832} 833 834static void CalculateAdvancedShaderConstants(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, const SDL_Texture *texture, GPU_AdvancedFragmentShaderUniformData *constants) 835{ 836 float output_headroom; 837 838 SDL_zerop(constants); 839 840 constants->scRGB_output = (float)SDL_RenderingLinearSpace(renderer); 841 constants->color_scale = cmd->data.draw.color_scale; 842 843 switch (texture->format) { 844 case SDL_PIXELFORMAT_INDEX8: 845 switch (cmd->data.draw.texture_scale_mode) { 846 case SDL_SCALEMODE_NEAREST: 847 constants->texture_type = TEXTURETYPE_PALETTE_NEAREST; 848 break; 849 case SDL_SCALEMODE_LINEAR: 850 constants->texture_type = TEXTURETYPE_PALETTE_LINEAR; 851 break; 852 case SDL_SCALEMODE_PIXELART: 853 constants->texture_type = TEXTURETYPE_PALETTE_PIXELART; 854 break; 855 default: 856 SDL_assert(!"Unknown scale mode"); 857 break; 858 } 859 break; 860 case SDL_PIXELFORMAT_YV12: 861 case SDL_PIXELFORMAT_IYUV: 862 constants->texture_type = TEXTURETYPE_YUV; 863 constants->input_type = INPUTTYPE_SRGB; 864 break; 865 case SDL_PIXELFORMAT_NV12: 866 constants->texture_type = TEXTURETYPE_NV12; 867 constants->input_type = INPUTTYPE_SRGB; 868 break; 869 case SDL_PIXELFORMAT_NV21: 870 constants->texture_type = TEXTURETYPE_NV21; 871 constants->input_type = INPUTTYPE_SRGB; 872 break; 873 case SDL_PIXELFORMAT_P010: 874 constants->texture_type = TEXTURETYPE_NV12; 875 constants->input_type = INPUTTYPE_HDR10; 876 break; 877 default: 878 switch (texture->format) { 879 case SDL_PIXELFORMAT_BGRX32: 880 case SDL_PIXELFORMAT_RGBX32: 881 if (cmd->data.draw.texture_scale_mode == SDL_SCALEMODE_PIXELART) { 882 constants->texture_type = TEXTURETYPE_RGB_PIXELART; 883 } else { 884 constants->texture_type = TEXTURETYPE_RGB; 885 } 886 break; 887 default: 888 if (cmd->data.draw.texture_scale_mode == SDL_SCALEMODE_PIXELART) { 889 constants->texture_type = TEXTURETYPE_RGBA_PIXELART; 890 } else { 891 constants->texture_type = TEXTURETYPE_RGBA; 892 } 893 break; 894 } 895 if (texture->colorspace == SDL_COLORSPACE_SRGB_LINEAR) { 896 constants->input_type = INPUTTYPE_SCRGB; 897 } else if (texture->colorspace == SDL_COLORSPACE_HDR10) { 898 constants->input_type = INPUTTYPE_HDR10; 899 } else { 900 // The sampler will convert from sRGB to linear on load if working in linear colorspace 901 constants->input_type = INPUTTYPE_UNSPECIFIED; 902 } 903 break; 904 } 905 906 if (constants->texture_type == TEXTURETYPE_PALETTE_LINEAR || 907 constants->texture_type == TEXTURETYPE_PALETTE_PIXELART || 908 constants->texture_type == TEXTURETYPE_RGB_PIXELART || 909 constants->texture_type == TEXTURETYPE_RGBA_PIXELART) { 910 constants->texture_width = texture->w; 911 constants->texture_height = texture->h; 912 constants->texel_width = 1.0f / constants->texture_width; 913 constants->texel_height = 1.0f / constants->texture_height; 914 } 915 916 constants->sdr_white_point = texture->SDR_white_point; 917 918 if (renderer->target) { 919 output_headroom = renderer->target->HDR_headroom; 920 } else { 921 output_headroom = renderer->HDR_headroom; 922 } 923 924 if (texture->HDR_headroom > output_headroom && output_headroom > 0.0f) { 925 constants->tonemap_method = TONEMAP_CHROME; 926 constants->tonemap_factor1 = (output_headroom / (texture->HDR_headroom * texture->HDR_headroom)); 927 constants->tonemap_factor2 = (1.0f / output_headroom); 928 } 929 930#ifdef SDL_HAVE_YUV 931 GPU_TextureData *data = (GPU_TextureData *)texture->internal; 932 if (data->yuv || data->nv12) { 933 SDL_memcpy(constants->YCbCr_matrix, data->YCbCr_matrix, sizeof(constants->YCbCr_matrix)); 934 } 935#endif 936} 937 938static void Draw( 939 GPU_RenderData *data, SDL_RenderCommand *cmd, 940 Uint32 num_verts, 941 Uint32 offset, 942 SDL_GPUPrimitiveType prim) 943{ 944 if (!data->state.render_pass || data->state.color_attachment.load_op == SDL_GPU_LOADOP_CLEAR) { 945 RestartRenderPass(data); 946 } 947 948 SDL_GPURenderPass *pass = data->state.render_pass; 949 SDL_GPURenderState *custom_state = cmd->data.draw.gpu_render_state; 950 SDL_GPUShader *custom_frag_shader = custom_state ? custom_state->fragment_shader : NULL; 951 GPU_VertexShaderID v_shader; 952 GPU_FragmentShaderID f_shader; 953 GPU_SimpleFragmentShaderUniformData simple_constants = { cmd->data.draw.color_scale }; 954 GPU_AdvancedFragmentShaderUniformData advanced_constants; 955 956 if (prim == SDL_GPU_PRIMITIVETYPE_TRIANGLELIST) { 957 SDL_Texture *texture = cmd->data.draw.texture; 958 if (texture) { 959 v_shader = VERT_SHADER_TRI_TEXTURE; 960 961 CalculateAdvancedShaderConstants(texture->renderer, cmd, texture, &advanced_constants); 962 if ((advanced_constants.texture_type == TEXTURETYPE_RGB || 963 advanced_constants.texture_type == TEXTURETYPE_RGBA) && 964 advanced_constants.input_type == INPUTTYPE_UNSPECIFIED && 965 advanced_constants.tonemap_method == TONEMAP_NONE) { 966 if (texture->format == SDL_PIXELFORMAT_RGBA32 || texture->format == SDL_PIXELFORMAT_BGRA32) { 967 f_shader = FRAG_SHADER_TEXTURE_RGBA; 968 } else { 969 f_shader = FRAG_SHADER_TEXTURE_RGB; 970 } 971 } else { 972 f_shader = FRAG_SHADER_TEXTURE_ADVANCED; 973 } 974 } else { 975 v_shader = VERT_SHADER_TRI_COLOR; 976 f_shader = FRAG_SHADER_COLOR; 977 } 978 } else { 979 v_shader = VERT_SHADER_LINEPOINT; 980 f_shader = FRAG_SHADER_COLOR; 981 } 982 983 if (custom_frag_shader) { 984 f_shader = FRAG_SHADER_TEXTURE_CUSTOM; 985 data->shaders.frag_shaders[FRAG_SHADER_TEXTURE_CUSTOM] = custom_frag_shader; 986 } 987 988 GPU_PipelineParameters pipe_params; 989 SDL_zero(pipe_params); 990 pipe_params.blend_mode = cmd->data.draw.blend; 991 pipe_params.vert_shader = v_shader; 992 pipe_params.frag_shader = f_shader; 993 pipe_params.primitive_type = prim; 994 pipe_params.custom_frag_shader = custom_frag_shader; 995 996 if (data->state.render_target) { 997 pipe_params.attachment_format = ((GPU_TextureData *)data->state.render_target->internal)->format; 998 } else { 999 pipe_params.attachment_format = data->backbuffer.format; 1000 } 1001 1002 SDL_GPUGraphicsPipeline *pipe = GPU_GetPipeline(&data->pipeline_cache, &data->shaders, data->device, &pipe_params); 1003 if (!pipe) { 1004 return; 1005 } 1006 1007 SDL_BindGPUGraphicsPipeline(pass, pipe); 1008 1009 Uint32 sampler_slot = 0; 1010 if (cmd->data.draw.texture) { 1011 SDL_Texture *texture = cmd->data.draw.texture; 1012 GPU_TextureData *tdata = (GPU_TextureData *)texture->internal; 1013 SDL_GPUTextureSamplerBinding sampler_bind; 1014 SDL_zero(sampler_bind); 1015 sampler_bind.sampler = GetSampler(data, texture->format, cmd->data.draw.texture_scale_mode, cmd->data.draw.texture_address_mode_u, cmd->data.draw.texture_address_mode_v); 1016 sampler_bind.texture = tdata->texture; 1017 SDL_BindGPUFragmentSamplers(pass, sampler_slot++, &sampler_bind, 1); 1018 1019 if (f_shader == FRAG_SHADER_TEXTURE_ADVANCED) { 1020 if (texture->palette) { 1021 GPU_PaletteData *palette = (GPU_PaletteData *)texture->palette->internal; 1022 1023 sampler_bind.sampler = GetSampler(data, SDL_PIXELFORMAT_UNKNOWN, SDL_SCALEMODE_NEAREST, SDL_TEXTURE_ADDRESS_CLAMP, SDL_TEXTURE_ADDRESS_CLAMP); 1024 sampler_bind.texture = palette->texture; 1025 SDL_BindGPUFragmentSamplers(pass, sampler_slot++, &sampler_bind, 1); 1026#ifdef SDL_HAVE_YUV 1027 } else if (tdata->yuv) { 1028 sampler_bind.texture = tdata->textureU; 1029 SDL_BindGPUFragmentSamplers(pass, sampler_slot++, &sampler_bind, 1); 1030 sampler_bind.texture = tdata->textureV; 1031 SDL_BindGPUFragmentSamplers(pass, sampler_slot++, &sampler_bind, 1); 1032 } else if (tdata->nv12) { 1033 sampler_bind.texture = tdata->textureNV; 1034 SDL_BindGPUFragmentSamplers(pass, sampler_slot++, &sampler_bind, 1); 1035#endif 1036 } 1037 1038 // We need to fill 3 sampler slots for the advanced shader 1039 while (sampler_slot < 3) { 1040 SDL_BindGPUFragmentSamplers(pass, sampler_slot++, &sampler_bind, 1); 1041 } 1042 } 1043 } 1044 if (custom_state) { 1045 if (custom_state->num_sampler_bindings > 0) { 1046 SDL_BindGPUFragmentSamplers(pass, sampler_slot, custom_state->sampler_bindings, custom_state->num_sampler_bindings); 1047 } 1048 if (custom_state->num_storage_textures > 0) { 1049 SDL_BindGPUFragmentStorageTextures(pass, 0, custom_state->storage_textures, custom_state->num_storage_textures); 1050 } 1051 if (custom_state->num_storage_buffers > 0) { 1052 SDL_BindGPUFragmentStorageBuffers(pass, 0, custom_state->storage_buffers, custom_state->num_storage_buffers); 1053 } 1054 if (custom_state->num_uniform_buffers > 0) { 1055 for (int i = 0; i < custom_state->num_uniform_buffers; i++) { 1056 SDL_GPURenderStateUniformBuffer *ub = &custom_state->uniform_buffers[i]; 1057 SDL_PushGPUFragmentUniformData(data->state.command_buffer, ub->slot_index, ub->data, ub->length); 1058 } 1059 } 1060 } else { 1061 if (f_shader == FRAG_SHADER_TEXTURE_ADVANCED) { 1062 SDL_PushGPUFragmentUniformData(data->state.command_buffer, 0, &advanced_constants, sizeof(advanced_constants)); 1063 } else { 1064 SDL_PushGPUFragmentUniformData(data->state.command_buffer, 0, &simple_constants, sizeof(simple_constants)); 1065 } 1066 } 1067 1068 SDL_GPUBufferBinding buffer_bind; 1069 SDL_zero(buffer_bind); 1070 buffer_bind.buffer = data->vertices.buffer; 1071 buffer_bind.offset = offset; 1072 SDL_BindGPUVertexBuffers(pass, 0, &buffer_bind, 1); 1073 PushVertexUniforms(data, cmd); 1074 1075 SetViewportAndScissor(data); 1076 1077 SDL_DrawGPUPrimitives(pass, num_verts, 1, 0, 0); 1078} 1079 1080static void ReleaseVertexBuffer(GPU_RenderData *data) 1081{ 1082 if (data->vertices.buffer) { 1083 SDL_ReleaseGPUBuffer(data->device, data->vertices.buffer); 1084 } 1085 1086 if (data->vertices.transfer_buf) { 1087 SDL_ReleaseGPUTransferBuffer(data->device, data->vertices.transfer_buf); 1088 } 1089 1090 data->vertices.buffer_size = 0; 1091} 1092 1093static bool InitVertexBuffer(GPU_RenderData *data, Uint32 size) 1094{ 1095 SDL_GPUBufferCreateInfo bci; 1096 SDL_zero(bci); 1097 bci.size = size; 1098 bci.usage = SDL_GPU_BUFFERUSAGE_VERTEX; 1099 1100 data->vertices.buffer = SDL_CreateGPUBuffer(data->device, &bci); 1101 1102 if (!data->vertices.buffer) { 1103 return false; 1104 } 1105 1106 SDL_GPUTransferBufferCreateInfo tbci; 1107 SDL_zero(tbci); 1108 tbci.size = size; 1109 tbci.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD; 1110 1111 data->vertices.transfer_buf = SDL_CreateGPUTransferBuffer(data->device, &tbci); 1112 1113 if (!data->vertices.transfer_buf) { 1114 return false; 1115 } 1116 1117 data->vertices.buffer_size = size; 1118 1119 return true; 1120} 1121 1122static bool UploadVertices(GPU_RenderData *data, void *vertices, size_t vertsize) 1123{ 1124 if (vertsize == 0) { 1125 return true; 1126 } 1127 1128 if (vertsize > data->vertices.buffer_size) { 1129 ReleaseVertexBuffer(data); 1130 if (!InitVertexBuffer(data, (Uint32)vertsize)) { 1131 return false; 1132 } 1133 } 1134 1135 void *staging_buf = SDL_MapGPUTransferBuffer(data->device, data->vertices.transfer_buf, true); 1136 SDL_memcpy(staging_buf, vertices, vertsize); 1137 SDL_UnmapGPUTransferBuffer(data->device, data->vertices.transfer_buf); 1138 1139 SDL_GPUCopyPass *pass = SDL_BeginGPUCopyPass(data->state.command_buffer); 1140 1141 if (!pass) { 1142 return false; 1143 } 1144 1145 SDL_GPUTransferBufferLocation src; 1146 SDL_zero(src); 1147 src.transfer_buffer = data->vertices.transfer_buf; 1148 1149 SDL_GPUBufferRegion dst; 1150 SDL_zero(dst); 1151 dst.buffer = data->vertices.buffer; 1152 dst.size = (Uint32)vertsize; 1153 1154 SDL_UploadToGPUBuffer(pass, &src, &dst, true); 1155 SDL_EndGPUCopyPass(pass); 1156 1157 return true; 1158} 1159 1160// *** FIXME *** 1161// We might be able to run these data uploads on a separate command buffer 1162// which would allow us to avoid breaking render passes. 1163// Honestly I'm a little skeptical of this entire approach, 1164// we already have a command buffer structure 1165// so it feels weird to be deferring the operations manually. 1166// We could also fairly easily run the geometry transformations 1167// on compute shaders instead of the CPU, which would be a HUGE performance win. 1168// -cosmonaut 1169static bool GPU_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize) 1170{ 1171 GPU_RenderData *data = (GPU_RenderData *)renderer->internal; 1172 1173 if (!UploadVertices(data, vertices, vertsize)) { 1174 return false; 1175 } 1176 1177 data->state.color_attachment.load_op = SDL_GPU_LOADOP_LOAD; 1178 1179 if (renderer->target) { 1180 GPU_TextureData *tdata = renderer->target->internal; 1181 data->state.color_attachment.texture = tdata->texture; 1182 } else { 1183 data->state.color_attachment.texture = data->backbuffer.texture; 1184 } 1185 1186 if (!data->state.color_attachment.texture) { 1187 return SDL_SetError("Render target texture is NULL"); 1188 } 1189 1190 while (cmd) { 1191 switch (cmd->command) { 1192 case SDL_RENDERCMD_SETDRAWCOLOR: 1193 { 1194 break; // this isn't currently used in this render backend. 1195 } 1196 1197 case SDL_RENDERCMD_SETVIEWPORT: 1198 { 1199 SDL_Rect *viewport = &cmd->data.viewport.rect; 1200 data->state.viewport.x = viewport->x; 1201 data->state.viewport.y = viewport->y; 1202 data->state.viewport.w = viewport->w; 1203 data->state.viewport.h = viewport->h; 1204 break; 1205 } 1206 1207 case SDL_RENDERCMD_SETCLIPRECT: 1208 { 1209 const SDL_Rect *rect = &cmd->data.cliprect.rect; 1210 data->state.scissor.x = (int)data->state.viewport.x + rect->x; 1211 data->state.scissor.y = (int)data->state.viewport.y + rect->y; 1212 data->state.scissor.w = rect->w; 1213 data->state.scissor.h = rect->h; 1214 data->state.scissor_enabled = cmd->data.cliprect.enabled; 1215 break; 1216 } 1217 1218 case SDL_RENDERCMD_CLEAR: 1219 { 1220 bool convert_color = SDL_RenderingLinearSpace(renderer); 1221 SDL_FColor color = cmd->data.color.color; 1222 if (convert_color) { 1223 SDL_ConvertToLinear(&color); 1224 } 1225 color.r *= cmd->data.color.color_scale; 1226 color.g *= cmd->data.color.color_scale; 1227 color.b *= cmd->data.color.color_scale; 1228 data->state.color_attachment.clear_color = color; 1229 data->state.color_attachment.load_op = SDL_GPU_LOADOP_CLEAR; 1230 break; 1231 } 1232 1233 case SDL_RENDERCMD_FILL_RECTS: // unused 1234 break; 1235 1236 case SDL_RENDERCMD_COPY: // unused 1237 break; 1238 1239 case SDL_RENDERCMD_COPY_EX: // unused 1240 break; 1241 1242 case SDL_RENDERCMD_DRAW_LINES: 1243 { 1244 Uint32 count = (Uint32)cmd->data.draw.count; 1245 Uint32 offset = (Uint32)cmd->data.draw.first; 1246 1247 if (count > 2) { 1248 // joined lines cannot be grouped 1249 Draw(data, cmd, count, offset, SDL_GPU_PRIMITIVETYPE_LINESTRIP); 1250 } else { 1251 // let's group non joined lines 1252 SDL_RenderCommand *finalcmd = cmd; 1253 SDL_RenderCommand *nextcmd; 1254 float thiscolorscale = cmd->data.draw.color_scale; 1255 SDL_BlendMode thisblend = cmd->data.draw.blend; 1256 SDL_GPURenderState *thisrenderstate = cmd->data.draw.gpu_render_state; 1257 1258 for (nextcmd = cmd->next; nextcmd; nextcmd = nextcmd->next) { 1259 const SDL_RenderCommandType nextcmdtype = nextcmd->command; 1260 if (nextcmdtype != SDL_RENDERCMD_DRAW_LINES) { 1261 if (nextcmdtype == SDL_RENDERCMD_SETDRAWCOLOR) { 1262 // The vertex data has the draw color built in, ignore this 1263 continue; 1264 } 1265 break; // can't go any further on this draw call, different render command up next. 1266 } else if (nextcmd->data.draw.count != 2) { 1267 break; // can't go any further on this draw call, those are joined lines 1268 } else if (nextcmd->data.draw.blend != thisblend || 1269 nextcmd->data.draw.color_scale != thiscolorscale || 1270 nextcmd->data.draw.gpu_render_state != thisrenderstate) { 1271 break; // can't go any further on this draw call, different blendmode copy up next. 1272 } else { 1273 finalcmd = nextcmd; // we can combine copy operations here. Mark this one as the furthest okay command. 1274 count += (Uint32)nextcmd->data.draw.count; 1275 } 1276 } 1277 1278 Draw(data, cmd, count, offset, SDL_GPU_PRIMITIVETYPE_LINELIST); 1279 cmd = finalcmd; // skip any copy commands we just combined in here. 1280 } 1281 break; 1282 } 1283 1284 case SDL_RENDERCMD_DRAW_POINTS: 1285 case SDL_RENDERCMD_GEOMETRY: 1286 { 1287 /* as long as we have the same copy command in a row, with the 1288 same texture, we can combine them all into a single draw call. */ 1289 float thiscolorscale = cmd->data.draw.color_scale; 1290 SDL_Texture *thistexture = cmd->data.draw.texture; 1291 SDL_BlendMode thisblend = cmd->data.draw.blend; 1292 SDL_ScaleMode thisscalemode = cmd->data.draw.texture_scale_mode; 1293 SDL_TextureAddressMode thisaddressmode_u = cmd->data.draw.texture_address_mode_u; 1294 SDL_TextureAddressMode thisaddressmode_v = cmd->data.draw.texture_address_mode_v; 1295 SDL_GPURenderState *thisrenderstate = cmd->data.draw.gpu_render_state; 1296 const SDL_RenderCommandType thiscmdtype = cmd->command; 1297 SDL_RenderCommand *finalcmd = cmd; 1298 SDL_RenderCommand *nextcmd; 1299 Uint32 count = (Uint32)cmd->data.draw.count; 1300 Uint32 offset = (Uint32)cmd->data.draw.first; 1301 1302 for (nextcmd = cmd->next; nextcmd; nextcmd = nextcmd->next) { 1303 const SDL_RenderCommandType nextcmdtype = nextcmd->command; 1304 if (nextcmdtype != thiscmdtype) { 1305 if (nextcmdtype == SDL_RENDERCMD_SETDRAWCOLOR) { 1306 // The vertex data has the draw color built in, ignore this 1307 continue; 1308 } 1309 break; // can't go any further on this draw call, different render command up next. 1310 } else if (nextcmd->data.draw.texture != thistexture || 1311 nextcmd->data.draw.texture_scale_mode != thisscalemode || 1312 nextcmd->data.draw.texture_address_mode_u != thisaddressmode_u || 1313 nextcmd->data.draw.texture_address_mode_v != thisaddressmode_v || 1314 nextcmd->data.draw.blend != thisblend || 1315 nextcmd->data.draw.color_scale != thiscolorscale || 1316 nextcmd->data.draw.gpu_render_state != thisrenderstate) { 1317 break; // can't go any further on this draw call, different texture/blendmode copy up next. 1318 } else { 1319 finalcmd = nextcmd; // we can combine copy operations here. Mark this one as the furthest okay command. 1320 count += (Uint32)nextcmd->data.draw.count; 1321 } 1322 } 1323 1324 SDL_GPUPrimitiveType prim; 1325 if (thiscmdtype == SDL_RENDERCMD_GEOMETRY) { 1326 prim = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST; 1327 } else { 1328 prim = SDL_GPU_PRIMITIVETYPE_POINTLIST; 1329 } 1330 Draw(data, cmd, count, offset, prim); 1331 1332 cmd = finalcmd; // skip any copy commands we just combined in here. 1333 break; 1334 } 1335 1336 case SDL_RENDERCMD_NO_OP: 1337 break; 1338 } 1339 1340 cmd = cmd->next; 1341 } 1342 1343 if (data->state.color_attachment.load_op == SDL_GPU_LOADOP_CLEAR) { 1344 RestartRenderPass(data); 1345 } 1346 1347 if (data->state.render_pass) { 1348 SDL_EndGPURenderPass(data->state.render_pass); 1349 data->state.render_pass = NULL; 1350 } 1351 1352 return true; 1353} 1354 1355static SDL_Surface *GPU_RenderReadPixels(SDL_Renderer *renderer, const SDL_Rect *rect) 1356{ 1357 GPU_RenderData *data = (GPU_RenderData *)renderer->internal; 1358 SDL_GPUTexture *gpu_tex; 1359 SDL_PixelFormat pixfmt; 1360 1361 if (data->state.render_target) { 1362 SDL_Texture *texture = data->state.render_target; 1363 GPU_TextureData *texdata = texture->internal; 1364 gpu_tex = texdata->texture; 1365 pixfmt = texture->format; 1366 } else { 1367 gpu_tex = data->backbuffer.texture; 1368 pixfmt = SDL_GetPixelFormatFromGPUTextureFormat(data->backbuffer.format); 1369 1370 if (pixfmt == SDL_PIXELFORMAT_UNKNOWN) { 1371 SDL_SetError("Unsupported backbuffer format"); 1372 return NULL; 1373 } 1374 } 1375 1376 Uint32 bpp = SDL_BYTESPERPIXEL(pixfmt); 1377 size_t row_size, image_size; 1378 1379 if (!SDL_size_mul_check_overflow(rect->w, bpp, &row_size) || 1380 !SDL_size_mul_check_overflow(rect->h, row_size, &image_size)) { 1381 SDL_SetError("read size overflow"); 1382 return NULL; 1383 } 1384 1385 SDL_Surface *surface = SDL_CreateSurface(rect->w, rect->h, pixfmt); 1386 1387 if (!surface) { 1388 return NULL; 1389 } 1390 1391 SDL_GPUTransferBufferCreateInfo tbci; 1392 SDL_zero(tbci); 1393 tbci.size = (Uint32)image_size; 1394 tbci.usage = SDL_GPU_TRANSFERBUFFERUSAGE_DOWNLOAD; 1395 1396 SDL_GPUTransferBuffer *tbuf = SDL_CreateGPUTransferBuffer(data->device, &tbci); 1397 1398 if (!tbuf) { 1399 return NULL; 1400 } 1401 1402 SDL_GPUCopyPass *pass = SDL_BeginGPUCopyPass(data->state.command_buffer); 1403 1404 SDL_GPUTextureRegion src; 1405 SDL_zero(src); 1406 src.texture = gpu_tex; 1407 src.x = rect->x; 1408 src.y = rect->y; 1409 src.w = rect->w; 1410 src.h = rect->h; 1411 src.d = 1; 1412 1413 SDL_GPUTextureTransferInfo dst; 1414 SDL_zero(dst); 1415 dst.transfer_buffer = tbuf; 1416 dst.rows_per_layer = rect->h; 1417 dst.pixels_per_row = rect->w; 1418 1419 SDL_DownloadFromGPUTexture(pass, &src, &dst); 1420 SDL_EndGPUCopyPass(pass); 1421 1422 SDL_GPUFence *fence = SDL_SubmitGPUCommandBufferAndAcquireFence(data->state.command_buffer); 1423 SDL_WaitForGPUFences(data->device, true, &fence, 1); 1424 SDL_ReleaseGPUFence(data->device, fence); 1425 data->state.command_buffer = SDL_AcquireGPUCommandBuffer(data->device); 1426 1427 void *mapped_tbuf = SDL_MapGPUTransferBuffer(data->device, tbuf, false); 1428 1429 if ((size_t)surface->pitch == row_size) { 1430 SDL_memcpy(surface->pixels, mapped_tbuf, image_size); 1431 } else { 1432 Uint8 *input = mapped_tbuf; 1433 Uint8 *output = surface->pixels; 1434 1435 for (int row = 0; row < rect->h; ++row) { 1436 SDL_memcpy(output, input, row_size); 1437 output += surface->pitch; 1438 input += row_size; 1439 } 1440 } 1441 1442 SDL_UnmapGPUTransferBuffer(data->device, tbuf); 1443 SDL_ReleaseGPUTransferBuffer(data->device, tbuf); 1444 1445 return surface; 1446} 1447 1448static bool CreateBackbuffer(GPU_RenderData *data, Uint32 w, Uint32 h, SDL_GPUTextureFormat fmt) 1449{ 1450 SDL_GPUTextureCreateInfo tci; 1451 SDL_zero(tci); 1452 tci.width = w; 1453 tci.height = h; 1454 tci.format = fmt; 1455 tci.layer_count_or_depth = 1; 1456 tci.num_levels = 1; 1457 tci.sample_count = SDL_GPU_SAMPLECOUNT_1; 1458 tci.usage = SDL_GPU_TEXTUREUSAGE_COLOR_TARGET | SDL_GPU_TEXTUREUSAGE_SAMPLER; 1459 1460 data->backbuffer.texture = SDL_CreateGPUTexture(data->device, &tci); 1461 data->backbuffer.width = w; 1462 data->backbuffer.height = h; 1463 data->backbuffer.format = fmt; 1464 1465 if (!data->backbuffer.texture) { 1466 return false; 1467 } 1468 1469 return true; 1470} 1471 1472static bool GPU_RenderPresent(SDL_Renderer *renderer) 1473{ 1474 GPU_RenderData *data = (GPU_RenderData *)renderer->internal; 1475 1476 if (renderer->window) { 1477 SDL_GPUTexture *swapchain; 1478 Uint32 swapchain_texture_width, swapchain_texture_height; 1479 bool result = SDL_WaitAndAcquireGPUSwapchainTexture(data->state.command_buffer, renderer->window, &swapchain, &swapchain_texture_width, &swapchain_texture_height); 1480 1481 if (!result) { 1482 SDL_LogError(SDL_LOG_CATEGORY_RENDER, "Failed to acquire swapchain texture: %s", SDL_GetError()); 1483 } 1484 1485 if (swapchain != NULL) { 1486 SDL_GPUBlitInfo blit_info; 1487 SDL_zero(blit_info); 1488 1489 blit_info.source.texture = data->backbuffer.texture; 1490 blit_info.source.w = data->backbuffer.width; 1491 blit_info.source.h = data->backbuffer.height; 1492 blit_info.destination.texture = swapchain; 1493 blit_info.destination.w = swapchain_texture_width; 1494 blit_info.destination.h = swapchain_texture_height; 1495 blit_info.load_op = SDL_GPU_LOADOP_DONT_CARE; 1496 blit_info.filter = SDL_GPU_FILTER_LINEAR; 1497 1498 SDL_BlitGPUTexture(data->state.command_buffer, &blit_info); 1499 1500 SDL_SubmitGPUCommandBuffer(data->state.command_buffer); 1501 1502 if (swapchain_texture_width != data->backbuffer.width || swapchain_texture_height != data->backbuffer.height) { 1503 CreateBackbuffer(data, swapchain_texture_width, swapchain_texture_height, SDL_GetGPUSwapchainTextureFormat(data->device, renderer->window)); 1504 } 1505 } else { 1506 SDL_SubmitGPUCommandBuffer(data->state.command_buffer); 1507 } 1508 } else { 1509 SDL_SubmitGPUCommandBuffer(data->state.command_buffer); 1510 } 1511 1512 data->state.command_buffer = SDL_AcquireGPUCommandBuffer(data->device); 1513 1514 return true; 1515} 1516 1517static void GPU_DestroyTexture(SDL_Renderer *renderer, SDL_Texture *texture) 1518{ 1519 GPU_RenderData *renderdata = (GPU_RenderData *)renderer->internal; 1520 GPU_TextureData *data = (GPU_TextureData *)texture->internal; 1521 1522 if (renderdata->state.render_target == texture) { 1523 renderdata->state.render_target = NULL; 1524 } 1525 1526 if (!data) { 1527 return; 1528 } 1529 1530 if (!data->external_texture) { 1531 SDL_ReleaseGPUTexture(renderdata->device, data->texture); 1532 } 1533#ifdef SDL_HAVE_YUV 1534 if (!data->external_texture_u) { 1535 SDL_ReleaseGPUTexture(renderdata->device, data->textureU); 1536 } 1537 if (!data->external_texture_v) { 1538 SDL_ReleaseGPUTexture(renderdata->device, data->textureV); 1539 } 1540 if (!data->external_texture_nv) { 1541 SDL_ReleaseGPUTexture(renderdata->device, data->textureNV); 1542 } 1543#endif 1544 SDL_free(data->pixels); 1545 SDL_free(data); 1546 texture->internal = NULL; 1547} 1548 1549static void GPU_DestroyRenderer(SDL_Renderer *renderer) 1550{ 1551 GPU_RenderData *data = (GPU_RenderData *)renderer->internal; 1552 1553 if (!data) { 1554 return; 1555 } 1556 1557 if (data->state.command_buffer) { 1558 SDL_CancelGPUCommandBuffer(data->state.command_buffer); 1559 data->state.command_buffer = NULL; 1560 } 1561 1562 for (Uint32 i = 0; i < SDL_arraysize(data->samplers); ++i) { 1563 if (data->samplers[i]) { 1564 SDL_ReleaseGPUSampler(data->device, data->samplers[i]); 1565 } 1566 } 1567 1568 if (data->backbuffer.texture) { 1569 SDL_ReleaseGPUTexture(data->device, data->backbuffer.texture); 1570 } 1571 1572 if (renderer->window && data->device) { 1573 SDL_ReleaseWindowFromGPUDevice(data->device, renderer->window); 1574 } 1575 1576 ReleaseVertexBuffer(data); 1577 GPU_DestroyPipelineCache(&data->pipeline_cache); 1578 1579 if (data->device) { 1580 GPU_ReleaseShaders(&data->shaders, data->device); 1581 if (!data->external_device) { 1582 SDL_DestroyGPUDevice(data->device); 1583 } 1584 } 1585 1586 SDL_free(data); 1587} 1588 1589static bool ChoosePresentMode(SDL_GPUDevice *device, SDL_Window *window, const int vsync, SDL_GPUPresentMode *out_mode) 1590{ 1591 SDL_GPUPresentMode mode; 1592 1593 switch (vsync) { 1594 case 0: 1595 mode = SDL_GPU_PRESENTMODE_MAILBOX; 1596 1597 if (!SDL_WindowSupportsGPUPresentMode(device, window, mode)) { 1598 mode = SDL_GPU_PRESENTMODE_IMMEDIATE; 1599 1600 if (!SDL_WindowSupportsGPUPresentMode(device, window, mode)) { 1601 mode = SDL_GPU_PRESENTMODE_VSYNC; 1602 } 1603 } 1604 1605 // FIXME should we return an error if both mailbox and immediate fail? 1606 break; 1607 1608 case 1: 1609 mode = SDL_GPU_PRESENTMODE_VSYNC; 1610 break; 1611 1612 default: 1613 return SDL_Unsupported(); 1614 } 1615 1616 *out_mode = mode; 1617 return true; 1618} 1619 1620static bool GPU_SetVSync(SDL_Renderer *renderer, const int vsync) 1621{ 1622 GPU_RenderData *data = (GPU_RenderData *)renderer->internal; 1623 SDL_GPUPresentMode mode = SDL_GPU_PRESENTMODE_VSYNC; 1624 1625 if (!renderer->window) { 1626 if (!vsync) { 1627 return true; 1628 } else { 1629 return SDL_Unsupported(); 1630 } 1631 } 1632 1633 if (!ChoosePresentMode(data->device, renderer->window, vsync, &mode)) { 1634 return false; 1635 } 1636 1637 if (mode != data->swapchain.present_mode) { 1638 // XXX returns bool instead of SDL-style error code 1639 if (SDL_SetGPUSwapchainParameters(data->device, renderer->window, data->swapchain.composition, mode)) { 1640 data->swapchain.present_mode = mode; 1641 return true; 1642 } else { 1643 return false; 1644 } 1645 } 1646 1647 return true; 1648} 1649 1650static bool GPU_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_PropertiesID create_props) 1651{ 1652 GPU_RenderData *data = NULL; 1653 1654 SDL_SetupRendererColorspace(renderer, create_props); 1655 1656 if (renderer->output_colorspace != SDL_COLORSPACE_SRGB && 1657 renderer->output_colorspace != SDL_COLORSPACE_SRGB_LINEAR 1658 /*&& renderer->output_colorspace != SDL_COLORSPACE_HDR10*/) { 1659 return SDL_SetError("Unsupported output colorspace"); 1660 } 1661 1662 data = (GPU_RenderData *)SDL_calloc(1, sizeof(*data)); 1663 if (!data) { 1664 return false; 1665 } 1666 1667 renderer->SupportsBlendMode = GPU_SupportsBlendMode; 1668 renderer->CreatePalette = GPU_CreatePalette; 1669 renderer->UpdatePalette = GPU_UpdatePalette; 1670 renderer->DestroyPalette = GPU_DestroyPalette; 1671 renderer->CreateTexture = GPU_CreateTexture; 1672 renderer->UpdateTexture = GPU_UpdateTexture; 1673#ifdef SDL_HAVE_YUV 1674 renderer->UpdateTextureYUV = GPU_UpdateTextureYUV; 1675 renderer->UpdateTextureNV = GPU_UpdateTextureNV; 1676#endif 1677 renderer->LockTexture = GPU_LockTexture; 1678 renderer->UnlockTexture = GPU_UnlockTexture; 1679 renderer->SetRenderTarget = GPU_SetRenderTarget; 1680 renderer->QueueSetViewport = GPU_QueueNoOp; 1681 renderer->QueueSetDrawColor = GPU_QueueNoOp; 1682 renderer->QueueDrawPoints = GPU_QueueDrawPoints; 1683 renderer->QueueDrawLines = GPU_QueueDrawPoints; // lines and points queue vertices the same way. 1684 renderer->QueueGeometry = GPU_QueueGeometry; 1685 renderer->InvalidateCachedState = GPU_InvalidateCachedState; 1686 renderer->RunCommandQueue = GPU_RunCommandQueue; 1687 renderer->RenderReadPixels = GPU_RenderReadPixels; 1688 renderer->RenderPresent = GPU_RenderPresent; 1689 renderer->DestroyTexture = GPU_DestroyTexture; 1690 renderer->DestroyRenderer = GPU_DestroyRenderer; 1691 renderer->SetVSync = GPU_SetVSync; 1692 renderer->internal = data; 1693 renderer->window = window; 1694 renderer->name = GPU_RenderDriver.name; 1695 1696 data->device = SDL_GetPointerProperty(create_props, SDL_PROP_RENDERER_CREATE_GPU_DEVICE_POINTER, NULL); 1697 if (data->device) { 1698 data->external_device = true; 1699 } else { 1700 bool debug = SDL_GetBooleanProperty(create_props, SDL_PROP_GPU_DEVICE_CREATE_DEBUGMODE_BOOLEAN, false); 1701 bool lowpower = SDL_GetBooleanProperty(create_props, SDL_PROP_GPU_DEVICE_CREATE_PREFERLOWPOWER_BOOLEAN, false); 1702 1703 // Prefer environment variables/hints if they exist, otherwise defer to properties 1704 debug = SDL_GetHintBoolean(SDL_HINT_RENDER_GPU_DEBUG, debug); 1705 lowpower = SDL_GetHintBoolean(SDL_HINT_RENDER_GPU_LOW_POWER, lowpower); 1706 1707 SDL_SetBooleanProperty(create_props, SDL_PROP_GPU_DEVICE_CREATE_DEBUGMODE_BOOLEAN, debug); 1708 SDL_SetBooleanProperty(create_props, SDL_PROP_GPU_DEVICE_CREATE_PREFERLOWPOWER_BOOLEAN, lowpower); 1709 1710 // Vulkan windows get the Vulkan GPU backend by default 1711 if (!SDL_HasProperty(create_props, SDL_PROP_GPU_DEVICE_CREATE_NAME_STRING) && 1712 (SDL_GetWindowFlags(window) & SDL_WINDOW_VULKAN)) { 1713 SDL_SetStringProperty(create_props, SDL_PROP_GPU_DEVICE_CREATE_NAME_STRING, "vulkan"); 1714 } 1715 1716 // Set hints for the greatest hardware compatibility 1717 // This property allows using the renderer on Intel Haswell and Broadwell GPUs. 1718 if (!SDL_HasProperty(create_props, SDL_PROP_GPU_DEVICE_CREATE_D3D12_ALLOW_FEWER_RESOURCE_SLOTS_BOOLEAN)) { 1719 SDL_SetBooleanProperty(create_props, SDL_PROP_GPU_DEVICE_CREATE_D3D12_ALLOW_FEWER_RESOURCE_SLOTS_BOOLEAN, true); 1720 } 1721 // These properties allow using the renderer on more Android devices. 1722 if (!SDL_HasProperty(create_props, SDL_PROP_GPU_DEVICE_CREATE_FEATURE_CLIP_DISTANCE_BOOLEAN)) { 1723 SDL_SetBooleanProperty(create_props, SDL_PROP_GPU_DEVICE_CREATE_FEATURE_CLIP_DISTANCE_BOOLEAN, false); 1724 } 1725 if (!SDL_HasProperty(create_props, SDL_PROP_GPU_DEVICE_CREATE_FEATURE_DEPTH_CLAMPING_BOOLEAN)) { 1726 SDL_SetBooleanProperty(create_props, SDL_PROP_GPU_DEVICE_CREATE_FEATURE_DEPTH_CLAMPING_BOOLEAN, false); 1727 } 1728 if (!SDL_HasProperty(create_props, SDL_PROP_GPU_DEVICE_CREATE_FEATURE_INDIRECT_DRAW_FIRST_INSTANCE_BOOLEAN)) { 1729 SDL_SetBooleanProperty(create_props, SDL_PROP_GPU_DEVICE_CREATE_FEATURE_INDIRECT_DRAW_FIRST_INSTANCE_BOOLEAN, false); 1730 } 1731 if (!SDL_HasProperty(create_props, SDL_PROP_GPU_DEVICE_CREATE_FEATURE_ANISOTROPY_BOOLEAN)) { 1732 SDL_SetBooleanProperty(create_props, SDL_PROP_GPU_DEVICE_CREATE_FEATURE_ANISOTROPY_BOOLEAN, false); 1733 } 1734 1735 GPU_FillSupportedShaderFormats(create_props); 1736 data->device = SDL_CreateGPUDeviceWithProperties(create_props); 1737 1738 if (!data->device) { 1739 return false; 1740 } 1741 } 1742 1743 if (!GPU_InitShaders(&data->shaders, data->device)) { 1744 return false; 1745 } 1746 1747 if (!GPU_InitPipelineCache(&data->pipeline_cache, data->device)) { 1748 return false; 1749 } 1750 1751 // FIXME: What's a good initial size? 1752 if (!InitVertexBuffer(data, 1 << 16)) { 1753 return false; 1754 } 1755 1756 if (window) { 1757 if (!SDL_ClaimWindowForGPUDevice(data->device, window)) { 1758 return false; 1759 } 1760 1761 switch (renderer->output_colorspace) { 1762 case SDL_COLORSPACE_SRGB_LINEAR: 1763 data->swapchain.composition = SDL_GPU_SWAPCHAINCOMPOSITION_HDR_EXTENDED_LINEAR; 1764 break; 1765 case SDL_COLORSPACE_HDR10: 1766 data->swapchain.composition = SDL_GPU_SWAPCHAINCOMPOSITION_HDR10_ST2084; 1767 break; 1768 case SDL_COLORSPACE_SRGB: 1769 default: 1770 data->swapchain.composition = SDL_GPU_SWAPCHAINCOMPOSITION_SDR; 1771 break; 1772 } 1773 data->swapchain.present_mode = SDL_GPU_PRESENTMODE_VSYNC; 1774 1775 int vsync = (int)SDL_GetNumberProperty(create_props, SDL_PROP_RENDERER_CREATE_PRESENT_VSYNC_NUMBER, 0); 1776 ChoosePresentMode(data->device, window, vsync, &data->swapchain.present_mode); 1777 1778 SDL_SetGPUSwapchainParameters(data->device, window, data->swapchain.composition, data->swapchain.present_mode); 1779 1780 SDL_SetGPUAllowedFramesInFlight(data->device, 1); 1781 1782 int w, h; 1783 SDL_GetWindowSizeInPixels(window, &w, &h); 1784 1785 if (!CreateBackbuffer(data, w, h, SDL_GetGPUSwapchainTextureFormat(data->device, window))) { 1786 return false; 1787 } 1788 } 1789 1790 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_BGRA32); // SDL_PIXELFORMAT_ARGB8888 on little endian systems 1791 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_RGBA32); 1792 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_BGRX32); 1793 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_RGBX32); 1794 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ABGR2101010); 1795 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_RGBA64_FLOAT); 1796 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_INDEX8); 1797 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_YV12); 1798 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_IYUV); 1799 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV12); 1800 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV21); 1801 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P010); 1802 1803 SDL_SetNumberProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_MAX_TEXTURE_SIZE_NUMBER, 16384); 1804 1805 data->state.viewport.min_depth = 0; 1806 data->state.viewport.max_depth = 1; 1807 data->state.command_buffer = SDL_AcquireGPUCommandBuffer(data->device); 1808 1809 SDL_SetPointerProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_GPU_DEVICE_POINTER, data->device); 1810 1811 return true; 1812} 1813 1814SDL_RenderDriver GPU_RenderDriver = { 1815 GPU_CreateRenderer, "gpu" 1816}; 1817 1818#endif // SDL_VIDEO_RENDER_GPU 1819[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.