Atlas - SDL_render_gpu.c

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