1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifdef SDL_VIDEO_RENDER_D3D11
24
25#define COBJMACROS
26#include "../../core/windows/SDL_windows.h"
27#include "../../video/windows/SDL_windowswindow.h"
28#include "../SDL_sysrender.h"
29#include "../SDL_d3dmath.h"
30#include "../../video/SDL_pixels_c.h"
31
32#include <d3d11_1.h>
33#include <dxgi1_4.h>
34#include <dxgidebug.h>
35
36#include "SDL_shaders_d3d11.h"
37
38#if defined(_MSC_VER) && !defined(__clang__)
39#define SDL_COMPOSE_ERROR(str) __FUNCTION__ ", " str
40#else
41#define SDL_COMPOSE_ERROR(str) SDL_STRINGIFY_ARG(__FUNCTION__) ", " str
42#endif
43
44#define SAFE_RELEASE(X) \
45 if ((X)) { \
46 IUnknown_Release(SDL_static_cast(IUnknown *, X)); \
47 X = NULL; \
48 }
49
50/* !!! FIXME: vertex buffer bandwidth could be lower; only use UV coords when
51 !!! FIXME: textures are needed. */
52
53// Sampler types
54typedef enum
55{
56 D3D11_SAMPLER_NEAREST_CLAMP,
57 D3D11_SAMPLER_NEAREST_WRAP,
58 D3D11_SAMPLER_LINEAR_CLAMP,
59 D3D11_SAMPLER_LINEAR_WRAP,
60 D3D11_SAMPLER_COUNT
61} D3D11_Sampler;
62
63// Vertex shader, common values
64typedef struct
65{
66 Float4X4 model;
67 Float4X4 projectionAndView;
68} D3D11_VertexShaderConstants;
69
70// These should mirror the definitions in D3D11_PixelShader_Common.hlsli
71//static const float TONEMAP_NONE = 0;
72//static const float TONEMAP_LINEAR = 1;
73static const float TONEMAP_CHROME = 2;
74
75//static const float TEXTURETYPE_NONE = 0;
76static const float TEXTURETYPE_RGB = 1;
77static const float TEXTURETYPE_RGB_PIXELART = 2;
78static const float TEXTURETYPE_NV12 = 3;
79static const float TEXTURETYPE_NV21 = 4;
80static const float TEXTURETYPE_YUV = 5;
81
82static const float INPUTTYPE_UNSPECIFIED = 0;
83static const float INPUTTYPE_SRGB = 1;
84static const float INPUTTYPE_SCRGB = 2;
85static const float INPUTTYPE_HDR10 = 3;
86
87typedef struct
88{
89 float scRGB_output;
90 float texture_type;
91 float input_type;
92 float color_scale;
93
94 float texel_width;
95 float texel_height;
96 float texture_width;
97 float texture_height;
98
99 float tonemap_method;
100 float tonemap_factor1;
101 float tonemap_factor2;
102 float sdr_white_point;
103
104 float YCbCr_matrix[16];
105} D3D11_PixelShaderConstants;
106
107typedef struct
108{
109 ID3D11Buffer *constants;
110 D3D11_PixelShaderConstants shader_constants;
111} D3D11_PixelShaderState;
112
113// Per-vertex data
114typedef struct
115{
116 Float2 pos;
117 Float2 tex;
118 SDL_FColor color;
119} D3D11_VertexPositionColor;
120
121// Per-texture data
122typedef struct
123{
124 int w, h;
125 ID3D11Texture2D *mainTexture;
126 ID3D11ShaderResourceView *mainTextureResourceView;
127 ID3D11RenderTargetView *mainTextureRenderTargetView;
128 ID3D11Texture2D *stagingTexture;
129 int lockedTexturePositionX;
130 int lockedTexturePositionY;
131 D3D11_Shader shader;
132 const float *YCbCr_matrix;
133#ifdef SDL_HAVE_YUV
134 // YV12 texture support
135 bool yuv;
136 ID3D11Texture2D *mainTextureU;
137 ID3D11ShaderResourceView *mainTextureResourceViewU;
138 ID3D11Texture2D *mainTextureV;
139 ID3D11ShaderResourceView *mainTextureResourceViewV;
140
141 // NV12 texture support
142 bool nv12;
143 ID3D11ShaderResourceView *mainTextureResourceViewNV;
144
145 Uint8 *pixels;
146 int pitch;
147 SDL_Rect locked_rect;
148#endif
149} D3D11_TextureData;
150
151// Blend mode data
152typedef struct
153{
154 SDL_BlendMode blendMode;
155 ID3D11BlendState *blendState;
156} D3D11_BlendMode;
157
158// Private renderer data
159typedef struct
160{
161 SDL_SharedObject *hDXGIMod;
162 SDL_SharedObject *hD3D11Mod;
163 IDXGIFactory2 *dxgiFactory;
164 IDXGIAdapter *dxgiAdapter;
165 IDXGIDebug *dxgiDebug;
166 ID3D11Device1 *d3dDevice;
167 ID3D11DeviceContext1 *d3dContext;
168 IDXGISwapChain1 *swapChain;
169 DXGI_SWAP_EFFECT swapEffect;
170 UINT syncInterval;
171 UINT presentFlags;
172 ID3D11RenderTargetView *mainRenderTargetView;
173 ID3D11RenderTargetView *currentOffscreenRenderTargetView;
174 ID3D11InputLayout *inputLayout;
175 ID3D11Buffer *vertexBuffers[8];
176 size_t vertexBufferSizes[8];
177 ID3D11VertexShader *vertexShader;
178 ID3D11PixelShader *pixelShaders[NUM_SHADERS];
179 int blendModesCount;
180 D3D11_BlendMode *blendModes;
181 ID3D11SamplerState *samplers[D3D11_SAMPLER_COUNT];
182 D3D_FEATURE_LEVEL featureLevel;
183 bool pixelSizeChanged;
184
185 // Rasterizers
186 ID3D11RasterizerState *mainRasterizer;
187 ID3D11RasterizerState *clippedRasterizer;
188
189 // Vertex buffer constants
190 D3D11_VertexShaderConstants vertexShaderConstantsData;
191 ID3D11Buffer *vertexShaderConstants;
192
193 // Cached renderer properties
194 DXGI_MODE_ROTATION rotation;
195 ID3D11RenderTargetView *currentRenderTargetView;
196 ID3D11RasterizerState *currentRasterizerState;
197 ID3D11BlendState *currentBlendState;
198 D3D11_Shader currentShader;
199 D3D11_PixelShaderState currentShaderState[NUM_SHADERS];
200 ID3D11ShaderResourceView *currentShaderResource;
201 ID3D11SamplerState *currentSampler;
202 bool cliprectDirty;
203 bool currentCliprectEnabled;
204 SDL_Rect currentCliprect;
205 SDL_Rect currentViewport;
206 int currentViewportRotation;
207 bool viewportDirty;
208 Float4X4 identity;
209 int currentVertexBuffer;
210} D3D11_RenderData;
211
212// Define D3D GUIDs here so we don't have to include uuid.lib.
213
214#ifdef HAVE_GCC_DIAGNOSTIC_PRAGMA
215#pragma GCC diagnostic push
216#pragma GCC diagnostic ignored "-Wunused-const-variable"
217#endif
218
219static const GUID SDL_IID_IDXGIFactory2 = { 0x50c83a1c, 0xe072, 0x4c48, { 0x87, 0xb0, 0x36, 0x30, 0xfa, 0x36, 0xa6, 0xd0 } };
220static const GUID SDL_IID_IDXGIDevice1 = { 0x77db970f, 0x6276, 0x48ba, { 0xba, 0x28, 0x07, 0x01, 0x43, 0xb4, 0x39, 0x2c } };
221static const GUID SDL_IID_ID3D11Texture2D = { 0x6f15aaf2, 0xd208, 0x4e89, { 0x9a, 0xb4, 0x48, 0x95, 0x35, 0xd3, 0x4f, 0x9c } };
222static const GUID SDL_IID_ID3D11Device1 = { 0xa04bfb29, 0x08ef, 0x43d6, { 0xa4, 0x9c, 0xa9, 0xbd, 0xbd, 0xcb, 0xe6, 0x86 } };
223static const GUID SDL_IID_ID3D11DeviceContext1 = { 0xbb2c6faa, 0xb5fb, 0x4082, { 0x8e, 0x6b, 0x38, 0x8b, 0x8c, 0xfa, 0x90, 0xe1 } };
224static const GUID SDL_IID_IDXGISwapChain2 = { 0x94d99bdb, 0xf1f8, 0x4ab0, { 0xb2, 0x36, 0x7d, 0xa0, 0x17, 0x0e, 0xda, 0xb1 } };
225static const GUID SDL_IID_IDXGIDebug1 = { 0xc5a05f0c, 0x16f2, 0x4adf, { 0x9f, 0x4d, 0xa8, 0xc4, 0xd5, 0x8a, 0xc5, 0x50 } };
226static const GUID SDL_IID_IDXGIInfoQueue = { 0xD67441C7, 0x672A, 0x476f, { 0x9E, 0x82, 0xCD, 0x55, 0xB4, 0x49, 0x49, 0xCE } };
227static const GUID SDL_DXGI_DEBUG_ALL = { 0xe48ae283, 0xda80, 0x490b, { 0x87, 0xe6, 0x43, 0xe9, 0xa9, 0xcf, 0xda, 0x8 } };
228
229#ifdef HAVE_GCC_DIAGNOSTIC_PRAGMA
230#pragma GCC diagnostic pop
231#endif
232
233SDL_PixelFormat D3D11_DXGIFormatToSDLPixelFormat(DXGI_FORMAT dxgiFormat)
234{
235 switch (dxgiFormat) {
236 case DXGI_FORMAT_B8G8R8A8_UNORM:
237 case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:
238 return SDL_PIXELFORMAT_ARGB8888;
239 case DXGI_FORMAT_R8G8B8A8_UNORM:
240 case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
241 return SDL_PIXELFORMAT_ABGR8888;
242 case DXGI_FORMAT_B8G8R8X8_UNORM:
243 case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB:
244 return SDL_PIXELFORMAT_XRGB8888;
245 case DXGI_FORMAT_R10G10B10A2_UNORM:
246 return SDL_PIXELFORMAT_ABGR2101010;
247 case DXGI_FORMAT_R16G16B16A16_FLOAT:
248 return SDL_PIXELFORMAT_RGBA64_FLOAT;
249 default:
250 return SDL_PIXELFORMAT_UNKNOWN;
251 }
252}
253
254static DXGI_FORMAT SDLPixelFormatToDXGITextureFormat(Uint32 format, Uint32 output_colorspace)
255{
256 switch (format) {
257 case SDL_PIXELFORMAT_RGBA64_FLOAT:
258 return DXGI_FORMAT_R16G16B16A16_FLOAT;
259 case SDL_PIXELFORMAT_ABGR2101010:
260 return DXGI_FORMAT_R10G10B10A2_UNORM;
261 case SDL_PIXELFORMAT_ARGB8888:
262 if (output_colorspace == SDL_COLORSPACE_SRGB_LINEAR) {
263 return DXGI_FORMAT_B8G8R8A8_UNORM_SRGB;
264 }
265 return DXGI_FORMAT_B8G8R8A8_UNORM;
266 case SDL_PIXELFORMAT_ABGR8888:
267 if (output_colorspace == SDL_COLORSPACE_SRGB_LINEAR) {
268 return DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
269 }
270 return DXGI_FORMAT_R8G8B8A8_UNORM;
271 case SDL_PIXELFORMAT_XRGB8888:
272 if (output_colorspace == SDL_COLORSPACE_SRGB_LINEAR) {
273 return DXGI_FORMAT_B8G8R8X8_UNORM_SRGB;
274 }
275 return DXGI_FORMAT_B8G8R8X8_UNORM;
276 case SDL_PIXELFORMAT_YV12:
277 case SDL_PIXELFORMAT_IYUV:
278 return DXGI_FORMAT_R8_UNORM;
279 case SDL_PIXELFORMAT_NV12:
280 case SDL_PIXELFORMAT_NV21:
281 return DXGI_FORMAT_NV12;
282 case SDL_PIXELFORMAT_P010:
283 return DXGI_FORMAT_P010;
284 default:
285 return DXGI_FORMAT_UNKNOWN;
286 }
287}
288
289static DXGI_FORMAT SDLPixelFormatToDXGIMainResourceViewFormat(Uint32 format, Uint32 colorspace)
290{
291 switch (format) {
292 case SDL_PIXELFORMAT_RGBA64_FLOAT:
293 return DXGI_FORMAT_R16G16B16A16_FLOAT;
294 case SDL_PIXELFORMAT_ABGR2101010:
295 return DXGI_FORMAT_R10G10B10A2_UNORM;
296 case SDL_PIXELFORMAT_ARGB8888:
297 if (colorspace == SDL_COLORSPACE_SRGB_LINEAR) {
298 return DXGI_FORMAT_B8G8R8A8_UNORM_SRGB;
299 }
300 return DXGI_FORMAT_B8G8R8A8_UNORM;
301 case SDL_PIXELFORMAT_ABGR8888:
302 if (colorspace == SDL_COLORSPACE_SRGB_LINEAR) {
303 return DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
304 }
305 return DXGI_FORMAT_R8G8B8A8_UNORM;
306 case SDL_PIXELFORMAT_XRGB8888:
307 if (colorspace == SDL_COLORSPACE_SRGB_LINEAR) {
308 return DXGI_FORMAT_B8G8R8X8_UNORM_SRGB;
309 }
310 return DXGI_FORMAT_B8G8R8X8_UNORM;
311 case SDL_PIXELFORMAT_YV12:
312 case SDL_PIXELFORMAT_IYUV:
313 case SDL_PIXELFORMAT_NV12: // For the Y texture
314 case SDL_PIXELFORMAT_NV21: // For the Y texture
315 return DXGI_FORMAT_R8_UNORM;
316 case SDL_PIXELFORMAT_P010: // For the Y texture
317 return DXGI_FORMAT_R16_UNORM;
318 default:
319 return DXGI_FORMAT_UNKNOWN;
320 }
321}
322
323static void D3D11_DestroyTexture(SDL_Renderer *renderer, SDL_Texture *texture);
324
325static void D3D11_ReleaseAll(SDL_Renderer *renderer)
326{
327 D3D11_RenderData *data = (D3D11_RenderData *)renderer->internal;
328
329 // Release all textures
330 for (SDL_Texture *texture = renderer->textures; texture; texture = texture->next) {
331 D3D11_DestroyTexture(renderer, texture);
332 }
333
334 // Release/reset everything else
335 if (data) {
336 int i;
337
338 // Make sure the swap chain is fully released
339 if (data->d3dContext) {
340 ID3D11DeviceContext_ClearState(data->d3dContext);
341 ID3D11DeviceContext_Flush(data->d3dContext);
342 }
343
344 SAFE_RELEASE(data->vertexShaderConstants);
345 SAFE_RELEASE(data->clippedRasterizer);
346 SAFE_RELEASE(data->mainRasterizer);
347 for (i = 0; i < SDL_arraysize(data->samplers); ++i) {
348 SAFE_RELEASE(data->samplers[i]);
349 }
350
351 if (data->blendModesCount > 0) {
352 for (i = 0; i < data->blendModesCount; ++i) {
353 SAFE_RELEASE(data->blendModes[i].blendState);
354 }
355 SDL_free(data->blendModes);
356 data->blendModes = NULL;
357 data->blendModesCount = 0;
358 }
359 for (i = 0; i < SDL_arraysize(data->pixelShaders); ++i) {
360 SAFE_RELEASE(data->pixelShaders[i]);
361 }
362 for (i = 0; i < SDL_arraysize(data->currentShaderState); ++i) {
363 SAFE_RELEASE(data->currentShaderState[i].constants);
364 }
365 SAFE_RELEASE(data->vertexShader);
366 for (i = 0; i < SDL_arraysize(data->vertexBuffers); ++i) {
367 SAFE_RELEASE(data->vertexBuffers[i]);
368 }
369 SAFE_RELEASE(data->inputLayout);
370 SAFE_RELEASE(data->mainRenderTargetView);
371 SAFE_RELEASE(data->swapChain);
372
373 SAFE_RELEASE(data->d3dContext);
374 SAFE_RELEASE(data->d3dDevice);
375 SAFE_RELEASE(data->dxgiAdapter);
376 SAFE_RELEASE(data->dxgiFactory);
377
378 data->swapEffect = (DXGI_SWAP_EFFECT)0;
379 data->rotation = DXGI_MODE_ROTATION_UNSPECIFIED;
380 data->currentOffscreenRenderTargetView = NULL;
381 data->currentRenderTargetView = NULL;
382 data->currentRasterizerState = NULL;
383 data->currentBlendState = NULL;
384 data->currentShader = SHADER_NONE;
385 SDL_zero(data->currentShaderState);
386 data->currentShaderResource = NULL;
387 data->currentSampler = NULL;
388
389 // Check for any leaks if in debug mode
390 if (data->dxgiDebug) {
391 DXGI_DEBUG_RLO_FLAGS rloFlags = (DXGI_DEBUG_RLO_FLAGS)(DXGI_DEBUG_RLO_DETAIL | DXGI_DEBUG_RLO_IGNORE_INTERNAL);
392 IDXGIDebug_ReportLiveObjects(data->dxgiDebug, SDL_DXGI_DEBUG_ALL, rloFlags);
393 SAFE_RELEASE(data->dxgiDebug);
394 }
395
396 /* Unload the D3D libraries. This should be done last, in order
397 * to prevent IUnknown::Release() calls from crashing.
398 */
399 if (data->hD3D11Mod) {
400 SDL_UnloadObject(data->hD3D11Mod);
401 data->hD3D11Mod = NULL;
402 }
403 if (data->hDXGIMod) {
404 SDL_UnloadObject(data->hDXGIMod);
405 data->hDXGIMod = NULL;
406 }
407 }
408}
409
410static void D3D11_DestroyRenderer(SDL_Renderer *renderer)
411{
412 D3D11_RenderData *data = (D3D11_RenderData *)renderer->internal;
413 if (data) {
414 D3D11_ReleaseAll(renderer);
415 SDL_free(data);
416 }
417}
418
419static D3D11_BLEND GetBlendFunc(SDL_BlendFactor factor)
420{
421 switch (factor) {
422 case SDL_BLENDFACTOR_ZERO:
423 return D3D11_BLEND_ZERO;
424 case SDL_BLENDFACTOR_ONE:
425 return D3D11_BLEND_ONE;
426 case SDL_BLENDFACTOR_SRC_COLOR:
427 return D3D11_BLEND_SRC_COLOR;
428 case SDL_BLENDFACTOR_ONE_MINUS_SRC_COLOR:
429 return D3D11_BLEND_INV_SRC_COLOR;
430 case SDL_BLENDFACTOR_SRC_ALPHA:
431 return D3D11_BLEND_SRC_ALPHA;
432 case SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA:
433 return D3D11_BLEND_INV_SRC_ALPHA;
434 case SDL_BLENDFACTOR_DST_COLOR:
435 return D3D11_BLEND_DEST_COLOR;
436 case SDL_BLENDFACTOR_ONE_MINUS_DST_COLOR:
437 return D3D11_BLEND_INV_DEST_COLOR;
438 case SDL_BLENDFACTOR_DST_ALPHA:
439 return D3D11_BLEND_DEST_ALPHA;
440 case SDL_BLENDFACTOR_ONE_MINUS_DST_ALPHA:
441 return D3D11_BLEND_INV_DEST_ALPHA;
442 default:
443 return (D3D11_BLEND)0;
444 }
445}
446
447static D3D11_BLEND_OP GetBlendEquation(SDL_BlendOperation operation)
448{
449 switch (operation) {
450 case SDL_BLENDOPERATION_ADD:
451 return D3D11_BLEND_OP_ADD;
452 case SDL_BLENDOPERATION_SUBTRACT:
453 return D3D11_BLEND_OP_SUBTRACT;
454 case SDL_BLENDOPERATION_REV_SUBTRACT:
455 return D3D11_BLEND_OP_REV_SUBTRACT;
456 case SDL_BLENDOPERATION_MINIMUM:
457 return D3D11_BLEND_OP_MIN;
458 case SDL_BLENDOPERATION_MAXIMUM:
459 return D3D11_BLEND_OP_MAX;
460 default:
461 return (D3D11_BLEND_OP)0;
462 }
463}
464
465static ID3D11BlendState *D3D11_CreateBlendState(SDL_Renderer *renderer, SDL_BlendMode blendMode)
466{
467 D3D11_RenderData *data = (D3D11_RenderData *)renderer->internal;
468 SDL_BlendFactor srcColorFactor = SDL_GetBlendModeSrcColorFactor(blendMode);
469 SDL_BlendFactor srcAlphaFactor = SDL_GetBlendModeSrcAlphaFactor(blendMode);
470 SDL_BlendOperation colorOperation = SDL_GetBlendModeColorOperation(blendMode);
471 SDL_BlendFactor dstColorFactor = SDL_GetBlendModeDstColorFactor(blendMode);
472 SDL_BlendFactor dstAlphaFactor = SDL_GetBlendModeDstAlphaFactor(blendMode);
473 SDL_BlendOperation alphaOperation = SDL_GetBlendModeAlphaOperation(blendMode);
474 ID3D11BlendState *blendState = NULL;
475 D3D11_BlendMode *blendModes;
476 HRESULT result = S_OK;
477
478 D3D11_BLEND_DESC blendDesc;
479 SDL_zero(blendDesc);
480 blendDesc.AlphaToCoverageEnable = FALSE;
481 blendDesc.IndependentBlendEnable = FALSE;
482 blendDesc.RenderTarget[0].BlendEnable = TRUE;
483 blendDesc.RenderTarget[0].SrcBlend = GetBlendFunc(srcColorFactor);
484 blendDesc.RenderTarget[0].DestBlend = GetBlendFunc(dstColorFactor);
485 blendDesc.RenderTarget[0].BlendOp = GetBlendEquation(colorOperation);
486 blendDesc.RenderTarget[0].SrcBlendAlpha = GetBlendFunc(srcAlphaFactor);
487 blendDesc.RenderTarget[0].DestBlendAlpha = GetBlendFunc(dstAlphaFactor);
488 blendDesc.RenderTarget[0].BlendOpAlpha = GetBlendEquation(alphaOperation);
489 blendDesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
490 result = ID3D11Device_CreateBlendState(data->d3dDevice, &blendDesc, &blendState);
491 if (FAILED(result)) {
492 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreateBlendState"), result);
493 return NULL;
494 }
495
496 blendModes = (D3D11_BlendMode *)SDL_realloc(data->blendModes, (data->blendModesCount + 1) * sizeof(*blendModes));
497 if (!blendModes) {
498 SAFE_RELEASE(blendState);
499 return NULL;
500 }
501 blendModes[data->blendModesCount].blendMode = blendMode;
502 blendModes[data->blendModesCount].blendState = blendState;
503 data->blendModes = blendModes;
504 ++data->blendModesCount;
505
506 return blendState;
507}
508
509// Create resources that depend on the device.
510static HRESULT D3D11_CreateDeviceResources(SDL_Renderer *renderer)
511{
512 typedef HRESULT(WINAPI * PFN_CREATE_DXGI_FACTORY)(REFIID riid, void **ppFactory);
513 typedef HRESULT(WINAPI * PFN_CREATE_DXGI_FACTORY2)(UINT flags, REFIID riid, void **ppFactory);
514 PFN_CREATE_DXGI_FACTORY CreateDXGIFactoryFunc = NULL;
515 PFN_CREATE_DXGI_FACTORY2 CreateDXGIFactory2Func = NULL;
516 D3D11_RenderData *data = (D3D11_RenderData *)renderer->internal;
517 PFN_D3D11_CREATE_DEVICE D3D11CreateDeviceFunc;
518 ID3D11Device *d3dDevice = NULL;
519 ID3D11DeviceContext *d3dContext = NULL;
520 IDXGIDevice1 *dxgiDevice = NULL;
521 HRESULT result = S_OK;
522 UINT creationFlags = 0;
523 bool createDebug;
524
525 /* This array defines the set of DirectX hardware feature levels this app will support.
526 * Note the ordering should be preserved.
527 * Don't forget to declare your application's minimum required feature level in its
528 * description. All applications are assumed to support 9.1 unless otherwise stated.
529 */
530 D3D_FEATURE_LEVEL featureLevels[] = {
531 D3D_FEATURE_LEVEL_11_1,
532 D3D_FEATURE_LEVEL_11_0,
533 D3D_FEATURE_LEVEL_10_1,
534 D3D_FEATURE_LEVEL_10_0,
535 D3D_FEATURE_LEVEL_9_3,
536 D3D_FEATURE_LEVEL_9_2,
537 D3D_FEATURE_LEVEL_9_1
538 };
539
540 D3D11_BUFFER_DESC constantBufferDesc;
541 D3D11_SAMPLER_DESC samplerDesc;
542 D3D11_RASTERIZER_DESC rasterDesc;
543
544 // See if we need debug interfaces
545 createDebug = SDL_GetHintBoolean(SDL_HINT_RENDER_DIRECT3D11_DEBUG, false);
546
547 data->hDXGIMod = SDL_LoadObject("dxgi.dll");
548 if (!data->hDXGIMod) {
549 result = E_FAIL;
550 goto done;
551 }
552
553 CreateDXGIFactory2Func = (PFN_CREATE_DXGI_FACTORY2)SDL_LoadFunction(data->hDXGIMod, "CreateDXGIFactory2");
554 if (!CreateDXGIFactory2Func) {
555 CreateDXGIFactoryFunc = (PFN_CREATE_DXGI_FACTORY)SDL_LoadFunction(data->hDXGIMod, "CreateDXGIFactory");
556 if (!CreateDXGIFactoryFunc) {
557 result = E_FAIL;
558 goto done;
559 }
560 }
561
562 data->hD3D11Mod = SDL_LoadObject("d3d11.dll");
563 if (!data->hD3D11Mod) {
564 result = E_FAIL;
565 goto done;
566 }
567
568 D3D11CreateDeviceFunc = (PFN_D3D11_CREATE_DEVICE)SDL_LoadFunction(data->hD3D11Mod, "D3D11CreateDevice");
569 if (!D3D11CreateDeviceFunc) {
570 result = E_FAIL;
571 goto done;
572 }
573
574 if (createDebug) {
575#ifdef __IDXGIInfoQueue_INTERFACE_DEFINED__
576 IDXGIInfoQueue *dxgiInfoQueue = NULL;
577 PFN_CREATE_DXGI_FACTORY2 DXGIGetDebugInterfaceFunc;
578
579 // If the debug hint is set, also create the DXGI factory in debug mode
580 DXGIGetDebugInterfaceFunc = (PFN_CREATE_DXGI_FACTORY2)SDL_LoadFunction(data->hDXGIMod, "DXGIGetDebugInterface1");
581 if (!DXGIGetDebugInterfaceFunc) {
582 result = E_FAIL;
583 goto done;
584 }
585
586 result = DXGIGetDebugInterfaceFunc(0, &SDL_IID_IDXGIDebug1, (void **)&data->dxgiDebug);
587 if (FAILED(result)) {
588 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("DXGIGetDebugInterface1"), result);
589 goto done;
590 }
591
592 result = DXGIGetDebugInterfaceFunc(0, &SDL_IID_IDXGIInfoQueue, (void **)&dxgiInfoQueue);
593 if (FAILED(result)) {
594 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("DXGIGetDebugInterface1"), result);
595 goto done;
596 }
597
598 IDXGIInfoQueue_SetBreakOnSeverity(dxgiInfoQueue, SDL_DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_ERROR, TRUE);
599 IDXGIInfoQueue_SetBreakOnSeverity(dxgiInfoQueue, SDL_DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_CORRUPTION, TRUE);
600 SAFE_RELEASE(dxgiInfoQueue);
601#endif // __IDXGIInfoQueue_INTERFACE_DEFINED__
602 creationFlags = DXGI_CREATE_FACTORY_DEBUG;
603 }
604
605 if (CreateDXGIFactory2Func) {
606 result = CreateDXGIFactory2Func(creationFlags, &SDL_IID_IDXGIFactory2, (void **)&data->dxgiFactory);
607 } else {
608 result = CreateDXGIFactoryFunc(&SDL_IID_IDXGIFactory2, (void **)&data->dxgiFactory);
609 }
610 if (FAILED(result)) {
611 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("CreateDXGIFactory"), result);
612 goto done;
613 }
614
615 // FIXME: Should we use the default adapter?
616 result = IDXGIFactory2_EnumAdapters(data->dxgiFactory, 0, &data->dxgiAdapter);
617 if (FAILED(result)) {
618 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("D3D11CreateDevice"), result);
619 goto done;
620 }
621
622 /* This flag adds support for surfaces with a different color channel ordering
623 * than the API default. It is required for compatibility with Direct2D.
624 */
625 creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
626
627 // Make sure Direct3D's debugging feature gets used, if the app requests it.
628 if (createDebug) {
629 creationFlags |= D3D11_CREATE_DEVICE_DEBUG;
630 }
631
632 // Create a single-threaded device unless the app requests otherwise.
633 if (!SDL_GetHintBoolean(SDL_HINT_RENDER_DIRECT3D_THREADSAFE, false)) {
634 creationFlags |= D3D11_CREATE_DEVICE_SINGLETHREADED;
635 }
636
637 // Create the Direct3D 11 API device object and a corresponding context.
638 result = D3D11CreateDeviceFunc(
639 data->dxgiAdapter,
640 D3D_DRIVER_TYPE_UNKNOWN,
641 NULL,
642 creationFlags, // Set set debug and Direct2D compatibility flags.
643 featureLevels, // List of feature levels this app can support.
644 SDL_arraysize(featureLevels),
645 D3D11_SDK_VERSION, // Always set this to D3D11_SDK_VERSION for Windows Store apps.
646 &d3dDevice, // Returns the Direct3D device created.
647 &data->featureLevel, // Returns feature level of device created.
648 &d3dContext // Returns the device immediate context.
649 );
650 if (FAILED(result)) {
651 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("D3D11CreateDevice"), result);
652 goto done;
653 }
654
655 result = ID3D11Device_QueryInterface(d3dDevice, &SDL_IID_ID3D11Device1, (void **)&data->d3dDevice);
656 if (FAILED(result)) {
657 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device to ID3D11Device1"), result);
658 goto done;
659 }
660
661 result = ID3D11DeviceContext_QueryInterface(d3dContext, &SDL_IID_ID3D11DeviceContext1, (void **)&data->d3dContext);
662 if (FAILED(result)) {
663 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11DeviceContext to ID3D11DeviceContext1"), result);
664 goto done;
665 }
666
667 result = ID3D11Device_QueryInterface(d3dDevice, &SDL_IID_IDXGIDevice1, (void **)&dxgiDevice);
668 if (FAILED(result)) {
669 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device to IDXGIDevice1"), result);
670 goto done;
671 }
672
673 /* Ensure that DXGI does not queue more than one frame at a time. This both reduces latency and
674 * ensures that the application will only render after each VSync, minimizing power consumption.
675 */
676 result = IDXGIDevice1_SetMaximumFrameLatency(dxgiDevice, 1);
677 if (FAILED(result)) {
678 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("IDXGIDevice1::SetMaximumFrameLatency"), result);
679 goto done;
680 }
681
682 /* Make note of the maximum texture size
683 * Max texture sizes are documented on MSDN, at:
684 * http://msdn.microsoft.com/en-us/library/windows/apps/ff476876.aspx
685 */
686 switch (data->featureLevel) {
687 case D3D_FEATURE_LEVEL_11_1:
688 case D3D_FEATURE_LEVEL_11_0:
689 SDL_SetNumberProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_MAX_TEXTURE_SIZE_NUMBER, 16384);
690 break;
691
692 case D3D_FEATURE_LEVEL_10_1:
693 case D3D_FEATURE_LEVEL_10_0:
694 SDL_SetNumberProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_MAX_TEXTURE_SIZE_NUMBER, 8192);
695 break;
696
697 case D3D_FEATURE_LEVEL_9_3:
698 SDL_SetNumberProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_MAX_TEXTURE_SIZE_NUMBER, 4096);
699 break;
700
701 case D3D_FEATURE_LEVEL_9_2:
702 case D3D_FEATURE_LEVEL_9_1:
703 SDL_SetNumberProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_MAX_TEXTURE_SIZE_NUMBER, 2048);
704 break;
705
706 default:
707 SDL_SetError("%s, Unexpected feature level: %d", __FUNCTION__, data->featureLevel);
708 result = E_FAIL;
709 goto done;
710 }
711
712 if (!D3D11_CreateVertexShader(data->d3dDevice, &data->vertexShader, &data->inputLayout)) {
713 goto done;
714 }
715
716 // Setup space to hold vertex shader constants:
717 SDL_zero(constantBufferDesc);
718 constantBufferDesc.ByteWidth = sizeof(D3D11_VertexShaderConstants);
719 constantBufferDesc.Usage = D3D11_USAGE_DEFAULT;
720 constantBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
721 result = ID3D11Device_CreateBuffer(data->d3dDevice,
722 &constantBufferDesc,
723 NULL,
724 &data->vertexShaderConstants);
725 if (FAILED(result)) {
726 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreateBuffer [vertex shader constants]"), result);
727 goto done;
728 }
729
730 // Create samplers to use when drawing textures:
731 static struct
732 {
733 D3D11_FILTER filter;
734 D3D11_TEXTURE_ADDRESS_MODE address;
735 } samplerParams[] = {
736 { D3D11_FILTER_MIN_MAG_MIP_POINT, D3D11_TEXTURE_ADDRESS_CLAMP },
737 { D3D11_FILTER_MIN_MAG_MIP_POINT, D3D11_TEXTURE_ADDRESS_WRAP },
738 { D3D11_FILTER_MIN_MAG_MIP_LINEAR, D3D11_TEXTURE_ADDRESS_CLAMP },
739 { D3D11_FILTER_MIN_MAG_MIP_LINEAR, D3D11_TEXTURE_ADDRESS_WRAP },
740 };
741 SDL_COMPILE_TIME_ASSERT(samplerParams_SIZE, SDL_arraysize(samplerParams) == D3D11_SAMPLER_COUNT);
742 SDL_zero(samplerDesc);
743 samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
744 samplerDesc.MipLODBias = 0.0f;
745 samplerDesc.MaxAnisotropy = 1;
746 samplerDesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
747 samplerDesc.MinLOD = 0.0f;
748 samplerDesc.MaxLOD = D3D11_FLOAT32_MAX;
749 for (int i = 0; i < SDL_arraysize(samplerParams); ++i) {
750 samplerDesc.Filter = samplerParams[i].filter;
751 samplerDesc.AddressU = samplerParams[i].address;
752 samplerDesc.AddressV = samplerParams[i].address;
753 result = ID3D11Device_CreateSamplerState(data->d3dDevice,
754 &samplerDesc,
755 &data->samplers[i]);
756 if (FAILED(result)) {
757 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreateSamplerState [nearest-pixel filter]"), result);
758 goto done;
759 }
760 }
761
762 // Setup Direct3D rasterizer states
763 SDL_zero(rasterDesc);
764 rasterDesc.AntialiasedLineEnable = FALSE;
765 rasterDesc.CullMode = D3D11_CULL_NONE;
766 rasterDesc.DepthBias = 0;
767 rasterDesc.DepthBiasClamp = 0.0f;
768 rasterDesc.DepthClipEnable = TRUE;
769 rasterDesc.FillMode = D3D11_FILL_SOLID;
770 rasterDesc.FrontCounterClockwise = FALSE;
771 rasterDesc.MultisampleEnable = FALSE;
772 rasterDesc.ScissorEnable = FALSE;
773 rasterDesc.SlopeScaledDepthBias = 0.0f;
774 result = ID3D11Device_CreateRasterizerState(data->d3dDevice, &rasterDesc, &data->mainRasterizer);
775 if (FAILED(result)) {
776 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreateRasterizerState [main rasterizer]"), result);
777 goto done;
778 }
779
780 rasterDesc.ScissorEnable = TRUE;
781 result = ID3D11Device_CreateRasterizerState(data->d3dDevice, &rasterDesc, &data->clippedRasterizer);
782 if (FAILED(result)) {
783 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreateRasterizerState [clipped rasterizer]"), result);
784 goto done;
785 }
786
787 // Create blending states:
788 if (!D3D11_CreateBlendState(renderer, SDL_BLENDMODE_BLEND)) {
789 // D3D11_CreateBlendState will set the SDL error, if it fails
790 goto done;
791 }
792
793 // Setup render state that doesn't change
794 ID3D11DeviceContext_IASetInputLayout(data->d3dContext, data->inputLayout);
795 ID3D11DeviceContext_VSSetShader(data->d3dContext, data->vertexShader, NULL, 0);
796 ID3D11DeviceContext_VSSetConstantBuffers(data->d3dContext, 0, 1, &data->vertexShaderConstants);
797
798 SDL_SetPointerProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_D3D11_DEVICE_POINTER, data->d3dDevice);
799
800done:
801 SAFE_RELEASE(d3dDevice);
802 SAFE_RELEASE(d3dContext);
803 SAFE_RELEASE(dxgiDevice);
804 return result;
805}
806
807static DXGI_MODE_ROTATION D3D11_GetCurrentRotation(void)
808{
809 // FIXME
810 return DXGI_MODE_ROTATION_IDENTITY;
811}
812
813static BOOL D3D11_IsDisplayRotated90Degrees(DXGI_MODE_ROTATION rotation)
814{
815 switch (rotation) {
816 case DXGI_MODE_ROTATION_ROTATE90:
817 case DXGI_MODE_ROTATION_ROTATE270:
818 return TRUE;
819 default:
820 return FALSE;
821 }
822}
823
824static int D3D11_GetRotationForCurrentRenderTarget(SDL_Renderer *renderer)
825{
826 D3D11_RenderData *data = (D3D11_RenderData *)renderer->internal;
827 if (data->currentOffscreenRenderTargetView) {
828 return DXGI_MODE_ROTATION_IDENTITY;
829 } else {
830 return data->rotation;
831 }
832}
833
834static bool D3D11_GetViewportAlignedD3DRect(SDL_Renderer *renderer, const SDL_Rect *sdlRect, D3D11_RECT *outRect, BOOL includeViewportOffset)
835{
836 D3D11_RenderData *data = (D3D11_RenderData *)renderer->internal;
837 const int rotation = D3D11_GetRotationForCurrentRenderTarget(renderer);
838 const SDL_Rect *viewport = &data->currentViewport;
839
840 switch (rotation) {
841 case DXGI_MODE_ROTATION_IDENTITY:
842 outRect->left = sdlRect->x;
843 outRect->right = (LONG)sdlRect->x + sdlRect->w;
844 outRect->top = sdlRect->y;
845 outRect->bottom = (LONG)sdlRect->y + sdlRect->h;
846 if (includeViewportOffset) {
847 outRect->left += viewport->x;
848 outRect->right += viewport->x;
849 outRect->top += viewport->y;
850 outRect->bottom += viewport->y;
851 }
852 break;
853 case DXGI_MODE_ROTATION_ROTATE270:
854 outRect->left = sdlRect->y;
855 outRect->right = (LONG)sdlRect->y + sdlRect->h;
856 outRect->top = viewport->w - sdlRect->x - sdlRect->w;
857 outRect->bottom = viewport->w - sdlRect->x;
858 break;
859 case DXGI_MODE_ROTATION_ROTATE180:
860 outRect->left = viewport->w - sdlRect->x - sdlRect->w;
861 outRect->right = viewport->w - sdlRect->x;
862 outRect->top = viewport->h - sdlRect->y - sdlRect->h;
863 outRect->bottom = viewport->h - sdlRect->y;
864 break;
865 case DXGI_MODE_ROTATION_ROTATE90:
866 outRect->left = viewport->h - sdlRect->y - sdlRect->h;
867 outRect->right = viewport->h - sdlRect->y;
868 outRect->top = sdlRect->x;
869 outRect->bottom = (LONG)sdlRect->x + sdlRect->h;
870 break;
871 default:
872 return SDL_SetError("The physical display is in an unknown or unsupported rotation");
873 }
874 return true;
875}
876
877static HRESULT D3D11_CreateSwapChain(SDL_Renderer *renderer, int w, int h)
878{
879 D3D11_RenderData *data = (D3D11_RenderData *)renderer->internal;
880 IUnknown *coreWindow = NULL;
881 IDXGISwapChain3 *swapChain3 = NULL;
882 HRESULT result = S_OK;
883
884 // Create a swap chain using the same adapter as the existing Direct3D device.
885 DXGI_SWAP_CHAIN_DESC1 swapChainDesc;
886 SDL_zero(swapChainDesc);
887 swapChainDesc.Width = w;
888 swapChainDesc.Height = h;
889 switch (renderer->output_colorspace) {
890 case SDL_COLORSPACE_SRGB_LINEAR:
891 swapChainDesc.Format = DXGI_FORMAT_R16G16B16A16_FLOAT;
892 break;
893 case SDL_COLORSPACE_HDR10:
894 swapChainDesc.Format = DXGI_FORMAT_R10G10B10A2_UNORM;
895 break;
896 default:
897 swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // This is the most common swap chain format.
898 break;
899 }
900 swapChainDesc.Stereo = FALSE;
901 swapChainDesc.SampleDesc.Count = 1; // Don't use multi-sampling.
902 swapChainDesc.SampleDesc.Quality = 0;
903 swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
904 swapChainDesc.BufferCount = 2; // Use double-buffering to minimize latency.
905 if (WIN_IsWindows8OrGreater()) {
906 swapChainDesc.Scaling = DXGI_SCALING_NONE;
907 } else {
908 swapChainDesc.Scaling = DXGI_SCALING_STRETCH;
909 }
910 if (SDL_GetWindowFlags(renderer->window) & SDL_WINDOW_TRANSPARENT) {
911 swapChainDesc.Scaling = DXGI_SCALING_STRETCH;
912 swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
913 } else {
914 swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // All Windows Store apps must use this SwapEffect.
915 }
916 swapChainDesc.Flags = 0;
917
918 if (coreWindow) {
919 result = IDXGIFactory2_CreateSwapChainForCoreWindow(data->dxgiFactory,
920 (IUnknown *)data->d3dDevice,
921 coreWindow,
922 &swapChainDesc,
923 NULL, // Allow on all displays.
924 &data->swapChain);
925 if (FAILED(result)) {
926 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("IDXGIFactory2::CreateSwapChainForCoreWindow"), result);
927 goto done;
928 }
929 } else {
930#if defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK)
931 HWND hwnd = (HWND)SDL_GetPointerProperty(SDL_GetWindowProperties(renderer->window), SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL);
932 if (!hwnd) {
933 SDL_SetError("Couldn't get window handle");
934 result = E_FAIL;
935 goto done;
936 }
937
938 result = IDXGIFactory2_CreateSwapChainForHwnd(data->dxgiFactory,
939 (IUnknown *)data->d3dDevice,
940 hwnd,
941 &swapChainDesc,
942 NULL,
943 NULL, // Allow on all displays.
944 &data->swapChain);
945 if (FAILED(result)) {
946 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("IDXGIFactory2::CreateSwapChainForHwnd"), result);
947 goto done;
948 }
949
950 IDXGIFactory_MakeWindowAssociation(data->dxgiFactory, hwnd, DXGI_MWA_NO_WINDOW_CHANGES);
951#else
952 SDL_SetError(__FUNCTION__ ", Unable to find something to attach a swap chain to");
953 goto done;
954#endif // defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK) / else
955 }
956 data->swapEffect = swapChainDesc.SwapEffect;
957
958 if (SUCCEEDED(IDXGISwapChain1_QueryInterface(data->swapChain, &SDL_IID_IDXGISwapChain2, (void **)&swapChain3))) {
959 UINT colorspace_support = 0;
960 DXGI_COLOR_SPACE_TYPE colorspace;
961 switch (renderer->output_colorspace) {
962 case SDL_COLORSPACE_SRGB_LINEAR:
963 colorspace = DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709;
964 break;
965 case SDL_COLORSPACE_HDR10:
966 colorspace = DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020;
967 break;
968 default:
969 // sRGB
970 colorspace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709;
971 break;
972 }
973 if (SUCCEEDED(IDXGISwapChain3_CheckColorSpaceSupport(swapChain3, colorspace, &colorspace_support)) &&
974 (colorspace_support & DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT)) {
975 result = IDXGISwapChain3_SetColorSpace1(swapChain3, colorspace);
976 if (FAILED(result)) {
977 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("IDXGISwapChain3::SetColorSpace1"), result);
978 goto done;
979 }
980 } else if (colorspace != DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709) {
981 // Not the default, we're not going to be able to present in this colorspace
982 SDL_SetError("Unsupported output colorspace");
983 result = DXGI_ERROR_UNSUPPORTED;
984 }
985 }
986
987 SDL_SetPointerProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_D3D11_SWAPCHAIN_POINTER, data->swapChain);
988
989done:
990 SAFE_RELEASE(swapChain3);
991 SAFE_RELEASE(coreWindow);
992 return result;
993}
994
995static void D3D11_ReleaseMainRenderTargetView(SDL_Renderer *renderer)
996{
997 D3D11_RenderData *data = (D3D11_RenderData *)renderer->internal;
998 ID3D11DeviceContext_OMSetRenderTargets(data->d3dContext, 0, NULL, NULL);
999 SAFE_RELEASE(data->mainRenderTargetView);
1000}
1001
1002// Initialize all resources that change when the window's size changes.
1003static HRESULT D3D11_CreateWindowSizeDependentResources(SDL_Renderer *renderer)
1004{
1005 D3D11_RenderData *data = (D3D11_RenderData *)renderer->internal;
1006 ID3D11Texture2D *backBuffer = NULL;
1007 HRESULT result = S_OK;
1008 int w, h;
1009
1010 // Release the previous render target view
1011 D3D11_ReleaseMainRenderTargetView(renderer);
1012
1013 /* The width and height of the swap chain must be based on the display's
1014 * non-rotated size.
1015 */
1016 SDL_GetWindowSizeInPixels(renderer->window, &w, &h);
1017 data->rotation = D3D11_GetCurrentRotation();
1018 // SDL_Log("%s: windowSize={%d,%d}, orientation=%d", __FUNCTION__, w, h, (int)data->rotation);
1019 if (D3D11_IsDisplayRotated90Degrees(data->rotation)) {
1020 int tmp = w;
1021 w = h;
1022 h = tmp;
1023 }
1024
1025 if (data->swapChain) {
1026 // If the swap chain already exists, resize it.
1027 result = IDXGISwapChain_ResizeBuffers(data->swapChain,
1028 0,
1029 w, h,
1030 DXGI_FORMAT_UNKNOWN,
1031 0);
1032 if (FAILED(result)) {
1033 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("IDXGISwapChain::ResizeBuffers"), result);
1034 goto done;
1035 }
1036 } else {
1037 result = D3D11_CreateSwapChain(renderer, w, h);
1038 if (FAILED(result) || !data->swapChain) {
1039 goto done;
1040 }
1041 }
1042
1043 // Set the proper rotation for the swap chain.
1044 if (WIN_IsWindows8OrGreater()) {
1045 if (data->swapEffect == DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL) {
1046 result = IDXGISwapChain1_SetRotation(data->swapChain, data->rotation);
1047 if (FAILED(result)) {
1048 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("IDXGISwapChain1::SetRotation"), result);
1049 goto done;
1050 }
1051 }
1052 }
1053
1054 result = IDXGISwapChain_GetBuffer(data->swapChain,
1055 0,
1056 &SDL_IID_ID3D11Texture2D,
1057 (void **)&backBuffer);
1058 if (FAILED(result)) {
1059 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("IDXGISwapChain::GetBuffer [back-buffer]"), result);
1060 goto done;
1061 }
1062
1063 // Create a render target view of the swap chain back buffer.
1064 result = ID3D11Device_CreateRenderTargetView(data->d3dDevice,
1065 (ID3D11Resource *)backBuffer,
1066 NULL,
1067 &data->mainRenderTargetView);
1068 if (FAILED(result)) {
1069 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device::CreateRenderTargetView"), result);
1070 goto done;
1071 }
1072
1073 /* Set the swap chain target immediately, so that a target is always set
1074 * even before we get to SetDrawState. Without this it's possible to hit
1075 * null references in places like ReadPixels!
1076 */
1077 ID3D11DeviceContext_OMSetRenderTargets(data->d3dContext,
1078 1,
1079 &data->mainRenderTargetView,
1080 NULL);
1081
1082 data->viewportDirty = true;
1083
1084done:
1085 SAFE_RELEASE(backBuffer);
1086 return result;
1087}
1088
1089static bool D3D11_HandleDeviceLost(SDL_Renderer *renderer)
1090{
1091 bool recovered = false;
1092
1093 D3D11_ReleaseAll(renderer);
1094
1095 if (SUCCEEDED(D3D11_CreateDeviceResources(renderer)) &&
1096 SUCCEEDED(D3D11_CreateWindowSizeDependentResources(renderer))) {
1097 recovered = true;
1098 } else {
1099 SDL_LogError(SDL_LOG_CATEGORY_RENDER, "Renderer couldn't recover from device lost: %s", SDL_GetError());
1100 D3D11_ReleaseAll(renderer);
1101 }
1102
1103 // Let the application know that the device has been reset or lost
1104 SDL_Event event;
1105 SDL_zero(event);
1106 event.type = recovered ? SDL_EVENT_RENDER_DEVICE_RESET : SDL_EVENT_RENDER_DEVICE_LOST;
1107 event.render.windowID = SDL_GetWindowID(SDL_GetRenderWindow(renderer));
1108 SDL_PushEvent(&event);
1109
1110 return recovered;
1111}
1112
1113// This method is called when the window's size changes.
1114static HRESULT D3D11_UpdateForWindowSizeChange(SDL_Renderer *renderer)
1115{
1116 return D3D11_CreateWindowSizeDependentResources(renderer);
1117}
1118
1119static void D3D11_WindowEvent(SDL_Renderer *renderer, const SDL_WindowEvent *event)
1120{
1121 D3D11_RenderData *data = (D3D11_RenderData *)renderer->internal;
1122
1123 if (event->type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED) {
1124 data->pixelSizeChanged = true;
1125 }
1126}
1127
1128static bool D3D11_SupportsBlendMode(SDL_Renderer *renderer, SDL_BlendMode blendMode)
1129{
1130 SDL_BlendFactor srcColorFactor = SDL_GetBlendModeSrcColorFactor(blendMode);
1131 SDL_BlendFactor srcAlphaFactor = SDL_GetBlendModeSrcAlphaFactor(blendMode);
1132 SDL_BlendOperation colorOperation = SDL_GetBlendModeColorOperation(blendMode);
1133 SDL_BlendFactor dstColorFactor = SDL_GetBlendModeDstColorFactor(blendMode);
1134 SDL_BlendFactor dstAlphaFactor = SDL_GetBlendModeDstAlphaFactor(blendMode);
1135 SDL_BlendOperation alphaOperation = SDL_GetBlendModeAlphaOperation(blendMode);
1136
1137 if (!GetBlendFunc(srcColorFactor) || !GetBlendFunc(srcAlphaFactor) ||
1138 !GetBlendEquation(colorOperation) ||
1139 !GetBlendFunc(dstColorFactor) || !GetBlendFunc(dstAlphaFactor) ||
1140 !GetBlendEquation(alphaOperation)) {
1141 return false;
1142 }
1143 return true;
1144}
1145
1146static bool GetTextureProperty(SDL_PropertiesID props, const char *name, ID3D11Texture2D **texture)
1147{
1148 IUnknown *unknown = SDL_GetPointerProperty(props, name, NULL);
1149 if (unknown) {
1150 HRESULT result = IUnknown_QueryInterface(unknown, &SDL_IID_ID3D11Texture2D, (void **)texture);
1151 if (FAILED(result)) {
1152 return WIN_SetErrorFromHRESULT(name, result);
1153 }
1154 }
1155 return true;
1156}
1157
1158static bool D3D11_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props)
1159{
1160 D3D11_RenderData *rendererData = (D3D11_RenderData *)renderer->internal;
1161 D3D11_TextureData *textureData;
1162 HRESULT result;
1163 DXGI_FORMAT textureFormat = SDLPixelFormatToDXGITextureFormat(texture->format, renderer->output_colorspace);
1164 D3D11_TEXTURE2D_DESC textureDesc;
1165 D3D11_SHADER_RESOURCE_VIEW_DESC resourceViewDesc;
1166
1167 if (!rendererData->d3dDevice) {
1168 return SDL_SetError("Device lost and couldn't be recovered");
1169 }
1170
1171 if (textureFormat == DXGI_FORMAT_UNKNOWN) {
1172 return SDL_SetError("%s, An unsupported SDL pixel format (0x%x) was specified",
1173 __FUNCTION__, texture->format);
1174 }
1175
1176 textureData = (D3D11_TextureData *)SDL_calloc(1, sizeof(*textureData));
1177 if (!textureData) {
1178 return false;
1179 }
1180
1181 texture->internal = textureData;
1182
1183 SDL_zero(textureDesc);
1184 textureDesc.Width = texture->w;
1185 textureDesc.Height = texture->h;
1186 textureDesc.MipLevels = 1;
1187 textureDesc.ArraySize = 1;
1188 textureDesc.Format = textureFormat;
1189 textureDesc.SampleDesc.Count = 1;
1190 textureDesc.SampleDesc.Quality = 0;
1191 textureDesc.MiscFlags = 0;
1192
1193 // NV12 textures must have even width and height
1194 if (texture->format == SDL_PIXELFORMAT_NV12 ||
1195 texture->format == SDL_PIXELFORMAT_NV21 ||
1196 texture->format == SDL_PIXELFORMAT_P010) {
1197 textureDesc.Width = (textureDesc.Width + 1) & ~1;
1198 textureDesc.Height = (textureDesc.Height + 1) & ~1;
1199 }
1200 textureData->w = (int)textureDesc.Width;
1201 textureData->h = (int)textureDesc.Height;
1202 if (SDL_COLORSPACETRANSFER(texture->colorspace) == SDL_TRANSFER_CHARACTERISTICS_SRGB) {
1203 textureData->shader = SHADER_RGB;
1204 } else {
1205 textureData->shader = SHADER_ADVANCED;
1206 }
1207
1208 if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
1209 textureDesc.Usage = D3D11_USAGE_DYNAMIC;
1210 textureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
1211 } else {
1212 textureDesc.Usage = D3D11_USAGE_DEFAULT;
1213 textureDesc.CPUAccessFlags = 0;
1214 }
1215
1216 if (texture->access == SDL_TEXTUREACCESS_TARGET) {
1217 textureDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
1218 } else {
1219 textureDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
1220 }
1221
1222 if (!GetTextureProperty(create_props, SDL_PROP_TEXTURE_CREATE_D3D11_TEXTURE_POINTER, &textureData->mainTexture)) {
1223 return false;
1224 }
1225 if (!textureData->mainTexture) {
1226 result = ID3D11Device_CreateTexture2D(rendererData->d3dDevice,
1227 &textureDesc,
1228 NULL,
1229 &textureData->mainTexture);
1230 if (FAILED(result)) {
1231 return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreateTexture2D"), result);
1232 }
1233 }
1234 SDL_SetPointerProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_D3D11_TEXTURE_POINTER, textureData->mainTexture);
1235#ifdef SDL_HAVE_YUV
1236 if (texture->format == SDL_PIXELFORMAT_YV12 ||
1237 texture->format == SDL_PIXELFORMAT_IYUV) {
1238 textureData->yuv = true;
1239
1240 textureDesc.Width = (textureDesc.Width + 1) / 2;
1241 textureDesc.Height = (textureDesc.Height + 1) / 2;
1242
1243 if (!GetTextureProperty(create_props, SDL_PROP_TEXTURE_CREATE_D3D11_TEXTURE_U_POINTER, &textureData->mainTextureU)) {
1244 return false;
1245 }
1246 if (!textureData->mainTextureU) {
1247 result = ID3D11Device_CreateTexture2D(rendererData->d3dDevice,
1248 &textureDesc,
1249 NULL,
1250 &textureData->mainTextureU);
1251 if (FAILED(result)) {
1252 return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreateTexture2D"), result);
1253 }
1254 }
1255 SDL_SetPointerProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_D3D11_TEXTURE_U_POINTER, textureData->mainTextureU);
1256
1257 if (!GetTextureProperty(create_props, SDL_PROP_TEXTURE_CREATE_D3D11_TEXTURE_V_POINTER, &textureData->mainTextureV)) {
1258 return false;
1259 }
1260 if (!textureData->mainTextureV) {
1261 result = ID3D11Device_CreateTexture2D(rendererData->d3dDevice,
1262 &textureDesc,
1263 NULL,
1264 &textureData->mainTextureV);
1265 if (FAILED(result)) {
1266 return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreateTexture2D"), result);
1267 }
1268 }
1269 SDL_SetPointerProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_D3D11_TEXTURE_V_POINTER, textureData->mainTextureV);
1270
1271 textureData->YCbCr_matrix = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, 8);
1272 if (!textureData->YCbCr_matrix) {
1273 return SDL_SetError("Unsupported YUV colorspace");
1274 }
1275 }
1276 if (texture->format == SDL_PIXELFORMAT_NV12 ||
1277 texture->format == SDL_PIXELFORMAT_NV21 ||
1278 texture->format == SDL_PIXELFORMAT_P010) {
1279 int bits_per_pixel;
1280
1281 textureData->nv12 = true;
1282
1283 switch (texture->format) {
1284 case SDL_PIXELFORMAT_P010:
1285 bits_per_pixel = 10;
1286 break;
1287 default:
1288 bits_per_pixel = 8;
1289 break;
1290 }
1291 textureData->YCbCr_matrix = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, bits_per_pixel);
1292 if (!textureData->YCbCr_matrix) {
1293 return SDL_SetError("Unsupported YUV colorspace");
1294 }
1295 }
1296#endif // SDL_HAVE_YUV
1297 SDL_zero(resourceViewDesc);
1298 resourceViewDesc.Format = SDLPixelFormatToDXGIMainResourceViewFormat(texture->format, renderer->output_colorspace);
1299 resourceViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
1300 resourceViewDesc.Texture2D.MostDetailedMip = 0;
1301 resourceViewDesc.Texture2D.MipLevels = textureDesc.MipLevels;
1302 result = ID3D11Device_CreateShaderResourceView(rendererData->d3dDevice,
1303 (ID3D11Resource *)textureData->mainTexture,
1304 &resourceViewDesc,
1305 &textureData->mainTextureResourceView);
1306 if (FAILED(result)) {
1307 return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreateShaderResourceView"), result);
1308 }
1309#ifdef SDL_HAVE_YUV
1310 if (textureData->yuv) {
1311 result = ID3D11Device_CreateShaderResourceView(rendererData->d3dDevice,
1312 (ID3D11Resource *)textureData->mainTextureU,
1313 &resourceViewDesc,
1314 &textureData->mainTextureResourceViewU);
1315 if (FAILED(result)) {
1316 return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreateShaderResourceView"), result);
1317 }
1318 result = ID3D11Device_CreateShaderResourceView(rendererData->d3dDevice,
1319 (ID3D11Resource *)textureData->mainTextureV,
1320 &resourceViewDesc,
1321 &textureData->mainTextureResourceViewV);
1322 if (FAILED(result)) {
1323 return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreateShaderResourceView"), result);
1324 }
1325 }
1326
1327 if (textureData->nv12) {
1328 D3D11_SHADER_RESOURCE_VIEW_DESC nvResourceViewDesc = resourceViewDesc;
1329
1330 if (texture->format == SDL_PIXELFORMAT_NV12 || texture->format == SDL_PIXELFORMAT_NV21) {
1331 nvResourceViewDesc.Format = DXGI_FORMAT_R8G8_UNORM;
1332 } else if (texture->format == SDL_PIXELFORMAT_P010) {
1333 nvResourceViewDesc.Format = DXGI_FORMAT_R16G16_UNORM;
1334 }
1335
1336 result = ID3D11Device_CreateShaderResourceView(rendererData->d3dDevice,
1337 (ID3D11Resource *)textureData->mainTexture,
1338 &nvResourceViewDesc,
1339 &textureData->mainTextureResourceViewNV);
1340 if (FAILED(result)) {
1341 return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreateShaderResourceView"), result);
1342 }
1343 }
1344#endif // SDL_HAVE_YUV
1345
1346 if (texture->access & SDL_TEXTUREACCESS_TARGET) {
1347 D3D11_RENDER_TARGET_VIEW_DESC renderTargetViewDesc;
1348 SDL_zero(renderTargetViewDesc);
1349 renderTargetViewDesc.Format = textureDesc.Format;
1350 renderTargetViewDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
1351 renderTargetViewDesc.Texture2D.MipSlice = 0;
1352
1353 result = ID3D11Device_CreateRenderTargetView(rendererData->d3dDevice,
1354 (ID3D11Resource *)textureData->mainTexture,
1355 &renderTargetViewDesc,
1356 &textureData->mainTextureRenderTargetView);
1357 if (FAILED(result)) {
1358 return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreateRenderTargetView"), result);
1359 }
1360 }
1361
1362 return true;
1363}
1364
1365static void D3D11_DestroyTexture(SDL_Renderer *renderer,
1366 SDL_Texture *texture)
1367{
1368 D3D11_TextureData *data = (D3D11_TextureData *)texture->internal;
1369
1370 if (!data) {
1371 return;
1372 }
1373
1374 SAFE_RELEASE(data->mainTexture);
1375 SAFE_RELEASE(data->mainTextureResourceView);
1376 SAFE_RELEASE(data->mainTextureRenderTargetView);
1377 SAFE_RELEASE(data->stagingTexture);
1378#ifdef SDL_HAVE_YUV
1379 SAFE_RELEASE(data->mainTextureU);
1380 SAFE_RELEASE(data->mainTextureResourceViewU);
1381 SAFE_RELEASE(data->mainTextureV);
1382 SAFE_RELEASE(data->mainTextureResourceViewV);
1383 SAFE_RELEASE(data->mainTextureResourceViewNV);
1384 SDL_free(data->pixels);
1385#endif
1386 SDL_free(data);
1387 texture->internal = NULL;
1388}
1389
1390static bool D3D11_UpdateTextureInternal(D3D11_RenderData *rendererData, ID3D11Texture2D *texture, int bpp, int x, int y, int w, int h, const void *pixels, int pitch)
1391{
1392 ID3D11Texture2D *stagingTexture;
1393 const Uint8 *src;
1394 Uint8 *dst;
1395 int row;
1396 UINT length;
1397 HRESULT result;
1398 D3D11_TEXTURE2D_DESC stagingTextureDesc;
1399 D3D11_MAPPED_SUBRESOURCE textureMemory;
1400
1401 // Create a 'staging' texture, which will be used to write to a portion of the main texture.
1402 ID3D11Texture2D_GetDesc(texture, &stagingTextureDesc);
1403 stagingTextureDesc.Width = w;
1404 stagingTextureDesc.Height = h;
1405 stagingTextureDesc.BindFlags = 0;
1406 stagingTextureDesc.MiscFlags = 0;
1407 stagingTextureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
1408 stagingTextureDesc.Usage = D3D11_USAGE_STAGING;
1409 if (stagingTextureDesc.Format == DXGI_FORMAT_NV12 ||
1410 stagingTextureDesc.Format == DXGI_FORMAT_P010) {
1411 stagingTextureDesc.Width = (stagingTextureDesc.Width + 1) & ~1;
1412 stagingTextureDesc.Height = (stagingTextureDesc.Height + 1) & ~1;
1413 }
1414 result = ID3D11Device_CreateTexture2D(rendererData->d3dDevice,
1415 &stagingTextureDesc,
1416 NULL,
1417 &stagingTexture);
1418 if (FAILED(result)) {
1419 return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreateTexture2D [create staging texture]"), result);
1420 }
1421
1422 // Get a write-only pointer to data in the staging texture:
1423 result = ID3D11DeviceContext_Map(rendererData->d3dContext,
1424 (ID3D11Resource *)stagingTexture,
1425 0,
1426 D3D11_MAP_WRITE,
1427 0,
1428 &textureMemory);
1429 if (FAILED(result)) {
1430 SAFE_RELEASE(stagingTexture);
1431 return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11DeviceContext1::Map [map staging texture]"), result);
1432 }
1433
1434 src = (const Uint8 *)pixels;
1435 dst = (Uint8 *)textureMemory.pData;
1436 length = w * bpp;
1437 if (length == (UINT)pitch && length == textureMemory.RowPitch) {
1438 SDL_memcpy(dst, src, (size_t)length * h);
1439 } else {
1440 if (length > (UINT)pitch) {
1441 length = pitch;
1442 }
1443 if (length > textureMemory.RowPitch) {
1444 length = textureMemory.RowPitch;
1445 }
1446 for (row = 0; row < h; ++row) {
1447 SDL_memcpy(dst, src, length);
1448 src += pitch;
1449 dst += textureMemory.RowPitch;
1450 }
1451 }
1452
1453 if (stagingTextureDesc.Format == DXGI_FORMAT_NV12 ||
1454 stagingTextureDesc.Format == DXGI_FORMAT_P010) {
1455 // Copy the UV plane as well
1456 h = (h + 1) / 2;
1457 if (stagingTextureDesc.Format == DXGI_FORMAT_P010) {
1458 length = (length + 3) & ~3;
1459 pitch = (pitch + 3) & ~3;
1460 } else {
1461 length = (length + 1) & ~1;
1462 pitch = (pitch + 1) & ~1;
1463 }
1464 dst = (Uint8 *)textureMemory.pData + stagingTextureDesc.Height * textureMemory.RowPitch;
1465 for (row = 0; row < h; ++row) {
1466 SDL_memcpy(dst, src, length);
1467 src += pitch;
1468 dst += textureMemory.RowPitch;
1469 }
1470 }
1471
1472 // Commit the pixel buffer's changes back to the staging texture:
1473 ID3D11DeviceContext_Unmap(rendererData->d3dContext,
1474 (ID3D11Resource *)stagingTexture,
1475 0);
1476
1477 // Copy the staging texture's contents back to the texture:
1478 ID3D11DeviceContext_CopySubresourceRegion(rendererData->d3dContext,
1479 (ID3D11Resource *)texture,
1480 0,
1481 x,
1482 y,
1483 0,
1484 (ID3D11Resource *)stagingTexture,
1485 0,
1486 NULL);
1487
1488 SAFE_RELEASE(stagingTexture);
1489
1490 return true;
1491}
1492
1493#ifdef SDL_HAVE_YUV
1494static bool D3D11_UpdateTextureNV(SDL_Renderer *renderer, SDL_Texture *texture,
1495 const SDL_Rect *rect,
1496 const Uint8 *Yplane, int Ypitch,
1497 const Uint8 *UVplane, int UVpitch);
1498
1499static bool D3D11_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *texture,
1500 const SDL_Rect *rect,
1501 const Uint8 *Yplane, int Ypitch,
1502 const Uint8 *Uplane, int Upitch,
1503 const Uint8 *Vplane, int Vpitch);
1504#endif
1505
1506static bool D3D11_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture,
1507 const SDL_Rect *rect, const void *srcPixels,
1508 int srcPitch)
1509{
1510 D3D11_RenderData *rendererData = (D3D11_RenderData *)renderer->internal;
1511 D3D11_TextureData *textureData = (D3D11_TextureData *)texture->internal;
1512
1513 if (!textureData) {
1514 return SDL_SetError("Texture is not currently available");
1515 }
1516
1517#ifdef SDL_HAVE_YUV
1518 if (textureData->nv12) {
1519 const Uint8 *Yplane = (const Uint8 *)srcPixels;
1520 const Uint8 *UVplane = Yplane + rect->h * srcPitch;
1521
1522 return D3D11_UpdateTextureNV(renderer, texture, rect, Yplane, srcPitch, UVplane, srcPitch);
1523
1524 } else if (textureData->yuv) {
1525 int Ypitch = srcPitch;
1526 int UVpitch = ((Ypitch + 1) / 2);
1527 const Uint8 *Yplane = (const Uint8 *)srcPixels;
1528 const Uint8 *Uplane = Yplane + rect->h * Ypitch;
1529 const Uint8 *Vplane = Uplane + ((rect->h + 1) / 2) * UVpitch;
1530
1531 if (texture->format == SDL_PIXELFORMAT_YV12) {
1532 return D3D11_UpdateTextureYUV(renderer, texture, rect, Yplane, Ypitch, Vplane, UVpitch, Uplane, UVpitch);
1533 } else {
1534 return D3D11_UpdateTextureYUV(renderer, texture, rect, Yplane, Ypitch, Uplane, UVpitch, Vplane, UVpitch);
1535 }
1536 }
1537#endif
1538
1539 if (!D3D11_UpdateTextureInternal(rendererData, textureData->mainTexture, SDL_BYTESPERPIXEL(texture->format), rect->x, rect->y, rect->w, rect->h, srcPixels, srcPitch)) {
1540 return false;
1541 }
1542 return true;
1543}
1544
1545#ifdef SDL_HAVE_YUV
1546static bool D3D11_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *texture,
1547 const SDL_Rect *rect,
1548 const Uint8 *Yplane, int Ypitch,
1549 const Uint8 *Uplane, int Upitch,
1550 const Uint8 *Vplane, int Vpitch)
1551{
1552 D3D11_RenderData *rendererData = (D3D11_RenderData *)renderer->internal;
1553 D3D11_TextureData *textureData = (D3D11_TextureData *)texture->internal;
1554
1555 if (!textureData) {
1556 return SDL_SetError("Texture is not currently available");
1557 }
1558
1559 if (!D3D11_UpdateTextureInternal(rendererData, textureData->mainTexture, SDL_BYTESPERPIXEL(texture->format), rect->x, rect->y, rect->w, rect->h, Yplane, Ypitch)) {
1560 return false;
1561 }
1562 if (!D3D11_UpdateTextureInternal(rendererData, textureData->mainTextureU, SDL_BYTESPERPIXEL(texture->format), rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Uplane, Upitch)) {
1563 return false;
1564 }
1565 if (!D3D11_UpdateTextureInternal(rendererData, textureData->mainTextureV, SDL_BYTESPERPIXEL(texture->format), rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Vplane, Vpitch)) {
1566 return false;
1567 }
1568 return true;
1569}
1570
1571static bool D3D11_UpdateTextureNV(SDL_Renderer *renderer, SDL_Texture *texture,
1572 const SDL_Rect *rect,
1573 const Uint8 *Yplane, int Ypitch,
1574 const Uint8 *UVplane, int UVpitch)
1575{
1576 D3D11_RenderData *rendererData = (D3D11_RenderData *)renderer->internal;
1577 D3D11_TextureData *textureData = (D3D11_TextureData *)texture->internal;
1578 ID3D11Texture2D *stagingTexture;
1579 const Uint8 *src;
1580 Uint8 *dst;
1581 int w, h, row;
1582 UINT length;
1583 HRESULT result;
1584 D3D11_TEXTURE2D_DESC stagingTextureDesc;
1585 D3D11_MAPPED_SUBRESOURCE textureMemory;
1586
1587 if (!textureData) {
1588 return SDL_SetError("Texture is not currently available");
1589 }
1590
1591 w = rect->w;
1592 h = rect->h;
1593
1594 // Create a 'staging' texture, which will be used to write to a portion of the main texture.
1595 ID3D11Texture2D_GetDesc(textureData->mainTexture, &stagingTextureDesc);
1596 stagingTextureDesc.Width = w;
1597 stagingTextureDesc.Height = h;
1598 stagingTextureDesc.BindFlags = 0;
1599 stagingTextureDesc.MiscFlags = 0;
1600 stagingTextureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
1601 stagingTextureDesc.Usage = D3D11_USAGE_STAGING;
1602 if (stagingTextureDesc.Format == DXGI_FORMAT_NV12 ||
1603 stagingTextureDesc.Format == DXGI_FORMAT_P010) {
1604 stagingTextureDesc.Width = (stagingTextureDesc.Width + 1) & ~1;
1605 stagingTextureDesc.Height = (stagingTextureDesc.Height + 1) & ~1;
1606 }
1607 result = ID3D11Device_CreateTexture2D(rendererData->d3dDevice,
1608 &stagingTextureDesc,
1609 NULL,
1610 &stagingTexture);
1611 if (FAILED(result)) {
1612 return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreateTexture2D [create staging texture]"), result);
1613 }
1614
1615 // Get a write-only pointer to data in the staging texture:
1616 result = ID3D11DeviceContext_Map(rendererData->d3dContext,
1617 (ID3D11Resource *)stagingTexture,
1618 0,
1619 D3D11_MAP_WRITE,
1620 0,
1621 &textureMemory);
1622 if (FAILED(result)) {
1623 SAFE_RELEASE(stagingTexture);
1624 return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11DeviceContext1::Map [map staging texture]"), result);
1625 }
1626
1627 src = Yplane;
1628 dst = (Uint8 *)textureMemory.pData;
1629 length = w;
1630 if (length == (UINT)Ypitch && length == textureMemory.RowPitch) {
1631 SDL_memcpy(dst, src, (size_t)length * h);
1632 } else {
1633 if (length > (UINT)Ypitch) {
1634 length = Ypitch;
1635 }
1636 if (length > textureMemory.RowPitch) {
1637 length = textureMemory.RowPitch;
1638 }
1639 for (row = 0; row < h; ++row) {
1640 SDL_memcpy(dst, src, length);
1641 src += Ypitch;
1642 dst += textureMemory.RowPitch;
1643 }
1644 }
1645
1646 src = UVplane;
1647 length = w;
1648 h = (h + 1) / 2;
1649 if (stagingTextureDesc.Format == DXGI_FORMAT_P010) {
1650 length = (length + 3) & ~3;
1651 UVpitch = (UVpitch + 3) & ~3;
1652 } else {
1653 length = (length + 1) & ~1;
1654 UVpitch = (UVpitch + 1) & ~1;
1655 }
1656 dst = (Uint8 *)textureMemory.pData + stagingTextureDesc.Height * textureMemory.RowPitch;
1657 for (row = 0; row < h; ++row) {
1658 SDL_memcpy(dst, src, length);
1659 src += UVpitch;
1660 dst += textureMemory.RowPitch;
1661 }
1662
1663 // Commit the pixel buffer's changes back to the staging texture:
1664 ID3D11DeviceContext_Unmap(rendererData->d3dContext,
1665 (ID3D11Resource *)stagingTexture,
1666 0);
1667
1668 // Copy the staging texture's contents back to the texture:
1669 ID3D11DeviceContext_CopySubresourceRegion(rendererData->d3dContext,
1670 (ID3D11Resource *)textureData->mainTexture,
1671 0,
1672 rect->x,
1673 rect->y,
1674 0,
1675 (ID3D11Resource *)stagingTexture,
1676 0,
1677 NULL);
1678
1679 SAFE_RELEASE(stagingTexture);
1680
1681 return true;
1682}
1683#endif
1684
1685static bool D3D11_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture,
1686 const SDL_Rect *rect, void **pixels, int *pitch)
1687{
1688 D3D11_RenderData *rendererData = (D3D11_RenderData *)renderer->internal;
1689 D3D11_TextureData *textureData = (D3D11_TextureData *)texture->internal;
1690 HRESULT result = S_OK;
1691 D3D11_TEXTURE2D_DESC stagingTextureDesc;
1692 D3D11_MAPPED_SUBRESOURCE textureMemory;
1693
1694 if (!textureData) {
1695 return SDL_SetError("Texture is not currently available");
1696 }
1697#ifdef SDL_HAVE_YUV
1698 if (textureData->yuv || textureData->nv12) {
1699 // It's more efficient to upload directly...
1700 if (!textureData->pixels) {
1701 textureData->pitch = texture->w;
1702 textureData->pixels = (Uint8 *)SDL_malloc((texture->h * textureData->pitch * 3) / 2);
1703 if (!textureData->pixels) {
1704 return false;
1705 }
1706 }
1707 textureData->locked_rect = *rect;
1708 *pixels =
1709 (void *)(textureData->pixels + rect->y * textureData->pitch +
1710 rect->x * SDL_BYTESPERPIXEL(texture->format));
1711 *pitch = textureData->pitch;
1712 return true;
1713 }
1714#endif
1715 if (textureData->stagingTexture) {
1716 return SDL_SetError("texture is already locked");
1717 }
1718
1719 /* Create a 'staging' texture, which will be used to write to a portion
1720 * of the main texture. This is necessary, as Direct3D 11.1 does not
1721 * have the ability to write a CPU-bound pixel buffer to a rectangular
1722 * subrect of a texture. Direct3D 11.1 can, however, write a pixel
1723 * buffer to an entire texture, hence the use of a staging texture.
1724 */
1725 ID3D11Texture2D_GetDesc(textureData->mainTexture, &stagingTextureDesc);
1726 stagingTextureDesc.Width = rect->w;
1727 stagingTextureDesc.Height = rect->h;
1728 stagingTextureDesc.BindFlags = 0;
1729 stagingTextureDesc.MiscFlags = 0;
1730 stagingTextureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
1731 stagingTextureDesc.Usage = D3D11_USAGE_STAGING;
1732 result = ID3D11Device_CreateTexture2D(rendererData->d3dDevice,
1733 &stagingTextureDesc,
1734 NULL,
1735 &textureData->stagingTexture);
1736 if (FAILED(result)) {
1737 return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreateTexture2D [create staging texture]"), result);
1738 }
1739
1740 // Get a write-only pointer to data in the staging texture:
1741 result = ID3D11DeviceContext_Map(rendererData->d3dContext,
1742 (ID3D11Resource *)textureData->stagingTexture,
1743 0,
1744 D3D11_MAP_WRITE,
1745 0,
1746 &textureMemory);
1747 if (FAILED(result)) {
1748 SAFE_RELEASE(textureData->stagingTexture);
1749 return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11DeviceContext1::Map [map staging texture]"), result);
1750 }
1751
1752 /* Make note of where the staging texture will be written to
1753 * (on a call to SDL_UnlockTexture):
1754 */
1755 textureData->lockedTexturePositionX = rect->x;
1756 textureData->lockedTexturePositionY = rect->y;
1757
1758 /* Make sure the caller has information on the texture's pixel buffer,
1759 * then return:
1760 */
1761 *pixels = textureMemory.pData;
1762 *pitch = textureMemory.RowPitch;
1763 return true;
1764}
1765
1766static void D3D11_UnlockTexture(SDL_Renderer *renderer, SDL_Texture *texture)
1767{
1768 D3D11_RenderData *rendererData = (D3D11_RenderData *)renderer->internal;
1769 D3D11_TextureData *textureData = (D3D11_TextureData *)texture->internal;
1770
1771 if (!textureData) {
1772 return;
1773 }
1774#ifdef SDL_HAVE_YUV
1775 if (textureData->yuv || textureData->nv12) {
1776 const SDL_Rect *rect = &textureData->locked_rect;
1777 void *pixels =
1778 (void *)(textureData->pixels + rect->y * textureData->pitch +
1779 rect->x * SDL_BYTESPERPIXEL(texture->format));
1780 D3D11_UpdateTexture(renderer, texture, rect, pixels, textureData->pitch);
1781 return;
1782 }
1783#endif
1784 // Commit the pixel buffer's changes back to the staging texture:
1785 ID3D11DeviceContext_Unmap(rendererData->d3dContext,
1786 (ID3D11Resource *)textureData->stagingTexture,
1787 0);
1788
1789 // Copy the staging texture's contents back to the main texture:
1790 ID3D11DeviceContext_CopySubresourceRegion(rendererData->d3dContext,
1791 (ID3D11Resource *)textureData->mainTexture,
1792 0,
1793 textureData->lockedTexturePositionX,
1794 textureData->lockedTexturePositionY,
1795 0,
1796 (ID3D11Resource *)textureData->stagingTexture,
1797 0,
1798 NULL);
1799
1800 SAFE_RELEASE(textureData->stagingTexture);
1801}
1802
1803static bool D3D11_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture)
1804{
1805 D3D11_RenderData *rendererData = (D3D11_RenderData *)renderer->internal;
1806 D3D11_TextureData *textureData = NULL;
1807
1808 if (!texture) {
1809 rendererData->currentOffscreenRenderTargetView = NULL;
1810 return true;
1811 }
1812
1813 textureData = (D3D11_TextureData *)texture->internal;
1814
1815 if (!textureData->mainTextureRenderTargetView) {
1816 return SDL_SetError("specified texture is not a render target");
1817 }
1818
1819 rendererData->currentOffscreenRenderTargetView = textureData->mainTextureRenderTargetView;
1820
1821 return true;
1822}
1823
1824static bool D3D11_QueueNoOp(SDL_Renderer *renderer, SDL_RenderCommand *cmd)
1825{
1826 return true; // nothing to do in this backend.
1827}
1828
1829static bool D3D11_QueueDrawPoints(SDL_Renderer *renderer, SDL_RenderCommand *cmd, const SDL_FPoint *points, int count)
1830{
1831 D3D11_VertexPositionColor *verts = (D3D11_VertexPositionColor *)SDL_AllocateRenderVertices(renderer, count * sizeof(D3D11_VertexPositionColor), 0, &cmd->data.draw.first);
1832 int i;
1833 SDL_FColor color = cmd->data.draw.color;
1834 bool convert_color = SDL_RenderingLinearSpace(renderer);
1835
1836 if (!verts) {
1837 return false;
1838 }
1839
1840 cmd->data.draw.count = count;
1841
1842 if (convert_color) {
1843 SDL_ConvertToLinear(&color);
1844 }
1845
1846 for (i = 0; i < count; i++) {
1847 verts->pos.x = points[i].x + 0.5f;
1848 verts->pos.y = points[i].y + 0.5f;
1849 verts->tex.x = 0.0f;
1850 verts->tex.y = 0.0f;
1851 verts->color = color;
1852 verts++;
1853 }
1854
1855 return true;
1856}
1857
1858static bool D3D11_QueueGeometry(SDL_Renderer *renderer, SDL_RenderCommand *cmd, SDL_Texture *texture,
1859 const float *xy, int xy_stride, const SDL_FColor *color, int color_stride, const float *uv, int uv_stride,
1860 int num_vertices, const void *indices, int num_indices, int size_indices,
1861 float scale_x, float scale_y)
1862{
1863 int i;
1864 int count = indices ? num_indices : num_vertices;
1865 D3D11_VertexPositionColor *verts = (D3D11_VertexPositionColor *)SDL_AllocateRenderVertices(renderer, count * sizeof(D3D11_VertexPositionColor), 0, &cmd->data.draw.first);
1866 bool convert_color = SDL_RenderingLinearSpace(renderer);
1867 D3D11_TextureData *textureData = texture ? (D3D11_TextureData *)texture->internal : NULL;
1868 float u_scale = textureData ? (float)texture->w / textureData->w : 0.0f;
1869 float v_scale = textureData ? (float)texture->h / textureData->h : 0.0f;
1870
1871 if (!verts) {
1872 return false;
1873 }
1874
1875 cmd->data.draw.count = count;
1876 size_indices = indices ? size_indices : 0;
1877
1878 for (i = 0; i < count; i++) {
1879 int j;
1880 float *xy_;
1881 if (size_indices == 4) {
1882 j = ((const Uint32 *)indices)[i];
1883 } else if (size_indices == 2) {
1884 j = ((const Uint16 *)indices)[i];
1885 } else if (size_indices == 1) {
1886 j = ((const Uint8 *)indices)[i];
1887 } else {
1888 j = i;
1889 }
1890
1891 xy_ = (float *)((char *)xy + j * xy_stride);
1892
1893 verts->pos.x = xy_[0] * scale_x;
1894 verts->pos.y = xy_[1] * scale_y;
1895 verts->color = *(SDL_FColor *)((char *)color + j * color_stride);
1896 if (convert_color) {
1897 SDL_ConvertToLinear(&verts->color);
1898 }
1899
1900 if (texture) {
1901 float *uv_ = (float *)((char *)uv + j * uv_stride);
1902 verts->tex.x = uv_[0] * u_scale;
1903 verts->tex.y = uv_[1] * v_scale;
1904 } else {
1905 verts->tex.x = 0.0f;
1906 verts->tex.y = 0.0f;
1907 }
1908
1909 verts += 1;
1910 }
1911 return true;
1912}
1913
1914static bool D3D11_UpdateVertexBuffer(SDL_Renderer *renderer,
1915 const void *vertexData, size_t dataSizeInBytes)
1916{
1917 D3D11_RenderData *rendererData = (D3D11_RenderData *)renderer->internal;
1918 HRESULT result = S_OK;
1919 const int vbidx = rendererData->currentVertexBuffer;
1920 const UINT stride = sizeof(D3D11_VertexPositionColor);
1921 const UINT offset = 0;
1922
1923 if (dataSizeInBytes == 0) {
1924 return true; // nothing to do.
1925 }
1926
1927 if (rendererData->vertexBuffers[vbidx] && rendererData->vertexBufferSizes[vbidx] >= dataSizeInBytes) {
1928 D3D11_MAPPED_SUBRESOURCE mappedResource;
1929 result = ID3D11DeviceContext_Map(rendererData->d3dContext,
1930 (ID3D11Resource *)rendererData->vertexBuffers[vbidx],
1931 0,
1932 D3D11_MAP_WRITE_DISCARD,
1933 0,
1934 &mappedResource);
1935 if (FAILED(result)) {
1936 return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11DeviceContext1::Map [vertex buffer]"), result);
1937 }
1938 SDL_memcpy(mappedResource.pData, vertexData, dataSizeInBytes);
1939 ID3D11DeviceContext_Unmap(rendererData->d3dContext, (ID3D11Resource *)rendererData->vertexBuffers[vbidx], 0);
1940 } else {
1941 D3D11_BUFFER_DESC vertexBufferDesc;
1942 D3D11_SUBRESOURCE_DATA vertexBufferData;
1943
1944 SAFE_RELEASE(rendererData->vertexBuffers[vbidx]);
1945
1946 SDL_zero(vertexBufferDesc);
1947 vertexBufferDesc.ByteWidth = (UINT)dataSizeInBytes;
1948 vertexBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
1949 vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
1950 vertexBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
1951
1952 SDL_zero(vertexBufferData);
1953 vertexBufferData.pSysMem = vertexData;
1954 vertexBufferData.SysMemPitch = 0;
1955 vertexBufferData.SysMemSlicePitch = 0;
1956
1957 result = ID3D11Device_CreateBuffer(rendererData->d3dDevice,
1958 &vertexBufferDesc,
1959 &vertexBufferData,
1960 &rendererData->vertexBuffers[vbidx]);
1961 if (FAILED(result)) {
1962 return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreateBuffer [vertex buffer]"), result);
1963 }
1964
1965 rendererData->vertexBufferSizes[vbidx] = dataSizeInBytes;
1966 }
1967
1968 ID3D11DeviceContext_IASetVertexBuffers(rendererData->d3dContext,
1969 0,
1970 1,
1971 &rendererData->vertexBuffers[vbidx],
1972 &stride,
1973 &offset);
1974
1975 rendererData->currentVertexBuffer++;
1976 if (rendererData->currentVertexBuffer >= SDL_arraysize(rendererData->vertexBuffers)) {
1977 rendererData->currentVertexBuffer = 0;
1978 }
1979
1980 return true;
1981}
1982
1983static bool D3D11_UpdateViewport(SDL_Renderer *renderer)
1984{
1985 D3D11_RenderData *data = (D3D11_RenderData *)renderer->internal;
1986 const SDL_Rect *viewport = &data->currentViewport;
1987 Float4X4 projection;
1988 Float4X4 view;
1989 SDL_FRect orientationAlignedViewport;
1990 BOOL swapDimensions;
1991 D3D11_VIEWPORT d3dviewport;
1992 const int rotation = D3D11_GetRotationForCurrentRenderTarget(renderer);
1993
1994 if (viewport->w == 0 || viewport->h == 0) {
1995 /* If the viewport is empty, assume that it is because
1996 * SDL_CreateRenderer is calling it, and will call it again later
1997 * with a non-empty viewport.
1998 */
1999 // SDL_Log("%s, no viewport was set!", __FUNCTION__);
2000 return false;
2001 }
2002
2003 /* Make sure the SDL viewport gets rotated to that of the physical display's rotation.
2004 * Keep in mind here that the Y-axis will be been inverted (from Direct3D's
2005 * default coordinate system) so rotations will be done in the opposite
2006 * direction of the DXGI_MODE_ROTATION enumeration.
2007 */
2008 switch (rotation) {
2009 case DXGI_MODE_ROTATION_IDENTITY:
2010 projection = MatrixIdentity();
2011 break;
2012 case DXGI_MODE_ROTATION_ROTATE270:
2013 projection = MatrixRotationZ(SDL_PI_F * 0.5f);
2014 break;
2015 case DXGI_MODE_ROTATION_ROTATE180:
2016 projection = MatrixRotationZ(SDL_PI_F);
2017 break;
2018 case DXGI_MODE_ROTATION_ROTATE90:
2019 projection = MatrixRotationZ(-SDL_PI_F * 0.5f);
2020 break;
2021 default:
2022 return SDL_SetError("An unknown DisplayOrientation is being used");
2023 }
2024
2025 // Update the view matrix
2026 SDL_zero(view);
2027 view.m[0][0] = 2.0f / viewport->w;
2028 view.m[1][1] = -2.0f / viewport->h;
2029 view.m[2][2] = 1.0f;
2030 view.m[3][0] = -1.0f;
2031 view.m[3][1] = 1.0f;
2032 view.m[3][3] = 1.0f;
2033
2034 /* Combine the projection + view matrix together now, as both only get
2035 * set here (as of this writing, on Dec 26, 2013). When done, store it
2036 * for eventual transfer to the GPU.
2037 */
2038 data->vertexShaderConstantsData.projectionAndView = MatrixMultiply(
2039 view,
2040 projection);
2041
2042 /* Update the Direct3D viewport, which seems to be aligned to the
2043 * swap buffer's coordinate space, which is always in either
2044 * a landscape mode, for all Windows 8/RT devices, or a portrait mode,
2045 * for Windows Phone devices.
2046 */
2047 swapDimensions = D3D11_IsDisplayRotated90Degrees(rotation);
2048 if (swapDimensions) {
2049 orientationAlignedViewport.x = (float)viewport->y;
2050 orientationAlignedViewport.y = (float)viewport->x;
2051 orientationAlignedViewport.w = (float)viewport->h;
2052 orientationAlignedViewport.h = (float)viewport->w;
2053 } else {
2054 orientationAlignedViewport.x = (float)viewport->x;
2055 orientationAlignedViewport.y = (float)viewport->y;
2056 orientationAlignedViewport.w = (float)viewport->w;
2057 orientationAlignedViewport.h = (float)viewport->h;
2058 }
2059
2060 d3dviewport.TopLeftX = orientationAlignedViewport.x;
2061 d3dviewport.TopLeftY = orientationAlignedViewport.y;
2062 d3dviewport.Width = orientationAlignedViewport.w;
2063 d3dviewport.Height = orientationAlignedViewport.h;
2064 d3dviewport.MinDepth = 0.0f;
2065 d3dviewport.MaxDepth = 1.0f;
2066 // SDL_Log("%s: D3D viewport = {%f,%f,%f,%f}", __FUNCTION__, d3dviewport.TopLeftX, d3dviewport.TopLeftY, d3dviewport.Width, d3dviewport.Height);
2067 ID3D11DeviceContext_RSSetViewports(data->d3dContext, 1, &d3dviewport);
2068
2069 data->viewportDirty = false;
2070
2071 return true;
2072}
2073
2074static ID3D11RenderTargetView *D3D11_GetCurrentRenderTargetView(SDL_Renderer *renderer)
2075{
2076 D3D11_RenderData *data = (D3D11_RenderData *)renderer->internal;
2077 if (data->currentOffscreenRenderTargetView) {
2078 return data->currentOffscreenRenderTargetView;
2079 } else {
2080 return data->mainRenderTargetView;
2081 }
2082}
2083
2084static void D3D11_SetupShaderConstants(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, const SDL_Texture *texture, D3D11_PixelShaderConstants *constants)
2085{
2086 float output_headroom;
2087
2088 SDL_zerop(constants);
2089
2090 constants->scRGB_output = (float)SDL_RenderingLinearSpace(renderer);
2091 constants->color_scale = cmd->data.draw.color_scale;
2092
2093 if (texture) {
2094 D3D11_TextureData *textureData = (D3D11_TextureData *)texture->internal;
2095
2096 switch (texture->format) {
2097 case SDL_PIXELFORMAT_YV12:
2098 case SDL_PIXELFORMAT_IYUV:
2099 constants->texture_type = TEXTURETYPE_YUV;
2100 constants->input_type = INPUTTYPE_SRGB;
2101 break;
2102 case SDL_PIXELFORMAT_NV12:
2103 constants->texture_type = TEXTURETYPE_NV12;
2104 constants->input_type = INPUTTYPE_SRGB;
2105 break;
2106 case SDL_PIXELFORMAT_NV21:
2107 constants->texture_type = TEXTURETYPE_NV21;
2108 constants->input_type = INPUTTYPE_SRGB;
2109 break;
2110 case SDL_PIXELFORMAT_P010:
2111 constants->texture_type = TEXTURETYPE_NV12;
2112 constants->input_type = INPUTTYPE_HDR10;
2113 break;
2114 default:
2115 if (cmd->data.draw.texture_scale_mode == SDL_SCALEMODE_PIXELART) {
2116 constants->texture_type = TEXTURETYPE_RGB_PIXELART;
2117 constants->texture_width = texture->w;
2118 constants->texture_height = texture->h;
2119 constants->texel_width = 1.0f / constants->texture_width;
2120 constants->texel_height = 1.0f / constants->texture_height;
2121 } else {
2122 constants->texture_type = TEXTURETYPE_RGB;
2123 }
2124 if (texture->colorspace == SDL_COLORSPACE_SRGB_LINEAR) {
2125 constants->input_type = INPUTTYPE_SCRGB;
2126 } else if (texture->colorspace == SDL_COLORSPACE_HDR10) {
2127 constants->input_type = INPUTTYPE_HDR10;
2128 } else {
2129 // The sampler will convert from sRGB to linear on load if working in linear colorspace
2130 constants->input_type = INPUTTYPE_UNSPECIFIED;
2131 }
2132 break;
2133 }
2134
2135 constants->sdr_white_point = texture->SDR_white_point;
2136
2137 if (renderer->target) {
2138 output_headroom = renderer->target->HDR_headroom;
2139 } else {
2140 output_headroom = renderer->HDR_headroom;
2141 }
2142
2143 if (texture->HDR_headroom > output_headroom) {
2144 constants->tonemap_method = TONEMAP_CHROME;
2145 constants->tonemap_factor1 = (output_headroom / (texture->HDR_headroom * texture->HDR_headroom));
2146 constants->tonemap_factor2 = (1.0f / output_headroom);
2147 }
2148
2149 if (textureData->YCbCr_matrix) {
2150 SDL_memcpy(constants->YCbCr_matrix, textureData->YCbCr_matrix, sizeof(constants->YCbCr_matrix));
2151 }
2152 }
2153}
2154
2155static bool D3D11_SetDrawState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd,
2156 D3D11_Shader shader, const D3D11_PixelShaderConstants *shader_constants,
2157 const int numShaderResources, ID3D11ShaderResourceView **shaderResources,
2158 ID3D11SamplerState *sampler, const Float4X4 *matrix)
2159
2160{
2161 D3D11_RenderData *rendererData = (D3D11_RenderData *)renderer->internal;
2162 const Float4X4 *newmatrix = matrix ? matrix : &rendererData->identity;
2163 ID3D11RasterizerState *rasterizerState;
2164 ID3D11RenderTargetView *renderTargetView = D3D11_GetCurrentRenderTargetView(renderer);
2165 ID3D11ShaderResourceView *shaderResource;
2166 const SDL_BlendMode blendMode = cmd->data.draw.blend;
2167 ID3D11BlendState *blendState = NULL;
2168 bool updateSubresource = false;
2169 D3D11_PixelShaderState *shader_state = &rendererData->currentShaderState[shader];
2170 D3D11_PixelShaderConstants solid_constants;
2171
2172 if (numShaderResources > 0) {
2173 shaderResource = shaderResources[0];
2174 } else {
2175 shaderResource = NULL;
2176 }
2177
2178 // Make sure the render target isn't bound to a shader
2179 if (shaderResource != rendererData->currentShaderResource) {
2180 ID3D11ShaderResourceView *pNullResource = NULL;
2181 ID3D11DeviceContext_PSSetShaderResources(rendererData->d3dContext, 0, 1, &pNullResource);
2182 rendererData->currentShaderResource = NULL;
2183 }
2184
2185 if (renderTargetView != rendererData->currentRenderTargetView) {
2186 ID3D11DeviceContext_OMSetRenderTargets(rendererData->d3dContext,
2187 1,
2188 &renderTargetView,
2189 NULL);
2190 rendererData->currentRenderTargetView = renderTargetView;
2191 }
2192
2193 if (rendererData->viewportDirty) {
2194 if (D3D11_UpdateViewport(renderer)) {
2195 // vertexShaderConstantsData.projectionAndView has changed
2196 updateSubresource = true;
2197 }
2198 }
2199
2200 if (rendererData->cliprectDirty) {
2201 if (!rendererData->currentCliprectEnabled) {
2202 ID3D11DeviceContext_RSSetScissorRects(rendererData->d3dContext, 0, NULL);
2203 } else {
2204 D3D11_RECT scissorRect;
2205 if (!D3D11_GetViewportAlignedD3DRect(renderer, &rendererData->currentCliprect, &scissorRect, TRUE)) {
2206 // D3D11_GetViewportAlignedD3DRect will have set the SDL error
2207 return false;
2208 }
2209 ID3D11DeviceContext_RSSetScissorRects(rendererData->d3dContext, 1, &scissorRect);
2210 }
2211 rendererData->cliprectDirty = false;
2212 }
2213
2214 if (!rendererData->currentCliprectEnabled) {
2215 rasterizerState = rendererData->mainRasterizer;
2216 } else {
2217 rasterizerState = rendererData->clippedRasterizer;
2218 }
2219 if (rasterizerState != rendererData->currentRasterizerState) {
2220 ID3D11DeviceContext_RSSetState(rendererData->d3dContext, rasterizerState);
2221 rendererData->currentRasterizerState = rasterizerState;
2222 }
2223
2224 if (blendMode != SDL_BLENDMODE_NONE) {
2225 int i;
2226 for (i = 0; i < rendererData->blendModesCount; ++i) {
2227 if (blendMode == rendererData->blendModes[i].blendMode) {
2228 blendState = rendererData->blendModes[i].blendState;
2229 break;
2230 }
2231 }
2232 if (!blendState) {
2233 blendState = D3D11_CreateBlendState(renderer, blendMode);
2234 if (!blendState) {
2235 return false;
2236 }
2237 }
2238 }
2239 if (blendState != rendererData->currentBlendState) {
2240 ID3D11DeviceContext_OMSetBlendState(rendererData->d3dContext, blendState, 0, 0xFFFFFFFF);
2241 rendererData->currentBlendState = blendState;
2242 }
2243
2244 if (!shader_constants) {
2245 D3D11_SetupShaderConstants(renderer, cmd, NULL, &solid_constants);
2246 shader_constants = &solid_constants;
2247 }
2248
2249 if (!shader_state->constants ||
2250 SDL_memcmp(shader_constants, &shader_state->shader_constants, sizeof(*shader_constants)) != 0) {
2251 SAFE_RELEASE(shader_state->constants);
2252
2253 D3D11_BUFFER_DESC desc;
2254 SDL_zero(desc);
2255 desc.Usage = D3D11_USAGE_DEFAULT;
2256 desc.ByteWidth = sizeof(*shader_constants);
2257 desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
2258
2259 D3D11_SUBRESOURCE_DATA data;
2260 SDL_zero(data);
2261 data.pSysMem = shader_constants;
2262
2263 HRESULT result = ID3D11Device_CreateBuffer(rendererData->d3dDevice, &desc, &data, &shader_state->constants);
2264 if (FAILED(result)) {
2265 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device::CreateBuffer [create shader constants]"), result);
2266 return false;
2267 }
2268 SDL_memcpy(&shader_state->shader_constants, shader_constants, sizeof(*shader_constants));
2269
2270 // Force the shader parameters to be re-set
2271 rendererData->currentShader = SHADER_NONE;
2272 }
2273 if (shader != rendererData->currentShader) {
2274 if (!rendererData->pixelShaders[shader]) {
2275 if (!D3D11_CreatePixelShader(rendererData->d3dDevice, shader, &rendererData->pixelShaders[shader])) {
2276 return false;
2277 }
2278 }
2279 ID3D11DeviceContext_PSSetShader(rendererData->d3dContext, rendererData->pixelShaders[shader], NULL, 0);
2280 if (shader_state->constants) {
2281 ID3D11DeviceContext_PSSetConstantBuffers(rendererData->d3dContext, 0, 1, &shader_state->constants);
2282 }
2283 rendererData->currentShader = shader;
2284 }
2285 if (shaderResource != rendererData->currentShaderResource) {
2286 ID3D11DeviceContext_PSSetShaderResources(rendererData->d3dContext, 0, numShaderResources, shaderResources);
2287 rendererData->currentShaderResource = shaderResource;
2288 }
2289 if (sampler != rendererData->currentSampler) {
2290 ID3D11DeviceContext_PSSetSamplers(rendererData->d3dContext, 0, 1, &sampler);
2291 rendererData->currentSampler = sampler;
2292 }
2293
2294 if (updateSubresource == true || SDL_memcmp(&rendererData->vertexShaderConstantsData.model, newmatrix, sizeof(*newmatrix)) != 0) {
2295 SDL_copyp(&rendererData->vertexShaderConstantsData.model, newmatrix);
2296 ID3D11DeviceContext_UpdateSubresource(rendererData->d3dContext,
2297 (ID3D11Resource *)rendererData->vertexShaderConstants,
2298 0,
2299 NULL,
2300 &rendererData->vertexShaderConstantsData,
2301 0,
2302 0);
2303 }
2304
2305 return true;
2306}
2307
2308static bool D3D11_SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, const Float4X4 *matrix)
2309{
2310 SDL_Texture *texture = cmd->data.draw.texture;
2311 D3D11_RenderData *rendererData = (D3D11_RenderData *)renderer->internal;
2312 D3D11_TextureData *textureData = (D3D11_TextureData *)texture->internal;
2313 ID3D11SamplerState *textureSampler;
2314 D3D11_PixelShaderConstants constants;
2315
2316 if (!textureData) {
2317 return SDL_SetError("Texture is not currently available");
2318 }
2319
2320 D3D11_SetupShaderConstants(renderer, cmd, texture, &constants);
2321
2322 switch (cmd->data.draw.texture_scale_mode) {
2323 case SDL_SCALEMODE_NEAREST:
2324 switch (cmd->data.draw.texture_address_mode) {
2325 case SDL_TEXTURE_ADDRESS_CLAMP:
2326 textureSampler = rendererData->samplers[D3D11_SAMPLER_NEAREST_CLAMP];
2327 break;
2328 case SDL_TEXTURE_ADDRESS_WRAP:
2329 textureSampler = rendererData->samplers[D3D11_SAMPLER_NEAREST_WRAP];
2330 break;
2331 default:
2332 return SDL_SetError("Unknown texture address mode: %d", cmd->data.draw.texture_address_mode);
2333 }
2334 break;
2335 case SDL_SCALEMODE_PIXELART: // Uses linear sampling
2336 case SDL_SCALEMODE_LINEAR:
2337 switch (cmd->data.draw.texture_address_mode) {
2338 case SDL_TEXTURE_ADDRESS_CLAMP:
2339 textureSampler = rendererData->samplers[D3D11_SAMPLER_LINEAR_CLAMP];
2340 break;
2341 case SDL_TEXTURE_ADDRESS_WRAP:
2342 textureSampler = rendererData->samplers[D3D11_SAMPLER_LINEAR_WRAP];
2343 break;
2344 default:
2345 return SDL_SetError("Unknown texture address mode: %d", cmd->data.draw.texture_address_mode);
2346 }
2347 break;
2348 default:
2349 return SDL_SetError("Unknown scale mode: %d", cmd->data.draw.texture_scale_mode);
2350 }
2351#ifdef SDL_HAVE_YUV
2352 if (textureData->yuv) {
2353 ID3D11ShaderResourceView *shaderResources[3];
2354
2355 shaderResources[0] = textureData->mainTextureResourceView;
2356 shaderResources[1] = textureData->mainTextureResourceViewU;
2357 shaderResources[2] = textureData->mainTextureResourceViewV;
2358
2359 return D3D11_SetDrawState(renderer, cmd, textureData->shader, &constants,
2360 SDL_arraysize(shaderResources), shaderResources, textureSampler, matrix);
2361
2362 } else if (textureData->nv12) {
2363 ID3D11ShaderResourceView *shaderResources[2];
2364
2365 shaderResources[0] = textureData->mainTextureResourceView;
2366 shaderResources[1] = textureData->mainTextureResourceViewNV;
2367
2368 return D3D11_SetDrawState(renderer, cmd, textureData->shader, &constants,
2369 SDL_arraysize(shaderResources), shaderResources, textureSampler, matrix);
2370 }
2371#endif // SDL_HAVE_YUV
2372 return D3D11_SetDrawState(renderer, cmd, textureData->shader, &constants,
2373 1, &textureData->mainTextureResourceView, textureSampler, matrix);
2374}
2375
2376static void D3D11_DrawPrimitives(SDL_Renderer *renderer, D3D11_PRIMITIVE_TOPOLOGY primitiveTopology, const size_t vertexStart, const size_t vertexCount)
2377{
2378 D3D11_RenderData *rendererData = (D3D11_RenderData *)renderer->internal;
2379 ID3D11DeviceContext_IASetPrimitiveTopology(rendererData->d3dContext, primitiveTopology);
2380 ID3D11DeviceContext_Draw(rendererData->d3dContext, (UINT)vertexCount, (UINT)vertexStart);
2381}
2382
2383static void D3D11_InvalidateCachedState(SDL_Renderer *renderer)
2384{
2385 D3D11_RenderData *data = (D3D11_RenderData *)renderer->internal;
2386 data->currentRenderTargetView = NULL;
2387 data->currentRasterizerState = NULL;
2388 data->currentBlendState = NULL;
2389 data->currentShader = SHADER_NONE;
2390 data->currentShaderResource = NULL;
2391 data->currentSampler = NULL;
2392 data->cliprectDirty = true;
2393 data->viewportDirty = true;
2394}
2395
2396static bool D3D11_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize)
2397{
2398 D3D11_RenderData *rendererData = (D3D11_RenderData *)renderer->internal;
2399 const int viewportRotation = D3D11_GetRotationForCurrentRenderTarget(renderer);
2400
2401 if (!rendererData->d3dDevice) {
2402 return SDL_SetError("Device lost and couldn't be recovered");
2403 }
2404
2405 if (rendererData->pixelSizeChanged) {
2406 D3D11_UpdateForWindowSizeChange(renderer);
2407 rendererData->pixelSizeChanged = false;
2408 }
2409
2410 if (rendererData->currentViewportRotation != viewportRotation) {
2411 rendererData->currentViewportRotation = viewportRotation;
2412 rendererData->viewportDirty = true;
2413 }
2414
2415 if (!D3D11_UpdateVertexBuffer(renderer, vertices, vertsize)) {
2416 return false;
2417 }
2418
2419 while (cmd) {
2420 switch (cmd->command) {
2421 case SDL_RENDERCMD_SETDRAWCOLOR:
2422 {
2423 break; // this isn't currently used in this render backend.
2424 }
2425
2426 case SDL_RENDERCMD_SETVIEWPORT:
2427 {
2428 SDL_Rect *viewport = &rendererData->currentViewport;
2429 if (SDL_memcmp(viewport, &cmd->data.viewport.rect, sizeof(cmd->data.viewport.rect)) != 0) {
2430 SDL_copyp(viewport, &cmd->data.viewport.rect);
2431 rendererData->viewportDirty = true;
2432 rendererData->cliprectDirty = true;
2433 }
2434 break;
2435 }
2436
2437 case SDL_RENDERCMD_SETCLIPRECT:
2438 {
2439 const SDL_Rect *rect = &cmd->data.cliprect.rect;
2440 if (rendererData->currentCliprectEnabled != cmd->data.cliprect.enabled) {
2441 rendererData->currentCliprectEnabled = cmd->data.cliprect.enabled;
2442 rendererData->cliprectDirty = true;
2443 }
2444 if (SDL_memcmp(&rendererData->currentCliprect, rect, sizeof(*rect)) != 0) {
2445 SDL_copyp(&rendererData->currentCliprect, rect);
2446 rendererData->cliprectDirty = true;
2447 }
2448 break;
2449 }
2450
2451 case SDL_RENDERCMD_CLEAR:
2452 {
2453 bool convert_color = SDL_RenderingLinearSpace(renderer);
2454 SDL_FColor color = cmd->data.color.color;
2455 if (convert_color) {
2456 SDL_ConvertToLinear(&color);
2457 }
2458 color.r *= cmd->data.color.color_scale;
2459 color.g *= cmd->data.color.color_scale;
2460 color.b *= cmd->data.color.color_scale;
2461 ID3D11DeviceContext_ClearRenderTargetView(rendererData->d3dContext, D3D11_GetCurrentRenderTargetView(renderer), &color.r);
2462 break;
2463 }
2464
2465 case SDL_RENDERCMD_DRAW_POINTS:
2466 {
2467 const size_t count = cmd->data.draw.count;
2468 const size_t first = cmd->data.draw.first;
2469 const size_t start = first / sizeof(D3D11_VertexPositionColor);
2470 D3D11_SetDrawState(renderer, cmd, SHADER_SOLID, NULL, 0, NULL, NULL, NULL);
2471 D3D11_DrawPrimitives(renderer, D3D11_PRIMITIVE_TOPOLOGY_POINTLIST, start, count);
2472 break;
2473 }
2474
2475 case SDL_RENDERCMD_DRAW_LINES:
2476 {
2477 const size_t count = cmd->data.draw.count;
2478 const size_t first = cmd->data.draw.first;
2479 const size_t start = first / sizeof(D3D11_VertexPositionColor);
2480 const D3D11_VertexPositionColor *verts = (D3D11_VertexPositionColor *)(((Uint8 *)vertices) + first);
2481 D3D11_SetDrawState(renderer, cmd, SHADER_SOLID, NULL, 0, NULL, NULL, NULL);
2482 D3D11_DrawPrimitives(renderer, D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP, start, count);
2483 if (verts[0].pos.x != verts[count - 1].pos.x || verts[0].pos.y != verts[count - 1].pos.y) {
2484 D3D11_DrawPrimitives(renderer, D3D11_PRIMITIVE_TOPOLOGY_POINTLIST, start + (count - 1), 1);
2485 }
2486 break;
2487 }
2488
2489 case SDL_RENDERCMD_FILL_RECTS: // unused
2490 break;
2491
2492 case SDL_RENDERCMD_COPY: // unused
2493 break;
2494
2495 case SDL_RENDERCMD_COPY_EX: // unused
2496 break;
2497
2498 case SDL_RENDERCMD_GEOMETRY:
2499 {
2500 SDL_Texture *texture = cmd->data.draw.texture;
2501 const size_t count = cmd->data.draw.count;
2502 const size_t first = cmd->data.draw.first;
2503 const size_t start = first / sizeof(D3D11_VertexPositionColor);
2504
2505 if (texture) {
2506 D3D11_SetCopyState(renderer, cmd, NULL);
2507 } else {
2508 D3D11_SetDrawState(renderer, cmd, SHADER_SOLID, NULL, 0, NULL, NULL, NULL);
2509 }
2510
2511 D3D11_DrawPrimitives(renderer, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST, start, count);
2512 break;
2513 }
2514
2515 case SDL_RENDERCMD_NO_OP:
2516 break;
2517 }
2518
2519 cmd = cmd->next;
2520 }
2521
2522 return true;
2523}
2524
2525static SDL_Surface *D3D11_RenderReadPixels(SDL_Renderer *renderer, const SDL_Rect *rect)
2526{
2527 D3D11_RenderData *data = (D3D11_RenderData *)renderer->internal;
2528 ID3D11RenderTargetView *renderTargetView = NULL;
2529 ID3D11Texture2D *backBuffer = NULL;
2530 ID3D11Texture2D *stagingTexture = NULL;
2531 HRESULT result;
2532 D3D11_TEXTURE2D_DESC stagingTextureDesc;
2533 D3D11_RECT srcRect = { 0, 0, 0, 0 };
2534 D3D11_BOX srcBox;
2535 D3D11_MAPPED_SUBRESOURCE textureMemory;
2536 SDL_Surface *output = NULL;
2537
2538 renderTargetView = D3D11_GetCurrentRenderTargetView(renderer);
2539 if (!renderTargetView) {
2540 SDL_SetError("%s, ID3D11DeviceContext::OMGetRenderTargets failed", __FUNCTION__);
2541 goto done;
2542 }
2543
2544 ID3D11View_GetResource(renderTargetView, (ID3D11Resource **)&backBuffer);
2545 if (!backBuffer) {
2546 SDL_SetError("%s, ID3D11View::GetResource failed", __FUNCTION__);
2547 goto done;
2548 }
2549
2550 // Create a staging texture to copy the screen's data to:
2551 ID3D11Texture2D_GetDesc(backBuffer, &stagingTextureDesc);
2552 stagingTextureDesc.Width = rect->w;
2553 stagingTextureDesc.Height = rect->h;
2554 stagingTextureDesc.BindFlags = 0;
2555 stagingTextureDesc.MiscFlags = 0;
2556 stagingTextureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
2557 stagingTextureDesc.Usage = D3D11_USAGE_STAGING;
2558 result = ID3D11Device_CreateTexture2D(data->d3dDevice,
2559 &stagingTextureDesc,
2560 NULL,
2561 &stagingTexture);
2562 if (FAILED(result)) {
2563 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreateTexture2D [create staging texture]"), result);
2564 goto done;
2565 }
2566
2567 // Copy the desired portion of the back buffer to the staging texture:
2568 if (!D3D11_GetViewportAlignedD3DRect(renderer, rect, &srcRect, FALSE)) {
2569 // D3D11_GetViewportAlignedD3DRect will have set the SDL error
2570 goto done;
2571 }
2572
2573 srcBox.left = srcRect.left;
2574 srcBox.right = srcRect.right;
2575 srcBox.top = srcRect.top;
2576 srcBox.bottom = srcRect.bottom;
2577 srcBox.front = 0;
2578 srcBox.back = 1;
2579 ID3D11DeviceContext_CopySubresourceRegion(data->d3dContext,
2580 (ID3D11Resource *)stagingTexture,
2581 0,
2582 0, 0, 0,
2583 (ID3D11Resource *)backBuffer,
2584 0,
2585 &srcBox);
2586
2587 // Map the staging texture's data to CPU-accessible memory:
2588 result = ID3D11DeviceContext_Map(data->d3dContext,
2589 (ID3D11Resource *)stagingTexture,
2590 0,
2591 D3D11_MAP_READ,
2592 0,
2593 &textureMemory);
2594 if (FAILED(result)) {
2595 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11DeviceContext1::Map [map staging texture]"), result);
2596 goto done;
2597 }
2598
2599 output = SDL_DuplicatePixels(
2600 rect->w, rect->h,
2601 D3D11_DXGIFormatToSDLPixelFormat(stagingTextureDesc.Format),
2602 renderer->target ? renderer->target->colorspace : renderer->output_colorspace,
2603 textureMemory.pData,
2604 textureMemory.RowPitch);
2605
2606 // Unmap the texture:
2607 ID3D11DeviceContext_Unmap(data->d3dContext,
2608 (ID3D11Resource *)stagingTexture,
2609 0);
2610
2611done:
2612 SAFE_RELEASE(backBuffer);
2613 SAFE_RELEASE(stagingTexture);
2614 return output;
2615}
2616
2617static bool D3D11_RenderPresent(SDL_Renderer *renderer)
2618{
2619 D3D11_RenderData *data = (D3D11_RenderData *)renderer->internal;
2620 HRESULT result;
2621 DXGI_PRESENT_PARAMETERS parameters;
2622
2623 if (!data->d3dDevice) {
2624 return SDL_SetError("Device lost and couldn't be recovered");
2625 }
2626
2627 SDL_zero(parameters);
2628
2629 /* The application may optionally specify "dirty" or "scroll"
2630 * rects to improve efficiency in certain scenarios.
2631 */
2632 result = IDXGISwapChain1_Present1(data->swapChain, data->syncInterval, data->presentFlags, &parameters);
2633
2634 /* Discard the contents of the render target.
2635 * This is a valid operation only when the existing contents will be entirely
2636 * overwritten. If dirty or scroll rects are used, this call should be removed.
2637 */
2638 ID3D11DeviceContext1_DiscardView(data->d3dContext, (ID3D11View *)data->mainRenderTargetView);
2639
2640 // When the present flips, it unbinds the current view, so bind it again on the next draw call
2641 data->currentRenderTargetView = NULL;
2642
2643 if (FAILED(result) && result != DXGI_ERROR_WAS_STILL_DRAWING) {
2644 /* If the device was removed either by a disconnect or a driver upgrade, we
2645 * must recreate all device resources.
2646 */
2647 if (result == DXGI_ERROR_DEVICE_REMOVED) {
2648 if (D3D11_HandleDeviceLost(renderer)) {
2649 SDL_SetError("Present failed, device lost");
2650 } else {
2651 // Recovering from device lost failed, error is already set
2652 }
2653 } else if (result == DXGI_ERROR_INVALID_CALL) {
2654 // We probably went through a fullscreen <-> windowed transition
2655 D3D11_CreateWindowSizeDependentResources(renderer);
2656 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("IDXGISwapChain::Present"), result);
2657 } else {
2658 WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("IDXGISwapChain::Present"), result);
2659 }
2660 return false;
2661 }
2662 return true;
2663}
2664
2665static bool D3D11_SetVSync(SDL_Renderer *renderer, const int vsync)
2666{
2667 D3D11_RenderData *data = (D3D11_RenderData *)renderer->internal;
2668
2669 if (vsync < 0) {
2670 return SDL_Unsupported();
2671 }
2672
2673 if (vsync > 0) {
2674 data->syncInterval = vsync;
2675 data->presentFlags = 0;
2676 } else {
2677 data->syncInterval = 0;
2678 data->presentFlags = DXGI_PRESENT_DO_NOT_WAIT;
2679 }
2680 return true;
2681}
2682
2683static bool D3D11_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_PropertiesID create_props)
2684{
2685 D3D11_RenderData *data;
2686
2687 HWND hwnd = (HWND)SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL);
2688 if (!hwnd) {
2689 return SDL_SetError("Couldn't get window handle");
2690 }
2691
2692 SDL_SetupRendererColorspace(renderer, create_props);
2693
2694 if (renderer->output_colorspace != SDL_COLORSPACE_SRGB &&
2695 renderer->output_colorspace != SDL_COLORSPACE_SRGB_LINEAR
2696 /*&& renderer->output_colorspace != SDL_COLORSPACE_HDR10*/) {
2697 return SDL_SetError("Unsupported output colorspace");
2698 }
2699
2700 data = (D3D11_RenderData *)SDL_calloc(1, sizeof(*data));
2701 if (!data) {
2702 return false;
2703 }
2704
2705 data->identity = MatrixIdentity();
2706
2707 renderer->WindowEvent = D3D11_WindowEvent;
2708 renderer->SupportsBlendMode = D3D11_SupportsBlendMode;
2709 renderer->CreateTexture = D3D11_CreateTexture;
2710 renderer->UpdateTexture = D3D11_UpdateTexture;
2711#ifdef SDL_HAVE_YUV
2712 renderer->UpdateTextureYUV = D3D11_UpdateTextureYUV;
2713 renderer->UpdateTextureNV = D3D11_UpdateTextureNV;
2714#endif
2715 renderer->LockTexture = D3D11_LockTexture;
2716 renderer->UnlockTexture = D3D11_UnlockTexture;
2717 renderer->SetRenderTarget = D3D11_SetRenderTarget;
2718 renderer->QueueSetViewport = D3D11_QueueNoOp;
2719 renderer->QueueSetDrawColor = D3D11_QueueNoOp;
2720 renderer->QueueDrawPoints = D3D11_QueueDrawPoints;
2721 renderer->QueueDrawLines = D3D11_QueueDrawPoints; // lines and points queue vertices the same way.
2722 renderer->QueueGeometry = D3D11_QueueGeometry;
2723 renderer->InvalidateCachedState = D3D11_InvalidateCachedState;
2724 renderer->RunCommandQueue = D3D11_RunCommandQueue;
2725 renderer->RenderReadPixels = D3D11_RenderReadPixels;
2726 renderer->RenderPresent = D3D11_RenderPresent;
2727 renderer->DestroyTexture = D3D11_DestroyTexture;
2728 renderer->DestroyRenderer = D3D11_DestroyRenderer;
2729 renderer->SetVSync = D3D11_SetVSync;
2730 renderer->internal = data;
2731 D3D11_InvalidateCachedState(renderer);
2732
2733 renderer->name = D3D11_RenderDriver.name;
2734 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ARGB8888);
2735 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ABGR8888);
2736 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_XRGB8888);
2737 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ABGR2101010);
2738 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_RGBA64_FLOAT);
2739 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_YV12);
2740 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_IYUV);
2741 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV12);
2742 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV21);
2743 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P010);
2744
2745 data->syncInterval = 0;
2746 data->presentFlags = DXGI_PRESENT_DO_NOT_WAIT;
2747
2748 /* HACK: make sure the SDL_Renderer references the SDL_Window data now, in
2749 * order to give init functions access to the underlying window handle:
2750 */
2751 renderer->window = window;
2752
2753 // Initialize Direct3D resources
2754 if (FAILED(D3D11_CreateDeviceResources(renderer))) {
2755 return false;
2756 }
2757 if (FAILED(D3D11_CreateWindowSizeDependentResources(renderer))) {
2758 return false;
2759 }
2760
2761 return true;
2762}
2763
2764SDL_RenderDriver D3D11_RenderDriver = {
2765 D3D11_CreateRenderer, "direct3d11"
2766};
2767
2768#endif // SDL_VIDEO_RENDER_D3D11
2769