This repository has been archived by the owner on Feb 11, 2021. It is now read-only.
/
SDL_winrtrenderer.cpp
431 lines (377 loc) · 15.6 KB
1
2
3
4
5
#include <fstream>
#include <string>
#include <vector>
#include "SDLmain_WinRT_common.h"
6
#include "SDL_winrtrenderer.h"
7
8
9
using namespace DirectX;
using namespace Microsoft::WRL;
10
using namespace std;
11
using namespace Windows::UI::Core;
12
13
using namespace Windows::Foundation;
using namespace Windows::Graphics::Display;
14
15
// Constructor.
16
SDL_winrtrenderer::SDL_winrtrenderer() :
17
m_mainTextureHelperSurface(NULL),
18
m_sdlRenderer(NULL),
19
m_sdlRendererData(NULL)
20
21
22
{
}
23
24
25
26
27
28
29
30
SDL_winrtrenderer::~SDL_winrtrenderer()
{
if (m_mainTextureHelperSurface) {
SDL_FreeSurface(m_mainTextureHelperSurface);
m_mainTextureHelperSurface = NULL;
}
}
31
32
33
// Initialize the Direct3D resources required to run.
void SDL_winrtrenderer::Initialize(CoreWindow^ window)
{
34
35
36
37
m_window = window;
CreateDeviceResources();
CreateWindowSizeDependentResources();
38
39
40
41
42
}
// Recreate all device resources and set them back to the current state.
void SDL_winrtrenderer::HandleDeviceLost()
{
43
44
45
// Reset these member variables to ensure that UpdateForWindowSizeChange recreates all resources.
m_windowBounds.Width = 0;
m_windowBounds.Height = 0;
46
m_sdlRendererData->swapChain = nullptr;
47
48
// TODO, WinRT: reconnect HandleDeviceLost to SDL_Renderer
49
50
CreateDeviceResources();
UpdateForWindowSizeChange();
51
52
}
53
extern HRESULT WINRT_CreateDeviceResources(SDL_Renderer * renderer);
54
55
// These are the resources that depend on the device.
56
void SDL_winrtrenderer::CreateDeviceResources()
57
{
58
DX::ThrowIfFailed(WINRT_CreateDeviceResources(m_sdlRenderer));
59
60
}
61
62
// Allocate all memory resources that change on a window SizeChanged event.
void SDL_winrtrenderer::CreateWindowSizeDependentResources()
63
{
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
// Store the window bounds so the next time we get a SizeChanged event we can
// avoid rebuilding everything if the size is identical.
m_windowBounds = m_window->Bounds;
// Calculate the necessary swap chain and render target size in pixels.
float windowWidth = ConvertDipsToPixels(m_windowBounds.Width);
float windowHeight = ConvertDipsToPixels(m_windowBounds.Height);
// The width and height of the swap chain must be based on the window's
// landscape-oriented width and height. If the window is in a portrait
// orientation, the dimensions must be reversed.
m_orientation = DisplayProperties::CurrentOrientation;
bool swapDimensions =
m_orientation == DisplayOrientations::Portrait ||
m_orientation == DisplayOrientations::PortraitFlipped;
m_renderTargetSize.Width = swapDimensions ? windowHeight : windowWidth;
m_renderTargetSize.Height = swapDimensions ? windowWidth : windowHeight;
82
if(m_sdlRendererData->swapChain != nullptr)
83
84
85
{
// If the swap chain already exists, resize it.
DX::ThrowIfFailed(
86
m_sdlRendererData->swapChain->ResizeBuffers(
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
2, // Double-buffered swap chain.
static_cast<UINT>(m_renderTargetSize.Width),
static_cast<UINT>(m_renderTargetSize.Height),
DXGI_FORMAT_B8G8R8A8_UNORM,
0
)
);
}
else
{
// Otherwise, create a new one using the same adapter as the existing Direct3D device.
DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {0};
swapChainDesc.Width = static_cast<UINT>(m_renderTargetSize.Width); // Match the size of the window.
swapChainDesc.Height = static_cast<UINT>(m_renderTargetSize.Height);
swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // This is the most common swap chain format.
swapChainDesc.Stereo = false;
swapChainDesc.SampleDesc.Count = 1; // Don't use multi-sampling.
swapChainDesc.SampleDesc.Quality = 0;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.BufferCount = 2; // Use double-buffering to minimize latency.
107
108
109
110
#if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
swapChainDesc.Scaling = DXGI_SCALING_STRETCH; // On phone, only stretch and aspect-ratio stretch scaling are allowed.
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; // On phone, no swap effects are supported.
#else
111
112
swapChainDesc.Scaling = DXGI_SCALING_NONE;
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // All Windows Store apps must use this SwapEffect.
113
#endif
114
115
116
117
swapChainDesc.Flags = 0;
ComPtr<IDXGIDevice1> dxgiDevice;
DX::ThrowIfFailed(
118
m_sdlRendererData->d3dDevice.As(&dxgiDevice)
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
);
ComPtr<IDXGIAdapter> dxgiAdapter;
DX::ThrowIfFailed(
dxgiDevice->GetAdapter(&dxgiAdapter)
);
ComPtr<IDXGIFactory2> dxgiFactory;
DX::ThrowIfFailed(
dxgiAdapter->GetParent(
__uuidof(IDXGIFactory2),
&dxgiFactory
)
);
Windows::UI::Core::CoreWindow^ window = m_window.Get();
DX::ThrowIfFailed(
dxgiFactory->CreateSwapChainForCoreWindow(
137
m_sdlRendererData->d3dDevice.Get(),
138
139
140
reinterpret_cast<IUnknown*>(window),
&swapChainDesc,
nullptr, // Allow on all displays.
141
&m_sdlRendererData->swapChain
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
)
);
// Ensure that DXGI does not queue more than one frame at a time. This both reduces latency and
// ensures that the application will only render after each VSync, minimizing power consumption.
DX::ThrowIfFailed(
dxgiDevice->SetMaximumFrameLatency(1)
);
}
// Set the proper orientation for the swap chain, and generate the
// 3D matrix transformation for rendering to the rotated swap chain.
DXGI_MODE_ROTATION rotation = DXGI_MODE_ROTATION_UNSPECIFIED;
switch (m_orientation)
{
case DisplayOrientations::Landscape:
rotation = DXGI_MODE_ROTATION_IDENTITY;
m_orientationTransform3D = XMFLOAT4X4( // 0-degree Z-rotation
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
);
break;
case DisplayOrientations::Portrait:
rotation = DXGI_MODE_ROTATION_ROTATE270;
m_orientationTransform3D = XMFLOAT4X4( // 90-degree Z-rotation
0.0f, 1.0f, 0.0f, 0.0f,
-1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
);
break;
case DisplayOrientations::LandscapeFlipped:
rotation = DXGI_MODE_ROTATION_ROTATE180;
m_orientationTransform3D = XMFLOAT4X4( // 180-degree Z-rotation
-1.0f, 0.0f, 0.0f, 0.0f,
0.0f, -1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
);
break;
case DisplayOrientations::PortraitFlipped:
rotation = DXGI_MODE_ROTATION_ROTATE90;
m_orientationTransform3D = XMFLOAT4X4( // 270-degree Z-rotation
0.0f, -1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
);
break;
default:
throw ref new Platform::FailureException();
}
201
202
#if WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP
// TODO, WinRT: Windows Phone does not have the IDXGISwapChain1::SetRotation method. Check if an alternative is available, or needed.
203
DX::ThrowIfFailed(
204
m_sdlRendererData->swapChain->SetRotation(rotation)
205
);
206
#endif
207
208
209
210
// Create a render target view of the swap chain back buffer.
ComPtr<ID3D11Texture2D> backBuffer;
DX::ThrowIfFailed(
211
m_sdlRendererData->swapChain->GetBuffer(
212
213
214
215
216
217
218
0,
__uuidof(ID3D11Texture2D),
&backBuffer
)
);
DX::ThrowIfFailed(
219
m_sdlRendererData->d3dDevice->CreateRenderTargetView(
220
221
backBuffer.Get(),
nullptr,
222
&m_sdlRendererData->renderTargetView
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
)
);
// Create a depth stencil view.
CD3D11_TEXTURE2D_DESC depthStencilDesc(
DXGI_FORMAT_D24_UNORM_S8_UINT,
static_cast<UINT>(m_renderTargetSize.Width),
static_cast<UINT>(m_renderTargetSize.Height),
1,
1,
D3D11_BIND_DEPTH_STENCIL
);
ComPtr<ID3D11Texture2D> depthStencil;
DX::ThrowIfFailed(
238
m_sdlRendererData->d3dDevice->CreateTexture2D(
239
240
241
242
243
244
245
246
247
248
249
250
251
252
&depthStencilDesc,
nullptr,
&depthStencil
)
);
// Set the rendering viewport to target the entire window.
CD3D11_VIEWPORT viewport(
0.0f,
0.0f,
m_renderTargetSize.Width,
m_renderTargetSize.Height
);
253
m_sdlRendererData->d3dContext->RSSetViewports(1, &viewport);
254
255
}
256
257
void SDL_winrtrenderer::ResizeMainTexture(int w, int h)
{
258
259
const int pixelSizeInBytes = 4;
260
D3D11_TEXTURE2D_DESC textureDesc = {0};
261
262
263
264
265
266
267
268
269
270
271
textureDesc.Width = w;
textureDesc.Height = h;
textureDesc.MipLevels = 1;
textureDesc.ArraySize = 1;
textureDesc.Format = DXGI_FORMAT_B8G8R8X8_UNORM;
textureDesc.SampleDesc.Count = 1;
textureDesc.SampleDesc.Quality = 0;
textureDesc.Usage = D3D11_USAGE_DYNAMIC;
textureDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
textureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
textureDesc.MiscFlags = 0;
272
273
274
275
276
277
278
279
280
281
282
283
const int numPixels = textureDesc.Width * textureDesc.Height;
std::vector<uint8> initialTexturePixels(numPixels * pixelSizeInBytes, 0x00);
// Fill the texture with a non-black color, for debugging purposes:
//for (int i = 0; i < (numPixels * pixelSizeInBytes); i += pixelSizeInBytes) {
// initialTexturePixels[i+0] = 0xff;
// initialTexturePixels[i+1] = 0xff;
// initialTexturePixels[i+2] = 0x00;
// initialTexturePixels[i+3] = 0xff;
//}
284
285
286
287
288
D3D11_SUBRESOURCE_DATA initialTextureData = {0};
initialTextureData.pSysMem = (void *)&(initialTexturePixels[0]);
initialTextureData.SysMemPitch = textureDesc.Width * pixelSizeInBytes;
initialTextureData.SysMemSlicePitch = numPixels * pixelSizeInBytes;
DX::ThrowIfFailed(
289
m_sdlRendererData->d3dDevice->CreateTexture2D(
290
291
&textureDesc,
&initialTextureData,
292
&m_sdlRendererData->mainTexture
293
294
)
);
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
if (m_mainTextureHelperSurface) {
SDL_FreeSurface(m_mainTextureHelperSurface);
m_mainTextureHelperSurface = NULL;
}
m_mainTextureHelperSurface = SDL_CreateRGBSurfaceFrom(
NULL,
textureDesc.Width, textureDesc.Height,
(pixelSizeInBytes * 8),
0, // Use an nil pitch for now. This'll be filled in when updating the texture.
0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000); // TODO, WinRT: calculate masks given the Direct3D-defined pixel format of the texture
if (m_mainTextureHelperSurface == NULL) {
DX::ThrowIfFailed(E_FAIL); // TODO, WinRT: generate a better error here, taking into account who's calling this function.
}
310
311
312
313
314
315
D3D11_SHADER_RESOURCE_VIEW_DESC resourceViewDesc;
resourceViewDesc.Format = textureDesc.Format;
resourceViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
resourceViewDesc.Texture2D.MostDetailedMip = 0;
resourceViewDesc.Texture2D.MipLevels = textureDesc.MipLevels;
DX::ThrowIfFailed(
316
317
m_sdlRendererData->d3dDevice->CreateShaderResourceView(
m_sdlRendererData->mainTexture.Get(),
318
&resourceViewDesc,
319
&m_sdlRendererData->mainTextureResourceView)
320
);
321
322
}
323
324
325
// This method is called in the event handler for the SizeChanged event.
void SDL_winrtrenderer::UpdateForWindowSizeChange()
{
326
327
328
329
330
if (m_window->Bounds.Width != m_windowBounds.Width ||
m_window->Bounds.Height != m_windowBounds.Height ||
m_orientation != DisplayProperties::CurrentOrientation)
{
ID3D11RenderTargetView* nullViews[] = {nullptr};
331
332
333
m_sdlRendererData->d3dContext->OMSetRenderTargets(ARRAYSIZE(nullViews), nullViews, nullptr);
m_sdlRendererData->renderTargetView = nullptr;
m_sdlRendererData->d3dContext->Flush();
334
335
CreateWindowSizeDependentResources();
}
336
337
}
338
void SDL_winrtrenderer::Render(SDL_Surface * surface, SDL_Rect * rects, int numrects)
339
{
340
const float blackColor[] = { 0.0f, 0.0f, 0.0f, 0.0f };
341
342
m_sdlRendererData->d3dContext->ClearRenderTargetView(
m_sdlRendererData->renderTargetView.Get(),
343
344
345
346
blackColor
);
// Only draw the screen once it is loaded (some loading is asynchronous).
347
if (!m_sdlRendererData->loadingComplete)
348
349
350
{
return;
}
351
if (!m_sdlRendererData->mainTextureResourceView)
352
353
354
{
return;
}
355
356
// Update the main texture (for SDL usage). Start by mapping the SDL
357
// window's main texture to CPU-accessible memory:
358
359
D3D11_MAPPED_SUBRESOURCE textureMemory = {0};
DX::ThrowIfFailed(
360
361
m_sdlRendererData->d3dContext->Map(
m_sdlRendererData->mainTexture.Get(),
362
363
364
365
366
0,
D3D11_MAP_WRITE_DISCARD,
0,
&textureMemory)
);
367
368
369
370
371
// Copy pixel data to the locked texture's memory:
m_mainTextureHelperSurface->pixels = textureMemory.pData;
m_mainTextureHelperSurface->pitch = textureMemory.RowPitch;
SDL_BlitSurface(surface, NULL, m_mainTextureHelperSurface, NULL);
372
// TODO, WinRT: only update the requested rects (passed to SDL_UpdateWindowSurface), rather than everything
373
374
375
376
// Clean up a bit, then commit the texture's memory back to Direct3D:
m_mainTextureHelperSurface->pixels = NULL;
m_mainTextureHelperSurface->pitch = 0;
377
378
m_sdlRendererData->d3dContext->Unmap(
m_sdlRendererData->mainTexture.Get(),
379
380
0);
381
m_sdlRendererData->d3dContext->OMSetRenderTargets(
382
1,
383
m_sdlRendererData->renderTargetView.GetAddressOf(),
384
385
386
387
388
nullptr
);
UINT stride = sizeof(VertexPositionColor);
UINT offset = 0;
389
m_sdlRendererData->d3dContext->IASetVertexBuffers(
390
391
0,
1,
392
m_sdlRendererData->vertexBuffer.GetAddressOf(),
393
394
395
396
&stride,
&offset
);
397
m_sdlRendererData->d3dContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
398
399
m_sdlRendererData->d3dContext->IASetInputLayout(m_sdlRendererData->inputLayout.Get());
400
401
402
m_sdlRendererData->d3dContext->VSSetShader(
m_sdlRendererData->vertexShader.Get(),
403
404
405
406
nullptr,
0
);
407
408
m_sdlRendererData->d3dContext->PSSetShader(
m_sdlRendererData->pixelShader.Get(),
409
410
411
412
nullptr,
0
);
413
m_sdlRendererData->d3dContext->PSSetShaderResources(0, 1, m_sdlRendererData->mainTextureResourceView.GetAddressOf());
414
415
m_sdlRendererData->d3dContext->PSSetSamplers(0, 1, m_sdlRendererData->mainSampler.GetAddressOf());
416
417
m_sdlRendererData->d3dContext->Draw(4, 0);
418
}
419
420
421
422
// Method to deliver the final image to the display.
void SDL_winrtrenderer::Present()
{
423
SDL_RenderPresent(m_sdlRenderer);
424
425
426
427
428
}
// Method to convert a length in device-independent pixels (DIPs) to a length in physical pixels.
float SDL_winrtrenderer::ConvertDipsToPixels(float dips)
{
429
430
static const float dipsPerInch = 96.0f;
return floor(dips * DisplayProperties::LogicalDpi / dipsPerInch + 0.5f); // Round to nearest integer.
431
}