src/video/windowsrt/SDL_winrtrenderer.cpp
author David Ludwig <dludwig@pobox.com>
Sun, 09 Dec 2012 22:43:34 -0500
changeset 8375 e33eb49b7f42
parent 8371 0814a5de5b48
child 8384 bc7a52629e1e
permissions -rw-r--r--
WinRT: fixed a window-surface-updating bug (WinRT/DLudwig #25, http://bit.ly/RkawRR)
     1 #include "SDLmain_WinRT_common.h"
     2 #include "SDL_winrtrenderer.h"
     3 
     4 using namespace DirectX;
     5 using namespace Microsoft::WRL;
     6 using namespace Windows::UI::Core;
     7 using namespace Windows::Foundation;
     8 using namespace Windows::Graphics::Display;
     9 
    10 // Constructor.
    11 SDL_winrtrenderer::SDL_winrtrenderer() :
    12     m_mainTextureHelperSurface(NULL),
    13     m_loadingComplete(false),
    14 	m_vertexCount(0)
    15 {
    16 }
    17 
    18 SDL_winrtrenderer::~SDL_winrtrenderer()
    19 {
    20     if (m_mainTextureHelperSurface) {
    21         SDL_FreeSurface(m_mainTextureHelperSurface);
    22         m_mainTextureHelperSurface = NULL;
    23     }
    24 }
    25 
    26 // Initialize the Direct3D resources required to run.
    27 void SDL_winrtrenderer::Initialize(CoreWindow^ window)
    28 {
    29 	m_window = window;
    30 	
    31 	CreateDeviceResources();
    32 	CreateWindowSizeDependentResources();
    33 }
    34 
    35 // Recreate all device resources and set them back to the current state.
    36 void SDL_winrtrenderer::HandleDeviceLost()
    37 {
    38 	// Reset these member variables to ensure that UpdateForWindowSizeChange recreates all resources.
    39 	m_windowBounds.Width = 0;
    40 	m_windowBounds.Height = 0;
    41 	m_swapChain = nullptr;
    42 
    43 	CreateDeviceResources();
    44 	UpdateForWindowSizeChange();
    45 }
    46 
    47 // These are the resources that depend on the device.
    48 void SDL_winrtrenderer::CreateDeviceResources()
    49 {
    50 	// This flag adds support for surfaces with a different color channel ordering
    51 	// than the API default. It is required for compatibility with Direct2D.
    52 	UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
    53 
    54 #if defined(_DEBUG)
    55 	// If the project is in a debug build, enable debugging via SDK Layers with this flag.
    56 	creationFlags |= D3D11_CREATE_DEVICE_DEBUG;
    57 #endif
    58 
    59 	// This array defines the set of DirectX hardware feature levels this app will support.
    60 	// Note the ordering should be preserved.
    61 	// Don't forget to declare your application's minimum required feature level in its
    62 	// description.  All applications are assumed to support 9.1 unless otherwise stated.
    63 	D3D_FEATURE_LEVEL featureLevels[] = 
    64 	{
    65 		D3D_FEATURE_LEVEL_11_1,
    66 		D3D_FEATURE_LEVEL_11_0,
    67 		D3D_FEATURE_LEVEL_10_1,
    68 		D3D_FEATURE_LEVEL_10_0,
    69 		D3D_FEATURE_LEVEL_9_3,
    70 		D3D_FEATURE_LEVEL_9_2,
    71 		D3D_FEATURE_LEVEL_9_1
    72 	};
    73 
    74 	// Create the Direct3D 11 API device object and a corresponding context.
    75 	ComPtr<ID3D11Device> device;
    76 	ComPtr<ID3D11DeviceContext> context;
    77 	DX::ThrowIfFailed(
    78 		D3D11CreateDevice(
    79 			nullptr, // Specify nullptr to use the default adapter.
    80 			D3D_DRIVER_TYPE_HARDWARE,
    81 			nullptr,
    82 			creationFlags, // Set set debug and Direct2D compatibility flags.
    83 			featureLevels, // List of feature levels this app can support.
    84 			ARRAYSIZE(featureLevels),
    85 			D3D11_SDK_VERSION, // Always set this to D3D11_SDK_VERSION for Windows Store apps.
    86 			&device, // Returns the Direct3D device created.
    87 			&m_featureLevel, // Returns feature level of device created.
    88 			&context // Returns the device immediate context.
    89 			)
    90 		);
    91 
    92 	// Get the Direct3D 11.1 API device and context interfaces.
    93 	DX::ThrowIfFailed(
    94 		device.As(&m_d3dDevice)
    95 		);
    96 
    97 	DX::ThrowIfFailed(
    98 		context.As(&m_d3dContext)
    99 		);
   100 
   101     auto loadVSTask = DX::ReadDataAsync("SDL_VS2012_WinRT\\SimpleVertexShader.cso");
   102 	auto loadPSTask = DX::ReadDataAsync("SDL_VS2012_WinRT\\SimplePixelShader.cso");
   103 
   104 	auto createVSTask = loadVSTask.then([this](Platform::Array<byte>^ fileData) {
   105 		DX::ThrowIfFailed(
   106 			m_d3dDevice->CreateVertexShader(
   107  				fileData->Data,
   108 				fileData->Length,
   109 				nullptr,
   110 				&m_vertexShader
   111 				)
   112 			);
   113 
   114 		const D3D11_INPUT_ELEMENT_DESC vertexDesc[] = 
   115 		{
   116 			{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0,  D3D11_INPUT_PER_VERTEX_DATA, 0 },
   117 			{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
   118 		};
   119 
   120 		DX::ThrowIfFailed(
   121 			m_d3dDevice->CreateInputLayout(
   122 				vertexDesc,
   123 				ARRAYSIZE(vertexDesc),
   124 				fileData->Data,
   125 				fileData->Length,
   126 				&m_inputLayout
   127 				)
   128 			);
   129 	});
   130 
   131 	auto createPSTask = loadPSTask.then([this](Platform::Array<byte>^ fileData) {
   132 		DX::ThrowIfFailed(
   133 			m_d3dDevice->CreatePixelShader(
   134 				fileData->Data,
   135 				fileData->Length,
   136 				nullptr,
   137 				&m_pixelShader
   138 				)
   139 			);
   140 	});
   141 
   142 	auto createVertexBuffer = (createPSTask && createVSTask).then([this] () {
   143 		VertexPositionColor vertices[] = 
   144 		{
   145 			{XMFLOAT3(-1.0f, -1.0f, 0.0f),  XMFLOAT2(0.0f, 1.0f)},
   146 			{XMFLOAT3(-1.0f, 1.0f, 0.0f), XMFLOAT2(0.0f, 0.0f)},
   147 			{XMFLOAT3(1.0f, -1.0f, 0.0f), XMFLOAT2(1.0f, 1.0f)},
   148 			{XMFLOAT3(1.0f, 1.0f, 0.0f), XMFLOAT2(1.0f, 0.0f)},
   149 		};
   150 
   151 		m_vertexCount = ARRAYSIZE(vertices);
   152 
   153 		D3D11_SUBRESOURCE_DATA vertexBufferData = {0};
   154 		vertexBufferData.pSysMem = vertices;
   155 		vertexBufferData.SysMemPitch = 0;
   156 		vertexBufferData.SysMemSlicePitch = 0;
   157 		CD3D11_BUFFER_DESC vertexBufferDesc(sizeof(vertices), D3D11_BIND_VERTEX_BUFFER);
   158 		DX::ThrowIfFailed(
   159 			m_d3dDevice->CreateBuffer(
   160 				&vertexBufferDesc,
   161 				&vertexBufferData,
   162 				&m_vertexBuffer
   163 				)
   164 			);
   165 	});
   166 
   167     auto createMainSamplerTask = createVertexBuffer.then([this] () {
   168 		D3D11_SAMPLER_DESC samplerDesc;
   169 		samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
   170 		samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
   171 		samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
   172 		samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
   173 		samplerDesc.MipLODBias = 0.0f;
   174 		samplerDesc.MaxAnisotropy = 1;
   175 		samplerDesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
   176 		samplerDesc.BorderColor[0] = 0.0f;
   177 		samplerDesc.BorderColor[1] = 0.0f;
   178 		samplerDesc.BorderColor[2] = 0.0f;
   179 		samplerDesc.BorderColor[3] = 0.0f;
   180 		samplerDesc.MinLOD = 0.0f;
   181 		samplerDesc.MaxLOD = D3D11_FLOAT32_MAX;
   182 		DX::ThrowIfFailed(
   183 			m_d3dDevice->CreateSamplerState(
   184 				&samplerDesc,
   185 				&m_mainSampler
   186 				)
   187 			);
   188 	});
   189 
   190 	createMainSamplerTask.then([this] () {
   191 		m_loadingComplete = true;
   192 	});
   193 }
   194 
   195 // Allocate all memory resources that change on a window SizeChanged event.
   196 void SDL_winrtrenderer::CreateWindowSizeDependentResources()
   197 { 
   198 	// Store the window bounds so the next time we get a SizeChanged event we can
   199 	// avoid rebuilding everything if the size is identical.
   200 	m_windowBounds = m_window->Bounds;
   201 
   202 	// Calculate the necessary swap chain and render target size in pixels.
   203 	float windowWidth = ConvertDipsToPixels(m_windowBounds.Width);
   204 	float windowHeight = ConvertDipsToPixels(m_windowBounds.Height);
   205 
   206 	// The width and height of the swap chain must be based on the window's
   207 	// landscape-oriented width and height. If the window is in a portrait
   208 	// orientation, the dimensions must be reversed.
   209 	m_orientation = DisplayProperties::CurrentOrientation;
   210 	bool swapDimensions =
   211 		m_orientation == DisplayOrientations::Portrait ||
   212 		m_orientation == DisplayOrientations::PortraitFlipped;
   213 	m_renderTargetSize.Width = swapDimensions ? windowHeight : windowWidth;
   214 	m_renderTargetSize.Height = swapDimensions ? windowWidth : windowHeight;
   215 
   216 	if(m_swapChain != nullptr)
   217 	{
   218 		// If the swap chain already exists, resize it.
   219 		DX::ThrowIfFailed(
   220 			m_swapChain->ResizeBuffers(
   221 				2, // Double-buffered swap chain.
   222 				static_cast<UINT>(m_renderTargetSize.Width),
   223 				static_cast<UINT>(m_renderTargetSize.Height),
   224 				DXGI_FORMAT_B8G8R8A8_UNORM,
   225 				0
   226 				)
   227 			);
   228 	}
   229 	else
   230 	{
   231 		// Otherwise, create a new one using the same adapter as the existing Direct3D device.
   232 		DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {0};
   233 		swapChainDesc.Width = static_cast<UINT>(m_renderTargetSize.Width); // Match the size of the window.
   234 		swapChainDesc.Height = static_cast<UINT>(m_renderTargetSize.Height);
   235 		swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // This is the most common swap chain format.
   236 		swapChainDesc.Stereo = false;
   237 		swapChainDesc.SampleDesc.Count = 1; // Don't use multi-sampling.
   238 		swapChainDesc.SampleDesc.Quality = 0;
   239 		swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
   240 		swapChainDesc.BufferCount = 2; // Use double-buffering to minimize latency.
   241 		swapChainDesc.Scaling = DXGI_SCALING_NONE;
   242 		swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // All Windows Store apps must use this SwapEffect.
   243 		swapChainDesc.Flags = 0;
   244 
   245 		ComPtr<IDXGIDevice1>  dxgiDevice;
   246 		DX::ThrowIfFailed(
   247 			m_d3dDevice.As(&dxgiDevice)
   248 			);
   249 
   250 		ComPtr<IDXGIAdapter> dxgiAdapter;
   251 		DX::ThrowIfFailed(
   252 			dxgiDevice->GetAdapter(&dxgiAdapter)
   253 			);
   254 
   255 		ComPtr<IDXGIFactory2> dxgiFactory;
   256 		DX::ThrowIfFailed(
   257 			dxgiAdapter->GetParent(
   258 				__uuidof(IDXGIFactory2), 
   259 				&dxgiFactory
   260 				)
   261 			);
   262 
   263 		Windows::UI::Core::CoreWindow^ window = m_window.Get();
   264 		DX::ThrowIfFailed(
   265 			dxgiFactory->CreateSwapChainForCoreWindow(
   266 				m_d3dDevice.Get(),
   267 				reinterpret_cast<IUnknown*>(window),
   268 				&swapChainDesc,
   269 				nullptr, // Allow on all displays.
   270 				&m_swapChain
   271 				)
   272 			);
   273 			
   274 		// Ensure that DXGI does not queue more than one frame at a time. This both reduces latency and
   275 		// ensures that the application will only render after each VSync, minimizing power consumption.
   276 		DX::ThrowIfFailed(
   277 			dxgiDevice->SetMaximumFrameLatency(1)
   278 			);
   279 	}
   280 	
   281 	// Set the proper orientation for the swap chain, and generate the
   282 	// 3D matrix transformation for rendering to the rotated swap chain.
   283 	DXGI_MODE_ROTATION rotation = DXGI_MODE_ROTATION_UNSPECIFIED;
   284 	switch (m_orientation)
   285 	{
   286 		case DisplayOrientations::Landscape:
   287 			rotation = DXGI_MODE_ROTATION_IDENTITY;
   288 			m_orientationTransform3D = XMFLOAT4X4( // 0-degree Z-rotation
   289 				1.0f, 0.0f, 0.0f, 0.0f,
   290 				0.0f, 1.0f, 0.0f, 0.0f,
   291 				0.0f, 0.0f, 1.0f, 0.0f,
   292 				0.0f, 0.0f, 0.0f, 1.0f
   293 				);
   294 			break;
   295 
   296 		case DisplayOrientations::Portrait:
   297 			rotation = DXGI_MODE_ROTATION_ROTATE270;
   298 			m_orientationTransform3D = XMFLOAT4X4( // 90-degree Z-rotation
   299 				0.0f, 1.0f, 0.0f, 0.0f,
   300 				-1.0f, 0.0f, 0.0f, 0.0f,
   301 				0.0f, 0.0f, 1.0f, 0.0f,
   302 				0.0f, 0.0f, 0.0f, 1.0f
   303 				);
   304 			break;
   305 
   306 		case DisplayOrientations::LandscapeFlipped:
   307 			rotation = DXGI_MODE_ROTATION_ROTATE180;
   308 			m_orientationTransform3D = XMFLOAT4X4( // 180-degree Z-rotation
   309 				-1.0f, 0.0f, 0.0f, 0.0f,
   310 				0.0f, -1.0f, 0.0f, 0.0f,
   311 				0.0f, 0.0f, 1.0f, 0.0f,
   312 				0.0f, 0.0f, 0.0f, 1.0f
   313 				);
   314 			break;
   315 
   316 		case DisplayOrientations::PortraitFlipped:
   317 			rotation = DXGI_MODE_ROTATION_ROTATE90;
   318 			m_orientationTransform3D = XMFLOAT4X4( // 270-degree Z-rotation
   319 				0.0f, -1.0f, 0.0f, 0.0f,
   320 				1.0f, 0.0f, 0.0f, 0.0f,
   321 				0.0f, 0.0f, 1.0f, 0.0f,
   322 				0.0f, 0.0f, 0.0f, 1.0f
   323 				);
   324 			break;
   325 
   326 		default:
   327 			throw ref new Platform::FailureException();
   328 	}
   329 
   330 	DX::ThrowIfFailed(
   331 		m_swapChain->SetRotation(rotation)
   332 		);
   333 
   334 	// Create a render target view of the swap chain back buffer.
   335 	ComPtr<ID3D11Texture2D> backBuffer;
   336 	DX::ThrowIfFailed(
   337 		m_swapChain->GetBuffer(
   338 			0,
   339 			__uuidof(ID3D11Texture2D),
   340 			&backBuffer
   341 			)
   342 		);
   343 
   344 	DX::ThrowIfFailed(
   345 		m_d3dDevice->CreateRenderTargetView(
   346 			backBuffer.Get(),
   347 			nullptr,
   348 			&m_renderTargetView
   349 			)
   350 		);
   351 
   352 	// Create a depth stencil view.
   353 	CD3D11_TEXTURE2D_DESC depthStencilDesc(
   354 		DXGI_FORMAT_D24_UNORM_S8_UINT, 
   355 		static_cast<UINT>(m_renderTargetSize.Width),
   356 		static_cast<UINT>(m_renderTargetSize.Height),
   357 		1,
   358 		1,
   359 		D3D11_BIND_DEPTH_STENCIL
   360 		);
   361 
   362 	ComPtr<ID3D11Texture2D> depthStencil;
   363 	DX::ThrowIfFailed(
   364 		m_d3dDevice->CreateTexture2D(
   365 			&depthStencilDesc,
   366 			nullptr,
   367 			&depthStencil
   368 			)
   369 		);
   370 
   371 	// Set the rendering viewport to target the entire window.
   372 	CD3D11_VIEWPORT viewport(
   373 		0.0f,
   374 		0.0f,
   375 		m_renderTargetSize.Width,
   376 		m_renderTargetSize.Height
   377 		);
   378 
   379 	m_d3dContext->RSSetViewports(1, &viewport);
   380 }
   381 
   382 void SDL_winrtrenderer::ResizeMainTexture(int w, int h)
   383 {
   384     const int pixelSizeInBytes = 4;
   385 
   386     D3D11_TEXTURE2D_DESC textureDesc = {0};
   387 	textureDesc.Width = w;
   388 	textureDesc.Height = h;
   389 	textureDesc.MipLevels = 1;
   390 	textureDesc.ArraySize = 1;
   391 	textureDesc.Format = DXGI_FORMAT_B8G8R8X8_UNORM;
   392 	textureDesc.SampleDesc.Count = 1;
   393 	textureDesc.SampleDesc.Quality = 0;
   394 	textureDesc.Usage = D3D11_USAGE_DYNAMIC;
   395 	textureDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
   396 	textureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
   397 	textureDesc.MiscFlags = 0;
   398 
   399     const int numPixels = textureDesc.Width * textureDesc.Height;
   400     std::vector<uint8> initialTexturePixels(numPixels * pixelSizeInBytes, 0x00);
   401 
   402     // Fill the texture with a non-black color, for debugging purposes:
   403     //for (int i = 0; i < (numPixels * pixelSizeInBytes); i += pixelSizeInBytes) {
   404     //    initialTexturePixels[i+0] = 0xff;
   405     //    initialTexturePixels[i+1] = 0xff;
   406     //    initialTexturePixels[i+2] = 0x00;
   407     //    initialTexturePixels[i+3] = 0xff;
   408     //}
   409 
   410 	D3D11_SUBRESOURCE_DATA initialTextureData = {0};
   411 	initialTextureData.pSysMem = (void *)&(initialTexturePixels[0]);
   412 	initialTextureData.SysMemPitch = textureDesc.Width * pixelSizeInBytes;
   413 	initialTextureData.SysMemSlicePitch = numPixels * pixelSizeInBytes;
   414 	DX::ThrowIfFailed(
   415 		m_d3dDevice->CreateTexture2D(
   416 			&textureDesc,
   417 			&initialTextureData,
   418 			&m_mainTexture
   419 			)
   420 		);
   421 
   422     if (m_mainTextureHelperSurface) {
   423         SDL_FreeSurface(m_mainTextureHelperSurface);
   424         m_mainTextureHelperSurface = NULL;
   425     }
   426     m_mainTextureHelperSurface = SDL_CreateRGBSurfaceFrom(
   427         NULL,
   428         textureDesc.Width, textureDesc.Height,
   429         (pixelSizeInBytes * 8),
   430         0,      // Use an nil pitch for now.  This'll be filled in when updating the texture.
   431         0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000);    // TODO, WinRT: calculate masks given the Direct3D-defined pixel format of the texture
   432     if (m_mainTextureHelperSurface == NULL) {
   433         DX::ThrowIfFailed(E_FAIL);  // TODO, WinRT: generate a better error here, taking into account who's calling this function.
   434     }
   435 
   436 	D3D11_SHADER_RESOURCE_VIEW_DESC resourceViewDesc;
   437 	resourceViewDesc.Format = textureDesc.Format;
   438 	resourceViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
   439 	resourceViewDesc.Texture2D.MostDetailedMip = 0;
   440 	resourceViewDesc.Texture2D.MipLevels = textureDesc.MipLevels;
   441 	DX::ThrowIfFailed(
   442 		m_d3dDevice->CreateShaderResourceView(
   443 			m_mainTexture.Get(),
   444 			&resourceViewDesc,
   445 			&m_mainTextureResourceView)
   446 		);
   447 }
   448 
   449 // This method is called in the event handler for the SizeChanged event.
   450 void SDL_winrtrenderer::UpdateForWindowSizeChange()
   451 {
   452 	if (m_window->Bounds.Width  != m_windowBounds.Width ||
   453 		m_window->Bounds.Height != m_windowBounds.Height ||
   454 		m_orientation != DisplayProperties::CurrentOrientation)
   455 	{
   456 		ID3D11RenderTargetView* nullViews[] = {nullptr};
   457 		m_d3dContext->OMSetRenderTargets(ARRAYSIZE(nullViews), nullViews, nullptr);
   458 		m_renderTargetView = nullptr;
   459 		m_d3dContext->Flush();
   460 		CreateWindowSizeDependentResources();
   461 	}
   462 }
   463 
   464 void SDL_winrtrenderer::Render(SDL_Surface * surface, SDL_Rect * rects, int numrects)
   465 {
   466 	const float blackColor[] = { 0.0f, 0.0f, 0.0f, 0.0f };
   467 	m_d3dContext->ClearRenderTargetView(
   468 		m_renderTargetView.Get(),
   469 		blackColor
   470 		);
   471 
   472 	// Only draw the screen once it is loaded (some loading is asynchronous).
   473 	if (!m_loadingComplete)
   474 	{
   475 		return;
   476 	}
   477     if (!m_mainTextureResourceView)
   478     {
   479         return;
   480     }
   481 
   482 	// Update the main texture (for SDL usage).  Start by mapping the SDL
   483     // window's main texture to CPU-accessible memory:
   484 	D3D11_MAPPED_SUBRESOURCE textureMemory = {0};
   485 	DX::ThrowIfFailed(
   486 		m_d3dContext->Map(
   487 			m_mainTexture.Get(),
   488 			0,
   489 			D3D11_MAP_WRITE_DISCARD,
   490 			0,
   491 			&textureMemory)
   492 		);
   493 
   494     // Copy pixel data to the locked texture's memory:
   495     m_mainTextureHelperSurface->pixels = textureMemory.pData;
   496     m_mainTextureHelperSurface->pitch = textureMemory.RowPitch;
   497     SDL_BlitSurface(surface, NULL, m_mainTextureHelperSurface, NULL);
   498 	// TODO, WinRT: only update the requested rects (passed to SDL_UpdateWindowSurface), rather than everything
   499 
   500     // Clean up a bit, then commit the texture's memory back to Direct3D:
   501     m_mainTextureHelperSurface->pixels = NULL;
   502     m_mainTextureHelperSurface->pitch = 0;
   503 	m_d3dContext->Unmap(
   504 		m_mainTexture.Get(),
   505 		0);
   506 
   507 	m_d3dContext->OMSetRenderTargets(
   508 		1,
   509 		m_renderTargetView.GetAddressOf(),
   510 		nullptr
   511 		);
   512 
   513 	UINT stride = sizeof(VertexPositionColor);
   514 	UINT offset = 0;
   515 	m_d3dContext->IASetVertexBuffers(
   516 		0,
   517 		1,
   518 		m_vertexBuffer.GetAddressOf(),
   519 		&stride,
   520 		&offset
   521 		);
   522 
   523 	m_d3dContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
   524 
   525 	m_d3dContext->IASetInputLayout(m_inputLayout.Get());
   526 
   527 	m_d3dContext->VSSetShader(
   528 		m_vertexShader.Get(),
   529 		nullptr,
   530 		0
   531 		);
   532 
   533 	m_d3dContext->PSSetShader(
   534 		m_pixelShader.Get(),
   535 		nullptr,
   536 		0
   537 		);
   538 
   539 	m_d3dContext->PSSetShaderResources(0, 1, m_mainTextureResourceView.GetAddressOf());
   540 
   541 	m_d3dContext->PSSetSamplers(0, 1, m_mainSampler.GetAddressOf());
   542 
   543 	m_d3dContext->Draw(4, 0);
   544 }
   545 
   546 // Method to deliver the final image to the display.
   547 void SDL_winrtrenderer::Present()
   548 {
   549 	// The application may optionally specify "dirty" or "scroll"
   550 	// rects to improve efficiency in certain scenarios.
   551 	DXGI_PRESENT_PARAMETERS parameters = {0};
   552 	parameters.DirtyRectsCount = 0;
   553 	parameters.pDirtyRects = nullptr;
   554 	parameters.pScrollRect = nullptr;
   555 	parameters.pScrollOffset = nullptr;
   556 	
   557 	// The first argument instructs DXGI to block until VSync, putting the application
   558 	// to sleep until the next VSync. This ensures we don't waste any cycles rendering
   559 	// frames that will never be displayed to the screen.
   560 	HRESULT hr = m_swapChain->Present1(1, 0, &parameters);
   561 
   562 	// Discard the contents of the render target.
   563 	// This is a valid operation only when the existing contents will be entirely
   564 	// overwritten. If dirty or scroll rects are used, this call should be removed.
   565 	m_d3dContext->DiscardView(m_renderTargetView.Get());
   566 
   567 	// If the device was removed either by a disconnect or a driver upgrade, we 
   568 	// must recreate all device resources.
   569 	if (hr == DXGI_ERROR_DEVICE_REMOVED)
   570 	{
   571 		HandleDeviceLost();
   572 	}
   573 	else
   574 	{
   575 		DX::ThrowIfFailed(hr);
   576 	}
   577 }
   578 
   579 // Method to convert a length in device-independent pixels (DIPs) to a length in physical pixels.
   580 float SDL_winrtrenderer::ConvertDipsToPixels(float dips)
   581 {
   582 	static const float dipsPerInch = 96.0f;
   583 	return floor(dips * DisplayProperties::LogicalDpi / dipsPerInch + 0.5f); // Round to nearest integer.
   584 }