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