Last active
October 12, 2017 16:09
-
-
Save terriblememory/cca5cede44e32e086f4c3040e8de6d23 to your computer and use it in GitHub Desktop.
Single file C++ UWP D3D11 shell
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
App.cpp | |
*/ | |
#include "pch.h" | |
#include <agile.h> | |
#include <dxgi1_4.h> | |
#include <d3d11_3.h> | |
#include <DirectXMath.h> | |
using namespace Windows::ApplicationModel; | |
using namespace Windows::ApplicationModel::Core; | |
using namespace Windows::ApplicationModel::Activation; | |
using namespace Windows::UI::Core; | |
using namespace Windows::UI::Input; | |
using namespace Windows::System; | |
using namespace Windows::Foundation; | |
using namespace Windows::Graphics::Display; | |
using namespace Microsoft::WRL; | |
using namespace DirectX; | |
static float MOUSE_SCALE = 100.0f; | |
static inline void ChkTrue(bool x) | |
{ | |
assert(x); | |
if (!x) abort(); | |
} | |
static inline void ChkOk(HRESULT hr) | |
{ | |
ChkTrue(hr == S_OK); | |
} | |
ref class AppView sealed : public IFrameworkView | |
{ | |
public: | |
// 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(); | |
private: | |
// Event handlers. | |
void OnActivated(Windows::ApplicationModel::Core::CoreApplicationView^ applicationView, Windows::ApplicationModel::Activation::IActivatedEventArgs^ args); | |
void OnWindowSizeChanged(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::WindowSizeChangedEventArgs^ args); | |
void OnVisibilityChanged(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::VisibilityChangedEventArgs^ args); | |
void OnWindowClosed(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::CoreWindowEventArgs^ args); | |
void OnDpiChanged(Windows::Graphics::Display::DisplayInformation^ sender, Platform::Object^ args); | |
void OnOrientationChanged(Windows::Graphics::Display::DisplayInformation^ sender, Platform::Object^ args); | |
void OnDisplayContentsInvalidated(Windows::Graphics::Display::DisplayInformation^ sender, Platform::Object^ args); | |
void OnPointerPressed(Windows::UI::Core::CoreWindow ^sender, Windows::UI::Core::PointerEventArgs ^args); | |
void OnPointerReleased(Windows::UI::Core::CoreWindow ^sender, Windows::UI::Core::PointerEventArgs ^args); | |
void OnPointerWheelChanged(Windows::UI::Core::CoreWindow ^sender, Windows::UI::Core::PointerEventArgs ^args); | |
void OnMouseMoved(Windows::Devices::Input::MouseDevice ^sender, Windows::Devices::Input::MouseEventArgs ^args); | |
// Internal methods. | |
void HandleDeviceLost(); | |
void CreateDeviceResources(); | |
void CreateWindowSizeDependentResources(); | |
DXGI_MODE_ROTATION ComputeDisplayRotation(); | |
float ConvertDipsToPixels(float dips); | |
void UpdatePointerButtons(Windows::UI::Core::PointerEventArgs^ args); | |
// Window state. | |
bool m_windowClosed = false; | |
bool m_windowVisible = true; | |
// Cached window and display properties. | |
Platform::Agile<Windows::UI::Core::CoreWindow> m_window; | |
float m_dpi = -1.0f; | |
// Direct3D objects. | |
Microsoft::WRL::ComPtr<ID3D11Device3> m_device; | |
Microsoft::WRL::ComPtr<ID3D11DeviceContext3> m_context; | |
Microsoft::WRL::ComPtr<IDXGISwapChain3> m_swapChain; | |
// Direct3D rendering objects. Required for 3D. | |
Microsoft::WRL::ComPtr<ID3D11RenderTargetView1> m_renderTargetView; | |
Microsoft::WRL::ComPtr<ID3D11DepthStencilView> m_depthStencilView; | |
D3D11_VIEWPORT m_viewport = D3D11_VIEWPORT(); | |
// Cached device properties. | |
D3D_FEATURE_LEVEL m_featureLevel = D3D_FEATURE_LEVEL_9_1; | |
Windows::Foundation::Size m_renderTargetSize = Windows::Foundation::Size(); | |
Windows::Foundation::Size m_outputSize = Windows::Foundation::Size(); | |
Windows::Foundation::Size m_logicalSize = Windows::Foundation::Size(); | |
Windows::Graphics::Display::DisplayOrientations m_nativeOrientation = Windows::Graphics::Display::DisplayOrientations::None; | |
Windows::Graphics::Display::DisplayOrientations m_currentOrientation = Windows::Graphics::Display::DisplayOrientations::None; | |
// Transforms used for display orientation. | |
DirectX::XMFLOAT4X4 m_orientationTransform; | |
// Mouse state. | |
float MouseDeltaX = 0.0f; | |
float MouseDeltaY = 0.0f; | |
float MouseDeltaWheel = 0.0f; | |
bool MouseButtonL = false; | |
bool MouseButtonM = false; | |
bool MouseButtonR = false; | |
// Previous pointer position. Used to compute deltas. | |
Windows::UI::Core::CoreCursor^ _defaultCursor; | |
Windows::UI::Core::CoreCursor^ _cursor; | |
// TODO: Add application specific state here! | |
}; | |
/** | |
Startup view provider class. | |
*/ | |
ref class FrameworkViewSource sealed : Windows::ApplicationModel::Core::IFrameworkViewSource | |
{ | |
public: | |
virtual Windows::ApplicationModel::Core::IFrameworkView^ CreateView() { return ref new AppView(); } | |
private: | |
}; | |
/** | |
Application entry point. | |
*/ | |
[Platform::MTAThread] | |
int main(Platform::Array<Platform::String^>^ args) | |
{ | |
CoreApplication::Run(ref new FrameworkViewSource()); | |
return 0; | |
} | |
/** | |
Initialize the view. | |
*/ | |
void AppView::Initialize(CoreApplicationView^ applicationView) | |
{ | |
// TODO: Add application specific initialization code here! | |
applicationView->Activated += ref new TypedEventHandler<CoreApplicationView^, IActivatedEventArgs^>(this, &AppView::OnActivated); | |
} | |
/** | |
Associate a CoreWindow with the view. | |
*/ | |
void AppView::SetWindow(CoreWindow^ window) | |
{ | |
m_window = window; | |
m_logicalSize = Windows::Foundation::Size(window->Bounds.Width, window->Bounds.Height); | |
window->SizeChanged += ref new TypedEventHandler<CoreWindow^, WindowSizeChangedEventArgs^>(this, &AppView::OnWindowSizeChanged); | |
window->VisibilityChanged += ref new TypedEventHandler<CoreWindow^, VisibilityChangedEventArgs^>(this, &AppView::OnVisibilityChanged); | |
window->Closed += ref new TypedEventHandler<CoreWindow^, CoreWindowEventArgs^>(this, &AppView::OnWindowClosed); | |
window->PointerPressed += ref new Windows::Foundation::TypedEventHandler<Windows::UI::Core::CoreWindow ^, Windows::UI::Core::PointerEventArgs ^>(this, &AppView::OnPointerPressed); | |
window->PointerReleased += ref new Windows::Foundation::TypedEventHandler<Windows::UI::Core::CoreWindow ^, Windows::UI::Core::PointerEventArgs ^>(this, &AppView::OnPointerReleased); | |
window->PointerWheelChanged += ref new Windows::Foundation::TypedEventHandler<Windows::UI::Core::CoreWindow ^, Windows::UI::Core::PointerEventArgs ^>(this, &AppView::OnPointerWheelChanged); | |
Windows::Devices::Input::MouseDevice::GetForCurrentView()->MouseMoved += ref new Windows::Foundation::TypedEventHandler<Windows::Devices::Input::MouseDevice ^, Windows::Devices::Input::MouseEventArgs ^>(this, &AppView::OnMouseMoved); | |
_defaultCursor = m_window->PointerCursor; | |
_cursor = _defaultCursor; | |
DisplayInformation^ currentDisplayInformation = DisplayInformation::GetForCurrentView(); | |
m_nativeOrientation = currentDisplayInformation->NativeOrientation; | |
m_currentOrientation = currentDisplayInformation->CurrentOrientation; | |
m_dpi = currentDisplayInformation->LogicalDpi; | |
currentDisplayInformation->DpiChanged += ref new TypedEventHandler<DisplayInformation^, Object^>(this, &AppView::OnDpiChanged); | |
currentDisplayInformation->OrientationChanged += ref new TypedEventHandler<DisplayInformation^, Object^>(this, &AppView::OnOrientationChanged); | |
DisplayInformation::DisplayContentsInvalidated += ref new TypedEventHandler<DisplayInformation^, Object^>(this, &AppView::OnDisplayContentsInvalidated); | |
// TODO: Add application specific SetWindow() code here! | |
} | |
/** | |
Create the D3D swap chain, etc. | |
*/ | |
void AppView::Load(Platform::String^ entryPoint) | |
{ | |
CreateDeviceResources(); | |
CreateWindowSizeDependentResources(); | |
// TODO: Add application specific Load() code here! | |
} | |
/** | |
Run the app. | |
*/ | |
void AppView::Run() | |
{ | |
while (!m_windowClosed) | |
{ | |
if (m_windowVisible) | |
{ | |
CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent); | |
// Reset the viewport to target the whole screen. | |
m_context->RSSetViewports(1, &m_viewport); | |
// Reset render targets to the screen. | |
ID3D11RenderTargetView *const targets[1] = { m_renderTargetView.Get() }; | |
m_context->OMSetRenderTargets(1, targets, m_depthStencilView.Get()); | |
// Clear the back buffer and depth stencil view. | |
FLOAT clearColor[] = { 0.25f, 0.15f, 0.5f, 1 }; | |
m_context->ClearRenderTargetView(m_renderTargetView.Get(), clearColor); | |
m_context->ClearDepthStencilView(m_depthStencilView.Get(), D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0); | |
// Get device rotation. | |
DXGI_MODE_ROTATION swapChainRotation = DXGI_MODE_ROTATION_IDENTITY; | |
m_swapChain->GetRotation(&swapChainRotation); // can fail (see docs) but ignore & just use default | |
// TODO: Add application specific Run() code here! | |
// NOTE: In particular, this is where your rendering call will happen. | |
// Reset mouse look state for next frame. | |
Windows::UI::Core::CoreWindow::GetForCurrentThread()->PointerCursor = _cursor; | |
MouseDeltaX = 0.0f; | |
MouseDeltaY = 0.0f; | |
MouseDeltaWheel = 0.0f; | |
// 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. | |
DXGI_PRESENT_PARAMETERS parameters = { 0 }; | |
HRESULT hrPresent = 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_context->DiscardView1(m_renderTargetView.Get(), nullptr, 0); | |
// Discard the contents of the depth stencil. | |
m_context->DiscardView1(m_depthStencilView.Get(), nullptr, 0); | |
// If the device was removed either by a disconnection or a driver upgrade, we | |
// must recreate all device resources. | |
if (hrPresent == DXGI_ERROR_DEVICE_REMOVED || | |
hrPresent == DXGI_ERROR_DEVICE_RESET) | |
{ | |
HandleDeviceLost(); | |
} | |
else | |
{ | |
ChkOk(hrPresent); | |
} | |
} | |
else | |
{ | |
CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessOneAndAllPending); | |
} | |
} | |
} | |
/** | |
Uninitialize the view. | |
*/ | |
void AppView::Uninitialize() | |
{ | |
// nothing! | |
} | |
/** | |
Handle app view activation. | |
*/ | |
void AppView::OnActivated(CoreApplicationView^ applicationView, IActivatedEventArgs^ args) | |
{ | |
CoreWindow::GetForCurrentThread()->Activate(); | |
} | |
/** | |
Handle window size change. | |
*/ | |
void AppView::OnWindowSizeChanged(CoreWindow^ sender, WindowSizeChangedEventArgs^ args) | |
{ | |
Size logicalSize(sender->Bounds.Width, sender->Bounds.Height); | |
if (m_logicalSize != logicalSize) | |
{ | |
m_logicalSize = logicalSize; | |
CreateWindowSizeDependentResources(); | |
} | |
} | |
/** | |
Handle window visibility change. | |
*/ | |
void AppView::OnVisibilityChanged(CoreWindow^ sender, VisibilityChangedEventArgs^ args) | |
{ | |
m_windowVisible = args->Visible; | |
} | |
/** | |
Handle window close. | |
*/ | |
void AppView::OnWindowClosed(CoreWindow^ sender, CoreWindowEventArgs^ args) | |
{ | |
m_windowClosed = true; | |
} | |
/** | |
Handle DPI change. | |
*/ | |
void AppView::OnDpiChanged(DisplayInformation^ sender, Object^ args) | |
{ | |
if (sender->LogicalDpi != m_dpi) | |
{ | |
m_dpi = sender->LogicalDpi; | |
// When the display DPI changes, the logical size of the window | |
// (measured in Dips) also changes and needs to be updated. | |
m_logicalSize = Windows::Foundation::Size(m_window->Bounds.Width, m_window->Bounds.Height); | |
CreateWindowSizeDependentResources(); | |
} | |
} | |
/** | |
Handle orientation change. | |
*/ | |
void AppView::OnOrientationChanged(DisplayInformation^ sender, Object^ args) | |
{ | |
if (m_currentOrientation != sender->CurrentOrientation) | |
{ | |
m_currentOrientation = sender->CurrentOrientation; | |
CreateWindowSizeDependentResources(); | |
} | |
} | |
/** | |
*/ | |
void AppView::OnDisplayContentsInvalidated(DisplayInformation^ sender, Object^ args) | |
{ | |
// The D3D Device is no longer valid if the default adapter changed | |
// since the device was created or if the device has been removed. | |
// First, get the information for the default adapter from when the | |
// device was created. | |
ComPtr<IDXGIDevice3> dxgiDevice; | |
ChkOk(m_device.As(&dxgiDevice)); | |
ComPtr<IDXGIAdapter> deviceAdapter; | |
ChkOk(dxgiDevice->GetAdapter(&deviceAdapter)); | |
ComPtr<IDXGIFactory4> deviceFactory; | |
ChkOk(deviceAdapter->GetParent(IID_PPV_ARGS(&deviceFactory))); | |
ComPtr<IDXGIAdapter1> previousDefaultAdapter; | |
ChkOk(deviceFactory->EnumAdapters1(0, &previousDefaultAdapter)); | |
DXGI_ADAPTER_DESC1 previousDesc; | |
ChkOk(previousDefaultAdapter->GetDesc1(&previousDesc)); | |
// Next, get the information for the current default adapter. | |
ComPtr<IDXGIFactory4> currentFactory; | |
ChkOk(CreateDXGIFactory1(IID_PPV_ARGS(¤tFactory))); | |
ComPtr<IDXGIAdapter1> currentDefaultAdapter; | |
ChkOk(currentFactory->EnumAdapters1(0, ¤tDefaultAdapter)); | |
DXGI_ADAPTER_DESC1 currentDesc; | |
ChkOk(currentDefaultAdapter->GetDesc1(¤tDesc)); | |
// If the adapter LUIDs don't match, or if the device reports | |
// that it has been removed, a new D3D device must be created. | |
if (previousDesc.AdapterLuid.LowPart != currentDesc.AdapterLuid.LowPart || | |
previousDesc.AdapterLuid.HighPart != currentDesc.AdapterLuid.HighPart || | |
FAILED(m_device->GetDeviceRemovedReason())) | |
{ | |
// Release references to resources related to the old device. | |
dxgiDevice = nullptr; | |
deviceAdapter = nullptr; | |
deviceFactory = nullptr; | |
previousDefaultAdapter = nullptr; | |
// Create a new device and swap chain. | |
HandleDeviceLost(); | |
} | |
} | |
/** | |
Create the Direct3D device. | |
*/ | |
void AppView::CreateDeviceResources() | |
{ | |
UINT creationFlags = 0; | |
#if defined(_DEBUG) | |
HRESULT hrSdkLayersCheck = D3D11CreateDevice( | |
nullptr, | |
D3D_DRIVER_TYPE_NULL, // There is no need to create a real hardware device. | |
0, | |
D3D11_CREATE_DEVICE_DEBUG, // Check for the SDK layers. | |
nullptr, // Any feature level will do. | |
0, | |
D3D11_SDK_VERSION, // Always set this to D3D11_SDK_VERSION for Windows Store apps. | |
nullptr, // No need to keep the D3D device reference. | |
nullptr, // No need to know the feature level. | |
nullptr // No need to keep the D3D device context reference. | |
); | |
if (SUCCEEDED(hrSdkLayersCheck)) | |
{ | |
// 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. | |
static const D3D_FEATURE_LEVEL featureLevels[] = | |
{ | |
D3D_FEATURE_LEVEL_12_1, | |
D3D_FEATURE_LEVEL_12_0, | |
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<ID3D11Device> device; | |
ComPtr<ID3D11DeviceContext> context; | |
HRESULT hr = D3D11CreateDevice( | |
nullptr, // Specify nullptr to use the default adapter. | |
D3D_DRIVER_TYPE_HARDWARE, // Create a device using the hardware graphics driver. | |
0, // Should be 0 unless the driver is D3D_DRIVER_TYPE_SOFTWARE. | |
creationFlags, // Set debug and Direct2D compatibility flags. | |
featureLevels, // List of feature levels this app can support. | |
ARRAYSIZE(featureLevels), // Size of the list above. | |
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. | |
); | |
if (FAILED(hr)) | |
{ | |
// Initialization failed, fall back to the WARP device. | |
ChkOk( | |
D3D11CreateDevice( | |
nullptr, | |
D3D_DRIVER_TYPE_WARP, | |
0, | |
creationFlags, | |
featureLevels, | |
ARRAYSIZE(featureLevels), | |
D3D11_SDK_VERSION, | |
&device, | |
&m_featureLevel, | |
&context)); | |
} | |
// Store pointers to the Direct3D 11.3 API device and immediate context. | |
ChkOk(device.As(&m_device)); | |
ChkOk(context.As(&m_context)); | |
// Create the Direct2D device object and a corresponding context. | |
ComPtr<IDXGIDevice3> dxgiDevice; | |
ChkOk(m_device.As(&dxgiDevice)); | |
} | |
/** | |
This method determines the rotation between the display device's native | |
orientation and the current display orientation. | |
*/ | |
DXGI_MODE_ROTATION AppView::ComputeDisplayRotation() | |
{ | |
// Note: m_nativeOrientation can only be Landscape or Portrait even though | |
// the DisplayOrientations enum has other values. | |
switch (m_nativeOrientation) | |
{ | |
case DisplayOrientations::Landscape: | |
{ | |
switch (m_currentOrientation) | |
{ | |
case DisplayOrientations::Landscape: return DXGI_MODE_ROTATION_IDENTITY; | |
case DisplayOrientations::Portrait: return DXGI_MODE_ROTATION_ROTATE270; | |
case DisplayOrientations::LandscapeFlipped: return DXGI_MODE_ROTATION_ROTATE180; | |
case DisplayOrientations::PortraitFlipped: return DXGI_MODE_ROTATION_ROTATE90; | |
} | |
} | |
break; | |
case DisplayOrientations::Portrait: | |
{ | |
switch (m_currentOrientation) | |
{ | |
case DisplayOrientations::Landscape: return DXGI_MODE_ROTATION_ROTATE90; | |
case DisplayOrientations::Portrait: return DXGI_MODE_ROTATION_IDENTITY; | |
case DisplayOrientations::LandscapeFlipped: return DXGI_MODE_ROTATION_ROTATE270; | |
case DisplayOrientations::PortraitFlipped: return DXGI_MODE_ROTATION_ROTATE180; | |
} | |
} | |
break; | |
} | |
return DXGI_MODE_ROTATION_UNSPECIFIED; | |
} | |
/** | |
Converts a length in device-independent pixels to a length in physical pixels. | |
*/ | |
float AppView::ConvertDipsToPixels(float dips) | |
{ | |
static const float dipsPerInch = 96.0f; | |
return floorf(dips * m_dpi / dipsPerInch + 0.5f); // Round to nearest integer. | |
} | |
/** | |
These resources need to be recreated every time the window size is changed. | |
*/ | |
void AppView::CreateWindowSizeDependentResources() | |
{ | |
// Clear the previous window size specific context. | |
ID3D11RenderTargetView* nullViews[] = { nullptr }; | |
m_context->OMSetRenderTargets(ARRAYSIZE(nullViews), nullViews, nullptr); | |
m_renderTargetView = nullptr; | |
m_depthStencilView = nullptr; | |
m_context->Flush1(D3D11_CONTEXT_TYPE_ALL, nullptr); | |
// Calculate the necessary render target size in pixels. | |
// The std::max calls make sure we don't pass zero to D3D. | |
m_outputSize.Width = (std::max)(1.0f, ConvertDipsToPixels(m_logicalSize.Width)); | |
m_outputSize.Height = (std::max)(1.0f, ConvertDipsToPixels(m_logicalSize.Height)); | |
// The width and height of the swap chain must be based on the window's | |
// natively-oriented width and height. If the window is not in the native | |
// orientation, the dimensions must be reversed. | |
DXGI_MODE_ROTATION displayRotation = ComputeDisplayRotation(); | |
bool swapDimensions = displayRotation == DXGI_MODE_ROTATION_ROTATE90 || displayRotation == DXGI_MODE_ROTATION_ROTATE270; | |
m_renderTargetSize.Width = swapDimensions ? m_outputSize.Height : m_outputSize.Width; | |
m_renderTargetSize.Height = swapDimensions ? m_outputSize.Width : m_outputSize.Height; | |
if (m_swapChain != nullptr) | |
{ | |
// The swap chain already exists, resize it. | |
HRESULT hr = m_swapChain->ResizeBuffers( | |
2, // Double-buffered swap chain. | |
lround(m_renderTargetSize.Width), | |
lround(m_renderTargetSize.Height), | |
DXGI_FORMAT_B8G8R8A8_UNORM, | |
0); | |
if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) | |
{ | |
// If the device was removed for any reason, a new device | |
// and swap chain will need to be created. | |
HandleDeviceLost(); | |
// Everything is set up now. Do not continue execution of this | |
// method. HandleDeviceLost will reenter this method and correctly | |
// set up the new device. | |
return; | |
} | |
else | |
{ | |
ChkOk(hr); | |
} | |
} | |
else | |
{ | |
// Create a new swap chain using the same adapter as the existing device. | |
DXGI_SWAP_CHAIN_DESC1 swapChainDesc = { 0 }; | |
swapChainDesc.Width = lround(m_renderTargetSize.Width); // Match the size of the window. | |
swapChainDesc.Height = lround(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.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // All Windows Store apps must use this SwapEffect. | |
swapChainDesc.Flags = 0; | |
swapChainDesc.Scaling = DXGI_SCALING_STRETCH; | |
swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_IGNORE; | |
// This sequence obtains the DXGI factory that was used to create the Direct3D device above. | |
ComPtr<IDXGIDevice3> dxgiDevice; | |
ChkOk(m_device.As(&dxgiDevice)); | |
ComPtr<IDXGIAdapter> dxgiAdapter; | |
ChkOk(dxgiDevice->GetAdapter(&dxgiAdapter)); | |
ComPtr<IDXGIFactory4> dxgiFactory; | |
ChkOk(dxgiAdapter->GetParent(IID_PPV_ARGS(&dxgiFactory))); | |
ComPtr<IDXGISwapChain1> swapChain; | |
ChkOk( | |
dxgiFactory->CreateSwapChainForCoreWindow( | |
m_device.Get(), | |
reinterpret_cast<IUnknown*>(m_window.Get()), | |
&swapChainDesc, | |
nullptr, | |
&swapChain)); | |
ChkOk(swapChain.As(&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. | |
ChkOk(dxgiDevice->SetMaximumFrameLatency(1)); | |
} | |
// Set the proper orientation for the swap chain and | |
// the transform for rendering to the rotated swap chain. | |
ChkOk(m_swapChain->SetRotation(displayRotation)); | |
// 0-degree Z-rotation | |
static const XMFLOAT4X4 Rotation0( | |
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); | |
// 90-degree Z-rotation | |
static const XMFLOAT4X4 Rotation90( | |
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); | |
// 180-degree Z-rotation | |
static const XMFLOAT4X4 Rotation180( | |
-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); | |
// 270-degree Z-rotation | |
static const XMFLOAT4X4 Rotation270( | |
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); | |
switch (displayRotation) | |
{ | |
case DXGI_MODE_ROTATION_IDENTITY: m_orientationTransform = Rotation0; break; | |
case DXGI_MODE_ROTATION_ROTATE90: m_orientationTransform = Rotation270; break; | |
case DXGI_MODE_ROTATION_ROTATE180: m_orientationTransform = Rotation180; break; | |
case DXGI_MODE_ROTATION_ROTATE270: m_orientationTransform = Rotation90; break; | |
default: ChkTrue(false); | |
} | |
// Create a render target view of the swap chain back buffer. | |
ComPtr<ID3D11Texture2D1> backBuffer; | |
ChkOk(m_swapChain->GetBuffer(0, IID_PPV_ARGS(&backBuffer))); | |
ChkOk(m_device->CreateRenderTargetView1(backBuffer.Get(), nullptr, &m_renderTargetView)); | |
// Create a depth stencil view for use with 3D rendering if needed. | |
CD3D11_TEXTURE2D_DESC1 depthStencilDesc( | |
DXGI_FORMAT_D24_UNORM_S8_UINT, | |
lround(m_renderTargetSize.Width), | |
lround(m_renderTargetSize.Height), | |
1, // This depth stencil view has only one texture. | |
1, // Use a single mipmap level. | |
D3D11_BIND_DEPTH_STENCIL); | |
ComPtr<ID3D11Texture2D1> depthStencil; | |
ChkOk(m_device->CreateTexture2D1(&depthStencilDesc, nullptr, &depthStencil)); | |
CD3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc(D3D11_DSV_DIMENSION_TEXTURE2D); | |
ChkOk(m_device->CreateDepthStencilView(depthStencil.Get(), &depthStencilViewDesc, &m_depthStencilView)); | |
// Set the 3D rendering viewport to target the entire window. | |
m_viewport = CD3D11_VIEWPORT(0.0f, 0.0f, m_renderTargetSize.Width, m_renderTargetSize.Height); | |
m_context->RSSetViewports(1, &m_viewport); | |
// TODO: Add application specific device resource creation here! | |
} | |
/** | |
Recreate all device resources and set them back to the current state. | |
*/ | |
void AppView::HandleDeviceLost() | |
{ | |
m_swapChain = nullptr; | |
CreateDeviceResources(); | |
CreateWindowSizeDependentResources(); | |
} | |
/** | |
*/ | |
void AppView::OnPointerPressed(Windows::UI::Core::CoreWindow ^sender, Windows::UI::Core::PointerEventArgs ^args) | |
{ | |
// NOTE: If implementing ImGui wrap this in the following condition | |
// if (!ImGui::GetIO().WantCaptureMouse) { ... } | |
// to prevent our mouse look behavior when an ImGui control has | |
// captured the pointer. | |
// This assumes we're implementing some kind of "mouse look" functionality, | |
// i.e. if the pointer is pressed we're dragging the view direction around. | |
// By settings _cursor to nullptr we will hide the mouse pointer (see the | |
// AppView::Run method) and trigger calculation of mouse pointer deltas in | |
// the AppView::OnMouseMoved method. | |
UpdatePointerButtons(args); | |
_cursor = nullptr; | |
} | |
/** | |
*/ | |
void AppView::OnPointerReleased(Windows::UI::Core::CoreWindow ^sender, Windows::UI::Core::PointerEventArgs ^args) | |
{ | |
UpdatePointerButtons(args); | |
_cursor = _defaultCursor; | |
} | |
/** | |
*/ | |
void AppView::OnMouseMoved(Windows::Devices::Input::MouseDevice ^sender, Windows::Devices::Input::MouseEventArgs ^args) | |
{ | |
if (_cursor == nullptr) | |
{ | |
float dx = float(args->MouseDelta.X) / MOUSE_SCALE; | |
float dy = float(args->MouseDelta.Y) / MOUSE_SCALE; | |
MouseDeltaX = (std::max)(-1.0f, (std::min)(+1.0f, dx)); | |
MouseDeltaY = (std::max)(-1.0f, (std::min)(+1.0f, dy)); | |
} | |
} | |
/** | |
*/ | |
void AppView::OnPointerWheelChanged(Windows::UI::Core::CoreWindow ^sender, Windows::UI::Core::PointerEventArgs ^args) | |
{ | |
auto d = args->CurrentPoint->Properties->MouseWheelDelta; | |
auto nd = float(d) / float(WHEEL_DELTA * MOUSE_SCALE); | |
MouseDeltaWheel = (std::max)(-1.0f, (std::min)(+1.0f, nd)); | |
} | |
/** | |
*/ | |
void AppView::UpdatePointerButtons(Windows::UI::Core::PointerEventArgs^ args) | |
{ | |
auto device = args->CurrentPoint->PointerDevice; | |
if (device->PointerDeviceType == Windows::Devices::Input::PointerDeviceType::Mouse) | |
{ | |
auto properties = args->CurrentPoint->Properties; | |
MouseButtonL = properties->IsLeftButtonPressed; | |
MouseButtonM = properties->IsMiddleButtonPressed; | |
MouseButtonR = properties->IsRightButtonPressed; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment