From 7652fa94d0e7b8a0c7ef68d4ae064abb8182c541 Mon Sep 17 00:00:00 2001 From: David Ludwig Date: Sat, 27 Oct 2012 20:12:31 -0400 Subject: [PATCH] WinRT: created SDLmain library using most of VC++ 2012's template for Direct3D 11 apps. Most of this will be moved into SDL itself. --- VisualC/SDLmain/SDLmain_VS2012_WinRT.vcxproj | 199 +++++++++++ src/main/windowsrt/BasicTimer.h | 76 ++++ src/main/windowsrt/CubeRenderer.cpp | 256 ++++++++++++++ src/main/windowsrt/CubeRenderer.h | 44 +++ src/main/windowsrt/Direct3DBase.cpp | 344 +++++++++++++++++++ src/main/windowsrt/Direct3DBase.h | 38 ++ src/main/windowsrt/DirectXHelper.h | 36 ++ src/main/windowsrt/SDL_WinRTApp.cpp | 148 ++++++++ src/main/windowsrt/SDL_WinRTApp.h | 40 +++ src/main/windowsrt/SDLmain_WinRT_common.h | 7 + src/main/windowsrt/SimplePixelShader.hlsl | 10 + src/main/windowsrt/SimpleVertexShader.hlsl | 35 ++ 12 files changed, 1233 insertions(+) create mode 100644 VisualC/SDLmain/SDLmain_VS2012_WinRT.vcxproj create mode 100644 src/main/windowsrt/BasicTimer.h create mode 100644 src/main/windowsrt/CubeRenderer.cpp create mode 100644 src/main/windowsrt/CubeRenderer.h create mode 100644 src/main/windowsrt/Direct3DBase.cpp create mode 100644 src/main/windowsrt/Direct3DBase.h create mode 100644 src/main/windowsrt/DirectXHelper.h create mode 100644 src/main/windowsrt/SDL_WinRTApp.cpp create mode 100644 src/main/windowsrt/SDL_WinRTApp.h create mode 100644 src/main/windowsrt/SDLmain_WinRT_common.h create mode 100644 src/main/windowsrt/SimplePixelShader.hlsl create mode 100644 src/main/windowsrt/SimpleVertexShader.hlsl diff --git a/VisualC/SDLmain/SDLmain_VS2012_WinRT.vcxproj b/VisualC/SDLmain/SDLmain_VS2012_WinRT.vcxproj new file mode 100644 index 000000000..200bbd478 --- /dev/null +++ b/VisualC/SDLmain/SDLmain_VS2012_WinRT.vcxproj @@ -0,0 +1,199 @@ + + + + + Debug + ARM + + + Debug + Win32 + + + Debug + x64 + + + Release + ARM + + + Release + Win32 + + + Release + x64 + + + + + + + + + + + + + + + + + {48666378-b527-43f5-8968-f867a6f8d2a3} + Win32Proj + SDLmain_VS2012_WinRT + SDLmain_VS2012_WinRT + en-US + 11.0 + true + + + + StaticLibrary + true + v110 + + + StaticLibrary + true + v110 + + + StaticLibrary + true + v110 + + + StaticLibrary + false + true + v110 + + + StaticLibrary + false + true + v110 + + + StaticLibrary + false + true + v110 + + + + + + + + + + + + + + + + + + + + + + + + false + + + false + + + false + + + false + + + false + + + false + + + + NotUsing + true + true + + + Console + false + false + + + + + NotUsing + true + true + + + Console + false + false + + + + + Use + false + true + + + Console + false + false + + + + + Use + false + true + + + Console + false + false + + + + + Use + false + true + + + Console + false + false + + + + + Use + false + true + + + Console + false + false + + + + + + \ No newline at end of file diff --git a/src/main/windowsrt/BasicTimer.h b/src/main/windowsrt/BasicTimer.h new file mode 100644 index 000000000..4f097d4eb --- /dev/null +++ b/src/main/windowsrt/BasicTimer.h @@ -0,0 +1,76 @@ +#pragma once + +#include + +// Helper class for basic timing. +ref class BasicTimer sealed +{ +public: + // Initializes internal timer values. + BasicTimer() + { + if (!QueryPerformanceFrequency(&m_frequency)) + { + throw ref new Platform::FailureException(); + } + Reset(); + } + + // Reset the timer to initial values. + void Reset() + { + Update(); + m_startTime = m_currentTime; + m_total = 0.0f; + m_delta = 1.0f / 60.0f; + } + + // Update the timer's internal values. + void Update() + { + if (!QueryPerformanceCounter(&m_currentTime)) + { + throw ref new Platform::FailureException(); + } + + m_total = static_cast( + static_cast(m_currentTime.QuadPart - m_startTime.QuadPart) / + static_cast(m_frequency.QuadPart) + ); + + if (m_lastTime.QuadPart == m_startTime.QuadPart) + { + // If the timer was just reset, report a time delta equivalent to 60Hz frame time. + m_delta = 1.0f / 60.0f; + } + else + { + m_delta = static_cast( + static_cast(m_currentTime.QuadPart - m_lastTime.QuadPart) / + static_cast(m_frequency.QuadPart) + ); + } + + m_lastTime = m_currentTime; + } + + // Duration in seconds between the last call to Reset() and the last call to Update(). + property float Total + { + float get() { return m_total; } + } + + // Duration in seconds between the previous two calls to Update(). + property float Delta + { + float get() { return m_delta; } + } + +private: + LARGE_INTEGER m_frequency; + LARGE_INTEGER m_currentTime; + LARGE_INTEGER m_startTime; + LARGE_INTEGER m_lastTime; + float m_total; + float m_delta; +}; diff --git a/src/main/windowsrt/CubeRenderer.cpp b/src/main/windowsrt/CubeRenderer.cpp new file mode 100644 index 000000000..52e858607 --- /dev/null +++ b/src/main/windowsrt/CubeRenderer.cpp @@ -0,0 +1,256 @@ +#include "SDLmain_WinRT_common.h" +#include "CubeRenderer.h" + +using namespace DirectX; +using namespace Microsoft::WRL; +using namespace Windows::Foundation; +using namespace Windows::UI::Core; + +CubeRenderer::CubeRenderer() : + m_loadingComplete(false), + m_indexCount(0) +{ +} + +void CubeRenderer::CreateDeviceResources() +{ + Direct3DBase::CreateDeviceResources(); + + auto loadVSTask = DX::ReadDataAsync("SimpleVertexShader.cso"); + auto loadPSTask = DX::ReadDataAsync("SimplePixelShader.cso"); + + auto createVSTask = loadVSTask.then([this](Platform::Array^ fileData) { + DX::ThrowIfFailed( + m_d3dDevice->CreateVertexShader( + fileData->Data, + fileData->Length, + nullptr, + &m_vertexShader + ) + ); + + const D3D11_INPUT_ELEMENT_DESC vertexDesc[] = + { + { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "COLOR", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + }; + + DX::ThrowIfFailed( + m_d3dDevice->CreateInputLayout( + vertexDesc, + ARRAYSIZE(vertexDesc), + fileData->Data, + fileData->Length, + &m_inputLayout + ) + ); + }); + + auto createPSTask = loadPSTask.then([this](Platform::Array^ fileData) { + DX::ThrowIfFailed( + m_d3dDevice->CreatePixelShader( + fileData->Data, + fileData->Length, + nullptr, + &m_pixelShader + ) + ); + + CD3D11_BUFFER_DESC constantBufferDesc(sizeof(ModelViewProjectionConstantBuffer), D3D11_BIND_CONSTANT_BUFFER); + DX::ThrowIfFailed( + m_d3dDevice->CreateBuffer( + &constantBufferDesc, + nullptr, + &m_constantBuffer + ) + ); + }); + + auto createCubeTask = (createPSTask && createVSTask).then([this] () { + VertexPositionColor cubeVertices[] = + { + {XMFLOAT3(-0.5f, -0.5f, -0.5f), XMFLOAT3(0.0f, 0.0f, 0.0f)}, + {XMFLOAT3(-0.5f, -0.5f, 0.5f), XMFLOAT3(0.0f, 0.0f, 1.0f)}, + {XMFLOAT3(-0.5f, 0.5f, -0.5f), XMFLOAT3(0.0f, 1.0f, 0.0f)}, + {XMFLOAT3(-0.5f, 0.5f, 0.5f), XMFLOAT3(0.0f, 1.0f, 1.0f)}, + {XMFLOAT3( 0.5f, -0.5f, -0.5f), XMFLOAT3(1.0f, 0.0f, 0.0f)}, + {XMFLOAT3( 0.5f, -0.5f, 0.5f), XMFLOAT3(1.0f, 0.0f, 1.0f)}, + {XMFLOAT3( 0.5f, 0.5f, -0.5f), XMFLOAT3(1.0f, 1.0f, 0.0f)}, + {XMFLOAT3( 0.5f, 0.5f, 0.5f), XMFLOAT3(1.0f, 1.0f, 1.0f)}, + }; + + D3D11_SUBRESOURCE_DATA vertexBufferData = {0}; + vertexBufferData.pSysMem = cubeVertices; + vertexBufferData.SysMemPitch = 0; + vertexBufferData.SysMemSlicePitch = 0; + CD3D11_BUFFER_DESC vertexBufferDesc(sizeof(cubeVertices), D3D11_BIND_VERTEX_BUFFER); + DX::ThrowIfFailed( + m_d3dDevice->CreateBuffer( + &vertexBufferDesc, + &vertexBufferData, + &m_vertexBuffer + ) + ); + + unsigned short cubeIndices[] = + { + 0,2,1, // -x + 1,2,3, + + 4,5,6, // +x + 5,7,6, + + 0,1,5, // -y + 0,5,4, + + 2,6,7, // +y + 2,7,3, + + 0,4,6, // -z + 0,6,2, + + 1,3,7, // +z + 1,7,5, + }; + + m_indexCount = ARRAYSIZE(cubeIndices); + + D3D11_SUBRESOURCE_DATA indexBufferData = {0}; + indexBufferData.pSysMem = cubeIndices; + indexBufferData.SysMemPitch = 0; + indexBufferData.SysMemSlicePitch = 0; + CD3D11_BUFFER_DESC indexBufferDesc(sizeof(cubeIndices), D3D11_BIND_INDEX_BUFFER); + DX::ThrowIfFailed( + m_d3dDevice->CreateBuffer( + &indexBufferDesc, + &indexBufferData, + &m_indexBuffer + ) + ); + }); + + createCubeTask.then([this] () { + m_loadingComplete = true; + }); +} + +void CubeRenderer::CreateWindowSizeDependentResources() +{ + Direct3DBase::CreateWindowSizeDependentResources(); + + float aspectRatio = m_windowBounds.Width / m_windowBounds.Height; + float fovAngleY = 70.0f * XM_PI / 180.0f; + + // Note that the m_orientationTransform3D matrix is post-multiplied here + // in order to correctly orient the scene to match the display orientation. + // This post-multiplication step is required for any draw calls that are + // made to the swap chain render target. For draw calls to other targets, + // this transform should not be applied. + XMStoreFloat4x4( + &m_constantBufferData.projection, + XMMatrixTranspose( + XMMatrixMultiply( + XMMatrixPerspectiveFovRH( + fovAngleY, + aspectRatio, + 0.01f, + 100.0f + ), + XMLoadFloat4x4(&m_orientationTransform3D) + ) + ) + ); +} + +void CubeRenderer::Update(float timeTotal, float timeDelta) +{ + (void) timeDelta; // Unused parameter. + + XMVECTOR eye = XMVectorSet(0.0f, 0.7f, 1.5f, 0.0f); + XMVECTOR at = XMVectorSet(0.0f, -0.1f, 0.0f, 0.0f); + XMVECTOR up = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f); + + XMStoreFloat4x4(&m_constantBufferData.view, XMMatrixTranspose(XMMatrixLookAtRH(eye, at, up))); + XMStoreFloat4x4(&m_constantBufferData.model, XMMatrixTranspose(XMMatrixRotationY(timeTotal * XM_PIDIV4))); +} + +void CubeRenderer::Render() +{ + const float midnightBlue[] = { 0.098f, 0.098f, 0.439f, 1.000f }; + m_d3dContext->ClearRenderTargetView( + m_renderTargetView.Get(), + midnightBlue + ); + + m_d3dContext->ClearDepthStencilView( + m_depthStencilView.Get(), + D3D11_CLEAR_DEPTH, + 1.0f, + 0 + ); + + // Only draw the cube once it is loaded (loading is asynchronous). + if (!m_loadingComplete) + { + return; + } + + m_d3dContext->OMSetRenderTargets( + 1, + m_renderTargetView.GetAddressOf(), + m_depthStencilView.Get() + ); + + m_d3dContext->UpdateSubresource( + m_constantBuffer.Get(), + 0, + NULL, + &m_constantBufferData, + 0, + 0 + ); + + UINT stride = sizeof(VertexPositionColor); + UINT offset = 0; + m_d3dContext->IASetVertexBuffers( + 0, + 1, + m_vertexBuffer.GetAddressOf(), + &stride, + &offset + ); + + m_d3dContext->IASetIndexBuffer( + m_indexBuffer.Get(), + DXGI_FORMAT_R16_UINT, + 0 + ); + + m_d3dContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + + m_d3dContext->IASetInputLayout(m_inputLayout.Get()); + + m_d3dContext->VSSetShader( + m_vertexShader.Get(), + nullptr, + 0 + ); + + m_d3dContext->VSSetConstantBuffers( + 0, + 1, + m_constantBuffer.GetAddressOf() + ); + + m_d3dContext->PSSetShader( + m_pixelShader.Get(), + nullptr, + 0 + ); + + m_d3dContext->DrawIndexed( + m_indexCount, + 0, + 0 + ); +} diff --git a/src/main/windowsrt/CubeRenderer.h b/src/main/windowsrt/CubeRenderer.h new file mode 100644 index 000000000..b3ce23001 --- /dev/null +++ b/src/main/windowsrt/CubeRenderer.h @@ -0,0 +1,44 @@ +#pragma once + +#include "Direct3DBase.h" + +struct ModelViewProjectionConstantBuffer +{ + DirectX::XMFLOAT4X4 model; + DirectX::XMFLOAT4X4 view; + DirectX::XMFLOAT4X4 projection; +}; + +struct VertexPositionColor +{ + DirectX::XMFLOAT3 pos; + DirectX::XMFLOAT3 color; +}; + +// This class renders a simple spinning cube. +ref class CubeRenderer sealed : public Direct3DBase +{ +public: + CubeRenderer(); + + // Direct3DBase methods. + virtual void CreateDeviceResources() override; + virtual void CreateWindowSizeDependentResources() override; + virtual void Render() override; + + // Method for updating time-dependent objects. + void Update(float timeTotal, float timeDelta); + +private: + bool m_loadingComplete; + + Microsoft::WRL::ComPtr m_inputLayout; + Microsoft::WRL::ComPtr m_vertexBuffer; + Microsoft::WRL::ComPtr m_indexBuffer; + Microsoft::WRL::ComPtr m_vertexShader; + Microsoft::WRL::ComPtr m_pixelShader; + Microsoft::WRL::ComPtr m_constantBuffer; + + uint32 m_indexCount; + ModelViewProjectionConstantBuffer m_constantBufferData; +}; diff --git a/src/main/windowsrt/Direct3DBase.cpp b/src/main/windowsrt/Direct3DBase.cpp new file mode 100644 index 000000000..bb0ea593d --- /dev/null +++ b/src/main/windowsrt/Direct3DBase.cpp @@ -0,0 +1,344 @@ +#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/main/windowsrt/Direct3DBase.h b/src/main/windowsrt/Direct3DBase.h new file mode 100644 index 000000000..9a095dc87 --- /dev/null +++ b/src/main/windowsrt/Direct3DBase.h @@ -0,0 +1,38 @@ +#pragma once + +#include "DirectXHelper.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 Render() = 0; + virtual void Present(); + virtual float ConvertDipsToPixels(float dips); + +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/main/windowsrt/DirectXHelper.h b/src/main/windowsrt/DirectXHelper.h new file mode 100644 index 000000000..3d3e59b88 --- /dev/null +++ b/src/main/windowsrt/DirectXHelper.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include + +namespace DX +{ + inline void ThrowIfFailed(HRESULT hr) + { + if (FAILED(hr)) + { + // Set a breakpoint on this line to catch Win32 API errors. + throw Platform::Exception::CreateException(hr); + } + } + + // Function that reads from a binary file asynchronously. + inline Concurrency::task^> ReadDataAsync(Platform::String^ filename) + { + using namespace Windows::Storage; + using namespace Concurrency; + + auto folder = Windows::ApplicationModel::Package::Current->InstalledLocation; + + return create_task(folder->GetFileAsync(filename)).then([] (StorageFile^ file) + { + return FileIO::ReadBufferAsync(file); + }).then([] (Streams::IBuffer^ fileBuffer) -> Platform::Array^ + { + auto fileData = ref new Platform::Array(fileBuffer->Length); + Streams::DataReader::FromBuffer(fileBuffer)->ReadBytes(fileData); + return fileData; + }); + } +} diff --git a/src/main/windowsrt/SDL_WinRTApp.cpp b/src/main/windowsrt/SDL_WinRTApp.cpp new file mode 100644 index 000000000..970fac2ef --- /dev/null +++ b/src/main/windowsrt/SDL_WinRTApp.cpp @@ -0,0 +1,148 @@ +#include "SDLmain_WinRT_common.h" +#include "SDL_WinRTApp.h" +#include "BasicTimer.h" + +using namespace Windows::ApplicationModel; +using namespace Windows::ApplicationModel::Core; +using namespace Windows::ApplicationModel::Activation; +using namespace Windows::UI::Core; +using namespace Windows::System; +using namespace Windows::Foundation; +using namespace Windows::Graphics::Display; +using namespace concurrency; + +SDL_WinRTApp::SDL_WinRTApp() : + m_windowClosed(false), + m_windowVisible(true) +{ +} + +void SDL_WinRTApp::Initialize(CoreApplicationView^ applicationView) +{ + applicationView->Activated += + ref new TypedEventHandler(this, &SDL_WinRTApp::OnActivated); + + CoreApplication::Suspending += + ref new EventHandler(this, &SDL_WinRTApp::OnSuspending); + + CoreApplication::Resuming += + ref new EventHandler(this, &SDL_WinRTApp::OnResuming); + + m_renderer = ref new CubeRenderer(); +} + +void SDL_WinRTApp::SetWindow(CoreWindow^ window) +{ + window->SizeChanged += + ref new TypedEventHandler(this, &SDL_WinRTApp::OnWindowSizeChanged); + + window->VisibilityChanged += + ref new TypedEventHandler(this, &SDL_WinRTApp::OnVisibilityChanged); + + window->Closed += + ref new TypedEventHandler(this, &SDL_WinRTApp::OnWindowClosed); + + window->PointerCursor = ref new CoreCursor(CoreCursorType::Arrow, 0); + + window->PointerPressed += + ref new TypedEventHandler(this, &SDL_WinRTApp::OnPointerPressed); + + window->PointerMoved += + ref new TypedEventHandler(this, &SDL_WinRTApp::OnPointerMoved); + + m_renderer->Initialize(CoreWindow::GetForCurrentThread()); +} + +void SDL_WinRTApp::Load(Platform::String^ entryPoint) +{ +} + +void SDL_WinRTApp::Run() +{ + BasicTimer^ timer = ref new BasicTimer(); + + while (!m_windowClosed) + { + if (m_windowVisible) + { + timer->Update(); + CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent); + m_renderer->Update(timer->Total, timer->Delta); + m_renderer->Render(); + m_renderer->Present(); // This call is synchronized to the display frame rate. + } + else + { + CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessOneAndAllPending); + } + } +} + +void SDL_WinRTApp::Uninitialize() +{ +} + +void SDL_WinRTApp::OnWindowSizeChanged(CoreWindow^ sender, WindowSizeChangedEventArgs^ args) +{ + m_renderer->UpdateForWindowSizeChange(); +} + +void SDL_WinRTApp::OnVisibilityChanged(CoreWindow^ sender, VisibilityChangedEventArgs^ args) +{ + m_windowVisible = args->Visible; +} + +void SDL_WinRTApp::OnWindowClosed(CoreWindow^ sender, CoreWindowEventArgs^ args) +{ + m_windowClosed = true; +} + +void SDL_WinRTApp::OnPointerPressed(CoreWindow^ sender, PointerEventArgs^ args) +{ + // Insert your code here. +} + +void SDL_WinRTApp::OnPointerMoved(CoreWindow^ sender, PointerEventArgs^ args) +{ + // Insert your code here. +} + +void SDL_WinRTApp::OnActivated(CoreApplicationView^ applicationView, IActivatedEventArgs^ args) +{ + CoreWindow::GetForCurrentThread()->Activate(); +} + +void SDL_WinRTApp::OnSuspending(Platform::Object^ sender, SuspendingEventArgs^ args) +{ + // Save app state asynchronously after requesting a deferral. Holding a deferral + // indicates that the application is busy performing suspending operations. Be + // aware that a deferral may not be held indefinitely. After about five seconds, + // the app will be forced to exit. + SuspendingDeferral^ deferral = args->SuspendingOperation->GetDeferral(); + + create_task([this, deferral]() + { + // Insert your code here. + + deferral->Complete(); + }); +} + +void SDL_WinRTApp::OnResuming(Platform::Object^ sender, Platform::Object^ args) +{ + // Restore any data or state that was unloaded on suspend. By default, data + // and state are persisted when resuming from suspend. Note that this event + // does not occur if the app was previously terminated. +} + +IFrameworkView^ Direct3DApplicationSource::CreateView() +{ + return ref new SDL_WinRTApp(); +} + +int SDL_WinRT_RunApplication(/*Platform::Array^*/) +{ + auto direct3DApplicationSource = ref new Direct3DApplicationSource(); + CoreApplication::Run(direct3DApplicationSource); + return 0; +} diff --git a/src/main/windowsrt/SDL_WinRTApp.h b/src/main/windowsrt/SDL_WinRTApp.h new file mode 100644 index 000000000..cf6e88957 --- /dev/null +++ b/src/main/windowsrt/SDL_WinRTApp.h @@ -0,0 +1,40 @@ +#pragma once + +#include "SDLmain_WinRT_common.h" +#include "CubeRenderer.h" + +ref class SDL_WinRTApp sealed : public Windows::ApplicationModel::Core::IFrameworkView +{ +public: + SDL_WinRTApp(); + + // IFrameworkView Methods. + virtual void Initialize(Windows::ApplicationModel::Core::CoreApplicationView^ applicationView); + virtual void SetWindow(Windows::UI::Core::CoreWindow^ window); + virtual void Load(Platform::String^ entryPoint); + virtual void Run(); + virtual void Uninitialize(); + +protected: + // Event Handlers. + void OnWindowSizeChanged(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::WindowSizeChangedEventArgs^ args); + void OnLogicalDpiChanged(Platform::Object^ sender); + void OnActivated(Windows::ApplicationModel::Core::CoreApplicationView^ applicationView, Windows::ApplicationModel::Activation::IActivatedEventArgs^ args); + void OnSuspending(Platform::Object^ sender, Windows::ApplicationModel::SuspendingEventArgs^ args); + void OnResuming(Platform::Object^ sender, Platform::Object^ args); + void OnWindowClosed(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::CoreWindowEventArgs^ args); + void OnVisibilityChanged(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::VisibilityChangedEventArgs^ args); + void OnPointerPressed(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::PointerEventArgs^ args); + void OnPointerMoved(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::PointerEventArgs^ args); + +private: + CubeRenderer^ m_renderer; + bool m_windowClosed; + bool m_windowVisible; +}; + +ref class Direct3DApplicationSource sealed : Windows::ApplicationModel::Core::IFrameworkViewSource +{ +public: + virtual Windows::ApplicationModel::Core::IFrameworkView^ CreateView(); +}; diff --git a/src/main/windowsrt/SDLmain_WinRT_common.h b/src/main/windowsrt/SDLmain_WinRT_common.h new file mode 100644 index 000000000..a07dd0cdd --- /dev/null +++ b/src/main/windowsrt/SDLmain_WinRT_common.h @@ -0,0 +1,7 @@ +#pragma once + +#include +#include +#include +#include +#include \ No newline at end of file diff --git a/src/main/windowsrt/SimplePixelShader.hlsl b/src/main/windowsrt/SimplePixelShader.hlsl new file mode 100644 index 000000000..a9142692e --- /dev/null +++ b/src/main/windowsrt/SimplePixelShader.hlsl @@ -0,0 +1,10 @@ +struct PixelShaderInput +{ + float4 pos : SV_POSITION; + float3 color : COLOR0; +}; + +float4 main(PixelShaderInput input) : SV_TARGET +{ + return float4(input.color,1.0f); +} diff --git a/src/main/windowsrt/SimpleVertexShader.hlsl b/src/main/windowsrt/SimpleVertexShader.hlsl new file mode 100644 index 000000000..f4ef86899 --- /dev/null +++ b/src/main/windowsrt/SimpleVertexShader.hlsl @@ -0,0 +1,35 @@ +cbuffer ModelViewProjectionConstantBuffer : register(b0) +{ + matrix model; + matrix view; + matrix projection; +}; + +struct VertexShaderInput +{ + float3 pos : POSITION; + float3 color : COLOR0; +}; + +struct VertexShaderOutput +{ + float4 pos : SV_POSITION; + float3 color : COLOR0; +}; + +VertexShaderOutput main(VertexShaderInput input) +{ + VertexShaderOutput output; + float4 pos = float4(input.pos, 1.0f); + + // Transform the vertex position into projected space. + pos = mul(pos, model); + pos = mul(pos, view); + pos = mul(pos, projection); + output.pos = pos; + + // Pass through the color without modification. + output.color = input.color; + + return output; +}