From bbc13a8ecbaeca4e9436daa3cb4aa7edfe586fbb Mon Sep 17 00:00:00 2001 From: David Ludwig Date: Sun, 25 Nov 2012 19:05:56 -0500 Subject: [PATCH] WinRT: video code cleanup: combined files Direct3DBase.* and SDL_winrtrenderer.* --- VisualC/SDL/SDL_VS2012_WinRT.vcxproj | 9 - src/video/windowsrt/Direct3DBase.cpp | 344 ---------------------- src/video/windowsrt/Direct3DBase.h | 41 --- src/video/windowsrt/SDL_winrtrenderer.cpp | 336 ++++++++++++++++++++- src/video/windowsrt/SDL_winrtrenderer.h | 43 ++- 5 files changed, 365 insertions(+), 408 deletions(-) delete mode 100644 src/video/windowsrt/Direct3DBase.cpp delete mode 100644 src/video/windowsrt/Direct3DBase.h diff --git a/VisualC/SDL/SDL_VS2012_WinRT.vcxproj b/VisualC/SDL/SDL_VS2012_WinRT.vcxproj index 7d6deb876..3db48e32d 100644 --- a/VisualC/SDL/SDL_VS2012_WinRT.vcxproj +++ b/VisualC/SDL/SDL_VS2012_WinRT.vcxproj @@ -117,14 +117,6 @@ - - true - true - true - true - true - true - true true @@ -270,7 +262,6 @@ - diff --git a/src/video/windowsrt/Direct3DBase.cpp b/src/video/windowsrt/Direct3DBase.cpp deleted file mode 100644 index bb0ea593d..000000000 --- a/src/video/windowsrt/Direct3DBase.cpp +++ /dev/null @@ -1,344 +0,0 @@ -#include "SDLmain_WinRT_common.h" -#include "Direct3DBase.h" - -using namespace DirectX; -using namespace Microsoft::WRL; -using namespace Windows::UI::Core; -using namespace Windows::Foundation; -using namespace Windows::Graphics::Display; - -// Constructor. -Direct3DBase::Direct3DBase() -{ -} - -// Initialize the Direct3D resources required to run. -void Direct3DBase::Initialize(CoreWindow^ window) -{ - m_window = window; - - CreateDeviceResources(); - CreateWindowSizeDependentResources(); -} - -// Recreate all device resources and set them back to the current state. -void Direct3DBase::HandleDeviceLost() -{ - // Reset these member variables to ensure that UpdateForWindowSizeChange recreates all resources. - m_windowBounds.Width = 0; - m_windowBounds.Height = 0; - m_swapChain = nullptr; - - CreateDeviceResources(); - UpdateForWindowSizeChange(); -} - -// These are the resources that depend on the device. -void Direct3DBase::CreateDeviceResources() -{ - // This flag adds support for surfaces with a different color channel ordering - // than the API default. It is required for compatibility with Direct2D. - UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; - -#if defined(_DEBUG) - // If the project is in a debug build, enable debugging via SDK Layers with this flag. - creationFlags |= D3D11_CREATE_DEVICE_DEBUG; -#endif - - // This array defines the set of DirectX hardware feature levels this app will support. - // Note the ordering should be preserved. - // Don't forget to declare your application's minimum required feature level in its - // description. All applications are assumed to support 9.1 unless otherwise stated. - D3D_FEATURE_LEVEL featureLevels[] = - { - D3D_FEATURE_LEVEL_11_1, - D3D_FEATURE_LEVEL_11_0, - D3D_FEATURE_LEVEL_10_1, - D3D_FEATURE_LEVEL_10_0, - D3D_FEATURE_LEVEL_9_3, - D3D_FEATURE_LEVEL_9_2, - D3D_FEATURE_LEVEL_9_1 - }; - - // Create the Direct3D 11 API device object and a corresponding context. - ComPtr device; - ComPtr context; - DX::ThrowIfFailed( - D3D11CreateDevice( - nullptr, // Specify nullptr to use the default adapter. - D3D_DRIVER_TYPE_HARDWARE, - nullptr, - creationFlags, // Set set debug and Direct2D compatibility flags. - featureLevels, // List of feature levels this app can support. - ARRAYSIZE(featureLevels), - D3D11_SDK_VERSION, // Always set this to D3D11_SDK_VERSION for Windows Store apps. - &device, // Returns the Direct3D device created. - &m_featureLevel, // Returns feature level of device created. - &context // Returns the device immediate context. - ) - ); - - // Get the Direct3D 11.1 API device and context interfaces. - DX::ThrowIfFailed( - device.As(&m_d3dDevice) - ); - - DX::ThrowIfFailed( - context.As(&m_d3dContext) - ); -} - -// Allocate all memory resources that change on a window SizeChanged event. -void Direct3DBase::CreateWindowSizeDependentResources() -{ - // 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; - - if(m_swapChain != nullptr) - { - // If the swap chain already exists, resize it. - DX::ThrowIfFailed( - m_swapChain->ResizeBuffers( - 2, // Double-buffered swap chain. - static_cast(m_renderTargetSize.Width), - static_cast(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(m_renderTargetSize.Width); // Match the size of the window. - swapChainDesc.Height = static_cast(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. - swapChainDesc.Scaling = DXGI_SCALING_NONE; - swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // All Windows Store apps must use this SwapEffect. - swapChainDesc.Flags = 0; - - ComPtr dxgiDevice; - DX::ThrowIfFailed( - m_d3dDevice.As(&dxgiDevice) - ); - - ComPtr dxgiAdapter; - DX::ThrowIfFailed( - dxgiDevice->GetAdapter(&dxgiAdapter) - ); - - ComPtr dxgiFactory; - DX::ThrowIfFailed( - dxgiAdapter->GetParent( - __uuidof(IDXGIFactory2), - &dxgiFactory - ) - ); - - Windows::UI::Core::CoreWindow^ window = m_window.Get(); - DX::ThrowIfFailed( - dxgiFactory->CreateSwapChainForCoreWindow( - m_d3dDevice.Get(), - reinterpret_cast(window), - &swapChainDesc, - nullptr, // Allow on all displays. - &m_swapChain - ) - ); - - // 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(); - } - - DX::ThrowIfFailed( - m_swapChain->SetRotation(rotation) - ); - - // Create a render target view of the swap chain back buffer. - ComPtr backBuffer; - DX::ThrowIfFailed( - m_swapChain->GetBuffer( - 0, - __uuidof(ID3D11Texture2D), - &backBuffer - ) - ); - - DX::ThrowIfFailed( - m_d3dDevice->CreateRenderTargetView( - backBuffer.Get(), - nullptr, - &m_renderTargetView - ) - ); - - // Create a depth stencil view. - CD3D11_TEXTURE2D_DESC depthStencilDesc( - DXGI_FORMAT_D24_UNORM_S8_UINT, - static_cast(m_renderTargetSize.Width), - static_cast(m_renderTargetSize.Height), - 1, - 1, - D3D11_BIND_DEPTH_STENCIL - ); - - ComPtr depthStencil; - DX::ThrowIfFailed( - m_d3dDevice->CreateTexture2D( - &depthStencilDesc, - nullptr, - &depthStencil - ) - ); - - CD3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc(D3D11_DSV_DIMENSION_TEXTURE2D); - DX::ThrowIfFailed( - m_d3dDevice->CreateDepthStencilView( - depthStencil.Get(), - &depthStencilViewDesc, - &m_depthStencilView - ) - ); - - // Set the rendering viewport to target the entire window. - CD3D11_VIEWPORT viewport( - 0.0f, - 0.0f, - m_renderTargetSize.Width, - m_renderTargetSize.Height - ); - - m_d3dContext->RSSetViewports(1, &viewport); -} - -// This method is called in the event handler for the SizeChanged event. -void Direct3DBase::UpdateForWindowSizeChange() -{ - if (m_window->Bounds.Width != m_windowBounds.Width || - m_window->Bounds.Height != m_windowBounds.Height || - m_orientation != DisplayProperties::CurrentOrientation) - { - ID3D11RenderTargetView* nullViews[] = {nullptr}; - m_d3dContext->OMSetRenderTargets(ARRAYSIZE(nullViews), nullViews, nullptr); - m_renderTargetView = nullptr; - m_depthStencilView = nullptr; - m_d3dContext->Flush(); - CreateWindowSizeDependentResources(); - } -} - -// Method to deliver the final image to the display. -void Direct3DBase::Present() -{ - // The application may optionally specify "dirty" or "scroll" - // rects to improve efficiency in certain scenarios. - DXGI_PRESENT_PARAMETERS parameters = {0}; - parameters.DirtyRectsCount = 0; - parameters.pDirtyRects = nullptr; - parameters.pScrollRect = nullptr; - parameters.pScrollOffset = nullptr; - - // The first argument instructs DXGI to block until VSync, putting the application - // to sleep until the next VSync. This ensures we don't waste any cycles rendering - // frames that will never be displayed to the screen. - HRESULT hr = m_swapChain->Present1(1, 0, ¶meters); - - // Discard the contents of the render target. - // This is a valid operation only when the existing contents will be entirely - // overwritten. If dirty or scroll rects are used, this call should be removed. - m_d3dContext->DiscardView(m_renderTargetView.Get()); - - // Discard the contents of the depth stencil. - m_d3dContext->DiscardView(m_depthStencilView.Get()); - - // If the device was removed either by a disconnect or a driver upgrade, we - // must recreate all device resources. - if (hr == DXGI_ERROR_DEVICE_REMOVED) - { - HandleDeviceLost(); - } - else - { - DX::ThrowIfFailed(hr); - } -} - -// Method to convert a length in device-independent pixels (DIPs) to a length in physical pixels. -float Direct3DBase::ConvertDipsToPixels(float dips) -{ - static const float dipsPerInch = 96.0f; - return floor(dips * DisplayProperties::LogicalDpi / dipsPerInch + 0.5f); // Round to nearest integer. -} diff --git a/src/video/windowsrt/Direct3DBase.h b/src/video/windowsrt/Direct3DBase.h deleted file mode 100644 index eab1a178e..000000000 --- a/src/video/windowsrt/Direct3DBase.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include "DirectXHelper.h" -#include "SDL.h" - -// Helper class that initializes DirectX APIs for 3D rendering. -ref class Direct3DBase abstract -{ -internal: - Direct3DBase(); - -public: - virtual void Initialize(Windows::UI::Core::CoreWindow^ window); - virtual void HandleDeviceLost(); - virtual void CreateDeviceResources(); - virtual void CreateWindowSizeDependentResources(); - virtual void UpdateForWindowSizeChange(); - virtual void Present(); - virtual float ConvertDipsToPixels(float dips); - -internal: - virtual void Render(SDL_Surface * surface, SDL_Rect * rects, int numrects) = 0; - -protected private: - // Direct3D Objects. - Microsoft::WRL::ComPtr m_d3dDevice; - Microsoft::WRL::ComPtr m_d3dContext; - Microsoft::WRL::ComPtr m_swapChain; - Microsoft::WRL::ComPtr m_renderTargetView; - Microsoft::WRL::ComPtr m_depthStencilView; - - // Cached renderer properties. - D3D_FEATURE_LEVEL m_featureLevel; - Windows::Foundation::Size m_renderTargetSize; - Windows::Foundation::Rect m_windowBounds; - Platform::Agile m_window; - Windows::Graphics::Display::DisplayOrientations m_orientation; - - // Transform used for display orientation. - DirectX::XMFLOAT4X4 m_orientationTransform3D; -}; diff --git a/src/video/windowsrt/SDL_winrtrenderer.cpp b/src/video/windowsrt/SDL_winrtrenderer.cpp index 99a7824f1..a64cfddae 100644 --- a/src/video/windowsrt/SDL_winrtrenderer.cpp +++ b/src/video/windowsrt/SDL_winrtrenderer.cpp @@ -3,20 +3,93 @@ using namespace DirectX; using namespace Microsoft::WRL; -using namespace Windows::Foundation; using namespace Windows::UI::Core; +using namespace Windows::Foundation; +using namespace Windows::Graphics::Display; +// Constructor. SDL_winrtrenderer::SDL_winrtrenderer() : - m_loadingComplete(false), + m_loadingComplete(false), m_vertexCount(0) { } +// Initialize the Direct3D resources required to run. +void SDL_winrtrenderer::Initialize(CoreWindow^ window) +{ + m_window = window; + + CreateDeviceResources(); + CreateWindowSizeDependentResources(); +} + +// Recreate all device resources and set them back to the current state. +void SDL_winrtrenderer::HandleDeviceLost() +{ + // Reset these member variables to ensure that UpdateForWindowSizeChange recreates all resources. + m_windowBounds.Width = 0; + m_windowBounds.Height = 0; + m_swapChain = nullptr; + + CreateDeviceResources(); + UpdateForWindowSizeChange(); +} + +// These are the resources that depend on the device. void SDL_winrtrenderer::CreateDeviceResources() { - Direct3DBase::CreateDeviceResources(); + // This flag adds support for surfaces with a different color channel ordering + // than the API default. It is required for compatibility with Direct2D. + UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; - auto loadVSTask = DX::ReadDataAsync("SDL_VS2012_WinRT\\SimpleVertexShader.cso"); +#if defined(_DEBUG) + // If the project is in a debug build, enable debugging via SDK Layers with this flag. + creationFlags |= D3D11_CREATE_DEVICE_DEBUG; +#endif + + // This array defines the set of DirectX hardware feature levels this app will support. + // Note the ordering should be preserved. + // Don't forget to declare your application's minimum required feature level in its + // description. All applications are assumed to support 9.1 unless otherwise stated. + D3D_FEATURE_LEVEL featureLevels[] = + { + D3D_FEATURE_LEVEL_11_1, + D3D_FEATURE_LEVEL_11_0, + D3D_FEATURE_LEVEL_10_1, + D3D_FEATURE_LEVEL_10_0, + D3D_FEATURE_LEVEL_9_3, + D3D_FEATURE_LEVEL_9_2, + D3D_FEATURE_LEVEL_9_1 + }; + + // Create the Direct3D 11 API device object and a corresponding context. + ComPtr device; + ComPtr context; + DX::ThrowIfFailed( + D3D11CreateDevice( + nullptr, // Specify nullptr to use the default adapter. + D3D_DRIVER_TYPE_HARDWARE, + nullptr, + creationFlags, // Set set debug and Direct2D compatibility flags. + featureLevels, // List of feature levels this app can support. + ARRAYSIZE(featureLevels), + D3D11_SDK_VERSION, // Always set this to D3D11_SDK_VERSION for Windows Store apps. + &device, // Returns the Direct3D device created. + &m_featureLevel, // Returns feature level of device created. + &context // Returns the device immediate context. + ) + ); + + // Get the Direct3D 11.1 API device and context interfaces. + DX::ThrowIfFailed( + device.As(&m_d3dDevice) + ); + + DX::ThrowIfFailed( + context.As(&m_d3dContext) + ); + + auto loadVSTask = DX::ReadDataAsync("SDL_VS2012_WinRT\\SimpleVertexShader.cso"); auto loadPSTask = DX::ReadDataAsync("SDL_VS2012_WinRT\\SimplePixelShader.cso"); auto createVSTask = loadVSTask.then([this](Platform::Array^ fileData) { @@ -110,6 +183,202 @@ void SDL_winrtrenderer::CreateDeviceResources() }); } +// Allocate all memory resources that change on a window SizeChanged event. +void SDL_winrtrenderer::CreateWindowSizeDependentResources() +{ + // 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; + + if(m_swapChain != nullptr) + { + // If the swap chain already exists, resize it. + DX::ThrowIfFailed( + m_swapChain->ResizeBuffers( + 2, // Double-buffered swap chain. + static_cast(m_renderTargetSize.Width), + static_cast(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(m_renderTargetSize.Width); // Match the size of the window. + swapChainDesc.Height = static_cast(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. + swapChainDesc.Scaling = DXGI_SCALING_NONE; + swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // All Windows Store apps must use this SwapEffect. + swapChainDesc.Flags = 0; + + ComPtr dxgiDevice; + DX::ThrowIfFailed( + m_d3dDevice.As(&dxgiDevice) + ); + + ComPtr dxgiAdapter; + DX::ThrowIfFailed( + dxgiDevice->GetAdapter(&dxgiAdapter) + ); + + ComPtr dxgiFactory; + DX::ThrowIfFailed( + dxgiAdapter->GetParent( + __uuidof(IDXGIFactory2), + &dxgiFactory + ) + ); + + Windows::UI::Core::CoreWindow^ window = m_window.Get(); + DX::ThrowIfFailed( + dxgiFactory->CreateSwapChainForCoreWindow( + m_d3dDevice.Get(), + reinterpret_cast(window), + &swapChainDesc, + nullptr, // Allow on all displays. + &m_swapChain + ) + ); + + // 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(); + } + + DX::ThrowIfFailed( + m_swapChain->SetRotation(rotation) + ); + + // Create a render target view of the swap chain back buffer. + ComPtr backBuffer; + DX::ThrowIfFailed( + m_swapChain->GetBuffer( + 0, + __uuidof(ID3D11Texture2D), + &backBuffer + ) + ); + + DX::ThrowIfFailed( + m_d3dDevice->CreateRenderTargetView( + backBuffer.Get(), + nullptr, + &m_renderTargetView + ) + ); + + // Create a depth stencil view. + CD3D11_TEXTURE2D_DESC depthStencilDesc( + DXGI_FORMAT_D24_UNORM_S8_UINT, + static_cast(m_renderTargetSize.Width), + static_cast(m_renderTargetSize.Height), + 1, + 1, + D3D11_BIND_DEPTH_STENCIL + ); + + ComPtr depthStencil; + DX::ThrowIfFailed( + m_d3dDevice->CreateTexture2D( + &depthStencilDesc, + nullptr, + &depthStencil + ) + ); + + CD3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc(D3D11_DSV_DIMENSION_TEXTURE2D); + DX::ThrowIfFailed( + m_d3dDevice->CreateDepthStencilView( + depthStencil.Get(), + &depthStencilViewDesc, + &m_depthStencilView + ) + ); + + // Set the rendering viewport to target the entire window. + CD3D11_VIEWPORT viewport( + 0.0f, + 0.0f, + m_renderTargetSize.Width, + m_renderTargetSize.Height + ); + + m_d3dContext->RSSetViewports(1, &viewport); +} + void SDL_winrtrenderer::ResizeMainTexture(int w, int h) { D3D11_TEXTURE2D_DESC textureDesc = {0}; @@ -152,6 +421,22 @@ void SDL_winrtrenderer::ResizeMainTexture(int w, int h) ); } +// This method is called in the event handler for the SizeChanged event. +void SDL_winrtrenderer::UpdateForWindowSizeChange() +{ + if (m_window->Bounds.Width != m_windowBounds.Width || + m_window->Bounds.Height != m_windowBounds.Height || + m_orientation != DisplayProperties::CurrentOrientation) + { + ID3D11RenderTargetView* nullViews[] = {nullptr}; + m_d3dContext->OMSetRenderTargets(ARRAYSIZE(nullViews), nullViews, nullptr); + m_renderTargetView = nullptr; + m_depthStencilView = nullptr; + m_d3dContext->Flush(); + CreateWindowSizeDependentResources(); + } +} + void SDL_winrtrenderer::Render(SDL_Surface * surface, SDL_Rect * rects, int numrects) { const float blackColor[] = { 0.0f, 0.0f, 0.0f, 1.0f }; @@ -237,3 +522,46 @@ void SDL_winrtrenderer::Render(SDL_Surface * surface, SDL_Rect * rects, int numr m_d3dContext->Draw(4, 0); } + +// Method to deliver the final image to the display. +void SDL_winrtrenderer::Present() +{ + // The application may optionally specify "dirty" or "scroll" + // rects to improve efficiency in certain scenarios. + DXGI_PRESENT_PARAMETERS parameters = {0}; + parameters.DirtyRectsCount = 0; + parameters.pDirtyRects = nullptr; + parameters.pScrollRect = nullptr; + parameters.pScrollOffset = nullptr; + + // The first argument instructs DXGI to block until VSync, putting the application + // to sleep until the next VSync. This ensures we don't waste any cycles rendering + // frames that will never be displayed to the screen. + HRESULT hr = m_swapChain->Present1(1, 0, ¶meters); + + // Discard the contents of the render target. + // This is a valid operation only when the existing contents will be entirely + // overwritten. If dirty or scroll rects are used, this call should be removed. + m_d3dContext->DiscardView(m_renderTargetView.Get()); + + // Discard the contents of the depth stencil. + m_d3dContext->DiscardView(m_depthStencilView.Get()); + + // If the device was removed either by a disconnect or a driver upgrade, we + // must recreate all device resources. + if (hr == DXGI_ERROR_DEVICE_REMOVED) + { + HandleDeviceLost(); + } + else + { + DX::ThrowIfFailed(hr); + } +} + +// Method to convert a length in device-independent pixels (DIPs) to a length in physical pixels. +float SDL_winrtrenderer::ConvertDipsToPixels(float dips) +{ + static const float dipsPerInch = 96.0f; + return floor(dips * DisplayProperties::LogicalDpi / dipsPerInch + 0.5f); // Round to nearest integer. +} diff --git a/src/video/windowsrt/SDL_winrtrenderer.h b/src/video/windowsrt/SDL_winrtrenderer.h index 7d39a26fa..a32697b76 100644 --- a/src/video/windowsrt/SDL_winrtrenderer.h +++ b/src/video/windowsrt/SDL_winrtrenderer.h @@ -1,6 +1,7 @@ #pragma once -#include "Direct3DBase.h" +#include "DirectXHelper.h" +#include "SDL.h" struct VertexPositionColor { @@ -8,22 +9,32 @@ struct VertexPositionColor DirectX::XMFLOAT2 tex; }; -// This class renders a simple spinning cube. -ref class SDL_winrtrenderer sealed : public Direct3DBase +// Helper class that initializes DirectX APIs for 3D rendering. +ref class SDL_winrtrenderer { -public: +internal: SDL_winrtrenderer(); - // Direct3DBase methods. - virtual void CreateDeviceResources() override; +public: + virtual void Initialize(Windows::UI::Core::CoreWindow^ window); + virtual void HandleDeviceLost(); + virtual void CreateDeviceResources(); + virtual void CreateWindowSizeDependentResources(); + virtual void UpdateForWindowSizeChange(); + virtual void Present(); + virtual float ConvertDipsToPixels(float dips); internal: - virtual void Render(SDL_Surface * surface, SDL_Rect * rects, int numrects) override; + virtual void Render(SDL_Surface * surface, SDL_Rect * rects, int numrects); void ResizeMainTexture(int w, int h); -private: - bool m_loadingComplete; - +protected private: + // Direct3D Objects. + Microsoft::WRL::ComPtr m_d3dDevice; + Microsoft::WRL::ComPtr m_d3dContext; + Microsoft::WRL::ComPtr m_swapChain; + Microsoft::WRL::ComPtr m_renderTargetView; + Microsoft::WRL::ComPtr m_depthStencilView; Microsoft::WRL::ComPtr m_inputLayout; Microsoft::WRL::ComPtr m_vertexBuffer; Microsoft::WRL::ComPtr m_vertexShader; @@ -32,5 +43,17 @@ private: Microsoft::WRL::ComPtr m_mainTextureResourceView; Microsoft::WRL::ComPtr m_mainSampler; + // Cached renderer properties. + D3D_FEATURE_LEVEL m_featureLevel; + Windows::Foundation::Size m_renderTargetSize; + Windows::Foundation::Rect m_windowBounds; + Platform::Agile m_window; + Windows::Graphics::Display::DisplayOrientations m_orientation; uint32 m_vertexCount; + + // Transform used for display orientation. + DirectX::XMFLOAT4X4 m_orientationTransform3D; + + // Has the renderer finished loading? + bool m_loadingComplete; };