Atlas - SDL_render_d3d12.c

Home / ext / SDL / src / render / direct3d12 Lines: 1 | Size: 145086 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_D3D12 24 25#define SDL_D3D12_NUM_BUFFERS 2 26#define SDL_D3D12_NUM_VERTEX_BUFFERS 256 27#define SDL_D3D12_MAX_NUM_TEXTURES 16384 28#define SDL_D3D12_NUM_UPLOAD_BUFFERS 32 29 30#include "../../core/windows/SDL_windows.h" 31#include "../../video/windows/SDL_windowswindow.h" 32#include "../SDL_sysrender.h" 33#include "../SDL_d3dmath.h" 34#include "../../video/directx/SDL_d3d12.h" 35 36#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES) 37#include "SDL_render_d3d12_xbox.h" 38#endif 39 40#include "SDL_shaders_d3d12.h" 41 42// Set up for C function definitions, even when using C++ 43#ifdef __cplusplus 44extern "C" { 45#endif 46 47// This must be included here as the function definitions in SDL_pixels.c/_c.h are C, not C++ 48#include "../../video/SDL_pixels_c.h" 49 50/* !!! FIXME: vertex buffer bandwidth could be lower; only use UV coords when 51 !!! FIXME: textures are needed. */ 52 53// Vertex shader, common values 54typedef struct 55{ 56 Float4X4 mpv; 57} D3D12_VertexShaderConstants; 58 59// These should mirror the definitions in D3D12_PixelShader_Common.hlsli 60static const float TONEMAP_NONE = 0; 61//static const float TONEMAP_LINEAR = 1; 62static const float TONEMAP_CHROME = 2; 63 64//static const float TEXTURETYPE_NONE = 0; 65static const float TEXTURETYPE_RGB = 1; 66static const float TEXTURETYPE_RGB_PIXELART = 2; 67static const float TEXTURETYPE_PALETTE_NEAREST = 3; 68static const float TEXTURETYPE_PALETTE_LINEAR = 4; 69static const float TEXTURETYPE_PALETTE_PIXELART = 5; 70static const float TEXTURETYPE_NV12 = 6; 71static const float TEXTURETYPE_NV21 = 7; 72static const float TEXTURETYPE_YUV = 8; 73 74static const float INPUTTYPE_UNSPECIFIED = 0; 75static const float INPUTTYPE_SRGB = 1; 76static const float INPUTTYPE_SCRGB = 2; 77static const float INPUTTYPE_HDR10 = 3; 78 79typedef struct 80{ 81 float scRGB_output; 82 float texture_type; 83 float input_type; 84 float color_scale; 85 86 float texel_width; 87 float texel_height; 88 float texture_width; 89 float texture_height; 90 91 float tonemap_method; 92 float tonemap_factor1; 93 float tonemap_factor2; 94 float sdr_white_point; 95 96 float YCbCr_matrix[16]; 97} D3D12_PixelShaderConstants; 98 99// Per-vertex data 100typedef struct 101{ 102 Float2 pos; 103 Float2 tex; 104 SDL_FColor color; 105} D3D12_VertexPositionColor; 106 107// Per-palette data 108typedef struct 109{ 110 ID3D12Resource *texture; 111 D3D12_CPU_DESCRIPTOR_HANDLE resourceView; 112 D3D12_RESOURCE_STATES resourceState; 113 SIZE_T SRVIndex; 114} D3D12_PaletteData; 115 116// Per-texture data 117typedef struct 118{ 119 int w, h; 120 ID3D12Resource *mainTexture; 121 D3D12_CPU_DESCRIPTOR_HANDLE mainTextureResourceView; 122 D3D12_RESOURCE_STATES mainResourceState; 123 SIZE_T mainSRVIndex; 124 D3D12_CPU_DESCRIPTOR_HANDLE mainTextureRenderTargetView; 125 DXGI_FORMAT mainTextureFormat; 126 ID3D12Resource *stagingBuffer; 127 D3D12_RESOURCE_STATES stagingResourceState; 128 const float *YCbCr_matrix; 129#ifdef SDL_HAVE_YUV 130 // YV12 texture support 131 bool yuv; 132 ID3D12Resource *mainTextureU; 133 D3D12_CPU_DESCRIPTOR_HANDLE mainTextureResourceViewU; 134 D3D12_RESOURCE_STATES mainResourceStateU; 135 SIZE_T mainSRVIndexU; 136 ID3D12Resource *mainTextureV; 137 D3D12_CPU_DESCRIPTOR_HANDLE mainTextureResourceViewV; 138 D3D12_RESOURCE_STATES mainResourceStateV; 139 SIZE_T mainSRVIndexV; 140 141 // NV12 texture support 142 bool nv12; 143 D3D12_CPU_DESCRIPTOR_HANDLE mainTextureResourceViewNV; 144 SIZE_T mainSRVIndexNV; 145 146 Uint8 *pixels; 147 int pitch; 148#endif 149 SDL_Rect lockedRect; 150} D3D12_TextureData; 151 152// Pipeline State Object data 153typedef struct 154{ 155 D3D12_Shader shader; 156 D3D12_PixelShaderConstants shader_constants; 157 SDL_BlendMode blendMode; 158 D3D12_PRIMITIVE_TOPOLOGY_TYPE topology; 159 DXGI_FORMAT rtvFormat; 160 ID3D12PipelineState *pipelineState; 161} D3D12_PipelineState; 162 163// Vertex Buffer 164typedef struct 165{ 166 ID3D12Resource *resource; 167 D3D12_VERTEX_BUFFER_VIEW view; 168 size_t size; 169} D3D12_VertexBuffer; 170 171// For SRV pool allocator 172typedef struct 173{ 174 SIZE_T index; 175 void *next; 176} D3D12_SRVPoolNode; 177 178// Private renderer data 179typedef struct 180{ 181 SDL_SharedObject *hDXGIMod; 182 SDL_SharedObject *hD3D12Mod; 183#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES) 184 UINT64 frameToken; 185#else 186 IDXGIFactory6 *dxgiFactory; 187 IDXGIAdapter4 *dxgiAdapter; 188 IDXGIDebug *dxgiDebug; 189 IDXGISwapChain4 *swapChain; 190#endif 191 ID3D12Device1 *d3dDevice; 192 ID3D12Debug *debugInterface; 193 ID3D12CommandQueue *commandQueue; 194 ID3D12GraphicsCommandList2 *commandList; 195 DXGI_SWAP_EFFECT swapEffect; 196 UINT swapFlags; 197 UINT syncInterval; 198 UINT presentFlags; 199 DXGI_FORMAT renderTargetFormat; 200 bool pixelSizeChanged; 201 202 // Descriptor heaps 203 ID3D12DescriptorHeap *rtvDescriptorHeap; 204 UINT rtvDescriptorSize; 205 ID3D12DescriptorHeap *textureRTVDescriptorHeap; 206 ID3D12DescriptorHeap *srvDescriptorHeap; 207 UINT srvDescriptorSize; 208 ID3D12DescriptorHeap *samplerDescriptorHeap; 209 UINT samplerDescriptorSize; 210 211 // Data needed per backbuffer 212 ID3D12CommandAllocator *commandAllocators[SDL_D3D12_NUM_BUFFERS]; 213 ID3D12Resource *renderTargets[SDL_D3D12_NUM_BUFFERS]; 214 UINT64 fenceValue; 215 int currentBackBufferIndex; 216 217 // Fences 218 ID3D12Fence *fence; 219 HANDLE fenceEvent; 220 221 // Root signature and pipeline state data 222 ID3D12RootSignature *rootSignatures[NUM_ROOTSIGS]; 223 int pipelineStateCount; 224 D3D12_PipelineState *pipelineStates; 225 D3D12_PipelineState *currentPipelineState; 226 227 D3D12_VertexBuffer vertexBuffers[SDL_D3D12_NUM_VERTEX_BUFFERS]; 228 D3D12_CPU_DESCRIPTOR_HANDLE samplers[RENDER_SAMPLER_COUNT]; 229 bool samplers_created[RENDER_SAMPLER_COUNT]; 230 231 // Data for staging/allocating textures 232 ID3D12Resource *uploadBuffers[SDL_D3D12_NUM_UPLOAD_BUFFERS]; 233 int currentUploadBuffer; 234 235 // Pool allocator to handle reusing SRV heap indices 236 D3D12_SRVPoolNode *srvPoolHead; 237 D3D12_SRVPoolNode srvPoolNodes[SDL_D3D12_MAX_NUM_TEXTURES]; 238 239 // Vertex buffer constants 240 Float4X4 projectionAndView; 241 242 // Cached renderer properties 243 DXGI_MODE_ROTATION rotation; 244 D3D12_TextureData *textureRenderTarget; 245 D3D12_CPU_DESCRIPTOR_HANDLE currentRenderTargetView; 246 int numCurrentShaderResources; 247 D3D12_CPU_DESCRIPTOR_HANDLE currentShaderResource; 248 int numCurrentShaderSamplers; 249 D3D12_CPU_DESCRIPTOR_HANDLE currentShaderSampler; 250 bool cliprectDirty; 251 bool currentCliprectEnabled; 252 SDL_Rect currentCliprect; 253 SDL_Rect currentViewport; 254 int currentViewportRotation; 255 bool viewportDirty; 256 Float4X4 identity; 257 int currentVertexBuffer; 258 bool issueBatch; 259} D3D12_RenderData; 260 261// Define D3D GUIDs here so we don't have to include uuid.lib. 262 263#ifdef HAVE_GCC_DIAGNOSTIC_PRAGMA 264#pragma GCC diagnostic push 265#pragma GCC diagnostic ignored "-Wunused-const-variable" 266#endif 267 268static const GUID SDL_IID_IDXGIFactory6 = { 0xc1b6694f, 0xff09, 0x44a9, { 0xb0, 0x3c, 0x77, 0x90, 0x0a, 0x0a, 0x1d, 0x17 } }; 269static const GUID SDL_IID_IDXGIAdapter4 = { 0x3c8d99d1, 0x4fbf, 0x4181, { 0xa8, 0x2c, 0xaf, 0x66, 0xbf, 0x7b, 0xd2, 0x4e } }; 270static const GUID SDL_IID_IDXGIDevice1 = { 0x77db970f, 0x6276, 0x48ba, { 0xba, 0x28, 0x07, 0x01, 0x43, 0xb4, 0x39, 0x2c } }; 271static const GUID SDL_IID_ID3D12Device1 = { 0x77acce80, 0x638e, 0x4e65, { 0x88, 0x95, 0xc1, 0xf2, 0x33, 0x86, 0x86, 0x3e } }; 272static const GUID SDL_IID_IDXGISwapChain4 = { 0x3D585D5A, 0xBD4A, 0x489E, { 0xB1, 0xF4, 0x3D, 0xBC, 0xB6, 0x45, 0x2F, 0xFB } }; 273static const GUID SDL_IID_IDXGIDebug1 = { 0xc5a05f0c, 0x16f2, 0x4adf, { 0x9f, 0x4d, 0xa8, 0xc4, 0xd5, 0x8a, 0xc5, 0x50 } }; 274static const GUID SDL_IID_IDXGIInfoQueue = { 0xD67441C7, 0x672A, 0x476f, { 0x9E, 0x82, 0xCD, 0x55, 0xB4, 0x49, 0x49, 0xCE } }; 275static const GUID SDL_IID_ID3D12Debug = { 0x344488b7, 0x6846, 0x474b, { 0xb9, 0x89, 0xf0, 0x27, 0x44, 0x82, 0x45, 0xe0 } }; 276static const GUID SDL_DXGI_DEBUG_ALL = { 0xe48ae283, 0xda80, 0x490b, { 0x87, 0xe6, 0x43, 0xe9, 0xa9, 0xcf, 0xda, 0x8 } }; 277static const GUID SDL_IID_ID3D12CommandQueue = { 0x0ec870a6, 0x5d7e, 0x4c22, { 0x8c, 0xfc, 0x5b, 0xaa, 0xe0, 0x76, 0x16, 0xed } }; 278static const GUID SDL_IID_ID3D12DescriptorHeap = { 0x8efb471d, 0x616c, 0x4f49, { 0x90, 0xf7, 0x12, 0x7b, 0xb7, 0x63, 0xfa, 0x51 } }; 279static const GUID SDL_IID_ID3D12CommandAllocator = { 0x6102dee4, 0xaf59, 0x4b09, { 0xb9, 0x99, 0xb4, 0x4d, 0x73, 0xf0, 0x9b, 0x24 } }; 280static const GUID SDL_IID_ID3D12GraphicsCommandList2 = { 0x38C3E585, 0xFF17, 0x412C, { 0x91, 0x50, 0x4F, 0xC6, 0xF9, 0xD7, 0x2A, 0x28 } }; 281static const GUID SDL_IID_ID3D12Fence = { 0x0a753dcf, 0xc4d8, 0x4b91, { 0xad, 0xf6, 0xbe, 0x5a, 0x60, 0xd9, 0x5a, 0x76 } }; 282static const GUID SDL_IID_ID3D12Resource = { 0x696442be, 0xa72e, 0x4059, { 0xbc, 0x79, 0x5b, 0x5c, 0x98, 0x04, 0x0f, 0xad } }; 283static const GUID SDL_IID_ID3D12RootSignature = { 0xc54a6b66, 0x72df, 0x4ee8, { 0x8b, 0xe5, 0xa9, 0x46, 0xa1, 0x42, 0x92, 0x14 } }; 284static const GUID SDL_IID_ID3D12PipelineState = { 0x765a30f3, 0xf624, 0x4c6f, { 0xa8, 0x28, 0xac, 0xe9, 0x48, 0x62, 0x24, 0x45 } }; 285static const GUID SDL_IID_ID3D12Heap = { 0x6b3b2502, 0x6e51, 0x45b3, { 0x90, 0xee, 0x98, 0x84, 0x26, 0x5e, 0x8d, 0xf3 } }; 286static const GUID SDL_IID_ID3D12InfoQueue = { 0x0742a90b, 0xc387, 0x483f, { 0xb9, 0x46, 0x30, 0xa7, 0xe4, 0xe6, 0x14, 0x58 } }; 287 288#ifdef HAVE_GCC_DIAGNOSTIC_PRAGMA 289#pragma GCC diagnostic pop 290#endif 291 292static bool D3D12_UpdateTextureInternal(D3D12_RenderData *rendererData, ID3D12Resource *texture, int plane, int x, int y, int w, int h, const void *pixels, int pitch, D3D12_RESOURCE_STATES *resourceState); 293 294static UINT D3D12_Align(UINT location, UINT alignment) 295{ 296 return (location + (alignment - 1)) & ~(alignment - 1); 297} 298 299static const struct { 300 SDL_PixelFormat sdl; 301 DXGI_FORMAT unorm; 302 DXGI_FORMAT srgb; 303} dxgi_format_map[] = { 304 { SDL_PIXELFORMAT_ARGB8888, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM_SRGB }, 305 { SDL_PIXELFORMAT_ABGR8888, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB }, 306 { SDL_PIXELFORMAT_XRGB8888, DXGI_FORMAT_B8G8R8X8_UNORM, DXGI_FORMAT_B8G8R8X8_UNORM_SRGB }, 307 { SDL_PIXELFORMAT_ABGR2101010, DXGI_FORMAT_R10G10B10A2_UNORM, DXGI_FORMAT_R10G10B10A2_UNORM }, 308 { SDL_PIXELFORMAT_RGBA64_FLOAT, DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_R16G16B16A16_FLOAT }, 309 { SDL_PIXELFORMAT_RGB565, DXGI_FORMAT_B5G6R5_UNORM, DXGI_FORMAT_B5G6R5_UNORM }, 310 { SDL_PIXELFORMAT_ARGB1555, DXGI_FORMAT_B5G5R5A1_UNORM, DXGI_FORMAT_B5G5R5A1_UNORM }, 311 { SDL_PIXELFORMAT_ARGB4444, DXGI_FORMAT_B4G4R4A4_UNORM, DXGI_FORMAT_B4G4R4A4_UNORM } 312}; 313 314static SDL_PixelFormat D3D12_DXGIFormatToSDLPixelFormat(DXGI_FORMAT dxgiFormat) 315{ 316 for (int i = 0; i < SDL_arraysize(dxgi_format_map); i++) { 317 if (dxgi_format_map[i].unorm == dxgiFormat || 318 dxgi_format_map[i].srgb == dxgiFormat) { 319 return dxgi_format_map[i].sdl; 320 } 321 } 322 return SDL_PIXELFORMAT_UNKNOWN; 323} 324 325static DXGI_FORMAT SDLPixelFormatToDXGITextureFormat(SDL_PixelFormat format, Uint32 output_colorspace) 326{ 327 switch (format) { 328 case SDL_PIXELFORMAT_INDEX8: 329 case SDL_PIXELFORMAT_YV12: 330 case SDL_PIXELFORMAT_IYUV: 331 return DXGI_FORMAT_R8_UNORM; 332 case SDL_PIXELFORMAT_NV12: 333 case SDL_PIXELFORMAT_NV21: 334 return DXGI_FORMAT_NV12; 335 case SDL_PIXELFORMAT_P010: 336 return DXGI_FORMAT_P010; 337 default: 338 for (int i = 0; i < SDL_arraysize(dxgi_format_map); i++) { 339 if (dxgi_format_map[i].sdl == format) { 340 if (output_colorspace == SDL_COLORSPACE_SRGB_LINEAR) { 341 return dxgi_format_map[i].srgb; 342 } else { 343 return dxgi_format_map[i].unorm; 344 } 345 } 346 } 347 return DXGI_FORMAT_UNKNOWN; 348 } 349} 350 351static DXGI_FORMAT SDLPixelFormatToDXGIMainResourceViewFormat(SDL_PixelFormat format, Uint32 colorspace) 352{ 353 switch (format) { 354 case SDL_PIXELFORMAT_INDEX8: 355 case SDL_PIXELFORMAT_YV12: 356 case SDL_PIXELFORMAT_IYUV: 357 case SDL_PIXELFORMAT_NV12: // For the Y texture 358 case SDL_PIXELFORMAT_NV21: // For the Y texture 359 return DXGI_FORMAT_R8_UNORM; 360 case SDL_PIXELFORMAT_P010: // For the Y texture 361 return DXGI_FORMAT_R16_UNORM; 362 default: 363 return SDLPixelFormatToDXGITextureFormat(format, colorspace); 364 } 365} 366 367static void D3D12_DestroyTexture(SDL_Renderer *renderer, SDL_Texture *texture); 368 369static void D3D12_ReleaseAll(SDL_Renderer *renderer) 370{ 371 D3D12_RenderData *data = (D3D12_RenderData *)renderer->internal; 372 373 SDL_PropertiesID props = SDL_GetRendererProperties(renderer); 374 SDL_SetPointerProperty(props, SDL_PROP_RENDERER_D3D12_DEVICE_POINTER, NULL); 375 SDL_SetPointerProperty(props, SDL_PROP_RENDERER_D3D12_COMMAND_QUEUE_POINTER, NULL); 376 377 // Release all textures 378 for (SDL_Texture *texture = renderer->textures; texture; texture = texture->next) { 379 D3D12_DestroyTexture(renderer, texture); 380 } 381 382 // Release/reset everything else 383 if (data) { 384 int i; 385 386#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 387 D3D_SAFE_RELEASE(data->dxgiFactory); 388 D3D_SAFE_RELEASE(data->dxgiAdapter); 389 D3D_SAFE_RELEASE(data->swapChain); 390#endif 391 D3D_SAFE_RELEASE(data->d3dDevice); 392 D3D_SAFE_RELEASE(data->debugInterface); 393 D3D_SAFE_RELEASE(data->commandQueue); 394 D3D_SAFE_RELEASE(data->commandList); 395 D3D_SAFE_RELEASE(data->rtvDescriptorHeap); 396 D3D_SAFE_RELEASE(data->textureRTVDescriptorHeap); 397 D3D_SAFE_RELEASE(data->srvDescriptorHeap); 398 D3D_SAFE_RELEASE(data->samplerDescriptorHeap); 399 SDL_zeroa(data->samplers_created); 400 D3D_SAFE_RELEASE(data->fence); 401 402 for (i = 0; i < SDL_D3D12_NUM_BUFFERS; ++i) { 403 D3D_SAFE_RELEASE(data->commandAllocators[i]); 404 D3D_SAFE_RELEASE(data->renderTargets[i]); 405 } 406 407 if (data->pipelineStateCount > 0) { 408 for (i = 0; i < data->pipelineStateCount; ++i) { 409 D3D_SAFE_RELEASE(data->pipelineStates[i].pipelineState); 410 } 411 SDL_free(data->pipelineStates); 412 data->pipelineStates = NULL; 413 data->pipelineStateCount = 0; 414 } 415 416 for (i = 0; i < NUM_ROOTSIGS; ++i) { 417 D3D_SAFE_RELEASE(data->rootSignatures[i]); 418 } 419 420 for (i = 0; i < SDL_D3D12_NUM_VERTEX_BUFFERS; ++i) { 421 D3D_SAFE_RELEASE(data->vertexBuffers[i].resource); 422 data->vertexBuffers[i].size = 0; 423 } 424 425 data->swapEffect = (DXGI_SWAP_EFFECT)0; 426 data->swapFlags = 0; 427 data->currentRenderTargetView.ptr = 0; 428 data->numCurrentShaderResources = 0; 429 data->currentShaderResource.ptr = 0; 430 data->numCurrentShaderSamplers = 0; 431 data->currentShaderSampler.ptr = 0; 432 433#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 434 // Check for any leaks if in debug mode 435 if (data->dxgiDebug) { 436 DXGI_DEBUG_RLO_FLAGS rloFlags = (DXGI_DEBUG_RLO_FLAGS)(DXGI_DEBUG_RLO_DETAIL | DXGI_DEBUG_RLO_IGNORE_INTERNAL); 437 IDXGIDebug_ReportLiveObjects(data->dxgiDebug, SDL_DXGI_DEBUG_ALL, rloFlags); 438 D3D_SAFE_RELEASE(data->dxgiDebug); 439 } 440#endif 441 442 /* Unload the D3D libraries. This should be done last, in order 443 * to prevent IUnknown::Release() calls from crashing. 444 */ 445 if (data->hD3D12Mod) { 446 SDL_UnloadObject(data->hD3D12Mod); 447 data->hD3D12Mod = NULL; 448 } 449 if (data->hDXGIMod) { 450 SDL_UnloadObject(data->hDXGIMod); 451 data->hDXGIMod = NULL; 452 } 453 } 454} 455 456static D3D12_GPU_DESCRIPTOR_HANDLE D3D12_CPUtoGPUHandle(ID3D12DescriptorHeap *heap, D3D12_CPU_DESCRIPTOR_HANDLE CPUHandle) 457{ 458 D3D12_CPU_DESCRIPTOR_HANDLE CPUHeapStart; 459 D3D12_GPU_DESCRIPTOR_HANDLE GPUHandle; 460 SIZE_T offset; 461 462 // Calculate the correct offset into the heap 463 D3D_CALL_RET(heap, GetCPUDescriptorHandleForHeapStart, &CPUHeapStart); 464 offset = CPUHandle.ptr - CPUHeapStart.ptr; 465 466 D3D_CALL_RET(heap, GetGPUDescriptorHandleForHeapStart, &GPUHandle); 467 GPUHandle.ptr += offset; 468 469 return GPUHandle; 470} 471 472static void D3D12_WaitForGPU(D3D12_RenderData *data) 473{ 474 if (data->commandQueue && data->fence && data->fenceEvent) { 475 ID3D12CommandQueue_Signal(data->commandQueue, data->fence, data->fenceValue); 476 if (ID3D12Fence_GetCompletedValue(data->fence) < data->fenceValue) { 477 ID3D12Fence_SetEventOnCompletion(data->fence, 478 data->fenceValue, 479 data->fenceEvent); 480 WaitForSingleObjectEx(data->fenceEvent, INFINITE, FALSE); 481 } 482 483 data->fenceValue++; 484 } 485} 486 487static D3D12_CPU_DESCRIPTOR_HANDLE D3D12_GetCurrentRenderTargetView(SDL_Renderer *renderer) 488{ 489 D3D12_RenderData *data = (D3D12_RenderData *)renderer->internal; 490 D3D12_CPU_DESCRIPTOR_HANDLE rtvDescriptor; 491 492 if (data->textureRenderTarget) { 493 return data->textureRenderTarget->mainTextureRenderTargetView; 494 } 495 496 SDL_zero(rtvDescriptor); 497 D3D_CALL_RET(data->rtvDescriptorHeap, GetCPUDescriptorHandleForHeapStart, &rtvDescriptor); 498 rtvDescriptor.ptr += data->currentBackBufferIndex * data->rtvDescriptorSize; 499 return rtvDescriptor; 500} 501 502static void D3D12_TransitionResource(D3D12_RenderData *data, 503 ID3D12Resource *resource, 504 D3D12_RESOURCE_STATES beforeState, 505 D3D12_RESOURCE_STATES afterState) 506{ 507 D3D12_RESOURCE_BARRIER barrier; 508 509 if (beforeState != afterState) { 510 SDL_zero(barrier); 511 barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; 512 barrier.Transition.pResource = resource; 513 barrier.Transition.StateBefore = beforeState; 514 barrier.Transition.StateAfter = afterState; 515 barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; 516 517 ID3D12GraphicsCommandList2_ResourceBarrier(data->commandList, 1, &barrier); 518 } 519} 520 521static void D3D12_ResetCommandList(D3D12_RenderData *data) 522{ 523 int i; 524 ID3D12DescriptorHeap *rootDescriptorHeaps[] = { data->srvDescriptorHeap, data->samplerDescriptorHeap }; 525 ID3D12CommandAllocator *commandAllocator = data->commandAllocators[data->currentBackBufferIndex]; 526 527 ID3D12CommandAllocator_Reset(commandAllocator); 528 ID3D12GraphicsCommandList2_Reset(data->commandList, commandAllocator, NULL); 529 data->currentPipelineState = NULL; 530 data->currentVertexBuffer = 0; 531 data->issueBatch = false; 532 data->cliprectDirty = true; 533 data->viewportDirty = true; 534 data->currentRenderTargetView.ptr = 0; 535 // FIXME should we also clear currentSampler.ptr and currentRenderTargetView.ptr ? (and use D3D12_InvalidateCachedState() instead) 536 537 // Release any upload buffers that were inflight 538 for (i = 0; i < data->currentUploadBuffer; ++i) { 539 D3D_SAFE_RELEASE(data->uploadBuffers[i]); 540 } 541 data->currentUploadBuffer = 0; 542 543 ID3D12GraphicsCommandList2_SetDescriptorHeaps(data->commandList, 2, rootDescriptorHeaps); 544} 545 546static HRESULT D3D12_IssueBatch(D3D12_RenderData *data) 547{ 548 HRESULT result = S_OK; 549 550 // Issue the command list 551 result = ID3D12GraphicsCommandList2_Close(data->commandList); 552 if (FAILED(result)) { 553 WIN_SetErrorFromHRESULT("D3D12_IssueBatch", result); 554 return result; 555 } 556 ID3D12CommandQueue_ExecuteCommandLists(data->commandQueue, 1, (ID3D12CommandList *const *)&data->commandList); 557 558 D3D12_WaitForGPU(data); 559 560 D3D12_ResetCommandList(data); 561 562 return result; 563} 564 565#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES) 566 567static void D3D12_GDKSuspendRenderer(SDL_Renderer *renderer) 568{ 569 D3D12_RenderData *data = (D3D12_RenderData *)renderer->internal; 570 data->commandQueue->SuspendX(0); 571} 572 573static void D3D12_GDKResumeRenderer(SDL_Renderer *renderer) 574{ 575 D3D12_RenderData *data = (D3D12_RenderData *)renderer->internal; 576 data->commandQueue->ResumeX(); 577} 578 579#endif 580 581static void D3D12_DestroyRenderer(SDL_Renderer *renderer) 582{ 583 D3D12_RenderData *data = (D3D12_RenderData *)renderer->internal; 584 if (data) { 585 D3D12_WaitForGPU(data); 586 D3D12_ReleaseAll(renderer); 587 SDL_free(data); 588 } 589} 590 591static D3D12_BLEND GetBlendFunc(SDL_BlendFactor factor) 592{ 593 switch (factor) { 594 case SDL_BLENDFACTOR_ZERO: 595 return D3D12_BLEND_ZERO; 596 case SDL_BLENDFACTOR_ONE: 597 return D3D12_BLEND_ONE; 598 case SDL_BLENDFACTOR_SRC_COLOR: 599 return D3D12_BLEND_SRC_COLOR; 600 case SDL_BLENDFACTOR_ONE_MINUS_SRC_COLOR: 601 return D3D12_BLEND_INV_SRC_COLOR; 602 case SDL_BLENDFACTOR_SRC_ALPHA: 603 return D3D12_BLEND_SRC_ALPHA; 604 case SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: 605 return D3D12_BLEND_INV_SRC_ALPHA; 606 case SDL_BLENDFACTOR_DST_COLOR: 607 return D3D12_BLEND_DEST_COLOR; 608 case SDL_BLENDFACTOR_ONE_MINUS_DST_COLOR: 609 return D3D12_BLEND_INV_DEST_COLOR; 610 case SDL_BLENDFACTOR_DST_ALPHA: 611 return D3D12_BLEND_DEST_ALPHA; 612 case SDL_BLENDFACTOR_ONE_MINUS_DST_ALPHA: 613 return D3D12_BLEND_INV_DEST_ALPHA; 614 default: 615 return (D3D12_BLEND)0; 616 } 617} 618 619static D3D12_BLEND_OP GetBlendEquation(SDL_BlendOperation operation) 620{ 621 switch (operation) { 622 case SDL_BLENDOPERATION_ADD: 623 return D3D12_BLEND_OP_ADD; 624 case SDL_BLENDOPERATION_SUBTRACT: 625 return D3D12_BLEND_OP_SUBTRACT; 626 case SDL_BLENDOPERATION_REV_SUBTRACT: 627 return D3D12_BLEND_OP_REV_SUBTRACT; 628 case SDL_BLENDOPERATION_MINIMUM: 629 return D3D12_BLEND_OP_MIN; 630 case SDL_BLENDOPERATION_MAXIMUM: 631 return D3D12_BLEND_OP_MAX; 632 default: 633 return (D3D12_BLEND_OP)0; 634 } 635} 636 637static void D3D12_CreateBlendState(SDL_Renderer *renderer, SDL_BlendMode blendMode, D3D12_BLEND_DESC *outBlendDesc) 638{ 639 SDL_BlendFactor srcColorFactor = SDL_GetBlendModeSrcColorFactor(blendMode); 640 SDL_BlendFactor srcAlphaFactor = SDL_GetBlendModeSrcAlphaFactor(blendMode); 641 SDL_BlendOperation colorOperation = SDL_GetBlendModeColorOperation(blendMode); 642 SDL_BlendFactor dstColorFactor = SDL_GetBlendModeDstColorFactor(blendMode); 643 SDL_BlendFactor dstAlphaFactor = SDL_GetBlendModeDstAlphaFactor(blendMode); 644 SDL_BlendOperation alphaOperation = SDL_GetBlendModeAlphaOperation(blendMode); 645 646 SDL_zerop(outBlendDesc); 647 outBlendDesc->AlphaToCoverageEnable = FALSE; 648 outBlendDesc->IndependentBlendEnable = FALSE; 649 outBlendDesc->RenderTarget[0].BlendEnable = TRUE; 650 outBlendDesc->RenderTarget[0].SrcBlend = GetBlendFunc(srcColorFactor); 651 outBlendDesc->RenderTarget[0].DestBlend = GetBlendFunc(dstColorFactor); 652 outBlendDesc->RenderTarget[0].BlendOp = GetBlendEquation(colorOperation); 653 outBlendDesc->RenderTarget[0].SrcBlendAlpha = GetBlendFunc(srcAlphaFactor); 654 outBlendDesc->RenderTarget[0].DestBlendAlpha = GetBlendFunc(dstAlphaFactor); 655 outBlendDesc->RenderTarget[0].BlendOpAlpha = GetBlendEquation(alphaOperation); 656 outBlendDesc->RenderTarget[0].RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL; 657} 658 659static D3D12_PipelineState *D3D12_CreatePipelineState(SDL_Renderer *renderer, 660 D3D12_Shader shader, 661 SDL_BlendMode blendMode, 662 D3D12_PRIMITIVE_TOPOLOGY_TYPE topology, 663 DXGI_FORMAT rtvFormat) 664{ 665 const D3D12_INPUT_ELEMENT_DESC vertexDesc[] = { 666 { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, 667 { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 8, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, 668 { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 16, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, 669 }; 670 D3D12_RenderData *data = (D3D12_RenderData *)renderer->internal; 671 D3D12_GRAPHICS_PIPELINE_STATE_DESC pipelineDesc; 672 ID3D12PipelineState *pipelineState = NULL; 673 D3D12_PipelineState *pipelineStates; 674 HRESULT result = S_OK; 675 676 SDL_zero(pipelineDesc); 677 pipelineDesc.pRootSignature = data->rootSignatures[D3D12_GetRootSignatureType(shader)]; 678 D3D12_GetVertexShader(shader, &pipelineDesc.VS); 679 D3D12_GetPixelShader(shader, &pipelineDesc.PS); 680 D3D12_CreateBlendState(renderer, blendMode, &pipelineDesc.BlendState); 681 pipelineDesc.SampleMask = 0xffffffff; 682 683 pipelineDesc.RasterizerState.AntialiasedLineEnable = FALSE; 684 pipelineDesc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE; 685 pipelineDesc.RasterizerState.DepthBias = 0; 686 pipelineDesc.RasterizerState.DepthBiasClamp = 0.0f; 687 pipelineDesc.RasterizerState.DepthClipEnable = TRUE; 688 pipelineDesc.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID; 689 pipelineDesc.RasterizerState.FrontCounterClockwise = FALSE; 690 pipelineDesc.RasterizerState.MultisampleEnable = FALSE; 691 pipelineDesc.RasterizerState.SlopeScaledDepthBias = 0.0f; 692 693 pipelineDesc.InputLayout.pInputElementDescs = vertexDesc; 694 pipelineDesc.InputLayout.NumElements = 3; 695 696 pipelineDesc.PrimitiveTopologyType = topology; 697 698 pipelineDesc.NumRenderTargets = 1; 699 pipelineDesc.RTVFormats[0] = rtvFormat; 700 pipelineDesc.SampleDesc.Count = 1; 701 pipelineDesc.SampleDesc.Quality = 0; 702 703 result = ID3D12Device1_CreateGraphicsPipelineState(data->d3dDevice, 704 &pipelineDesc, 705 D3D_GUID(SDL_IID_ID3D12PipelineState), 706 (void **)&pipelineState); 707 if (FAILED(result)) { 708 WIN_SetErrorFromHRESULT("ID3D12Device::CreateGraphicsPipelineState", result); 709 return NULL; 710 } 711 712 pipelineStates = (D3D12_PipelineState *)SDL_realloc(data->pipelineStates, (data->pipelineStateCount + 1) * sizeof(*pipelineStates)); 713 if (!pipelineStates) { 714 D3D_SAFE_RELEASE(pipelineState); 715 return NULL; 716 } 717 718 pipelineStates[data->pipelineStateCount].shader = shader; 719 pipelineStates[data->pipelineStateCount].blendMode = blendMode; 720 pipelineStates[data->pipelineStateCount].topology = topology; 721 pipelineStates[data->pipelineStateCount].rtvFormat = rtvFormat; 722 pipelineStates[data->pipelineStateCount].pipelineState = pipelineState; 723 data->pipelineStates = pipelineStates; 724 ++data->pipelineStateCount; 725 726 return &pipelineStates[data->pipelineStateCount - 1]; 727} 728 729static HRESULT D3D12_CreateVertexBuffer(D3D12_RenderData *data, size_t vbidx, size_t size) 730{ 731 D3D12_HEAP_PROPERTIES vbufferHeapProps; 732 D3D12_RESOURCE_DESC vbufferDesc; 733 HRESULT result; 734 735 D3D_SAFE_RELEASE(data->vertexBuffers[vbidx].resource); 736 737 SDL_zero(vbufferHeapProps); 738 vbufferHeapProps.Type = D3D12_HEAP_TYPE_UPLOAD; 739 vbufferHeapProps.CreationNodeMask = 1; 740 vbufferHeapProps.VisibleNodeMask = 1; 741 742 SDL_zero(vbufferDesc); 743 vbufferDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; 744 vbufferDesc.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT; 745 vbufferDesc.Width = size; 746 vbufferDesc.Height = 1; 747 vbufferDesc.DepthOrArraySize = 1; 748 vbufferDesc.MipLevels = 1; 749 vbufferDesc.Format = DXGI_FORMAT_UNKNOWN; 750 vbufferDesc.SampleDesc.Count = 1; 751 vbufferDesc.SampleDesc.Quality = 0; 752 vbufferDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; 753 vbufferDesc.Flags = D3D12_RESOURCE_FLAG_NONE; 754 755 result = ID3D12Device1_CreateCommittedResource(data->d3dDevice, 756 &vbufferHeapProps, 757 D3D12_HEAP_FLAG_NONE, 758 &vbufferDesc, 759 D3D12_RESOURCE_STATE_GENERIC_READ, 760 NULL, 761 D3D_GUID(SDL_IID_ID3D12Resource), 762 (void **)&data->vertexBuffers[vbidx].resource); 763 764 if (FAILED(result)) { 765 WIN_SetErrorFromHRESULT("ID3D12Device::CreatePlacedResource [vertex buffer]", result); 766 return result; 767 } 768 769 data->vertexBuffers[vbidx].view.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(data->vertexBuffers[vbidx].resource); 770 data->vertexBuffers[vbidx].view.StrideInBytes = sizeof(D3D12_VertexPositionColor); 771 data->vertexBuffers[vbidx].size = size; 772 773 return result; 774} 775 776// Create resources that depend on the device. 777static HRESULT D3D12_CreateDeviceResources(SDL_Renderer *renderer) 778{ 779#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 780 typedef HRESULT (WINAPI *pfnCreateDXGIFactory2)(UINT flags, REFIID riid, void **ppFactory); 781 pfnCreateDXGIFactory2 pCreateDXGIFactory2; 782 PFN_D3D12_CREATE_DEVICE pD3D12CreateDevice; 783#endif 784 typedef HANDLE (WINAPI *pfnCreateEventExW)(LPSECURITY_ATTRIBUTES lpEventAttributes, LPCWSTR lpName, DWORD dwFlags, DWORD dwDesiredAccess); 785 pfnCreateEventExW pCreateEventExW; 786 787 D3D12_RenderData *data = (D3D12_RenderData *)renderer->internal; 788 ID3D12Device *d3dDevice = NULL; 789 HRESULT result = S_OK; 790 UINT creationFlags = 0; 791 int i; 792 bool createDebug; 793 794 D3D12_COMMAND_QUEUE_DESC queueDesc; 795 D3D12_DESCRIPTOR_HEAP_DESC descriptorHeapDesc; 796 ID3D12DescriptorHeap *rootDescriptorHeaps[2]; 797 798 // See if we need debug interfaces 799 createDebug = SDL_GetHintBoolean(SDL_HINT_RENDER_DIRECT3D11_DEBUG, false); 800 801#ifdef SDL_PLATFORM_GDK 802 pCreateEventExW = CreateEventExW; 803#else 804 // CreateEventExW() arrived in Vista, so we need to load it with GetProcAddress for XP. 805 { 806 HMODULE kernel32 = GetModuleHandle(TEXT("kernel32.dll")); 807 pCreateEventExW = NULL; 808 if (kernel32) { 809 pCreateEventExW = (pfnCreateEventExW)GetProcAddress(kernel32, "CreateEventExW"); 810 } 811 } 812#endif 813 if (!pCreateEventExW) { 814 result = E_FAIL; 815 goto done; 816 } 817 818#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 819 data->hDXGIMod = SDL_LoadObject("dxgi.dll"); 820 if (!data->hDXGIMod) { 821 result = E_FAIL; 822 goto done; 823 } 824 825 pCreateDXGIFactory2 = (pfnCreateDXGIFactory2)SDL_LoadFunction(data->hDXGIMod, "CreateDXGIFactory2"); 826 if (!pCreateDXGIFactory2) { 827 result = E_FAIL; 828 goto done; 829 } 830 831 data->hD3D12Mod = SDL_LoadObject("D3D12.dll"); 832 if (!data->hD3D12Mod) { 833 result = E_FAIL; 834 goto done; 835 } 836 837 pD3D12CreateDevice = (PFN_D3D12_CREATE_DEVICE)SDL_LoadFunction(data->hD3D12Mod, "D3D12CreateDevice"); 838 if (!pD3D12CreateDevice) { 839 result = E_FAIL; 840 goto done; 841 } 842 843 if (createDebug) { 844 PFN_D3D12_GET_DEBUG_INTERFACE D3D12GetDebugInterfaceFunc; 845 846 D3D12GetDebugInterfaceFunc = (PFN_D3D12_GET_DEBUG_INTERFACE)SDL_LoadFunction(data->hD3D12Mod, "D3D12GetDebugInterface"); 847 if (!D3D12GetDebugInterfaceFunc) { 848 result = E_FAIL; 849 goto done; 850 } 851 if (SUCCEEDED(D3D12GetDebugInterfaceFunc(D3D_GUID(SDL_IID_ID3D12Debug), (void **)&data->debugInterface))) { 852 ID3D12Debug_EnableDebugLayer(data->debugInterface); 853 } 854 } 855#endif // !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 856 857#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES) 858 result = D3D12_XBOX_CreateDevice(&d3dDevice, createDebug); 859 if (FAILED(result)) { 860 // SDL Error is set by D3D12_XBOX_CreateDevice 861 goto done; 862 } 863#else 864 if (createDebug) { 865#ifdef __IDXGIInfoQueue_INTERFACE_DEFINED__ 866 IDXGIInfoQueue *dxgiInfoQueue = NULL; 867 pfnCreateDXGIFactory2 DXGIGetDebugInterfaceFunc; 868 869 // If the debug hint is set, also create the DXGI factory in debug mode 870 DXGIGetDebugInterfaceFunc = (pfnCreateDXGIFactory2)SDL_LoadFunction(data->hDXGIMod, "DXGIGetDebugInterface1"); 871 if (!DXGIGetDebugInterfaceFunc) { 872 result = E_FAIL; 873 goto done; 874 } 875 876 result = DXGIGetDebugInterfaceFunc(0, D3D_GUID(SDL_IID_IDXGIDebug1), (void **)&data->dxgiDebug); 877 if (FAILED(result)) { 878 WIN_SetErrorFromHRESULT("DXGIGetDebugInterface1", result); 879 goto done; 880 } 881 882 result = DXGIGetDebugInterfaceFunc(0, D3D_GUID(SDL_IID_IDXGIInfoQueue), (void **)&dxgiInfoQueue); 883 if (FAILED(result)) { 884 WIN_SetErrorFromHRESULT("DXGIGetDebugInterface1", result); 885 goto done; 886 } 887 888 IDXGIInfoQueue_SetBreakOnSeverity(dxgiInfoQueue, SDL_DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_ERROR, TRUE); 889 IDXGIInfoQueue_SetBreakOnSeverity(dxgiInfoQueue, SDL_DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_CORRUPTION, TRUE); 890 D3D_SAFE_RELEASE(dxgiInfoQueue); 891#endif // __IDXGIInfoQueue_INTERFACE_DEFINED__ 892 creationFlags = DXGI_CREATE_FACTORY_DEBUG; 893 } 894 895 result = pCreateDXGIFactory2(creationFlags, D3D_GUID(SDL_IID_IDXGIFactory6), (void **)&data->dxgiFactory); 896 if (FAILED(result)) { 897 WIN_SetErrorFromHRESULT("CreateDXGIFactory", result); 898 goto done; 899 } 900 901 // Prefer a high performance adapter if there are multiple choices 902 result = IDXGIFactory6_EnumAdapterByGpuPreference(data->dxgiFactory, 903 0, 904 DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, 905 D3D_GUID(SDL_IID_IDXGIAdapter4), 906 (void **)&data->dxgiAdapter); 907 if (FAILED(result)) { 908 WIN_SetErrorFromHRESULT("IDXGIFactory6::EnumAdapterByGPUPreference", result); 909 goto done; 910 } 911 912 result = pD3D12CreateDevice((IUnknown *)data->dxgiAdapter, 913 D3D_FEATURE_LEVEL_11_0, // Request minimum feature level 11.0 for maximum compatibility 914 D3D_GUID(SDL_IID_ID3D12Device1), 915 (void **)&d3dDevice); 916 if (FAILED(result)) { 917 WIN_SetErrorFromHRESULT("D3D12CreateDevice", result); 918 goto done; 919 } 920 921 // Setup the info queue if in debug mode 922 if (createDebug) { 923 ID3D12InfoQueue *infoQueue = NULL; 924 D3D12_MESSAGE_SEVERITY severities[] = { D3D12_MESSAGE_SEVERITY_INFO }; 925 D3D12_INFO_QUEUE_FILTER filter; 926 927 result = ID3D12Device1_QueryInterface(d3dDevice, D3D_GUID(SDL_IID_ID3D12InfoQueue), (void **)&infoQueue); 928 if (FAILED(result)) { 929 WIN_SetErrorFromHRESULT("ID3D12Device to ID3D12InfoQueue", result); 930 goto done; 931 } 932 933 SDL_zero(filter); 934 filter.DenyList.NumSeverities = 1; 935 filter.DenyList.pSeverityList = severities; 936 ID3D12InfoQueue_PushStorageFilter(infoQueue, &filter); 937 938 ID3D12InfoQueue_SetBreakOnSeverity(infoQueue, D3D12_MESSAGE_SEVERITY_ERROR, TRUE); 939 ID3D12InfoQueue_SetBreakOnSeverity(infoQueue, D3D12_MESSAGE_SEVERITY_CORRUPTION, TRUE); 940 941 D3D_SAFE_RELEASE(infoQueue); 942 } 943#endif // !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 944 945 result = ID3D12Device_QueryInterface(d3dDevice, D3D_GUID(SDL_IID_ID3D12Device1), (void **)&data->d3dDevice); 946 if (FAILED(result)) { 947 WIN_SetErrorFromHRESULT("ID3D12Device to ID3D12Device1", result); 948 goto done; 949 } 950 951 // Create a command queue 952 SDL_zero(queueDesc); 953 queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; 954 queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; 955 956 result = ID3D12Device1_CreateCommandQueue(data->d3dDevice, 957 &queueDesc, 958 D3D_GUID(SDL_IID_ID3D12CommandQueue), 959 (void **)&data->commandQueue); 960 if (FAILED(result)) { 961 WIN_SetErrorFromHRESULT("ID3D12Device::CreateCommandQueue", result); 962 goto done; 963 } 964 965 // Create the descriptor heaps for the render target view, texture SRVs, and samplers 966 SDL_zero(descriptorHeapDesc); 967 descriptorHeapDesc.NumDescriptors = SDL_D3D12_NUM_BUFFERS; 968 descriptorHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; 969 result = ID3D12Device1_CreateDescriptorHeap(data->d3dDevice, 970 &descriptorHeapDesc, 971 D3D_GUID(SDL_IID_ID3D12DescriptorHeap), 972 (void **)&data->rtvDescriptorHeap); 973 if (FAILED(result)) { 974 WIN_SetErrorFromHRESULT("ID3D12Device::CreateDescriptorHeap [rtv]", result); 975 goto done; 976 } 977 data->rtvDescriptorSize = ID3D12Device1_GetDescriptorHandleIncrementSize(d3dDevice, D3D12_DESCRIPTOR_HEAP_TYPE_RTV); 978 979 descriptorHeapDesc.NumDescriptors = SDL_D3D12_MAX_NUM_TEXTURES; 980 result = ID3D12Device1_CreateDescriptorHeap(data->d3dDevice, 981 &descriptorHeapDesc, 982 D3D_GUID(SDL_IID_ID3D12DescriptorHeap), 983 (void **)&data->textureRTVDescriptorHeap); 984 if (FAILED(result)) { 985 WIN_SetErrorFromHRESULT("ID3D12Device::CreateDescriptorHeap [texture rtv]", result); 986 goto done; 987 } 988 989 SDL_zero(descriptorHeapDesc); 990 descriptorHeapDesc.NumDescriptors = SDL_D3D12_MAX_NUM_TEXTURES; 991 descriptorHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; 992 descriptorHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; 993 result = ID3D12Device1_CreateDescriptorHeap(data->d3dDevice, 994 &descriptorHeapDesc, 995 D3D_GUID(SDL_IID_ID3D12DescriptorHeap), 996 (void **)&data->srvDescriptorHeap); 997 if (FAILED(result)) { 998 WIN_SetErrorFromHRESULT("ID3D12Device::CreateDescriptorHeap [srv]", result); 999 goto done; 1000 } 1001 rootDescriptorHeaps[0] = data->srvDescriptorHeap; 1002 data->srvDescriptorSize = ID3D12Device1_GetDescriptorHandleIncrementSize(d3dDevice, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); 1003 1004 SDL_zero(descriptorHeapDesc); 1005 descriptorHeapDesc.NumDescriptors = SDL_arraysize(data->samplers); 1006 descriptorHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER; 1007 descriptorHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; 1008 result = ID3D12Device1_CreateDescriptorHeap(data->d3dDevice, 1009 &descriptorHeapDesc, 1010 D3D_GUID(SDL_IID_ID3D12DescriptorHeap), 1011 (void **)&data->samplerDescriptorHeap); 1012 if (FAILED(result)) { 1013 WIN_SetErrorFromHRESULT("ID3D12Device::CreateDescriptorHeap [sampler]", result); 1014 goto done; 1015 } 1016 rootDescriptorHeaps[1] = data->samplerDescriptorHeap; 1017 data->samplerDescriptorSize = ID3D12Device1_GetDescriptorHandleIncrementSize(d3dDevice, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER); 1018 1019 // Create a command allocator for each back buffer 1020 for (i = 0; i < SDL_D3D12_NUM_BUFFERS; ++i) { 1021 result = ID3D12Device1_CreateCommandAllocator(data->d3dDevice, 1022 D3D12_COMMAND_LIST_TYPE_DIRECT, 1023 D3D_GUID(SDL_IID_ID3D12CommandAllocator), 1024 (void **)&data->commandAllocators[i]); 1025 if (FAILED(result)) { 1026 WIN_SetErrorFromHRESULT("ID3D12Device::CreateCommandAllocator", result); 1027 goto done; 1028 } 1029 } 1030 1031 // Create the command list 1032 result = ID3D12Device1_CreateCommandList(data->d3dDevice, 1033 0, 1034 D3D12_COMMAND_LIST_TYPE_DIRECT, 1035 data->commandAllocators[0], 1036 NULL, 1037 D3D_GUID(SDL_IID_ID3D12GraphicsCommandList2), 1038 (void **)&data->commandList); 1039 if (FAILED(result)) { 1040 WIN_SetErrorFromHRESULT("ID3D12Device::CreateCommandList", result); 1041 goto done; 1042 } 1043 1044 // Set the descriptor heaps to the correct initial value 1045 ID3D12GraphicsCommandList2_SetDescriptorHeaps(data->commandList, 2, rootDescriptorHeaps); 1046 1047 // Create the fence and fence event 1048 result = ID3D12Device_CreateFence(data->d3dDevice, 1049 data->fenceValue, 1050 D3D12_FENCE_FLAG_NONE, 1051 D3D_GUID(SDL_IID_ID3D12Fence), 1052 (void **)&data->fence); 1053 if (FAILED(result)) { 1054 WIN_SetErrorFromHRESULT("ID3D12Device::CreateFence", result); 1055 goto done; 1056 } 1057 1058 data->fenceValue++; 1059 1060 data->fenceEvent = pCreateEventExW(NULL, NULL, 0, EVENT_MODIFY_STATE | SYNCHRONIZE); 1061 if (!data->fenceEvent) { 1062 WIN_SetErrorFromHRESULT("CreateEventEx", result); 1063 goto done; 1064 } 1065 1066 // Create all the root signatures 1067 for (i = 0; i < NUM_ROOTSIGS; ++i) { 1068 D3D12_SHADER_BYTECODE rootSigData; 1069 D3D12_GetRootSignatureData((D3D12_RootSignature)i, &rootSigData); 1070 result = ID3D12Device1_CreateRootSignature(data->d3dDevice, 1071 0, 1072 rootSigData.pShaderBytecode, 1073 rootSigData.BytecodeLength, 1074 D3D_GUID(SDL_IID_ID3D12RootSignature), 1075 (void **)&data->rootSignatures[i]); 1076 if (FAILED(result)) { 1077 WIN_SetErrorFromHRESULT("ID3D12Device::CreateRootSignature", result); 1078 goto done; 1079 } 1080 } 1081 1082 { 1083 const SDL_BlendMode defaultBlendModes[] = { 1084 SDL_BLENDMODE_BLEND, 1085 }; 1086 const DXGI_FORMAT defaultRTVFormats[] = { 1087 DXGI_FORMAT_B8G8R8A8_UNORM, 1088 }; 1089 int j, k, l; 1090 1091 // Create a few default pipeline state objects, to verify that this renderer will work 1092 for (i = 0; i < NUM_SHADERS; ++i) { 1093 for (j = 0; j < SDL_arraysize(defaultBlendModes); ++j) { 1094 for (k = D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT; k < D3D12_PRIMITIVE_TOPOLOGY_TYPE_PATCH; ++k) { 1095 for (l = 0; l < SDL_arraysize(defaultRTVFormats); ++l) { 1096 if (!D3D12_CreatePipelineState(renderer, (D3D12_Shader)i, defaultBlendModes[j], (D3D12_PRIMITIVE_TOPOLOGY_TYPE)k, defaultRTVFormats[l])) { 1097 // D3D12_CreatePipelineState will set the SDL error, if it fails 1098 result = E_FAIL; 1099 goto done; 1100 } 1101 } 1102 } 1103 } 1104 } 1105 } 1106 1107 // Create default vertex buffers 1108 for (i = 0; i < SDL_D3D12_NUM_VERTEX_BUFFERS; ++i) { 1109 D3D12_CreateVertexBuffer(data, i, D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT); 1110 } 1111 1112 // Create samplers to use when drawing textures: 1113 D3D_CALL_RET(data->samplerDescriptorHeap, GetCPUDescriptorHandleForHeapStart, &data->samplers[0]); 1114 for (i = 0; i < SDL_arraysize(data->samplers); ++i) { 1115 data->samplers[i].ptr = data->samplers[0].ptr + i * data->samplerDescriptorSize; 1116 } 1117 1118 // Initialize the pool allocator for SRVs 1119 for (i = 0; i < SDL_D3D12_MAX_NUM_TEXTURES; ++i) { 1120 data->srvPoolNodes[i].index = (SIZE_T)i; 1121 if (i != SDL_D3D12_MAX_NUM_TEXTURES - 1) { 1122 data->srvPoolNodes[i].next = &data->srvPoolNodes[i + 1]; 1123 } 1124 } 1125 data->srvPoolHead = &data->srvPoolNodes[0]; 1126 1127 SDL_PropertiesID props = SDL_GetRendererProperties(renderer); 1128 SDL_SetPointerProperty(props, SDL_PROP_RENDERER_D3D12_DEVICE_POINTER, data->d3dDevice); 1129 SDL_SetPointerProperty(props, SDL_PROP_RENDERER_D3D12_COMMAND_QUEUE_POINTER, data->commandQueue); 1130 1131done: 1132 D3D_SAFE_RELEASE(d3dDevice); 1133 return result; 1134} 1135 1136static DXGI_MODE_ROTATION D3D12_GetCurrentRotation(void) 1137{ 1138 // FIXME 1139 return DXGI_MODE_ROTATION_IDENTITY; 1140} 1141 1142static BOOL D3D12_IsDisplayRotated90Degrees(DXGI_MODE_ROTATION rotation) 1143{ 1144 switch (rotation) { 1145 case DXGI_MODE_ROTATION_ROTATE90: 1146 case DXGI_MODE_ROTATION_ROTATE270: 1147 return TRUE; 1148 default: 1149 return FALSE; 1150 } 1151} 1152 1153static int D3D12_GetRotationForCurrentRenderTarget(SDL_Renderer *renderer) 1154{ 1155 D3D12_RenderData *data = (D3D12_RenderData *)renderer->internal; 1156 if (data->textureRenderTarget) { 1157 return DXGI_MODE_ROTATION_IDENTITY; 1158 } else { 1159 return data->rotation; 1160 } 1161} 1162 1163static bool D3D12_GetViewportAlignedD3DRect(SDL_Renderer *renderer, const SDL_Rect *sdlRect, D3D12_RECT *outRect, BOOL includeViewportOffset) 1164{ 1165 D3D12_RenderData *data = (D3D12_RenderData *)renderer->internal; 1166 const int rotation = D3D12_GetRotationForCurrentRenderTarget(renderer); 1167 const SDL_Rect *viewport = &data->currentViewport; 1168 1169 switch (rotation) { 1170 case DXGI_MODE_ROTATION_IDENTITY: 1171 outRect->left = sdlRect->x; 1172 outRect->right = (LONG)sdlRect->x + sdlRect->w; 1173 outRect->top = sdlRect->y; 1174 outRect->bottom = (LONG)sdlRect->y + sdlRect->h; 1175 if (includeViewportOffset) { 1176 outRect->left += viewport->x; 1177 outRect->right += viewport->x; 1178 outRect->top += viewport->y; 1179 outRect->bottom += viewport->y; 1180 } 1181 break; 1182 case DXGI_MODE_ROTATION_ROTATE270: 1183 outRect->left = sdlRect->y; 1184 outRect->right = (LONG)sdlRect->y + sdlRect->h; 1185 outRect->top = viewport->w - sdlRect->x - sdlRect->w; 1186 outRect->bottom = viewport->w - sdlRect->x; 1187 break; 1188 case DXGI_MODE_ROTATION_ROTATE180: 1189 outRect->left = viewport->w - sdlRect->x - sdlRect->w; 1190 outRect->right = viewport->w - sdlRect->x; 1191 outRect->top = viewport->h - sdlRect->y - sdlRect->h; 1192 outRect->bottom = viewport->h - sdlRect->y; 1193 break; 1194 case DXGI_MODE_ROTATION_ROTATE90: 1195 outRect->left = viewport->h - sdlRect->y - sdlRect->h; 1196 outRect->right = viewport->h - sdlRect->y; 1197 outRect->top = sdlRect->x; 1198 outRect->bottom = (LONG)sdlRect->x + sdlRect->h; 1199 break; 1200 default: 1201 return SDL_SetError("The physical display is in an unknown or unsupported rotation"); 1202 } 1203 return true; 1204} 1205 1206#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 1207static HRESULT D3D12_CreateSwapChain(SDL_Renderer *renderer, int w, int h) 1208{ 1209 D3D12_RenderData *data = (D3D12_RenderData *)renderer->internal; 1210 IDXGISwapChain1 *swapChain = NULL; 1211 HRESULT result = S_OK; 1212 1213 // Create a swap chain using the same adapter as the existing Direct3D device. 1214 DXGI_SWAP_CHAIN_DESC1 swapChainDesc; 1215 SDL_zero(swapChainDesc); 1216 swapChainDesc.Width = w; 1217 swapChainDesc.Height = h; 1218 switch (renderer->output_colorspace) { 1219 case SDL_COLORSPACE_SRGB_LINEAR: 1220 swapChainDesc.Format = DXGI_FORMAT_R16G16B16A16_FLOAT; 1221 data->renderTargetFormat = DXGI_FORMAT_R16G16B16A16_FLOAT; 1222 break; 1223 case SDL_COLORSPACE_HDR10: 1224 swapChainDesc.Format = DXGI_FORMAT_R10G10B10A2_UNORM; 1225 data->renderTargetFormat = DXGI_FORMAT_R10G10B10A2_UNORM; 1226 break; 1227 default: 1228 swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // This is the most common swap chain format. 1229 data->renderTargetFormat = DXGI_FORMAT_B8G8R8A8_UNORM; 1230 break; 1231 } 1232 swapChainDesc.Stereo = FALSE; 1233 swapChainDesc.SampleDesc.Count = 1; // Don't use multi-sampling. 1234 swapChainDesc.SampleDesc.Quality = 0; 1235 swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; 1236 swapChainDesc.BufferCount = 2; // Use double-buffering to minimize latency. 1237 if (WIN_IsWindows8OrGreater()) { 1238 swapChainDesc.Scaling = DXGI_SCALING_NONE; 1239 } else { 1240 swapChainDesc.Scaling = DXGI_SCALING_STRETCH; 1241 } 1242 swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // All Windows Store apps must use this SwapEffect. 1243 swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT | // To support SetMaximumFrameLatency 1244 DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING; // To support presenting with allow tearing on 1245 1246 HWND hwnd = (HWND)SDL_GetPointerProperty(SDL_GetWindowProperties(renderer->window), SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL); 1247 if (!hwnd) { 1248 SDL_SetError("Couldn't get window handle"); 1249 result = E_FAIL; 1250 goto done; 1251 } 1252 1253 result = IDXGIFactory2_CreateSwapChainForHwnd(data->dxgiFactory, 1254 (IUnknown *)data->commandQueue, 1255 hwnd, 1256 &swapChainDesc, 1257 NULL, 1258 NULL, // Allow on all displays. 1259 &swapChain); 1260 if (FAILED(result)) { 1261 WIN_SetErrorFromHRESULT("IDXGIFactory2::CreateSwapChainForHwnd", result); 1262 goto done; 1263 } 1264 1265 IDXGIFactory6_MakeWindowAssociation(data->dxgiFactory, hwnd, DXGI_MWA_NO_WINDOW_CHANGES); 1266 1267 result = IDXGISwapChain1_QueryInterface(swapChain, D3D_GUID(SDL_IID_IDXGISwapChain4), (void **)&data->swapChain); 1268 if (FAILED(result)) { 1269 WIN_SetErrorFromHRESULT("IDXGISwapChain1::QueryInterface", result); 1270 goto done; 1271 } 1272 1273 /* Ensure that the swapchain does not queue more than one frame at a time. This both reduces latency 1274 * and ensures that the application will only render after each VSync, minimizing power consumption. 1275 */ 1276 result = IDXGISwapChain4_SetMaximumFrameLatency(data->swapChain, 1); 1277 if (FAILED(result)) { 1278 WIN_SetErrorFromHRESULT("IDXGISwapChain4::SetMaximumFrameLatency", result); 1279 goto done; 1280 } 1281 1282 data->swapEffect = swapChainDesc.SwapEffect; 1283 data->swapFlags = swapChainDesc.Flags; 1284 1285 UINT colorspace_support = 0; 1286 DXGI_COLOR_SPACE_TYPE colorspace; 1287 switch (renderer->output_colorspace) { 1288 case SDL_COLORSPACE_SRGB_LINEAR: 1289 colorspace = DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709; 1290 break; 1291 case SDL_COLORSPACE_HDR10: 1292 colorspace = DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020; 1293 break; 1294 default: 1295 // sRGB 1296 colorspace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; 1297 break; 1298 } 1299 if (SUCCEEDED(IDXGISwapChain3_CheckColorSpaceSupport(data->swapChain, colorspace, &colorspace_support)) && 1300 (colorspace_support & DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT)) { 1301 result = IDXGISwapChain3_SetColorSpace1(data->swapChain, colorspace); 1302 if (FAILED(result)) { 1303 WIN_SetErrorFromHRESULT("IDXGISwapChain3::SetColorSpace1", result); 1304 goto done; 1305 } 1306 } else { 1307 // Not the default, we're not going to be able to present in this colorspace 1308 SDL_SetError("Unsupported output colorspace"); 1309 result = DXGI_ERROR_UNSUPPORTED; 1310 } 1311 1312 SDL_SetPointerProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_D3D12_SWAPCHAIN_POINTER, data->swapChain); 1313 1314done: 1315 D3D_SAFE_RELEASE(swapChain); 1316 return result; 1317} 1318#endif 1319 1320// Initialize all resources that change when the window's size changes. 1321static HRESULT D3D12_CreateWindowSizeDependentResources(SDL_Renderer *renderer) 1322{ 1323 D3D12_RenderData *data = (D3D12_RenderData *)renderer->internal; 1324 HRESULT result = S_OK; 1325 int i, w, h; 1326 1327 D3D12_RENDER_TARGET_VIEW_DESC rtvDesc; 1328 D3D12_CPU_DESCRIPTOR_HANDLE rtvDescriptor; 1329 1330 // Release resources in the current command list 1331 D3D12_IssueBatch(data); 1332 ID3D12GraphicsCommandList2_OMSetRenderTargets(data->commandList, 0, NULL, FALSE, NULL); 1333 1334 // Release render targets 1335 for (i = 0; i < SDL_D3D12_NUM_BUFFERS; ++i) { 1336 D3D_SAFE_RELEASE(data->renderTargets[i]); 1337 } 1338 1339 /* The width and height of the swap chain must be based on the display's 1340 * non-rotated size. 1341 */ 1342 SDL_GetWindowSizeInPixels(renderer->window, &w, &h); 1343 data->rotation = D3D12_GetCurrentRotation(); 1344 if (D3D12_IsDisplayRotated90Degrees(data->rotation)) { 1345 int tmp = w; 1346 w = h; 1347 h = tmp; 1348 } 1349 1350#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 1351 if (data->swapChain) { 1352 // If the swap chain already exists, resize it. 1353 result = IDXGISwapChain_ResizeBuffers(data->swapChain, 1354 0, 1355 w, h, 1356 DXGI_FORMAT_UNKNOWN, 1357 data->swapFlags); 1358 if (FAILED(result)) { 1359 WIN_SetErrorFromHRESULT("IDXGISwapChain::ResizeBuffers", result); 1360 goto done; 1361 } 1362 } else { 1363 result = D3D12_CreateSwapChain(renderer, w, h); 1364 if (FAILED(result) || !data->swapChain) { 1365 goto done; 1366 } 1367 } 1368 1369 // Set the proper rotation for the swap chain. 1370 if (WIN_IsWindows8OrGreater()) { 1371 if (data->swapEffect == DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL) { 1372 result = IDXGISwapChain4_SetRotation(data->swapChain, data->rotation); // NOLINT(clang-analyzer-core.NullDereference) 1373 if (FAILED(result)) { 1374 WIN_SetErrorFromHRESULT("IDXGISwapChain4::SetRotation", result); 1375 goto done; 1376 } 1377 } 1378 } 1379#endif // !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 1380 1381 // Get each back buffer render target and create render target views 1382 for (i = 0; i < SDL_D3D12_NUM_BUFFERS; ++i) { 1383#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES) 1384 result = D3D12_XBOX_CreateBackBufferTarget(data->d3dDevice, renderer->window->w, renderer->window->h, (void **)&data->renderTargets[i]); 1385 if (FAILED(result)) { 1386 WIN_SetErrorFromHRESULT("D3D12_XBOX_CreateBackBufferTarget", result); 1387 goto done; 1388 } 1389#else 1390 result = IDXGISwapChain4_GetBuffer(data->swapChain, // NOLINT(clang-analyzer-core.NullDereference) 1391 i, 1392 D3D_GUID(SDL_IID_ID3D12Resource), 1393 (void **)&data->renderTargets[i]); 1394 if (FAILED(result)) { 1395 WIN_SetErrorFromHRESULT("IDXGISwapChain4::GetBuffer", result); 1396 goto done; 1397 } 1398#endif 1399 1400 SDL_zero(rtvDesc); 1401 rtvDesc.Format = data->renderTargetFormat; 1402 rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; 1403 1404 SDL_zero(rtvDescriptor); 1405 D3D_CALL_RET(data->rtvDescriptorHeap, GetCPUDescriptorHandleForHeapStart, &rtvDescriptor); 1406 rtvDescriptor.ptr += i * data->rtvDescriptorSize; 1407 ID3D12Device1_CreateRenderTargetView(data->d3dDevice, data->renderTargets[i], &rtvDesc, rtvDescriptor); 1408 } 1409 1410 // Set back buffer index to current buffer 1411#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES) 1412 data->currentBackBufferIndex = 0; 1413#else 1414 data->currentBackBufferIndex = IDXGISwapChain4_GetCurrentBackBufferIndex(data->swapChain); 1415#endif 1416 1417 /* Set the swap chain target immediately, so that a target is always set 1418 * even before we get to SetDrawState. Without this it's possible to hit 1419 * null references in places like ReadPixels! 1420 */ 1421 data->currentRenderTargetView = D3D12_GetCurrentRenderTargetView(renderer); 1422 ID3D12GraphicsCommandList2_OMSetRenderTargets(data->commandList, 1, &data->currentRenderTargetView, FALSE, NULL); 1423 D3D12_TransitionResource(data, 1424 data->renderTargets[data->currentBackBufferIndex], 1425 D3D12_RESOURCE_STATE_PRESENT, 1426 D3D12_RESOURCE_STATE_RENDER_TARGET); 1427 1428 data->viewportDirty = true; 1429 1430#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES) 1431 D3D12_XBOX_StartFrame(data->d3dDevice, &data->frameToken); 1432#endif 1433 1434done: 1435 return result; 1436} 1437 1438static bool D3D12_HandleDeviceLost(SDL_Renderer *renderer) 1439{ 1440 bool recovered = false; 1441 1442 D3D12_ReleaseAll(renderer); 1443 1444 if (SUCCEEDED(D3D12_CreateDeviceResources(renderer)) && 1445 SUCCEEDED(D3D12_CreateWindowSizeDependentResources(renderer))) { 1446 recovered = true; 1447 } else { 1448 SDL_LogError(SDL_LOG_CATEGORY_RENDER, "Renderer couldn't recover from device lost: %s", SDL_GetError()); 1449 D3D12_ReleaseAll(renderer); 1450 } 1451 1452 // Let the application know that the device has been reset or lost 1453 SDL_Event event; 1454 SDL_zero(event); 1455 event.type = recovered ? SDL_EVENT_RENDER_DEVICE_RESET : SDL_EVENT_RENDER_DEVICE_LOST; 1456 event.render.windowID = SDL_GetWindowID(SDL_GetRenderWindow(renderer)); 1457 SDL_PushEvent(&event); 1458 1459 return recovered; 1460} 1461 1462// This method is called when the window's size changes. 1463static HRESULT D3D12_UpdateForWindowSizeChange(SDL_Renderer *renderer) 1464{ 1465 D3D12_RenderData *data = (D3D12_RenderData *)renderer->internal; 1466 // If the GPU has previous work, wait for it to be done first 1467 D3D12_WaitForGPU(data); 1468 return D3D12_CreateWindowSizeDependentResources(renderer); 1469} 1470 1471static void D3D12_WindowEvent(SDL_Renderer *renderer, const SDL_WindowEvent *event) 1472{ 1473 D3D12_RenderData *data = (D3D12_RenderData *)renderer->internal; 1474 1475 if (event->type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED) { 1476 data->pixelSizeChanged = true; 1477 } 1478} 1479 1480static bool D3D12_SupportsBlendMode(SDL_Renderer *renderer, SDL_BlendMode blendMode) 1481{ 1482 SDL_BlendFactor srcColorFactor = SDL_GetBlendModeSrcColorFactor(blendMode); 1483 SDL_BlendFactor srcAlphaFactor = SDL_GetBlendModeSrcAlphaFactor(blendMode); 1484 SDL_BlendOperation colorOperation = SDL_GetBlendModeColorOperation(blendMode); 1485 SDL_BlendFactor dstColorFactor = SDL_GetBlendModeDstColorFactor(blendMode); 1486 SDL_BlendFactor dstAlphaFactor = SDL_GetBlendModeDstAlphaFactor(blendMode); 1487 SDL_BlendOperation alphaOperation = SDL_GetBlendModeAlphaOperation(blendMode); 1488 1489 if (!GetBlendFunc(srcColorFactor) || !GetBlendFunc(srcAlphaFactor) || 1490 !GetBlendEquation(colorOperation) || 1491 !GetBlendFunc(dstColorFactor) || !GetBlendFunc(dstAlphaFactor) || 1492 !GetBlendEquation(alphaOperation)) { 1493 return false; 1494 } 1495 return true; 1496} 1497 1498static SIZE_T D3D12_GetAvailableSRVIndex(SDL_Renderer *renderer) 1499{ 1500 D3D12_RenderData *rendererData = (D3D12_RenderData *)renderer->internal; 1501 if (rendererData->srvPoolHead) { 1502 SIZE_T index = rendererData->srvPoolHead->index; 1503 rendererData->srvPoolHead = (D3D12_SRVPoolNode *)(rendererData->srvPoolHead->next); 1504 return index; 1505 } else { 1506 SDL_SetError("[d3d12] Cannot allocate more than %d textures!", SDL_D3D12_MAX_NUM_TEXTURES); 1507 return SDL_D3D12_MAX_NUM_TEXTURES + 1; 1508 } 1509} 1510 1511static void D3D12_FreeSRVIndex(SDL_Renderer *renderer, SIZE_T index) 1512{ 1513 D3D12_RenderData *rendererData = (D3D12_RenderData *)renderer->internal; 1514 rendererData->srvPoolNodes[index].next = rendererData->srvPoolHead; 1515 rendererData->srvPoolHead = &rendererData->srvPoolNodes[index]; 1516} 1517 1518static bool GetTextureProperty(SDL_PropertiesID props, const char *name, ID3D12Resource **texture) 1519{ 1520 IUnknown *unknown = (IUnknown *)SDL_GetPointerProperty(props, name, NULL); 1521 if (unknown) { 1522#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES) 1523 HRESULT result = unknown->QueryInterface(D3D_GUID(SDL_IID_ID3D12Resource), (void **)texture); 1524#else 1525 HRESULT result = IUnknown_QueryInterface(unknown, D3D_GUID(SDL_IID_ID3D12Resource), (void **)texture); 1526#endif 1527 if (FAILED(result)) { 1528 return WIN_SetErrorFromHRESULT(name, result); 1529 } 1530 } 1531 return true; 1532} 1533 1534static bool D3D12_CreatePalette(SDL_Renderer *renderer, SDL_TexturePalette *palette) 1535{ 1536 D3D12_RenderData *data = (D3D12_RenderData *)renderer->internal; 1537 D3D12_PaletteData *palettedata = (D3D12_PaletteData *)SDL_calloc(1, sizeof(*palettedata)); 1538 if (!palettedata) { 1539 return false; 1540 } 1541 palette->internal = palettedata; 1542 1543 if (!data->d3dDevice) { 1544 return SDL_SetError("Device lost and couldn't be recovered"); 1545 } 1546 1547 D3D12_RESOURCE_DESC textureDesc; 1548 SDL_zero(textureDesc); 1549 textureDesc.Width = 256; 1550 textureDesc.Height = 1; 1551 textureDesc.MipLevels = 1; 1552 textureDesc.DepthOrArraySize = 1; 1553 textureDesc.Format = SDLPixelFormatToDXGITextureFormat(SDL_PIXELFORMAT_RGBA32, renderer->output_colorspace); 1554 textureDesc.SampleDesc.Count = 1; 1555 textureDesc.SampleDesc.Quality = 0; 1556 textureDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; 1557 textureDesc.Flags = D3D12_RESOURCE_FLAG_NONE; 1558 1559 D3D12_HEAP_PROPERTIES heapProps; 1560 SDL_zero(heapProps); 1561 heapProps.Type = D3D12_HEAP_TYPE_DEFAULT; 1562 heapProps.CreationNodeMask = 1; 1563 heapProps.VisibleNodeMask = 1; 1564 1565 HRESULT result = ID3D12Device1_CreateCommittedResource(data->d3dDevice, 1566 &heapProps, 1567 D3D12_HEAP_FLAG_NONE, 1568 &textureDesc, 1569 D3D12_RESOURCE_STATE_COPY_DEST, 1570 NULL, 1571 D3D_GUID(SDL_IID_ID3D12Resource), 1572 (void **)&palettedata->texture); 1573 if (FAILED(result)) { 1574 return WIN_SetErrorFromHRESULT("ID3D12Device::CreateCommittedResource [texture]", result); 1575 } 1576 palettedata->resourceState = D3D12_RESOURCE_STATE_COPY_DEST; 1577 1578 D3D12_SHADER_RESOURCE_VIEW_DESC resourceViewDesc; 1579 SDL_zero(resourceViewDesc); 1580 resourceViewDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; 1581 resourceViewDesc.Format = SDLPixelFormatToDXGIMainResourceViewFormat(SDL_PIXELFORMAT_RGBA32, renderer->output_colorspace); 1582 resourceViewDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; 1583 resourceViewDesc.Texture2D.MipLevels = textureDesc.MipLevels; 1584 1585 D3D_CALL_RET(data->srvDescriptorHeap, GetCPUDescriptorHandleForHeapStart, &palettedata->resourceView); 1586 palettedata->SRVIndex = D3D12_GetAvailableSRVIndex(renderer); 1587 palettedata->resourceView.ptr += palettedata->SRVIndex * data->srvDescriptorSize; 1588 1589 ID3D12Device1_CreateShaderResourceView(data->d3dDevice, 1590 palettedata->texture, 1591 &resourceViewDesc, 1592 palettedata->resourceView); 1593 1594 return true; 1595} 1596 1597static bool D3D12_UpdatePalette(SDL_Renderer *renderer, SDL_TexturePalette *palette, int ncolors, SDL_Color *colors) 1598{ 1599 D3D12_RenderData *data = (D3D12_RenderData *)renderer->internal; 1600 D3D12_PaletteData *palettedata = (D3D12_PaletteData *)palette->internal; 1601 1602 return D3D12_UpdateTextureInternal(data, palettedata->texture, 0, 0, 0, ncolors, 1, colors, ncolors * sizeof(*colors), &palettedata->resourceState); 1603} 1604 1605static void D3D12_DestroyPalette(SDL_Renderer *renderer, SDL_TexturePalette *palette) 1606{ 1607 D3D12_PaletteData *palettedata = (D3D12_PaletteData *)palette->internal; 1608 1609 if (palettedata) { 1610 D3D_SAFE_RELEASE(palettedata->texture); 1611 D3D12_FreeSRVIndex(renderer, palettedata->SRVIndex); 1612 SDL_free(palettedata); 1613 } 1614} 1615 1616static bool D3D12_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props) 1617{ 1618 D3D12_RenderData *rendererData = (D3D12_RenderData *)renderer->internal; 1619 D3D12_TextureData *textureData; 1620 HRESULT result; 1621 DXGI_FORMAT textureFormat = SDLPixelFormatToDXGITextureFormat(texture->format, renderer->output_colorspace); 1622 D3D12_RESOURCE_DESC textureDesc; 1623 D3D12_HEAP_PROPERTIES heapProps; 1624 D3D12_SHADER_RESOURCE_VIEW_DESC resourceViewDesc; 1625 1626 if (!rendererData->d3dDevice) { 1627 return SDL_SetError("Device lost and couldn't be recovered"); 1628 } 1629 1630 if (textureFormat == DXGI_FORMAT_UNKNOWN) { 1631 return SDL_SetError("%s, An unsupported SDL pixel format (0x%x) was specified", SDL_FUNCTION, texture->format); 1632 } 1633 1634 textureData = (D3D12_TextureData *)SDL_calloc(1, sizeof(*textureData)); 1635 if (!textureData) { 1636 return false; 1637 } 1638 1639 texture->internal = textureData; 1640 textureData->mainTextureFormat = textureFormat; 1641 1642 SDL_zero(textureDesc); 1643 textureDesc.Width = texture->w; 1644 textureDesc.Height = texture->h; 1645 textureDesc.MipLevels = 1; 1646 textureDesc.DepthOrArraySize = 1; 1647 textureDesc.Format = textureFormat; 1648 textureDesc.SampleDesc.Count = 1; 1649 textureDesc.SampleDesc.Quality = 0; 1650 textureDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; 1651 textureDesc.Flags = D3D12_RESOURCE_FLAG_NONE; 1652 1653 // NV12 textures must have even width and height 1654 if (texture->format == SDL_PIXELFORMAT_NV12 || 1655 texture->format == SDL_PIXELFORMAT_NV21 || 1656 texture->format == SDL_PIXELFORMAT_P010) { 1657 textureDesc.Width = (textureDesc.Width + 1) & ~1; 1658 textureDesc.Height = (textureDesc.Height + 1) & ~1; 1659 } 1660 textureData->w = (int)textureDesc.Width; 1661 textureData->h = (int)textureDesc.Height; 1662 1663 if (texture->access == SDL_TEXTUREACCESS_TARGET) { 1664 textureDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; 1665 } 1666 1667 SDL_zero(heapProps); 1668 heapProps.Type = D3D12_HEAP_TYPE_DEFAULT; 1669 heapProps.CreationNodeMask = 1; 1670 heapProps.VisibleNodeMask = 1; 1671 1672 if (!GetTextureProperty(create_props, SDL_PROP_TEXTURE_CREATE_D3D12_TEXTURE_POINTER, &textureData->mainTexture)) { 1673 return false; 1674 } 1675 if (!textureData->mainTexture) { 1676 result = ID3D12Device1_CreateCommittedResource(rendererData->d3dDevice, 1677 &heapProps, 1678 D3D12_HEAP_FLAG_NONE, 1679 &textureDesc, 1680 D3D12_RESOURCE_STATE_COPY_DEST, 1681 NULL, 1682 D3D_GUID(SDL_IID_ID3D12Resource), 1683 (void **)&textureData->mainTexture); 1684 if (FAILED(result)) { 1685 return WIN_SetErrorFromHRESULT("ID3D12Device::CreateCommittedResource [texture]", result); 1686 } 1687 } 1688 textureData->mainResourceState = D3D12_RESOURCE_STATE_COPY_DEST; 1689 SDL_SetPointerProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_D3D12_TEXTURE_POINTER, textureData->mainTexture); 1690 1691#ifdef SDL_HAVE_YUV 1692 if (texture->format == SDL_PIXELFORMAT_YV12 || 1693 texture->format == SDL_PIXELFORMAT_IYUV) { 1694 textureData->yuv = true; 1695 1696 textureDesc.Width = (textureDesc.Width + 1) / 2; 1697 textureDesc.Height = (textureDesc.Height + 1) / 2; 1698 1699 if (!GetTextureProperty(create_props, SDL_PROP_TEXTURE_CREATE_D3D12_TEXTURE_U_POINTER, &textureData->mainTextureU)) { 1700 return false; 1701 } 1702 if (!textureData->mainTextureU) { 1703 result = ID3D12Device1_CreateCommittedResource(rendererData->d3dDevice, 1704 &heapProps, 1705 D3D12_HEAP_FLAG_NONE, 1706 &textureDesc, 1707 D3D12_RESOURCE_STATE_COPY_DEST, 1708 NULL, 1709 D3D_GUID(SDL_IID_ID3D12Resource), 1710 (void **)&textureData->mainTextureU); 1711 if (FAILED(result)) { 1712 return WIN_SetErrorFromHRESULT("ID3D12Device::CreateCommittedResource [texture]", result); 1713 } 1714 } 1715 textureData->mainResourceStateU = D3D12_RESOURCE_STATE_COPY_DEST; 1716 SDL_SetPointerProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_D3D12_TEXTURE_U_POINTER, textureData->mainTextureU); 1717 1718 if (!GetTextureProperty(create_props, SDL_PROP_TEXTURE_CREATE_D3D12_TEXTURE_V_POINTER, &textureData->mainTextureV)) { 1719 return false; 1720 } 1721 if (!textureData->mainTextureV) { 1722 result = ID3D12Device1_CreateCommittedResource(rendererData->d3dDevice, 1723 &heapProps, 1724 D3D12_HEAP_FLAG_NONE, 1725 &textureDesc, 1726 D3D12_RESOURCE_STATE_COPY_DEST, 1727 NULL, 1728 D3D_GUID(SDL_IID_ID3D12Resource), 1729 (void **)&textureData->mainTextureV); 1730 if (FAILED(result)) { 1731 return WIN_SetErrorFromHRESULT("ID3D12Device::CreateCommittedResource [texture]", result); 1732 } 1733 } 1734 textureData->mainResourceStateV = D3D12_RESOURCE_STATE_COPY_DEST; 1735 SDL_SetPointerProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_D3D12_TEXTURE_V_POINTER, textureData->mainTextureV); 1736 1737 textureData->YCbCr_matrix = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, 8); 1738 if (!textureData->YCbCr_matrix) { 1739 return SDL_SetError("Unsupported YUV colorspace"); 1740 } 1741 } 1742 1743 if (texture->format == SDL_PIXELFORMAT_NV12 || 1744 texture->format == SDL_PIXELFORMAT_NV21 || 1745 texture->format == SDL_PIXELFORMAT_P010) { 1746 int bits_per_pixel; 1747 1748 textureData->nv12 = true; 1749 1750 switch (texture->format) { 1751 case SDL_PIXELFORMAT_P010: 1752 bits_per_pixel = 10; 1753 break; 1754 default: 1755 bits_per_pixel = 8; 1756 break; 1757 } 1758 textureData->YCbCr_matrix = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, bits_per_pixel); 1759 if (!textureData->YCbCr_matrix) { 1760 return SDL_SetError("Unsupported YUV colorspace"); 1761 } 1762 } 1763#endif // SDL_HAVE_YUV 1764 SDL_zero(resourceViewDesc); 1765 resourceViewDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; 1766 resourceViewDesc.Format = SDLPixelFormatToDXGIMainResourceViewFormat(texture->format, renderer->output_colorspace); 1767 resourceViewDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; 1768 resourceViewDesc.Texture2D.MipLevels = textureDesc.MipLevels; 1769 1770 D3D_CALL_RET(rendererData->srvDescriptorHeap, GetCPUDescriptorHandleForHeapStart, &textureData->mainTextureResourceView); 1771 textureData->mainSRVIndex = D3D12_GetAvailableSRVIndex(renderer); 1772 textureData->mainTextureResourceView.ptr += textureData->mainSRVIndex * rendererData->srvDescriptorSize; 1773 1774 ID3D12Device1_CreateShaderResourceView(rendererData->d3dDevice, 1775 textureData->mainTexture, 1776 &resourceViewDesc, 1777 textureData->mainTextureResourceView); 1778 1779#ifdef SDL_HAVE_YUV 1780 if (textureData->yuv) { 1781 D3D_CALL_RET(rendererData->srvDescriptorHeap, GetCPUDescriptorHandleForHeapStart, &textureData->mainTextureResourceViewU); 1782 textureData->mainSRVIndexU = D3D12_GetAvailableSRVIndex(renderer); 1783 textureData->mainTextureResourceViewU.ptr += textureData->mainSRVIndexU * rendererData->srvDescriptorSize; 1784 ID3D12Device1_CreateShaderResourceView(rendererData->d3dDevice, 1785 textureData->mainTextureU, 1786 &resourceViewDesc, 1787 textureData->mainTextureResourceViewU); 1788 1789 D3D_CALL_RET(rendererData->srvDescriptorHeap, GetCPUDescriptorHandleForHeapStart, &textureData->mainTextureResourceViewV); 1790 textureData->mainSRVIndexV = D3D12_GetAvailableSRVIndex(renderer); 1791 textureData->mainTextureResourceViewV.ptr += textureData->mainSRVIndexV * rendererData->srvDescriptorSize; 1792 ID3D12Device1_CreateShaderResourceView(rendererData->d3dDevice, 1793 textureData->mainTextureV, 1794 &resourceViewDesc, 1795 textureData->mainTextureResourceViewV); 1796 } 1797 1798 if (textureData->nv12) { 1799 D3D12_SHADER_RESOURCE_VIEW_DESC nvResourceViewDesc = resourceViewDesc; 1800 1801 if (texture->format == SDL_PIXELFORMAT_NV12 || texture->format == SDL_PIXELFORMAT_NV21) { 1802 nvResourceViewDesc.Format = DXGI_FORMAT_R8G8_UNORM; 1803 } else if (texture->format == SDL_PIXELFORMAT_P010) { 1804 nvResourceViewDesc.Format = DXGI_FORMAT_R16G16_UNORM; 1805 } 1806 nvResourceViewDesc.Texture2D.PlaneSlice = 1; 1807 1808 D3D_CALL_RET(rendererData->srvDescriptorHeap, GetCPUDescriptorHandleForHeapStart, &textureData->mainTextureResourceViewNV); 1809 textureData->mainSRVIndexNV = D3D12_GetAvailableSRVIndex(renderer); 1810 textureData->mainTextureResourceViewNV.ptr += textureData->mainSRVIndexNV * rendererData->srvDescriptorSize; 1811 ID3D12Device1_CreateShaderResourceView(rendererData->d3dDevice, 1812 textureData->mainTexture, 1813 &nvResourceViewDesc, 1814 textureData->mainTextureResourceViewNV); 1815 } 1816#endif // SDL_HAVE_YUV 1817 1818 if (texture->access & SDL_TEXTUREACCESS_TARGET) { 1819 D3D12_RENDER_TARGET_VIEW_DESC renderTargetViewDesc; 1820 SDL_zero(renderTargetViewDesc); 1821 renderTargetViewDesc.Format = textureDesc.Format; 1822 renderTargetViewDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; 1823 renderTargetViewDesc.Texture2D.MipSlice = 0; 1824 1825 D3D_CALL_RET(rendererData->textureRTVDescriptorHeap, GetCPUDescriptorHandleForHeapStart, &textureData->mainTextureRenderTargetView); 1826 textureData->mainTextureRenderTargetView.ptr += textureData->mainSRVIndex * rendererData->rtvDescriptorSize; 1827 1828 ID3D12Device1_CreateRenderTargetView(rendererData->d3dDevice, 1829 (ID3D12Resource *)textureData->mainTexture, 1830 &renderTargetViewDesc, 1831 textureData->mainTextureRenderTargetView); 1832 } 1833 1834 return true; 1835} 1836 1837static void D3D12_DestroyTexture(SDL_Renderer *renderer, 1838 SDL_Texture *texture) 1839{ 1840 D3D12_RenderData *rendererData = (D3D12_RenderData *)renderer->internal; 1841 D3D12_TextureData *textureData = (D3D12_TextureData *)texture->internal; 1842 1843 if (!textureData) { 1844 return; 1845 } 1846 1847 /* Because SDL_DestroyTexture might be called while the data is in-flight, we need to issue the batch first 1848 Unfortunately, this means that deleting a lot of textures mid-frame will have poor performance. */ 1849 D3D12_IssueBatch(rendererData); 1850 1851 D3D_SAFE_RELEASE(textureData->mainTexture); 1852 D3D_SAFE_RELEASE(textureData->stagingBuffer); 1853 D3D12_FreeSRVIndex(renderer, textureData->mainSRVIndex); 1854#ifdef SDL_HAVE_YUV 1855 D3D_SAFE_RELEASE(textureData->mainTextureU); 1856 D3D_SAFE_RELEASE(textureData->mainTextureV); 1857 if (textureData->yuv) { 1858 D3D12_FreeSRVIndex(renderer, textureData->mainSRVIndexU); 1859 D3D12_FreeSRVIndex(renderer, textureData->mainSRVIndexV); 1860 } 1861 if (textureData->nv12) { 1862 D3D12_FreeSRVIndex(renderer, textureData->mainSRVIndexNV); 1863 } 1864 SDL_free(textureData->pixels); 1865#endif 1866 SDL_free(textureData); 1867 texture->internal = NULL; 1868} 1869 1870static bool D3D12_UpdateTextureInternal(D3D12_RenderData *rendererData, ID3D12Resource *texture, int plane, int x, int y, int w, int h, const void *pixels, int pitch, D3D12_RESOURCE_STATES *resourceState) 1871{ 1872 const Uint8 *src; 1873 Uint8 *dst; 1874 UINT length; 1875 HRESULT result; 1876 D3D12_RESOURCE_DESC textureDesc; 1877 D3D12_RESOURCE_DESC uploadDesc; 1878 D3D12_HEAP_PROPERTIES heapProps; 1879 D3D12_PLACED_SUBRESOURCE_FOOTPRINT placedTextureDesc; 1880 D3D12_TEXTURE_COPY_LOCATION srcLocation; 1881 D3D12_TEXTURE_COPY_LOCATION dstLocation; 1882 BYTE *textureMemory; 1883 ID3D12Resource *uploadBuffer; 1884 UINT row, NumRows, RowPitch; 1885 UINT64 RowLength; 1886 1887 // Create an upload buffer, which will be used to write to the main texture. 1888 SDL_zero(textureDesc); 1889 D3D_CALL_RET(texture, GetDesc, &textureDesc); 1890 textureDesc.Width = w; 1891 textureDesc.Height = h; 1892 if (textureDesc.Format == DXGI_FORMAT_NV12 || 1893 textureDesc.Format == DXGI_FORMAT_P010) { 1894 textureDesc.Width = (textureDesc.Width + 1) & ~1; 1895 textureDesc.Height = (textureDesc.Height + 1) & ~1; 1896 } 1897 1898 SDL_zero(uploadDesc); 1899 uploadDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; 1900 uploadDesc.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT; 1901 uploadDesc.Height = 1; 1902 uploadDesc.DepthOrArraySize = 1; 1903 uploadDesc.MipLevels = 1; 1904 uploadDesc.Format = DXGI_FORMAT_UNKNOWN; 1905 uploadDesc.SampleDesc.Count = 1; 1906 uploadDesc.SampleDesc.Quality = 0; 1907 uploadDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; 1908 uploadDesc.Flags = D3D12_RESOURCE_FLAG_NONE; 1909 1910 // Figure out how much we need to allocate for the upload buffer 1911 ID3D12Device1_GetCopyableFootprints(rendererData->d3dDevice, 1912 &textureDesc, 1913 plane, 1914 1, 1915 0, 1916 &placedTextureDesc, 1917 &NumRows, 1918 &RowLength, 1919 &uploadDesc.Width); 1920 RowPitch = placedTextureDesc.Footprint.RowPitch; 1921 1922 SDL_zero(heapProps); 1923 heapProps.Type = D3D12_HEAP_TYPE_UPLOAD; 1924 heapProps.CreationNodeMask = 1; 1925 heapProps.VisibleNodeMask = 1; 1926 1927 // Create the upload buffer 1928 result = ID3D12Device1_CreateCommittedResource(rendererData->d3dDevice, 1929 &heapProps, 1930 D3D12_HEAP_FLAG_NONE, 1931 &uploadDesc, 1932 D3D12_RESOURCE_STATE_GENERIC_READ, 1933 NULL, 1934 D3D_GUID(SDL_IID_ID3D12Resource), 1935 (void **)&rendererData->uploadBuffers[rendererData->currentUploadBuffer]); 1936 if (FAILED(result)) { 1937 return WIN_SetErrorFromHRESULT("ID3D12Device::CreateCommittedResource [create upload buffer]", result); 1938 } 1939 1940 // Get a write-only pointer to data in the upload buffer: 1941 uploadBuffer = rendererData->uploadBuffers[rendererData->currentUploadBuffer]; 1942 result = ID3D12Resource_Map(uploadBuffer, 1943 0, 1944 NULL, 1945 (void **)&textureMemory); 1946 if (FAILED(result)) { 1947 D3D_SAFE_RELEASE(rendererData->uploadBuffers[rendererData->currentUploadBuffer]); 1948 return WIN_SetErrorFromHRESULT("ID3D12Resource::Map [map staging texture]", result); 1949 } 1950 1951 src = (const Uint8 *)pixels; 1952 dst = textureMemory; 1953 length = (UINT)RowLength; 1954 if (length == (UINT)pitch && length == RowPitch) { 1955 SDL_memcpy(dst, src, (size_t)length * NumRows); 1956 } else { 1957 if (length > (UINT)pitch) { 1958 length = pitch; 1959 } 1960 if (length > RowPitch) { 1961 length = RowPitch; 1962 } 1963 for (row = NumRows; row--; ) { 1964 SDL_memcpy(dst, src, length); 1965 src += pitch; 1966 dst += RowPitch; 1967 } 1968 } 1969 1970 // Commit the changes back to the upload buffer: 1971 ID3D12Resource_Unmap(uploadBuffer, 0, NULL); 1972 1973 // Make sure the destination is in the correct resource state 1974 D3D12_TransitionResource(rendererData, texture, *resourceState, D3D12_RESOURCE_STATE_COPY_DEST); 1975 *resourceState = D3D12_RESOURCE_STATE_COPY_DEST; 1976 1977 SDL_zero(dstLocation); 1978 dstLocation.pResource = texture; 1979 dstLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; 1980 dstLocation.SubresourceIndex = plane; 1981 1982 SDL_zero(srcLocation); 1983 srcLocation.pResource = rendererData->uploadBuffers[rendererData->currentUploadBuffer]; 1984 srcLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; 1985 srcLocation.PlacedFootprint = placedTextureDesc; 1986 1987 ID3D12GraphicsCommandList2_CopyTextureRegion(rendererData->commandList, 1988 &dstLocation, 1989 x, 1990 y, 1991 0, 1992 &srcLocation, 1993 NULL); 1994 1995 // Transition the texture to be shader accessible 1996 D3D12_TransitionResource(rendererData, texture, *resourceState, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); 1997 *resourceState = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; 1998 1999 rendererData->currentUploadBuffer++; 2000 // If we've used up all the upload buffers, we need to issue the batch 2001 if (rendererData->currentUploadBuffer == SDL_D3D12_NUM_UPLOAD_BUFFERS) { 2002 D3D12_IssueBatch(rendererData); 2003 } 2004 2005 return true; 2006} 2007 2008static bool D3D12_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, 2009 const SDL_Rect *rect, const void *srcPixels, 2010 int srcPitch) 2011{ 2012 D3D12_RenderData *rendererData = (D3D12_RenderData *)renderer->internal; 2013 D3D12_TextureData *textureData = (D3D12_TextureData *)texture->internal; 2014 2015 if (!textureData) { 2016 return SDL_SetError("Texture is not currently available"); 2017 } 2018 2019 if (!D3D12_UpdateTextureInternal(rendererData, textureData->mainTexture, 0, rect->x, rect->y, rect->w, rect->h, srcPixels, srcPitch, &textureData->mainResourceState)) { 2020 return false; 2021 } 2022#ifdef SDL_HAVE_YUV 2023 if (textureData->yuv) { 2024 // Skip to the correct offset into the next texture 2025 srcPixels = (const void *)((const Uint8 *)srcPixels + rect->h * srcPitch); 2026 2027 if (!D3D12_UpdateTextureInternal(rendererData, texture->format == SDL_PIXELFORMAT_YV12 ? textureData->mainTextureV : textureData->mainTextureU, 0, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, srcPixels, (srcPitch + 1) / 2, texture->format == SDL_PIXELFORMAT_YV12 ? &textureData->mainResourceStateV : &textureData->mainResourceStateU)) { 2028 return false; 2029 } 2030 2031 // Skip to the correct offset into the next texture 2032 srcPixels = (const void *)((const Uint8 *)srcPixels + ((rect->h + 1) / 2) * ((srcPitch + 1) / 2)); 2033 if (!D3D12_UpdateTextureInternal(rendererData, texture->format == SDL_PIXELFORMAT_YV12 ? textureData->mainTextureU : textureData->mainTextureV, 0, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, srcPixels, (srcPitch + 1) / 2, texture->format == SDL_PIXELFORMAT_YV12 ? &textureData->mainResourceStateU : &textureData->mainResourceStateV)) { 2034 return false; 2035 } 2036 } 2037 2038 if (textureData->nv12) { 2039 // Skip to the correct offset into the next texture 2040 srcPixels = (const void *)((const Uint8 *)srcPixels + rect->h * srcPitch); 2041 2042 if (texture->format == SDL_PIXELFORMAT_P010) { 2043 srcPitch = (srcPitch + 3) & ~3; 2044 } else { 2045 srcPitch = (srcPitch + 1) & ~1; 2046 } 2047 if (!D3D12_UpdateTextureInternal(rendererData, textureData->mainTexture, 1, rect->x, rect->y, (rect->w + 1) & ~1, (rect->h + 1) & ~1, srcPixels, srcPitch, &textureData->mainResourceState)) { 2048 return false; 2049 } 2050 } 2051#endif // SDL_HAVE_YUV 2052 if (textureData->mainTextureResourceView.ptr == rendererData->currentShaderResource.ptr) { 2053 // We'll need to rebind this resource after updating it 2054 rendererData->currentShaderResource.ptr = 0; 2055 } 2056 return true; 2057} 2058 2059#ifdef SDL_HAVE_YUV 2060static bool D3D12_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *texture, 2061 const SDL_Rect *rect, 2062 const Uint8 *Yplane, int Ypitch, 2063 const Uint8 *Uplane, int Upitch, 2064 const Uint8 *Vplane, int Vpitch) 2065{ 2066 D3D12_RenderData *rendererData = (D3D12_RenderData *)renderer->internal; 2067 D3D12_TextureData *textureData = (D3D12_TextureData *)texture->internal; 2068 2069 if (!textureData) { 2070 return SDL_SetError("Texture is not currently available"); 2071 } 2072 2073 if (!D3D12_UpdateTextureInternal(rendererData, textureData->mainTexture, 0, rect->x, rect->y, rect->w, rect->h, Yplane, Ypitch, &textureData->mainResourceState)) { 2074 return false; 2075 } 2076 if (!D3D12_UpdateTextureInternal(rendererData, textureData->mainTextureU, 0, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Uplane, Upitch, &textureData->mainResourceStateU)) { 2077 return false; 2078 } 2079 if (!D3D12_UpdateTextureInternal(rendererData, textureData->mainTextureV, 0, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Vplane, Vpitch, &textureData->mainResourceStateV)) { 2080 return false; 2081 } 2082 if (textureData->mainTextureResourceView.ptr == rendererData->currentShaderResource.ptr) { 2083 // We'll need to rebind this resource after updating it 2084 rendererData->currentShaderResource.ptr = 0; 2085 } 2086 return true; 2087} 2088 2089static bool D3D12_UpdateTextureNV(SDL_Renderer *renderer, SDL_Texture *texture, 2090 const SDL_Rect *rect, 2091 const Uint8 *Yplane, int Ypitch, 2092 const Uint8 *UVplane, int UVpitch) 2093{ 2094 D3D12_RenderData *rendererData = (D3D12_RenderData *)renderer->internal; 2095 D3D12_TextureData *textureData = (D3D12_TextureData *)texture->internal; 2096 2097 if (!textureData) { 2098 return SDL_SetError("Texture is not currently available"); 2099 } 2100 2101 if (!D3D12_UpdateTextureInternal(rendererData, textureData->mainTexture, 0, rect->x, rect->y, rect->w, rect->h, Yplane, Ypitch, &textureData->mainResourceState)) { 2102 return false; 2103 } 2104 if (!D3D12_UpdateTextureInternal(rendererData, textureData->mainTexture, 1, rect->x, rect->y, (rect->w + 1) & ~1, (rect->h + 1) & ~1, UVplane, UVpitch, &textureData->mainResourceState)) { 2105 return false; 2106 } 2107 if (textureData->mainTextureResourceView.ptr == rendererData->currentShaderResource.ptr) { 2108 // We'll need to rebind this resource after updating it 2109 rendererData->currentShaderResource.ptr = 0; 2110 } 2111 return true; 2112} 2113#endif 2114 2115static bool D3D12_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture, 2116 const SDL_Rect *rect, void **pixels, int *pitch) 2117{ 2118 D3D12_RenderData *rendererData = (D3D12_RenderData *)renderer->internal; 2119 D3D12_TextureData *textureData = (D3D12_TextureData *)texture->internal; 2120 HRESULT result = S_OK; 2121 2122 D3D12_RESOURCE_DESC textureDesc; 2123 D3D12_RESOURCE_DESC uploadDesc; 2124 D3D12_HEAP_PROPERTIES heapProps; 2125 D3D12_SUBRESOURCE_FOOTPRINT pitchedDesc; 2126 BYTE *textureMemory; 2127 int bpp; 2128 2129 if (!textureData) { 2130 return SDL_SetError("Texture is not currently available"); 2131 } 2132#ifdef SDL_HAVE_YUV 2133 if (textureData->yuv || textureData->nv12) { 2134 // It's more efficient to upload directly... 2135 if (!textureData->pixels) { 2136 textureData->pitch = texture->w; 2137 textureData->pixels = (Uint8 *)SDL_malloc((texture->h * textureData->pitch * 3) / 2); 2138 if (!textureData->pixels) { 2139 return false; 2140 } 2141 } 2142 textureData->lockedRect = *rect; 2143 *pixels = 2144 (void *)(textureData->pixels + rect->y * textureData->pitch + 2145 rect->x * SDL_BYTESPERPIXEL(texture->format)); 2146 *pitch = textureData->pitch; 2147 return true; 2148 } 2149#endif 2150 if (textureData->stagingBuffer) { 2151 return SDL_SetError("texture is already locked"); 2152 } 2153 2154 // Create an upload buffer, which will be used to write to the main texture. 2155 SDL_zero(textureDesc); 2156 D3D_CALL_RET(textureData->mainTexture, GetDesc, &textureDesc); 2157 textureDesc.Width = rect->w; 2158 textureDesc.Height = rect->h; 2159 2160 SDL_zero(uploadDesc); 2161 uploadDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; 2162 uploadDesc.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT; 2163 uploadDesc.Height = 1; 2164 uploadDesc.DepthOrArraySize = 1; 2165 uploadDesc.MipLevels = 1; 2166 uploadDesc.Format = DXGI_FORMAT_UNKNOWN; 2167 uploadDesc.SampleDesc.Count = 1; 2168 uploadDesc.SampleDesc.Quality = 0; 2169 uploadDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; 2170 uploadDesc.Flags = D3D12_RESOURCE_FLAG_NONE; 2171 2172 // Figure out how much we need to allocate for the upload buffer 2173 ID3D12Device1_GetCopyableFootprints(rendererData->d3dDevice, 2174 &textureDesc, 2175 0, 2176 1, 2177 0, 2178 NULL, 2179 NULL, 2180 NULL, 2181 &uploadDesc.Width); 2182 2183 SDL_zero(heapProps); 2184 heapProps.Type = D3D12_HEAP_TYPE_UPLOAD; 2185 heapProps.CreationNodeMask = 1; 2186 heapProps.VisibleNodeMask = 1; 2187 2188 // Create the upload buffer 2189 result = ID3D12Device1_CreateCommittedResource(rendererData->d3dDevice, 2190 &heapProps, 2191 D3D12_HEAP_FLAG_NONE, 2192 &uploadDesc, 2193 D3D12_RESOURCE_STATE_GENERIC_READ, 2194 NULL, 2195 D3D_GUID(SDL_IID_ID3D12Resource), 2196 (void **)&textureData->stagingBuffer); 2197 if (FAILED(result)) { 2198 return WIN_SetErrorFromHRESULT("ID3D12Device::CreateCommittedResource [create upload buffer]", result); 2199 } 2200 2201 // Get a write-only pointer to data in the upload buffer: 2202 result = ID3D12Resource_Map(textureData->stagingBuffer, 2203 0, 2204 NULL, 2205 (void **)&textureMemory); 2206 if (FAILED(result)) { 2207 D3D_SAFE_RELEASE(rendererData->uploadBuffers[rendererData->currentUploadBuffer]); 2208 return WIN_SetErrorFromHRESULT("ID3D12Resource::Map [map staging texture]", result); 2209 } 2210 2211 SDL_zero(pitchedDesc); 2212 pitchedDesc.Format = textureDesc.Format; 2213 pitchedDesc.Width = rect->w; 2214 pitchedDesc.Height = rect->h; 2215 pitchedDesc.Depth = 1; 2216 if (pitchedDesc.Format == DXGI_FORMAT_R8_UNORM) { 2217 bpp = 1; 2218 } else { 2219 bpp = SDL_BYTESPERPIXEL(texture->format); 2220 } 2221 pitchedDesc.RowPitch = D3D12_Align(rect->w * bpp, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT); 2222 2223 /* Make note of where the staging texture will be written to 2224 * (on a call to SDL_UnlockTexture): 2225 */ 2226 textureData->lockedRect = *rect; 2227 2228 /* Make sure the caller has information on the texture's pixel buffer, 2229 * then return: 2230 */ 2231 *pixels = textureMemory; 2232 *pitch = pitchedDesc.RowPitch; 2233 return true; 2234} 2235 2236static void D3D12_UnlockTexture(SDL_Renderer *renderer, SDL_Texture *texture) 2237{ 2238 D3D12_RenderData *rendererData = (D3D12_RenderData *)renderer->internal; 2239 D3D12_TextureData *textureData = (D3D12_TextureData *)texture->internal; 2240 2241 D3D12_RESOURCE_DESC textureDesc; 2242 D3D12_SUBRESOURCE_FOOTPRINT pitchedDesc; 2243 D3D12_PLACED_SUBRESOURCE_FOOTPRINT placedTextureDesc; 2244 D3D12_TEXTURE_COPY_LOCATION srcLocation; 2245 D3D12_TEXTURE_COPY_LOCATION dstLocation; 2246 int bpp; 2247 2248 if (!textureData) { 2249 return; 2250 } 2251#ifdef SDL_HAVE_YUV 2252 if (textureData->yuv || textureData->nv12) { 2253 const SDL_Rect *rect = &textureData->lockedRect; 2254 void *pixels = 2255 (void *)(textureData->pixels + rect->y * textureData->pitch + 2256 rect->x * SDL_BYTESPERPIXEL(texture->format)); 2257 D3D12_UpdateTexture(renderer, texture, rect, pixels, textureData->pitch); 2258 return; 2259 } 2260#endif 2261 // Commit the pixel buffer's changes back to the staging texture: 2262 ID3D12Resource_Unmap(textureData->stagingBuffer, 0, NULL); 2263 2264 SDL_zero(textureDesc); 2265 D3D_CALL_RET(textureData->mainTexture, GetDesc, &textureDesc); 2266 textureDesc.Width = textureData->lockedRect.w; 2267 textureDesc.Height = textureData->lockedRect.h; 2268 2269 SDL_zero(pitchedDesc); 2270 pitchedDesc.Format = textureDesc.Format; 2271 pitchedDesc.Width = (UINT)textureDesc.Width; 2272 pitchedDesc.Height = textureDesc.Height; 2273 pitchedDesc.Depth = 1; 2274 if (pitchedDesc.Format == DXGI_FORMAT_R8_UNORM) { 2275 bpp = 1; 2276 } else { 2277 bpp = SDL_BYTESPERPIXEL(texture->format); 2278 } 2279 pitchedDesc.RowPitch = D3D12_Align(textureData->lockedRect.w * bpp, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT); 2280 2281 SDL_zero(placedTextureDesc); 2282 placedTextureDesc.Offset = 0; 2283 placedTextureDesc.Footprint = pitchedDesc; 2284 2285 D3D12_TransitionResource(rendererData, textureData->mainTexture, textureData->mainResourceState, D3D12_RESOURCE_STATE_COPY_DEST); 2286 textureData->mainResourceState = D3D12_RESOURCE_STATE_COPY_DEST; 2287 2288 SDL_zero(dstLocation); 2289 dstLocation.pResource = textureData->mainTexture; 2290 dstLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; 2291 dstLocation.SubresourceIndex = 0; 2292 2293 SDL_zero(srcLocation); 2294 srcLocation.pResource = textureData->stagingBuffer; 2295 srcLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; 2296 srcLocation.PlacedFootprint = placedTextureDesc; 2297 2298 ID3D12GraphicsCommandList2_CopyTextureRegion(rendererData->commandList, 2299 &dstLocation, 2300 textureData->lockedRect.x, 2301 textureData->lockedRect.y, 2302 0, 2303 &srcLocation, 2304 NULL); 2305 2306 // Transition the texture to be shader accessible 2307 D3D12_TransitionResource(rendererData, textureData->mainTexture, textureData->mainResourceState, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); 2308 textureData->mainResourceState = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; 2309 2310 // Execute the command list before releasing the staging buffer 2311 D3D12_IssueBatch(rendererData); 2312 D3D_SAFE_RELEASE(textureData->stagingBuffer); 2313} 2314 2315static bool D3D12_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture) 2316{ 2317 D3D12_RenderData *rendererData = (D3D12_RenderData *)renderer->internal; 2318 D3D12_TextureData *textureData = NULL; 2319 2320 if (!texture) { 2321 if (rendererData->textureRenderTarget) { 2322 D3D12_TransitionResource(rendererData, 2323 rendererData->textureRenderTarget->mainTexture, 2324 rendererData->textureRenderTarget->mainResourceState, 2325 D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); 2326 rendererData->textureRenderTarget->mainResourceState = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; 2327 } 2328 rendererData->textureRenderTarget = NULL; 2329 return true; 2330 } 2331 2332 textureData = (D3D12_TextureData *)texture->internal; 2333 2334 if (!textureData->mainTextureRenderTargetView.ptr) { 2335 return SDL_SetError("specified texture is not a render target"); 2336 } 2337 2338 rendererData->textureRenderTarget = textureData; 2339 D3D12_TransitionResource(rendererData, 2340 rendererData->textureRenderTarget->mainTexture, 2341 rendererData->textureRenderTarget->mainResourceState, 2342 D3D12_RESOURCE_STATE_RENDER_TARGET); 2343 rendererData->textureRenderTarget->mainResourceState = D3D12_RESOURCE_STATE_RENDER_TARGET; 2344 2345 return true; 2346} 2347 2348static bool D3D12_QueueNoOp(SDL_Renderer *renderer, SDL_RenderCommand *cmd) 2349{ 2350 return true; // nothing to do in this backend. 2351} 2352 2353static bool D3D12_QueueDrawPoints(SDL_Renderer *renderer, SDL_RenderCommand *cmd, const SDL_FPoint *points, int count) 2354{ 2355 D3D12_VertexPositionColor *verts = (D3D12_VertexPositionColor *)SDL_AllocateRenderVertices(renderer, count * sizeof(D3D12_VertexPositionColor), 0, &cmd->data.draw.first); 2356 int i; 2357 SDL_FColor color = cmd->data.draw.color; 2358 bool convert_color = SDL_RenderingLinearSpace(renderer); 2359 2360 if (!verts) { 2361 return false; 2362 } 2363 2364 cmd->data.draw.count = count; 2365 2366 if (convert_color) { 2367 SDL_ConvertToLinear(&color); 2368 } 2369 2370 for (i = 0; i < count; i++) { 2371 verts->pos.x = points[i].x + 0.5f; 2372 verts->pos.y = points[i].y + 0.5f; 2373 verts->tex.x = 0.0f; 2374 verts->tex.y = 0.0f; 2375 verts->color = color; 2376 verts++; 2377 } 2378 2379 return true; 2380} 2381 2382static bool D3D12_QueueGeometry(SDL_Renderer *renderer, SDL_RenderCommand *cmd, SDL_Texture *texture, 2383 const float *xy, int xy_stride, const SDL_FColor *color, int color_stride, const float *uv, int uv_stride, 2384 int num_vertices, const void *indices, int num_indices, int size_indices, 2385 float scale_x, float scale_y) 2386{ 2387 int i; 2388 int count = indices ? num_indices : num_vertices; 2389 D3D12_VertexPositionColor *verts = (D3D12_VertexPositionColor *)SDL_AllocateRenderVertices(renderer, count * sizeof(D3D12_VertexPositionColor), 0, &cmd->data.draw.first); 2390 bool convert_color = SDL_RenderingLinearSpace(renderer); 2391 D3D12_TextureData *textureData = texture ? (D3D12_TextureData *)texture->internal : NULL; 2392 float u_scale = textureData ? (float)texture->w / textureData->w : 0.0f; 2393 float v_scale = textureData ? (float)texture->h / textureData->h : 0.0f; 2394 2395 if (!verts) { 2396 return false; 2397 } 2398 2399 cmd->data.draw.count = count; 2400 size_indices = indices ? size_indices : 0; 2401 2402 for (i = 0; i < count; i++) { 2403 int j; 2404 float *xy_; 2405 if (size_indices == 4) { 2406 j = ((const Uint32 *)indices)[i]; 2407 } else if (size_indices == 2) { 2408 j = ((const Uint16 *)indices)[i]; 2409 } else if (size_indices == 1) { 2410 j = ((const Uint8 *)indices)[i]; 2411 } else { 2412 j = i; 2413 } 2414 2415 xy_ = (float *)((char *)xy + j * xy_stride); 2416 2417 verts->pos.x = xy_[0] * scale_x; 2418 verts->pos.y = xy_[1] * scale_y; 2419 verts->color = *(SDL_FColor *)((char *)color + j * color_stride); 2420 if (convert_color) { 2421 SDL_ConvertToLinear(&verts->color); 2422 } 2423 2424 if (texture) { 2425 float *uv_ = (float *)((char *)uv + j * uv_stride); 2426 verts->tex.x = uv_[0] * u_scale; 2427 verts->tex.y = uv_[1] * v_scale; 2428 } else { 2429 verts->tex.x = 0.0f; 2430 verts->tex.y = 0.0f; 2431 } 2432 2433 verts += 1; 2434 } 2435 return true; 2436} 2437 2438static bool D3D12_UpdateVertexBuffer(SDL_Renderer *renderer, 2439 const void *vertexData, size_t dataSizeInBytes) 2440{ 2441 D3D12_RenderData *rendererData = (D3D12_RenderData *)renderer->internal; 2442 HRESULT result = S_OK; 2443 const int vbidx = rendererData->currentVertexBuffer; 2444 UINT8 *vertexBufferData = NULL; 2445 D3D12_RANGE range; 2446 ID3D12Resource *vertexBuffer; 2447 2448 range.Begin = 0; 2449 range.End = 0; 2450 2451 if (dataSizeInBytes == 0) { 2452 return true; // nothing to do. 2453 } 2454 2455 if (rendererData->issueBatch) { 2456 if (FAILED(D3D12_IssueBatch(rendererData))) { 2457 return SDL_SetError("Failed to issue intermediate batch"); 2458 } 2459 } 2460 2461 // If the existing vertex buffer isn't big enough, we need to recreate a big enough one 2462 if (dataSizeInBytes > rendererData->vertexBuffers[vbidx].size) { 2463 D3D12_CreateVertexBuffer(rendererData, vbidx, dataSizeInBytes); 2464 } 2465 2466 vertexBuffer = rendererData->vertexBuffers[vbidx].resource; 2467 result = ID3D12Resource_Map(vertexBuffer, 0, &range, (void **)&vertexBufferData); 2468 if (FAILED(result)) { 2469 return WIN_SetErrorFromHRESULT("ID3D12Resource::Map [vertex buffer]", result); 2470 } 2471 SDL_memcpy(vertexBufferData, vertexData, dataSizeInBytes); 2472 ID3D12Resource_Unmap(vertexBuffer, 0, NULL); 2473 2474 rendererData->vertexBuffers[vbidx].view.SizeInBytes = (UINT)dataSizeInBytes; 2475 2476 ID3D12GraphicsCommandList2_IASetVertexBuffers(rendererData->commandList, 0, 1, &rendererData->vertexBuffers[vbidx].view); 2477 2478 rendererData->currentVertexBuffer++; 2479 if (rendererData->currentVertexBuffer >= SDL_D3D12_NUM_VERTEX_BUFFERS) { 2480 rendererData->currentVertexBuffer = 0; 2481 rendererData->issueBatch = true; 2482 } 2483 2484 return true; 2485} 2486 2487static bool D3D12_UpdateViewport(SDL_Renderer *renderer) 2488{ 2489 D3D12_RenderData *data = (D3D12_RenderData *)renderer->internal; 2490 const SDL_Rect *viewport = &data->currentViewport; 2491 Float4X4 projection; 2492 Float4X4 view; 2493 SDL_FRect orientationAlignedViewport; 2494 BOOL swapDimensions; 2495 D3D12_VIEWPORT d3dviewport; 2496 const int rotation = D3D12_GetRotationForCurrentRenderTarget(renderer); 2497 2498 if (viewport->w == 0 || viewport->h == 0) { 2499 /* If the viewport is empty, assume that it is because 2500 * SDL_CreateRenderer is calling it, and will call it again later 2501 * with a non-empty viewport. 2502 */ 2503 // SDL_Log("%s, no viewport was set!", SDL_FUNCTION); 2504 return false; 2505 } 2506 2507 /* Make sure the SDL viewport gets rotated to that of the physical display's rotation. 2508 * Keep in mind here that the Y-axis will be been inverted (from Direct3D's 2509 * default coordinate system) so rotations will be done in the opposite 2510 * direction of the DXGI_MODE_ROTATION enumeration. 2511 */ 2512 switch (rotation) { 2513 case DXGI_MODE_ROTATION_IDENTITY: 2514 projection = MatrixIdentity(); 2515 break; 2516 case DXGI_MODE_ROTATION_ROTATE270: 2517 projection = MatrixRotationZ(SDL_PI_F * 0.5f); 2518 break; 2519 case DXGI_MODE_ROTATION_ROTATE180: 2520 projection = MatrixRotationZ(SDL_PI_F); 2521 break; 2522 case DXGI_MODE_ROTATION_ROTATE90: 2523 projection = MatrixRotationZ(-SDL_PI_F * 0.5f); 2524 break; 2525 default: 2526 return SDL_SetError("An unknown DisplayOrientation is being used"); 2527 } 2528 2529 // Update the view matrix 2530 SDL_zero(view); 2531 view.m[0][0] = 2.0f / viewport->w; 2532 view.m[1][1] = -2.0f / viewport->h; 2533 view.m[2][2] = 1.0f; 2534 view.m[3][0] = -1.0f; 2535 view.m[3][1] = 1.0f; 2536 view.m[3][3] = 1.0f; 2537 2538 /* Combine the projection + view matrix together now, as both only get 2539 * set here (as of this writing, on Dec 26, 2013). When done, store it 2540 * for eventual transfer to the GPU. 2541 */ 2542 data->projectionAndView = MatrixMultiply(view, projection); 2543 2544 /* Update the Direct3D viewport, which seems to be aligned to the 2545 * swap buffer's coordinate space, which is always in either 2546 * a landscape mode, for all Windows 8/RT devices, or a portrait mode, 2547 * for Windows Phone devices. 2548 */ 2549 swapDimensions = D3D12_IsDisplayRotated90Degrees((DXGI_MODE_ROTATION)rotation); 2550 if (swapDimensions) { 2551 orientationAlignedViewport.x = (float)viewport->y; 2552 orientationAlignedViewport.y = (float)viewport->x; 2553 orientationAlignedViewport.w = (float)viewport->h; 2554 orientationAlignedViewport.h = (float)viewport->w; 2555 } else { 2556 orientationAlignedViewport.x = (float)viewport->x; 2557 orientationAlignedViewport.y = (float)viewport->y; 2558 orientationAlignedViewport.w = (float)viewport->w; 2559 orientationAlignedViewport.h = (float)viewport->h; 2560 } 2561 2562 d3dviewport.TopLeftX = orientationAlignedViewport.x; 2563 d3dviewport.TopLeftY = orientationAlignedViewport.y; 2564 d3dviewport.Width = orientationAlignedViewport.w; 2565 d3dviewport.Height = orientationAlignedViewport.h; 2566 d3dviewport.MinDepth = 0.0f; 2567 d3dviewport.MaxDepth = 1.0f; 2568 // SDL_Log("%s: D3D viewport = {%f,%f,%f,%f}", SDL_FUNCTION, d3dviewport.TopLeftX, d3dviewport.TopLeftY, d3dviewport.Width, d3dviewport.Height); 2569 ID3D12GraphicsCommandList_RSSetViewports(data->commandList, 1, &d3dviewport); 2570 2571 data->viewportDirty = false; 2572 2573 return true; 2574} 2575 2576static void D3D12_SetupShaderConstants(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, const SDL_Texture *texture, D3D12_PixelShaderConstants *constants) 2577{ 2578 float output_headroom; 2579 2580 SDL_zerop(constants); 2581 2582 constants->scRGB_output = (float)SDL_RenderingLinearSpace(renderer); 2583 constants->color_scale = cmd->data.draw.color_scale; 2584 2585 if (texture) { 2586 D3D12_TextureData *textureData = (D3D12_TextureData *)texture->internal; 2587 2588 switch (texture->format) { 2589 case SDL_PIXELFORMAT_INDEX8: 2590 switch (cmd->data.draw.texture_scale_mode) { 2591 case SDL_SCALEMODE_NEAREST: 2592 constants->texture_type = TEXTURETYPE_PALETTE_NEAREST; 2593 break; 2594 case SDL_SCALEMODE_LINEAR: 2595 constants->texture_type = TEXTURETYPE_PALETTE_LINEAR; 2596 break; 2597 case SDL_SCALEMODE_PIXELART: 2598 constants->texture_type = TEXTURETYPE_PALETTE_PIXELART; 2599 break; 2600 default: 2601 SDL_assert(!"Unknown scale mode"); 2602 break; 2603 } 2604 break; 2605 case SDL_PIXELFORMAT_YV12: 2606 case SDL_PIXELFORMAT_IYUV: 2607 constants->texture_type = TEXTURETYPE_YUV; 2608 constants->input_type = INPUTTYPE_SRGB; 2609 break; 2610 case SDL_PIXELFORMAT_NV12: 2611 constants->texture_type = TEXTURETYPE_NV12; 2612 constants->input_type = INPUTTYPE_SRGB; 2613 break; 2614 case SDL_PIXELFORMAT_NV21: 2615 constants->texture_type = TEXTURETYPE_NV21; 2616 constants->input_type = INPUTTYPE_SRGB; 2617 break; 2618 case SDL_PIXELFORMAT_P010: 2619 constants->texture_type = TEXTURETYPE_NV12; 2620 constants->input_type = INPUTTYPE_HDR10; 2621 break; 2622 default: 2623 if (cmd->data.draw.texture_scale_mode == SDL_SCALEMODE_PIXELART) { 2624 constants->texture_type = TEXTURETYPE_RGB_PIXELART; 2625 } else { 2626 constants->texture_type = TEXTURETYPE_RGB; 2627 } 2628 if (texture->colorspace == SDL_COLORSPACE_SRGB_LINEAR) { 2629 constants->input_type = INPUTTYPE_SCRGB; 2630 } else if (texture->colorspace == SDL_COLORSPACE_HDR10) { 2631 constants->input_type = INPUTTYPE_HDR10; 2632 } else { 2633 // The sampler will convert from sRGB to linear on load if working in linear colorspace 2634 constants->input_type = INPUTTYPE_UNSPECIFIED; 2635 } 2636 break; 2637 } 2638 2639 if (constants->texture_type == TEXTURETYPE_PALETTE_LINEAR || 2640 constants->texture_type == TEXTURETYPE_PALETTE_PIXELART || 2641 constants->texture_type == TEXTURETYPE_RGB_PIXELART) { 2642 constants->texture_width = texture->w; 2643 constants->texture_height = texture->h; 2644 constants->texel_width = 1.0f / constants->texture_width; 2645 constants->texel_height = 1.0f / constants->texture_height; 2646 } 2647 2648 constants->sdr_white_point = texture->SDR_white_point; 2649 2650 if (renderer->target) { 2651 output_headroom = renderer->target->HDR_headroom; 2652 } else { 2653 output_headroom = renderer->HDR_headroom; 2654 } 2655 2656 if (texture->HDR_headroom > output_headroom && output_headroom > 0.0f) { 2657 constants->tonemap_method = TONEMAP_CHROME; 2658 constants->tonemap_factor1 = (output_headroom / (texture->HDR_headroom * texture->HDR_headroom)); 2659 constants->tonemap_factor2 = (1.0f / output_headroom); 2660 } 2661 2662 if (textureData->YCbCr_matrix) { 2663 SDL_memcpy(constants->YCbCr_matrix, textureData->YCbCr_matrix, sizeof(constants->YCbCr_matrix)); 2664 } 2665 } 2666} 2667 2668static D3D12_Shader SelectShader(const D3D12_PixelShaderConstants *shader_constants) 2669{ 2670 if (!shader_constants) { 2671 return SHADER_SOLID; 2672 } 2673 2674 if (shader_constants->texture_type == TEXTURETYPE_RGB && 2675 shader_constants->input_type == INPUTTYPE_UNSPECIFIED && 2676 shader_constants->tonemap_method == TONEMAP_NONE) { 2677 return SHADER_RGB; 2678 } 2679 2680 return SHADER_ADVANCED; 2681} 2682 2683static bool D3D12_SetDrawState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, const D3D12_PixelShaderConstants *shader_constants, 2684 D3D12_PRIMITIVE_TOPOLOGY_TYPE topology, 2685 int numShaderResources, D3D12_CPU_DESCRIPTOR_HANDLE *shaderResources, 2686 int numShaderSamplers, D3D12_CPU_DESCRIPTOR_HANDLE *shaderSamplers) 2687 2688{ 2689 D3D12_RenderData *rendererData = (D3D12_RenderData *)renderer->internal; 2690 D3D12_CPU_DESCRIPTOR_HANDLE renderTargetView = D3D12_GetCurrentRenderTargetView(renderer); 2691 const SDL_BlendMode blendMode = cmd->data.draw.blend; 2692 bool updateSubresource = false; 2693 int i; 2694 DXGI_FORMAT rtvFormat = rendererData->renderTargetFormat; 2695 D3D12_PipelineState *currentPipelineState = rendererData->currentPipelineState; 2696 D3D12_Shader shader = SelectShader(shader_constants); 2697 D3D12_PixelShaderConstants solid_constants; 2698 2699 bool shaderResourcesChanged = false; 2700 if (numShaderResources != rendererData->numCurrentShaderResources || 2701 (numShaderResources > 0 && shaderResources[0].ptr != rendererData->currentShaderResource.ptr)) { 2702 shaderResourcesChanged = true; 2703 } 2704 2705 bool shaderSamplersChanged = false; 2706 if (numShaderSamplers != rendererData->numCurrentShaderSamplers || 2707 (numShaderSamplers > 0 && shaderSamplers[0].ptr != rendererData->currentShaderSampler.ptr)) { 2708 shaderSamplersChanged = true; 2709 } 2710 2711 if (rendererData->textureRenderTarget) { 2712 rtvFormat = rendererData->textureRenderTarget->mainTextureFormat; 2713 } 2714 2715 // See if we need to change the pipeline state 2716 if (!currentPipelineState || 2717 currentPipelineState->shader != shader || 2718 currentPipelineState->blendMode != blendMode || 2719 currentPipelineState->topology != topology || 2720 currentPipelineState->rtvFormat != rtvFormat) { 2721 2722 /* Find the matching pipeline. 2723 NOTE: Although it may seem inefficient to linearly search through ~450 pipelines 2724 to find the correct one, in profiling this doesn't come up at all. 2725 It's unlikely that using a hash table would affect performance a measurable amount unless 2726 it's a degenerate case that's changing the pipeline state dozens of times per frame. 2727 */ 2728 currentPipelineState = NULL; 2729 for (i = 0; i < rendererData->pipelineStateCount; ++i) { 2730 D3D12_PipelineState *candidatePiplineState = &rendererData->pipelineStates[i]; 2731 if (candidatePiplineState->shader == shader && 2732 candidatePiplineState->blendMode == blendMode && 2733 candidatePiplineState->topology == topology && 2734 candidatePiplineState->rtvFormat == rtvFormat) { 2735 currentPipelineState = candidatePiplineState; 2736 break; 2737 } 2738 } 2739 2740 // If we didn't find a match, create a new one -- it must mean the blend mode is non-standard 2741 if (!currentPipelineState) { 2742 currentPipelineState = D3D12_CreatePipelineState(renderer, shader, blendMode, topology, rtvFormat); 2743 } 2744 2745 if (!currentPipelineState) { 2746 // The error has been set inside D3D12_CreatePipelineState() 2747 return false; 2748 } 2749 2750 ID3D12GraphicsCommandList2_SetPipelineState(rendererData->commandList, currentPipelineState->pipelineState); 2751 ID3D12GraphicsCommandList2_SetGraphicsRootSignature(rendererData->commandList, 2752 rendererData->rootSignatures[D3D12_GetRootSignatureType(currentPipelineState->shader)]); 2753 // When we change these we will need to re-upload the constant buffer and reset any descriptors 2754 updateSubresource = true; 2755 shaderResourcesChanged = true; 2756 shaderSamplersChanged = true; 2757 rendererData->currentPipelineState = currentPipelineState; 2758 } 2759 2760 if (renderTargetView.ptr != rendererData->currentRenderTargetView.ptr) { 2761 ID3D12GraphicsCommandList2_OMSetRenderTargets(rendererData->commandList, 1, &renderTargetView, FALSE, NULL); 2762 rendererData->currentRenderTargetView = renderTargetView; 2763 } 2764 2765 if (rendererData->viewportDirty) { 2766 if (D3D12_UpdateViewport(renderer)) { 2767 // vertexShaderConstantsData.projectionAndView has changed 2768 updateSubresource = true; 2769 } 2770 } 2771 2772 if (rendererData->cliprectDirty) { 2773 D3D12_RECT scissorRect; 2774 if (!D3D12_GetViewportAlignedD3DRect(renderer, &rendererData->currentCliprect, &scissorRect, TRUE)) { 2775 // D3D12_GetViewportAlignedD3DRect will have set the SDL error 2776 return false; 2777 } 2778 ID3D12GraphicsCommandList2_RSSetScissorRects(rendererData->commandList, 1, &scissorRect); 2779 rendererData->cliprectDirty = false; 2780 } 2781 2782 if (shaderResourcesChanged) { 2783 for (i = 0; i < numShaderResources; ++i) { 2784 D3D12_GPU_DESCRIPTOR_HANDLE GPUHandle = D3D12_CPUtoGPUHandle(rendererData->srvDescriptorHeap, shaderResources[i]); 2785 ID3D12GraphicsCommandList2_SetGraphicsRootDescriptorTable(rendererData->commandList, i + 2, GPUHandle); 2786 } 2787 rendererData->numCurrentShaderResources = numShaderResources; 2788 if (numShaderResources > 0) { 2789 rendererData->currentShaderResource.ptr = shaderResources[0].ptr; 2790 } 2791 } 2792 2793 if (shaderSamplersChanged) { 2794 if (numShaderSamplers > 0) { 2795 D3D12_GPU_DESCRIPTOR_HANDLE GPUHandle = D3D12_CPUtoGPUHandle(rendererData->samplerDescriptorHeap, shaderSamplers[0]); 2796 UINT tableIndex = 0; 2797 2798 // Figure out the correct sampler descriptor table index based on the type of shader 2799 switch (shader) { 2800 case SHADER_RGB: 2801 tableIndex = 3; 2802 break; 2803 case SHADER_ADVANCED: 2804 tableIndex = 5; 2805 break; 2806 default: 2807 return SDL_SetError("[direct3d12] Trying to set a sampler for a shader which doesn't have one"); 2808 } 2809 2810 ID3D12GraphicsCommandList2_SetGraphicsRootDescriptorTable(rendererData->commandList, tableIndex, GPUHandle); 2811 } 2812 2813 if (numShaderSamplers > 1) { 2814 D3D12_GPU_DESCRIPTOR_HANDLE GPUHandle = D3D12_CPUtoGPUHandle(rendererData->samplerDescriptorHeap, shaderSamplers[1]); 2815 UINT tableIndex = 6; 2816 ID3D12GraphicsCommandList2_SetGraphicsRootDescriptorTable(rendererData->commandList, tableIndex, GPUHandle); 2817 } 2818 2819 rendererData->numCurrentShaderSamplers = numShaderSamplers; 2820 if (numShaderSamplers > 0) { 2821 rendererData->currentShaderSampler.ptr = shaderSamplers[0].ptr; 2822 } 2823 } 2824 2825 if (updateSubresource) { 2826 D3D12_VertexShaderConstants vertex_constants; 2827 // Our model matrix is always identity 2828 vertex_constants.mpv = rendererData->projectionAndView; 2829 ID3D12GraphicsCommandList2_SetGraphicsRoot32BitConstants(rendererData->commandList, 2830 0, 2831 sizeof(vertex_constants) / sizeof(float), 2832 &vertex_constants, 2833 0); 2834 } 2835 2836 if (!shader_constants) { 2837 D3D12_SetupShaderConstants(renderer, cmd, NULL, &solid_constants); 2838 shader_constants = &solid_constants; 2839 } 2840 2841 if (updateSubresource || 2842 SDL_memcmp(shader_constants, &currentPipelineState->shader_constants, sizeof(*shader_constants)) != 0) { 2843 ID3D12GraphicsCommandList2_SetGraphicsRoot32BitConstants(rendererData->commandList, 2844 1, 2845 sizeof(*shader_constants) / sizeof(float), 2846 shader_constants, 2847 0); 2848 2849 SDL_memcpy(&currentPipelineState->shader_constants, shader_constants, sizeof(*shader_constants)); 2850 } 2851 2852 return true; 2853} 2854 2855static D3D12_CPU_DESCRIPTOR_HANDLE *D3D12_GetSamplerState(D3D12_RenderData *data, SDL_PixelFormat format, SDL_ScaleMode scale_mode, SDL_TextureAddressMode address_u, SDL_TextureAddressMode address_v) 2856{ 2857 if (format == SDL_PIXELFORMAT_INDEX8) { 2858 // We'll do linear sampling in the shader if needed 2859 scale_mode = SDL_SCALEMODE_NEAREST; 2860 } 2861 2862 Uint32 key = RENDER_SAMPLER_HASHKEY(scale_mode, address_u, address_v); 2863 SDL_assert(key < SDL_arraysize(data->samplers)); 2864 if (!data->samplers_created[key]) { 2865 D3D12_SAMPLER_DESC samplerDesc; 2866 SDL_zero(samplerDesc); 2867 samplerDesc.AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; 2868 samplerDesc.MipLODBias = 0.0f; 2869 samplerDesc.MaxAnisotropy = 1; 2870#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES) 2871 // FIXME: Xbox doesn't support FUNC_NONE as of the October 2025 GDK, but 2872 // at the same time it fixes validation issues on Windows. It's probably 2873 // the more appropriate value, so get rid of this when GDK catches up. 2874 // -flibit 2875 samplerDesc.ComparisonFunc = D3D12_COMPARISON_FUNC_ALWAYS; 2876#else 2877 samplerDesc.ComparisonFunc = D3D12_COMPARISON_FUNC_NONE; 2878#endif 2879 samplerDesc.MinLOD = 0.0f; 2880 samplerDesc.MaxLOD = D3D12_FLOAT32_MAX; 2881 switch (scale_mode) { 2882 case SDL_SCALEMODE_NEAREST: 2883 samplerDesc.Filter = D3D12_FILTER_MIN_MAG_MIP_POINT; 2884 break; 2885 case SDL_SCALEMODE_PIXELART: // Uses linear sampling 2886 case SDL_SCALEMODE_LINEAR: 2887 samplerDesc.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR; 2888 break; 2889 default: 2890 SDL_SetError("Unknown scale mode: %d", scale_mode); 2891 return NULL; 2892 } 2893 switch (address_u) { 2894 case SDL_TEXTURE_ADDRESS_CLAMP: 2895 samplerDesc.AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; 2896 break; 2897 case SDL_TEXTURE_ADDRESS_WRAP: 2898 samplerDesc.AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP; 2899 break; 2900 default: 2901 SDL_SetError("Unknown texture address mode: %d", address_u); 2902 return NULL; 2903 } 2904 switch (address_v) { 2905 case SDL_TEXTURE_ADDRESS_CLAMP: 2906 samplerDesc.AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; 2907 break; 2908 case SDL_TEXTURE_ADDRESS_WRAP: 2909 samplerDesc.AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP; 2910 break; 2911 default: 2912 SDL_SetError("Unknown texture address mode: %d", address_v); 2913 return NULL; 2914 } 2915 ID3D12Device1_CreateSampler(data->d3dDevice, &samplerDesc, data->samplers[key]); 2916 data->samplers_created[key] = true; 2917 } 2918 return &data->samplers[key]; 2919} 2920 2921static bool D3D12_SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd) 2922{ 2923 SDL_Texture *texture = cmd->data.draw.texture; 2924 D3D12_RenderData *rendererData = (D3D12_RenderData *)renderer->internal; 2925 D3D12_TextureData *textureData = (D3D12_TextureData *)texture->internal; 2926 D3D12_CPU_DESCRIPTOR_HANDLE *textureSampler; 2927 D3D12_PixelShaderConstants constants; 2928 int numShaderResources = 0; 2929 D3D12_CPU_DESCRIPTOR_HANDLE shaderResources[3]; 2930 int numShaderSamplers = 0; 2931 D3D12_CPU_DESCRIPTOR_HANDLE shaderSamplers[2]; 2932 2933 if (!textureData) { 2934 return SDL_SetError("Texture is not currently available"); 2935 } 2936 2937 D3D12_SetupShaderConstants(renderer, cmd, texture, &constants); 2938 2939 D3D12_TransitionResource(rendererData, textureData->mainTexture, textureData->mainResourceState, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); 2940 textureData->mainResourceState = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; 2941 shaderResources[numShaderResources++] = textureData->mainTextureResourceView; 2942 2943 textureSampler = D3D12_GetSamplerState(rendererData, texture->format, cmd->data.draw.texture_scale_mode, cmd->data.draw.texture_address_mode_u, cmd->data.draw.texture_address_mode_v); 2944 if (!textureSampler) { 2945 return false; 2946 } 2947 shaderSamplers[numShaderSamplers++] = *textureSampler; 2948 2949 if (texture->palette) { 2950 D3D12_PaletteData *palette = (D3D12_PaletteData *)texture->palette->internal; 2951 2952 D3D12_TransitionResource(rendererData, palette->texture, palette->resourceState, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); 2953 palette->resourceState = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; 2954 shaderResources[numShaderResources++] = palette->resourceView; 2955 2956 textureSampler = D3D12_GetSamplerState(rendererData, SDL_PIXELFORMAT_UNKNOWN, SDL_SCALEMODE_NEAREST, SDL_TEXTURE_ADDRESS_CLAMP, SDL_TEXTURE_ADDRESS_CLAMP); 2957 if (!textureSampler) { 2958 return false; 2959 } 2960 shaderSamplers[numShaderSamplers++] = *textureSampler; 2961 } 2962 2963#ifdef SDL_HAVE_YUV 2964 if (textureData->yuv) { 2965 D3D12_TransitionResource(rendererData, textureData->mainTextureU, textureData->mainResourceStateU, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); 2966 textureData->mainResourceStateU = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; 2967 shaderResources[numShaderResources++] = textureData->mainTextureResourceViewU; 2968 2969 D3D12_TransitionResource(rendererData, textureData->mainTextureV, textureData->mainResourceStateV, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); 2970 textureData->mainResourceStateV = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; 2971 shaderResources[numShaderResources++] = textureData->mainTextureResourceViewV; 2972 } else if (textureData->nv12) { 2973 D3D12_TransitionResource(rendererData, textureData->mainTexture, textureData->mainResourceState, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); 2974 textureData->mainResourceState = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; 2975 shaderResources[numShaderResources++] = textureData->mainTextureResourceViewNV; 2976 } 2977#endif // SDL_HAVE_YUV 2978 return D3D12_SetDrawState(renderer, cmd, &constants, D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE, numShaderResources, shaderResources, numShaderSamplers, shaderSamplers); 2979} 2980 2981static void D3D12_DrawPrimitives(SDL_Renderer *renderer, D3D12_PRIMITIVE_TOPOLOGY primitiveTopology, const size_t vertexStart, const size_t vertexCount) 2982{ 2983 D3D12_RenderData *rendererData = (D3D12_RenderData *)renderer->internal; 2984 ID3D12GraphicsCommandList2_IASetPrimitiveTopology(rendererData->commandList, primitiveTopology); 2985 ID3D12GraphicsCommandList2_DrawInstanced(rendererData->commandList, (UINT)vertexCount, 1, (UINT)vertexStart, 0); 2986} 2987 2988static void D3D12_InvalidateCachedState(SDL_Renderer *renderer) 2989{ 2990 D3D12_RenderData *data = (D3D12_RenderData *)renderer->internal; 2991 data->currentRenderTargetView.ptr = 0; 2992 data->numCurrentShaderResources = 0; 2993 data->currentShaderResource.ptr = 0; 2994 data->numCurrentShaderSamplers = 0; 2995 data->currentShaderSampler.ptr = 0; 2996 data->cliprectDirty = true; 2997 data->viewportDirty = true; 2998} 2999 3000static bool D3D12_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize) 3001{ 3002 D3D12_RenderData *rendererData = (D3D12_RenderData *)renderer->internal; 3003 const int viewportRotation = D3D12_GetRotationForCurrentRenderTarget(renderer); 3004 3005 if (!rendererData->d3dDevice) { 3006 return SDL_SetError("Device lost and couldn't be recovered"); 3007 } 3008 3009 if (rendererData->pixelSizeChanged) { 3010 D3D12_UpdateForWindowSizeChange(renderer); 3011 rendererData->pixelSizeChanged = false; 3012 } 3013 3014 if (rendererData->currentViewportRotation != viewportRotation) { 3015 rendererData->currentViewportRotation = viewportRotation; 3016 rendererData->viewportDirty = true; 3017 } 3018 3019 if (!D3D12_UpdateVertexBuffer(renderer, vertices, vertsize)) { 3020 return false; 3021 } 3022 3023 while (cmd) { 3024 switch (cmd->command) { 3025 case SDL_RENDERCMD_SETDRAWCOLOR: 3026 { 3027 break; // this isn't currently used in this render backend. 3028 } 3029 3030 case SDL_RENDERCMD_SETVIEWPORT: 3031 { 3032 SDL_Rect *viewport = &rendererData->currentViewport; 3033 if (SDL_memcmp(viewport, &cmd->data.viewport.rect, sizeof(cmd->data.viewport.rect)) != 0) { 3034 SDL_copyp(viewport, &cmd->data.viewport.rect); 3035 rendererData->viewportDirty = true; 3036 rendererData->cliprectDirty = true; 3037 } 3038 break; 3039 } 3040 3041 case SDL_RENDERCMD_SETCLIPRECT: 3042 { 3043 const SDL_Rect *rect = &cmd->data.cliprect.rect; 3044 SDL_Rect viewport_cliprect; 3045 if (rendererData->currentCliprectEnabled != cmd->data.cliprect.enabled) { 3046 rendererData->currentCliprectEnabled = cmd->data.cliprect.enabled; 3047 rendererData->cliprectDirty = true; 3048 } 3049 if (!rendererData->currentCliprectEnabled) { 3050 /* If the clip rect is disabled, then the scissor rect should be the whole viewport, 3051 since direct3d12 doesn't allow disabling the scissor rectangle */ 3052 viewport_cliprect.x = 0; 3053 viewport_cliprect.y = 0; 3054 viewport_cliprect.w = rendererData->currentViewport.w; 3055 viewport_cliprect.h = rendererData->currentViewport.h; 3056 rect = &viewport_cliprect; 3057 } 3058 if (SDL_memcmp(&rendererData->currentCliprect, rect, sizeof(*rect)) != 0) { 3059 SDL_copyp(&rendererData->currentCliprect, rect); 3060 rendererData->cliprectDirty = true; 3061 } 3062 break; 3063 } 3064 3065 case SDL_RENDERCMD_CLEAR: 3066 { 3067 D3D12_CPU_DESCRIPTOR_HANDLE rtvDescriptor = D3D12_GetCurrentRenderTargetView(renderer); 3068 bool convert_color = SDL_RenderingLinearSpace(renderer); 3069 SDL_FColor color = cmd->data.color.color; 3070 if (convert_color) { 3071 SDL_ConvertToLinear(&color); 3072 } 3073 color.r *= cmd->data.color.color_scale; 3074 color.g *= cmd->data.color.color_scale; 3075 color.b *= cmd->data.color.color_scale; 3076 ID3D12GraphicsCommandList2_ClearRenderTargetView(rendererData->commandList, rtvDescriptor, &color.r, 0, NULL); 3077 break; 3078 } 3079 3080 case SDL_RENDERCMD_DRAW_LINES: 3081 { 3082 size_t count = cmd->data.draw.count; 3083 const size_t first = cmd->data.draw.first; 3084 const size_t start = first / sizeof(D3D12_VertexPositionColor); 3085 const D3D12_VertexPositionColor *verts = (D3D12_VertexPositionColor *)(((Uint8 *)vertices) + first); 3086 bool have_point_draw_state = false; 3087 3088 // Add the final point in the line 3089 size_t line_start = 0; 3090 size_t line_end = line_start + count - 1; 3091 if (verts[line_start].pos.x != verts[line_end].pos.x || verts[line_start].pos.y != verts[line_end].pos.y) { 3092 D3D12_SetDrawState(renderer, cmd, NULL, D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT, 0, NULL, 0, NULL); 3093 D3D12_DrawPrimitives(renderer, D3D_PRIMITIVE_TOPOLOGY_POINTLIST, start + line_end, 1); 3094 have_point_draw_state = true; 3095 } 3096 3097 if (count > 2) { 3098 // joined lines cannot be grouped 3099 D3D12_SetDrawState(renderer, cmd, NULL, D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE, 0, NULL, 0, NULL); 3100 D3D12_DrawPrimitives(renderer, D3D_PRIMITIVE_TOPOLOGY_LINESTRIP, start, count); 3101 } else { 3102 // let's group non joined lines 3103 SDL_RenderCommand *finalcmd = cmd; 3104 SDL_RenderCommand *nextcmd; 3105 float thiscolorscale = cmd->data.draw.color_scale; 3106 SDL_BlendMode thisblend = cmd->data.draw.blend; 3107 3108 for (nextcmd = cmd->next; nextcmd; nextcmd = nextcmd->next) { 3109 const SDL_RenderCommandType nextcmdtype = nextcmd->command; 3110 if (nextcmdtype != SDL_RENDERCMD_DRAW_LINES) { 3111 if (nextcmdtype == SDL_RENDERCMD_SETDRAWCOLOR) { 3112 // The vertex data has the draw color built in, ignore this 3113 continue; 3114 } 3115 break; // can't go any further on this draw call, different render command up next. 3116 } else if (nextcmd->data.draw.count != 2) { 3117 break; // can't go any further on this draw call, those are joined lines 3118 } else if (nextcmd->data.draw.blend != thisblend || 3119 nextcmd->data.draw.color_scale != thiscolorscale) { 3120 break; // can't go any further on this draw call, different blendmode copy up next. 3121 } else { 3122 finalcmd = nextcmd; // we can combine copy operations here. Mark this one as the furthest okay command. 3123 3124 // Add the final point in the line 3125 line_start = count; 3126 line_end = line_start + nextcmd->data.draw.count - 1; 3127 if (verts[line_start].pos.x != verts[line_end].pos.x || verts[line_start].pos.y != verts[line_end].pos.y) { 3128 if (!have_point_draw_state) { 3129 D3D12_SetDrawState(renderer, cmd, NULL, D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT, 0, NULL, 0, NULL); 3130 have_point_draw_state = true; 3131 } 3132 D3D12_DrawPrimitives(renderer, D3D_PRIMITIVE_TOPOLOGY_POINTLIST, start + line_end, 1); 3133 } 3134 count += nextcmd->data.draw.count; 3135 } 3136 } 3137 3138 D3D12_SetDrawState(renderer, cmd, NULL, D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE, 0, NULL, 0, NULL); 3139 D3D12_DrawPrimitives(renderer, D3D_PRIMITIVE_TOPOLOGY_LINELIST, start, count); 3140 cmd = finalcmd; // skip any copy commands we just combined in here. 3141 } 3142 break; 3143 } 3144 3145 case SDL_RENDERCMD_FILL_RECTS: // unused 3146 break; 3147 3148 case SDL_RENDERCMD_COPY: // unused 3149 break; 3150 3151 case SDL_RENDERCMD_COPY_EX: // unused 3152 break; 3153 3154 case SDL_RENDERCMD_DRAW_POINTS: 3155 case SDL_RENDERCMD_GEOMETRY: 3156 { 3157 /* as long as we have the same copy command in a row, with the 3158 same texture, we can combine them all into a single draw call. */ 3159 float thiscolorscale = cmd->data.draw.color_scale; 3160 SDL_Texture *thistexture = cmd->data.draw.texture; 3161 SDL_BlendMode thisblend = cmd->data.draw.blend; 3162 SDL_ScaleMode thisscalemode = cmd->data.draw.texture_scale_mode; 3163 SDL_TextureAddressMode thisaddressmode_u = cmd->data.draw.texture_address_mode_u; 3164 SDL_TextureAddressMode thisaddressmode_v = cmd->data.draw.texture_address_mode_v; 3165 const SDL_RenderCommandType thiscmdtype = cmd->command; 3166 SDL_RenderCommand *finalcmd = cmd; 3167 SDL_RenderCommand *nextcmd; 3168 size_t count = cmd->data.draw.count; 3169 const size_t first = cmd->data.draw.first; 3170 const size_t start = first / sizeof(D3D12_VertexPositionColor); 3171 for (nextcmd = cmd->next; nextcmd; nextcmd = nextcmd->next) { 3172 const SDL_RenderCommandType nextcmdtype = nextcmd->command; 3173 if (nextcmdtype != thiscmdtype) { 3174 if (nextcmdtype == SDL_RENDERCMD_SETDRAWCOLOR) { 3175 // The vertex data has the draw color built in, ignore this 3176 continue; 3177 } 3178 break; // can't go any further on this draw call, different render command up next. 3179 } else if (nextcmd->data.draw.texture != thistexture || 3180 nextcmd->data.draw.texture_scale_mode != thisscalemode || 3181 nextcmd->data.draw.texture_address_mode_u != thisaddressmode_u || 3182 nextcmd->data.draw.texture_address_mode_v != thisaddressmode_v || 3183 nextcmd->data.draw.blend != thisblend || 3184 nextcmd->data.draw.color_scale != thiscolorscale) { 3185 break; // can't go any further on this draw call, different texture/blendmode copy up next. 3186 } else { 3187 finalcmd = nextcmd; // we can combine copy operations here. Mark this one as the furthest okay command. 3188 count += nextcmd->data.draw.count; 3189 } 3190 } 3191 3192 if (thiscmdtype == SDL_RENDERCMD_GEOMETRY) { 3193 if (thistexture) { 3194 D3D12_SetCopyState(renderer, cmd); 3195 } else { 3196 D3D12_SetDrawState(renderer, cmd, NULL, D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE, 0, NULL, 0, NULL); 3197 } 3198 D3D12_DrawPrimitives(renderer, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST, start, count); 3199 } else { 3200 D3D12_SetDrawState(renderer, cmd, NULL, D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT, 0, NULL, 0, NULL); 3201 D3D12_DrawPrimitives(renderer, D3D_PRIMITIVE_TOPOLOGY_POINTLIST, start, count); 3202 } 3203 cmd = finalcmd; // skip any copy commands we just combined in here. 3204 break; 3205 } 3206 3207 case SDL_RENDERCMD_NO_OP: 3208 break; 3209 } 3210 3211 cmd = cmd->next; 3212 } 3213 3214 return true; 3215} 3216 3217static SDL_Surface *D3D12_RenderReadPixels(SDL_Renderer *renderer, const SDL_Rect *rect) 3218{ 3219 D3D12_RenderData *data = (D3D12_RenderData *)renderer->internal; 3220 ID3D12Resource *backBuffer = NULL; 3221 ID3D12Resource *readbackBuffer = NULL; 3222 HRESULT result; 3223 D3D12_RESOURCE_DESC textureDesc; 3224 D3D12_RESOURCE_DESC readbackDesc; 3225 D3D12_HEAP_PROPERTIES heapProps; 3226 D3D12_RECT srcRect = { 0, 0, 0, 0 }; 3227 D3D12_BOX srcBox; 3228 D3D12_TEXTURE_COPY_LOCATION dstLocation; 3229 D3D12_TEXTURE_COPY_LOCATION srcLocation; 3230 D3D12_PLACED_SUBRESOURCE_FOOTPRINT placedTextureDesc; 3231 D3D12_SUBRESOURCE_FOOTPRINT pitchedDesc; 3232 BYTE *textureMemory; 3233 int bpp; 3234 SDL_Surface *output = NULL; 3235 3236 if (data->textureRenderTarget) { 3237 backBuffer = data->textureRenderTarget->mainTexture; 3238 } else { 3239 backBuffer = data->renderTargets[data->currentBackBufferIndex]; 3240 } 3241 3242 // Create a staging texture to copy the screen's data to: 3243 SDL_zero(textureDesc); 3244 D3D_CALL_RET(backBuffer, GetDesc, &textureDesc); 3245 textureDesc.Width = rect->w; 3246 textureDesc.Height = rect->h; 3247 3248 SDL_zero(readbackDesc); 3249 readbackDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; 3250 readbackDesc.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT; 3251 readbackDesc.Height = 1; 3252 readbackDesc.DepthOrArraySize = 1; 3253 readbackDesc.MipLevels = 1; 3254 readbackDesc.Format = DXGI_FORMAT_UNKNOWN; 3255 readbackDesc.SampleDesc.Count = 1; 3256 readbackDesc.SampleDesc.Quality = 0; 3257 readbackDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; 3258 readbackDesc.Flags = D3D12_RESOURCE_FLAG_NONE; 3259 3260 // Figure out how much we need to allocate for the upload buffer 3261 ID3D12Device1_GetCopyableFootprints(data->d3dDevice, 3262 &textureDesc, 3263 0, 3264 1, 3265 0, 3266 NULL, 3267 NULL, 3268 NULL, 3269 &readbackDesc.Width); 3270 3271 SDL_zero(heapProps); 3272 heapProps.Type = D3D12_HEAP_TYPE_READBACK; 3273 heapProps.CreationNodeMask = 1; 3274 heapProps.VisibleNodeMask = 1; 3275 3276 result = ID3D12Device1_CreateCommittedResource(data->d3dDevice, 3277 &heapProps, 3278 D3D12_HEAP_FLAG_NONE, 3279 &readbackDesc, 3280 D3D12_RESOURCE_STATE_COPY_DEST, 3281 NULL, 3282 D3D_GUID(SDL_IID_ID3D12Resource), 3283 (void **)&readbackBuffer); 3284 if (FAILED(result)) { 3285 WIN_SetErrorFromHRESULT("ID3D12Device::CreateTexture2D [create staging texture]", result); 3286 goto done; 3287 } 3288 3289 // Transition the render target to be copyable from 3290 D3D12_TransitionResource(data, backBuffer, D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE); 3291 3292 // Copy the desired portion of the back buffer to the staging texture: 3293 if (!D3D12_GetViewportAlignedD3DRect(renderer, rect, &srcRect, FALSE)) { 3294 // D3D12_GetViewportAlignedD3DRect will have set the SDL error 3295 goto done; 3296 } 3297 srcBox.left = srcRect.left; 3298 srcBox.right = srcRect.right; 3299 srcBox.top = srcRect.top; 3300 srcBox.bottom = srcRect.bottom; 3301 srcBox.front = 0; 3302 srcBox.back = 1; 3303 3304 // Issue the copy texture region 3305 SDL_zero(pitchedDesc); 3306 pitchedDesc.Format = textureDesc.Format; 3307 pitchedDesc.Width = (UINT)textureDesc.Width; 3308 pitchedDesc.Height = textureDesc.Height; 3309 pitchedDesc.Depth = 1; 3310 bpp = SDL_BYTESPERPIXEL(D3D12_DXGIFormatToSDLPixelFormat(pitchedDesc.Format)); 3311 pitchedDesc.RowPitch = D3D12_Align(pitchedDesc.Width * bpp, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT); 3312 3313 SDL_zero(placedTextureDesc); 3314 placedTextureDesc.Offset = 0; 3315 placedTextureDesc.Footprint = pitchedDesc; 3316 3317 SDL_zero(dstLocation); 3318 dstLocation.pResource = readbackBuffer; 3319 dstLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; 3320 dstLocation.PlacedFootprint = placedTextureDesc; 3321 3322 SDL_zero(srcLocation); 3323 srcLocation.pResource = backBuffer; 3324 srcLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; 3325 srcLocation.SubresourceIndex = 0; 3326 3327 ID3D12GraphicsCommandList2_CopyTextureRegion(data->commandList, 3328 &dstLocation, 3329 0, 0, 0, 3330 &srcLocation, 3331 &srcBox); 3332 3333 // We need to issue the command list for the copy to finish 3334 D3D12_IssueBatch(data); 3335 3336 // Transition the render target back to a render target 3337 D3D12_TransitionResource(data, backBuffer, D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET); 3338 3339 // Map the staging texture's data to CPU-accessible memory: 3340 result = ID3D12Resource_Map(readbackBuffer, 3341 0, 3342 NULL, 3343 (void **)&textureMemory); 3344 if (FAILED(result)) { 3345 WIN_SetErrorFromHRESULT("ID3D12Resource::Map [map staging texture]", result); 3346 goto done; 3347 } 3348 3349 output = SDL_DuplicatePixels( 3350 rect->w, rect->h, 3351 D3D12_DXGIFormatToSDLPixelFormat(textureDesc.Format), 3352 renderer->target ? renderer->target->colorspace : renderer->output_colorspace, 3353 textureMemory, 3354 pitchedDesc.RowPitch); 3355 3356 // Unmap the texture: 3357 ID3D12Resource_Unmap(readbackBuffer, 0, NULL); 3358 3359done: 3360 D3D_SAFE_RELEASE(readbackBuffer); 3361 return output; 3362} 3363 3364static bool D3D12_RenderPresent(SDL_Renderer *renderer) 3365{ 3366 D3D12_RenderData *data = (D3D12_RenderData *)renderer->internal; 3367 HRESULT result; 3368 3369 if (!data->d3dDevice) { 3370 return SDL_SetError("Device lost and couldn't be recovered"); 3371 } 3372 3373 // Transition the render target to present state 3374 D3D12_TransitionResource(data, 3375 data->renderTargets[data->currentBackBufferIndex], 3376 D3D12_RESOURCE_STATE_RENDER_TARGET, 3377 D3D12_RESOURCE_STATE_PRESENT); 3378 3379 // Issue the command list 3380 result = ID3D12GraphicsCommandList2_Close(data->commandList); 3381 ID3D12CommandQueue_ExecuteCommandLists(data->commandQueue, 1, (ID3D12CommandList *const *)&data->commandList); 3382 3383#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES) 3384 result = D3D12_XBOX_PresentFrame(data->commandQueue, data->frameToken, data->renderTargets[data->currentBackBufferIndex]); 3385#else 3386 /* The application may optionally specify "dirty" or "scroll" 3387 * rects to improve efficiency in certain scenarios. 3388 */ 3389 result = IDXGISwapChain_Present(data->swapChain, data->syncInterval, data->presentFlags); 3390#endif 3391 3392 if (FAILED(result) && result != DXGI_ERROR_WAS_STILL_DRAWING) { 3393 /* If the device was removed either by a disconnect or a driver upgrade, we 3394 * must recreate all device resources. 3395 */ 3396 if (result == DXGI_ERROR_DEVICE_REMOVED) { 3397 if (D3D12_HandleDeviceLost(renderer)) { 3398 SDL_SetError("Present failed, device lost"); 3399 } else { 3400 // Recovering from device lost failed, error is already set 3401 } 3402 } else if (result == DXGI_ERROR_INVALID_CALL) { 3403 // We probably went through a fullscreen <-> windowed transition 3404 D3D12_CreateWindowSizeDependentResources(renderer); 3405 WIN_SetErrorFromHRESULT("IDXGISwapChain::Present", result); 3406 } else { 3407 WIN_SetErrorFromHRESULT("IDXGISwapChain::Present", result); 3408 } 3409 return false; 3410 } else { 3411 // Wait for the GPU and move to the next frame 3412 result = ID3D12CommandQueue_Signal(data->commandQueue, data->fence, data->fenceValue); 3413 3414 if (ID3D12Fence_GetCompletedValue(data->fence) < data->fenceValue) { 3415 result = ID3D12Fence_SetEventOnCompletion(data->fence, 3416 data->fenceValue, 3417 data->fenceEvent); 3418 WaitForSingleObjectEx(data->fenceEvent, INFINITE, FALSE); 3419 } 3420 3421 data->fenceValue++; 3422#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES) 3423 data->currentBackBufferIndex++; 3424 data->currentBackBufferIndex %= SDL_D3D12_NUM_BUFFERS; 3425#else 3426 data->currentBackBufferIndex = IDXGISwapChain4_GetCurrentBackBufferIndex(data->swapChain); 3427#endif 3428 3429 // Reset the command allocator and command list, and transition back to render target 3430 D3D12_ResetCommandList(data); 3431 D3D12_TransitionResource(data, 3432 data->renderTargets[data->currentBackBufferIndex], 3433 D3D12_RESOURCE_STATE_PRESENT, 3434 D3D12_RESOURCE_STATE_RENDER_TARGET); 3435 3436#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES) 3437 D3D12_XBOX_StartFrame(data->d3dDevice, &data->frameToken); 3438#endif 3439 return true; 3440 } 3441} 3442 3443static bool D3D12_SetVSync(SDL_Renderer *renderer, const int vsync) 3444{ 3445 D3D12_RenderData *data = (D3D12_RenderData *)renderer->internal; 3446 3447 if (vsync < 0) { 3448 return SDL_Unsupported(); 3449 } 3450 3451 if (vsync > 0) { 3452 data->syncInterval = vsync; 3453 data->presentFlags = 0; 3454 } else { 3455 data->syncInterval = 0; 3456 data->presentFlags = DXGI_PRESENT_ALLOW_TEARING; 3457 } 3458 return true; 3459} 3460 3461bool D3D12_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_PropertiesID create_props) 3462{ 3463 D3D12_RenderData *data; 3464 3465 HWND hwnd = (HWND)SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL); 3466 if (!hwnd) { 3467 return SDL_SetError("Couldn't get window handle"); 3468 } 3469 3470 if (SDL_GetWindowFlags(window) & SDL_WINDOW_TRANSPARENT) { 3471 // D3D12 removed the swap effect needed to support transparent windows, use D3D11 instead 3472 return SDL_SetError("The direct3d12 renderer doesn't work with transparent windows"); 3473 } 3474 3475 SDL_SetupRendererColorspace(renderer, create_props); 3476 3477 if (renderer->output_colorspace != SDL_COLORSPACE_SRGB && 3478 renderer->output_colorspace != SDL_COLORSPACE_SRGB_LINEAR 3479 /*&& renderer->output_colorspace != SDL_COLORSPACE_HDR10*/) { 3480 return SDL_SetError("Unsupported output colorspace"); 3481 } 3482 3483 data = (D3D12_RenderData *)SDL_calloc(1, sizeof(*data)); 3484 if (!data) { 3485 return false; 3486 } 3487 3488 data->identity = MatrixIdentity(); 3489 3490 renderer->WindowEvent = D3D12_WindowEvent; 3491 renderer->SupportsBlendMode = D3D12_SupportsBlendMode; 3492 renderer->CreatePalette = D3D12_CreatePalette; 3493 renderer->UpdatePalette = D3D12_UpdatePalette; 3494 renderer->DestroyPalette = D3D12_DestroyPalette; 3495 renderer->CreateTexture = D3D12_CreateTexture; 3496 renderer->UpdateTexture = D3D12_UpdateTexture; 3497#ifdef SDL_HAVE_YUV 3498 renderer->UpdateTextureYUV = D3D12_UpdateTextureYUV; 3499 renderer->UpdateTextureNV = D3D12_UpdateTextureNV; 3500#endif 3501 renderer->LockTexture = D3D12_LockTexture; 3502 renderer->UnlockTexture = D3D12_UnlockTexture; 3503 renderer->SetRenderTarget = D3D12_SetRenderTarget; 3504 renderer->QueueSetViewport = D3D12_QueueNoOp; 3505 renderer->QueueSetDrawColor = D3D12_QueueNoOp; 3506 renderer->QueueDrawPoints = D3D12_QueueDrawPoints; 3507 renderer->QueueDrawLines = D3D12_QueueDrawPoints; // lines and points queue vertices the same way. 3508 renderer->QueueGeometry = D3D12_QueueGeometry; 3509 renderer->InvalidateCachedState = D3D12_InvalidateCachedState; 3510 renderer->RunCommandQueue = D3D12_RunCommandQueue; 3511 renderer->RenderReadPixels = D3D12_RenderReadPixels; 3512 renderer->RenderPresent = D3D12_RenderPresent; 3513 renderer->DestroyTexture = D3D12_DestroyTexture; 3514 renderer->DestroyRenderer = D3D12_DestroyRenderer; 3515 renderer->SetVSync = D3D12_SetVSync; 3516#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES) 3517 renderer->GDKSuspendRenderer = D3D12_GDKSuspendRenderer; 3518 renderer->GDKResumeRenderer = D3D12_GDKResumeRenderer; 3519#endif 3520 renderer->internal = data; 3521 D3D12_InvalidateCachedState(renderer); 3522 3523 renderer->name = D3D12_RenderDriver.name; 3524 SDL_SetNumberProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_MAX_TEXTURE_SIZE_NUMBER, 16384); 3525 3526 data->syncInterval = 0; 3527 data->presentFlags = DXGI_PRESENT_ALLOW_TEARING; 3528 3529 /* HACK: make sure the SDL_Renderer references the SDL_Window data now, in 3530 * order to give init functions access to the underlying window handle: 3531 */ 3532 renderer->window = window; 3533 3534 // Initialize Direct3D resources 3535 if (FAILED(D3D12_CreateDeviceResources(renderer))) { 3536 return false; 3537 } 3538 if (FAILED(D3D12_CreateWindowSizeDependentResources(renderer))) { 3539 return false; 3540 } 3541 3542 for (int i = 0; i < SDL_arraysize(dxgi_format_map); i++) { 3543 D3D12_FEATURE_DATA_FORMAT_SUPPORT unorm, srgb; 3544 3545 unorm.Format = dxgi_format_map[i].unorm; 3546 if (FAILED(ID3D12Device_CheckFeatureSupport(data->d3dDevice, 3547 D3D12_FEATURE_FORMAT_SUPPORT, 3548 &unorm, 3549 sizeof(unorm)))) { 3550 continue; 3551 } 3552 3553 srgb.Format = dxgi_format_map[i].srgb; 3554 if (FAILED(ID3D12Device_CheckFeatureSupport(data->d3dDevice, 3555 D3D12_FEATURE_FORMAT_SUPPORT, 3556 &srgb, 3557 sizeof(srgb)))) { 3558 continue; 3559 } 3560 3561 if ((unorm.Support1 & D3D12_FORMAT_SUPPORT1_TEXTURE2D) && 3562 (srgb.Support1 & D3D12_FORMAT_SUPPORT1_TEXTURE2D)) { 3563 SDL_AddSupportedTextureFormat(renderer, dxgi_format_map[i].sdl); 3564 } 3565 } 3566 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_INDEX8); 3567 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_YV12); 3568 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_IYUV); 3569 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV12); 3570 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV21); 3571 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P010); 3572 3573 return true; 3574} 3575 3576SDL_RenderDriver D3D12_RenderDriver = { 3577 D3D12_CreateRenderer, "direct3d12" 3578}; 3579 3580// Ends C function definitions when using C++ 3581#ifdef __cplusplus 3582} 3583#endif 3584 3585#endif // SDL_VIDEO_RENDER_D3D12 3586
[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.