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