src/video/windowsrt/SDL_winrtrenderer.cpp
author David Ludwig <dludwig@pobox.com>
Tue, 08 Jan 2013 23:11:22 -0500
changeset 8384 bc7a52629e1e
parent 8375 e33eb49b7f42
child 8399 1fa9dcfbeac5
permissions -rw-r--r--
WinRT: converted tabs to spaces in src/video/windowsrt/*
     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 }