Skip to content

Instantly share code, notes, and snippets.

@rygorous
Created April 7, 2014 18:11
Show Gist options
  • Save rygorous/10026160 to your computer and use it in GitHub Desktop.
Save rygorous/10026160 to your computer and use it in GitHub Desktop.
WinRT basic D3D example, one file; all cube rendering/framework-y stuff removed, this just clears the screen every frame.
#include <wrl/client.h>
#include <d3d11_1.h>
#include <agile.h>
#include <ppl.h>
#include <ppltasks.h>
using namespace Microsoft::WRL;
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;
static void ThrowIfFailed(HRESULT hr)
{
if (FAILED(hr))
{
// Set a breakpoint on this line to catch Win32 API errors.
throw Platform::Exception::CreateException(hr);
}
}
ref class winrt_basic sealed : public Windows::ApplicationModel::Core::IFrameworkView
{
public:
winrt_basic();
// 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);
// Renderer.
void InitRenderer(CoreWindow^ window);
void HandleDeviceLost();
void Render();
void Present();
void UpdateForWindowSizeChange();
float ConvertDipsToPixels(float dips);
void CreateDeviceResources();
void CreateWindowSizeDependentResources();
private:
ComPtr<ID3D11Device> m_d3dDevice;
ComPtr<ID3D11DeviceContext1> m_d3dContext;
ComPtr<IDXGISwapChain1> m_swapChain;
ComPtr<ID3D11RenderTargetView> m_renderTargetView;
ComPtr<ID3D11DepthStencilView> m_depthStencilView;
D3D_FEATURE_LEVEL m_featureLevel;
Rect m_renderTargetSize;
Rect m_windowBounds;
Platform::Agile<CoreWindow> m_window;
Windows::Graphics::Display::DisplayOrientations m_orientation;
bool m_windowClosed;
bool m_windowVisible;
};
winrt_basic::winrt_basic() :
m_windowClosed(false),
m_windowVisible(true)
{
}
void winrt_basic::Initialize(CoreApplicationView^ applicationView)
{
applicationView->Activated +=
ref new TypedEventHandler<CoreApplicationView^, IActivatedEventArgs^>(this, &winrt_basic::OnActivated);
CoreApplication::Suspending +=
ref new EventHandler<SuspendingEventArgs^>(this, &winrt_basic::OnSuspending);
CoreApplication::Resuming +=
ref new EventHandler<Platform::Object^>(this, &winrt_basic::OnResuming);
}
void winrt_basic::SetWindow(CoreWindow^ window)
{
window->SizeChanged +=
ref new TypedEventHandler<CoreWindow^, WindowSizeChangedEventArgs^>(this, &winrt_basic::OnWindowSizeChanged);
window->VisibilityChanged +=
ref new TypedEventHandler<CoreWindow^, VisibilityChangedEventArgs^>(this, &winrt_basic::OnVisibilityChanged);
window->Closed +=
ref new TypedEventHandler<CoreWindow^, CoreWindowEventArgs^>(this, &winrt_basic::OnWindowClosed);
window->PointerCursor = ref new CoreCursor(CoreCursorType::Arrow, 0);
window->PointerPressed +=
ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &winrt_basic::OnPointerPressed);
window->PointerMoved +=
ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &winrt_basic::OnPointerMoved);
InitRenderer(CoreWindow::GetForCurrentThread());
}
void winrt_basic::Load(Platform::String^ entryPoint)
{
}
void winrt_basic::Run()
{
while (!m_windowClosed)
{
if (m_windowVisible)
{
CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent);
Render();
Present();
}
else
{
CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessOneAndAllPending);
}
}
}
void winrt_basic::Uninitialize()
{
}
void winrt_basic::OnWindowSizeChanged(CoreWindow^ sender, WindowSizeChangedEventArgs^ args)
{
UpdateForWindowSizeChange();
}
void winrt_basic::OnVisibilityChanged(CoreWindow^ sender, VisibilityChangedEventArgs^ args)
{
m_windowVisible = args->Visible;
}
void winrt_basic::OnWindowClosed(CoreWindow^ sender, CoreWindowEventArgs^ args)
{
m_windowClosed = true;
}
void winrt_basic::OnPointerPressed(CoreWindow^ sender, PointerEventArgs^ args)
{
// Insert your code here.
}
void winrt_basic::OnPointerMoved(CoreWindow^ sender, PointerEventArgs^ args)
{
// Insert your code here.
}
void winrt_basic::OnActivated(CoreApplicationView^ applicationView, IActivatedEventArgs^ args)
{
CoreWindow::GetForCurrentThread()->Activate();
}
void winrt_basic::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();
}
void winrt_basic::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.
}
void winrt_basic::InitRenderer(CoreWindow^ window)
{
m_window = window;
CreateDeviceResources();
CreateWindowSizeDependentResources();
}
// Recreate all device resources and set them back to the current state.
void winrt_basic::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();
}
void winrt_basic::Render()
{
float clear_color[4] = { 0.3f, 0.6f, 0.9f, 1.0f };
m_d3dContext->ClearRenderTargetView(m_renderTargetView.Get(), clear_color);
}
void winrt_basic::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, &parameters);
// 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
{
ThrowIfFailed(hr);
}
}
void winrt_basic::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 convert a length in device-independent pixels (DIPs) to a length in physical pixels.
float winrt_basic::ConvertDipsToPixels(float dips)
{
static const float dipsPerInch = 96.0f;
return floor(dips * DisplayProperties::LogicalDpi / dipsPerInch + 0.5f); // Round to nearest integer.
}
void winrt_basic::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_9_1
};
// Create the Direct3D 11 API device object and a corresponding context.
ComPtr<ID3D11Device> device;
ComPtr<ID3D11DeviceContext> context;
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.
ThrowIfFailed(device.As(&m_d3dDevice));
ThrowIfFailed(context.As(&m_d3dContext));
}
void winrt_basic::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.
ThrowIfFailed(
m_swapChain->ResizeBuffers(
2, // Double-buffered swap chain.
static_cast<UINT>(m_renderTargetSize.Width),
static_cast<UINT>(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<UINT>(m_renderTargetSize.Width); // Match the size of the window.
swapChainDesc.Height = static_cast<UINT>(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<IDXGIDevice1> dxgiDevice;
ThrowIfFailed(m_d3dDevice.As(&dxgiDevice));
ComPtr<IDXGIAdapter> dxgiAdapter;
ThrowIfFailed(dxgiDevice->GetAdapter(&dxgiAdapter));
ComPtr<IDXGIFactory2> dxgiFactory;
ThrowIfFailed(dxgiAdapter->GetParent(__uuidof(IDXGIFactory2), &dxgiFactory));
Windows::UI::Core::CoreWindow^ window = m_window.Get();
ThrowIfFailed(
dxgiFactory->CreateSwapChainForCoreWindow(
m_d3dDevice.Get(),
reinterpret_cast<IUnknown*>(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.
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;
break;
case DisplayOrientations::Portrait:
rotation = DXGI_MODE_ROTATION_ROTATE270;
break;
case DisplayOrientations::LandscapeFlipped:
rotation = DXGI_MODE_ROTATION_ROTATE180;
break;
case DisplayOrientations::PortraitFlipped:
rotation = DXGI_MODE_ROTATION_ROTATE90;
break;
default:
throw ref new Platform::FailureException();
}
ThrowIfFailed(m_swapChain->SetRotation(rotation));
// Create a render target view of the swap chain back buffer.
ComPtr<ID3D11Texture2D> backBuffer;
ThrowIfFailed(
m_swapChain->GetBuffer(
0,
__uuidof(ID3D11Texture2D),
&backBuffer
)
);
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<UINT>(m_renderTargetSize.Width),
static_cast<UINT>(m_renderTargetSize.Height),
1,
1,
D3D11_BIND_DEPTH_STENCIL
);
ComPtr<ID3D11Texture2D> depthStencil;
ThrowIfFailed(
m_d3dDevice->CreateTexture2D(
&depthStencilDesc,
nullptr,
&depthStencil
)
);
CD3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc(D3D11_DSV_DIMENSION_TEXTURE2D);
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);
}
ref class Direct3DApplicationSource sealed : Windows::ApplicationModel::Core::IFrameworkViewSource
{
public:
virtual Windows::ApplicationModel::Core::IFrameworkView^ CreateView();
};
IFrameworkView^ Direct3DApplicationSource::CreateView()
{
return ref new winrt_basic();
}
[Platform::MTAThread]
int main(Platform::Array<Platform::String^>^)
{
auto direct3DApplicationSource = ref new Direct3DApplicationSource();
CoreApplication::Run(direct3DApplicationSource);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment