Windows with C++

Rendering for the Windows Runtime

Kenny Kerr

Kenny KerrIn my last column, I examined the Windows Runtime (WinRT) application model ( msdn.microsoft.com/magazine/dn342867). I showed you how to write a Windows Store or Windows Phone app with standard C++ and classic COM, using only a handful of WinRT API functions. There’s certainly no requirement that you must use a language projection such as C++/CX or C#. Being able to step around these abstractions is a powerful capability and is a great way to understand how this technology works.

My May 2013 column introduced Direct2D 1.1 and showed you how to use it to render in a desktop application ( msdn.microsoft.com/magazine/dn198239). The next column introduced the dx.h library—available from dx.codeplex.com—which dramatically simplifies DirectX programming in C++ ( msdn.microsoft.com/magazine/dn201741).

The code in my last column was enough to bring the CoreWindow-based app to life, but it didn’t provide any rendering.

This month, I’ll show you how to take this basic skeleton and add support for rendering. The WinRT application model is optimized for rendering with DirectX. I’ll show you how to take what you’ve learned in my previous columns about Direct2D and Direct3D rendering and apply it to your CoreWindow-based WinRT app—specifically using Direct2D 1.1, via the dx.h library. For the most part, the actual Direct2D and Direct3D drawing commands you’ll need to write are the same regardless of whether you’re targeting the desktop or the Windows Runtime. There are, however, some minor differences, and certainly getting it all hooked up in the first place is quite different. So I’ll pick up where I left off last time and show you how to get some pixels on the screen!

In order to support rendering properly, the window must be aware of certain events. At a minimum, these include changes to the window’s visibility and size, as well as changes to the logical display DPI configuration selected by the user. As with the Activated event I covered last time, these new events are all reported to the application via COM interface callbacks. The ICoreWindow interface provides methods to register for the VisibilityChanged and SizeChanged events, but first I need to implement the respective handlers. The two COM interfaces I need to implement are much like the Activated event handler with its Microsoft Interface Definition Language (MIDL)-generated class templates:

typedef ITypedEventHandler<CoreWindow *, VisibilityChangedEventArgs *>
  IVisibilityChangedEventHandler;
typedef ITypedEventHandler<CoreWindow *, WindowSizeChangedEventArgs *>
  IWindowSizeChangedEventHandler;

The next COM interface I must implement is called IDisplayPropertiesEventHandler, and thankfully, it’s already defined. I simply need to include the relevant header file:

#include <Windows.Graphics.Display.h>

In addition, the relevant types are defined in the following namespace:

using namespace ABI::Windows::Graphics::Display;

Given these definitions, I can update the SampleWindow class from my last column to inherit from these three interfaces as well:

struct SampleWindow :
  ...
  IVisibilityChangedEventHandler,
  IWindowSizeChangedEventHandler,
  IDisplayPropertiesEventHandler

I must also remember to update my QueryInterface implementation to indicate support for these interfaces. I’ll leave that for you to do. Of course, as I mentioned last time, the Windows Runtime doesn’t care where these COM interface callbacks are implemented. It follows that the Windows Runtime doesn’t assume that my app’s IFrameworkView, the primary interface implemented by the SampleWindow class, also implements these callback interfaces. So while it’s correct that QueryInterface properly handles queries for these interfaces, the Windows Runtime isn’t going to query for them. Instead, I need to register for the respective events, and the best place to do so is in my implementation of the IFrameworkView Load method. As a reminder, Load is where you should stick any and all code to prepare your app for initial presentation. I can then register for the VisibilityChanged and SizeChanged events inside the Load method:

EventRegistrationToken token;
HR(m_window->add_VisibilityChanged(this, &token));
HR(m_window->add_SizeChanged(this, &token));

This tells the Windows Runtime explicitly where to find the first two interface implementations. The third and final interface is for the LogicalDpiChanged event, but this event registration is provided by the IDisplayPropertiesStatics interface. This static interface is implemented by the WinRT DisplayProperties class. I can simply use the GetActivationFactory function template to get a hold of it (the implementation of GetActivationFactory can be found in my most recent column):

ComPtr<IDisplayPropertiesStatics> m_displayProperties;
m_displayProperties = GetActivationFactory<IDisplayPropertiesStatics>(
  RuntimeClass_Windows_Graphics_Display_DisplayProperties);

The member variable holds on to this interface pointer, as I’ll need to call it at various points during the lifecycle of the window. For now, I can just register for the LogicalDpiChanged event inside the Load method:

HR(m_displayProperties->add_LogicalDpiChanged(this, &token));

I’ll get back to the implementation of these three interfaces in a moment. Now it’s time to prepare the DirectX infrastructure. I’m going to need the standard set of device resource handlers that I’ve discussed numerous times in previous columns:

void CreateDeviceIndependentResources() {}
void CreateDeviceSizeResources() {}
void CreateDeviceResources() {}
void ReleaseDeviceResources() {}

The first is where I can create or load any resources that aren’t specific to the underlying Direct3D rendering device. The next two are for creating device-specific resources. It’s best to separate resources that are specific to the window’s size from those that are not. Finally, all device resources must be released. The remaining DirectX infrastructure relies on the app implementing these four methods correctly based on the app’s specific needs. It provides discrete points in the app for me to manage rendering resources and the efficient creation and recycling of those resources.

Now I can bring in dx.h to take care of all the DirectX heavy lifting:

#include "dx.h"

And every Direct2D app begins with the Direct2D factory:

Factory1 m_factory;

You can find this in the Direct2D namespace, and I typically include it as follows:

using namespace KennyKerr;
using namespace KennyKerr::Direct2D;

The dx.h library has discrete namespaces for Direct2D, Direct­Write, Direct3D, Microsoft DirectX Graphics Infrastructure (DXGI) and so on. Most of my apps use Direct2D heavily, so this makes sense for me. You can, of course, manage the namespaces in whatever way makes sense for your app.

The m_factory member variable represents the Direct2D 1.1 factory. It’s used to create the render target as well as a variety of other device-independent resources as needed. I’ll create the Direct2D factory and then allow any device-independent resources to be created as the final step in the Load method:

m_factory = CreateFactory();
CreateDeviceIndependentResources();

After the Load method returns, the WinRT CoreApplication class immediately calls the IFrameworkView Run method.

The implementation of the SampleWindow Run method from my last column simply blocked by calling the ProcessEvents method on the CoreWindow dispatcher. Blocking in this way is adequate if your app is only going to perform infrequent rendering based on various events. Perhaps you’re implementing a game or just need some high-resolution animation for your app. The other extreme is to use a continuous animation loop, but perhaps you want something a bit more intelligent. I’m going to implement something of a compromise between these two positions. First, I’ll add a member variable to keep track of whether the window is visible. This will let me throttle back the rendering when the window isn’t physically visible to the user:

bool m_visible;
SampleWindow() : m_visible(true) {}

I can then rewrite the Run method as shown in Figure 1.

Figure 1 A Dynamic Rendering Loop

auto __stdcall Run() -> HRESULT override
{
  ComPtr<ICoreDispatcher> dispatcher;
  HR(m_window->get_Dispatcher(dispatcher.GetAddressOf()));
  while (true)
  {
    if (m_visible)
    {
      Render();
      HR(dispatcher->
        ProcessEvents(CoreProcessEventsOption_ProcessAllIfPresent));
    }
    else
    {
      HR(dispatcher->
        ProcessEvents(CoreProcessEventsOption_ProcessOneAndAllPending));
    }
  }
  return S_OK;
}

As before, the Run method retrieves the CoreWindow dispatcher. It then enters an infinite loop, continuously rendering and processing any window messages (called “events” by the Windows Runtime) that may be in the queue. If, however, the window isn’t visible, it blocks until a message arrives. How does the app know when the window’s visibility changes? That’s what the IVisibilityChangedEventHandler interface is for. I can now implement its Invoke method to update the m_visible member variable:

auto __stdcall Invoke(ICoreWindow *,
  IVisibilityChangedEventArgs * args) -> HRESULT override
{
  unsigned char visible;
  HR(args->get_Visible(&visible));
  m_visible = 0 != visible;
  return S_OK;
}

The MIDL-generated interface uses an unsigned char as a portable Boolean data type. I simply get the window’s current visibility using the provided IVisibilityChangedEventArgs interface pointer and update the member variable accordingly. This event is raised whenever the window is hidden or shown and is a bit simpler than implementing this for desktop applications, as it takes care of a number of scenarios including app shutdown and power management, not to mention switching windows.

Next, I need to implement the Render method called by the Run method in Figure 1. This is where the rendering stack is created on-demand and when the actual drawing commands occur. The basic skeleton is shown in Figure 2.

Figure 2 Outline of the Render Method

void Render()
{
  if (!m_target)
  {
    // Prepare render target ...
  }
  m_target.BeginDraw();
  Draw();
  m_target.EndDraw();
  auto const hr = m_swapChain.Present();
  if (S_OK != hr && DXGI_STATUS_OCCLUDED != hr)
  {
    ReleaseDevice();
  }
}

The Render method should look familiar. It has the same basic form I’ve outlined before for Direct2D 1.1. It begins by creating the render target as needed. This is immediately followed by the actual drawing commands sandwiched between calls to BeginDraw and EndDraw. Because the render target is a Direct2D device context, actually getting the rendered pixels onto the screen involves presenting the swap chain. Speaking of which, I need to add the dx.h types representing the Direct2D 1.1 device context as well as the DirectX 11.1 version of the swap chain. The latter can be found in the Dxgi namespace:

DeviceContext m_target;
Dxgi::SwapChain1 m_swapChain;

The Render method concludes by calling ReleaseDevice if presentation fails:

void ReleaseDevice()
{
  m_target.Reset();
  m_swapChain.Reset();
  ReleaseDeviceResources();
}

This takes care of releasing the render target and swap chain. It also calls ReleaseDeviceResources to allow any device-specific resources such as brushes, bitmaps or effects to be released. This ReleaseDevice method might seem inconsequential, but it’s critical for reliably handling device loss in a DirectX app. Without properly releasing all device resources—any resource that’s backed by the GPU—your app will fail to recover from device loss and will come crashing down.

Next, I need to prepare the render target, the bit I omitted from the Render method in Figure 2. It starts with creating the Direct3D device (the dx.h library really simplifies the next few steps as well):

auto device = Direct3D::CreateDevice();

With the Direct3D device in hand, I can turn to the Direct2D factory to create the Direct2D device and the Direct2D device context:

m_target = m_factory.CreateDevice(device).CreateDeviceContext();

Next, I need to create the window’s swap chain. I’ll first retrieve the DXGI factory from the Direct3D device:

auto dxgi = device.GetDxgiFactory();

I can then create a swap chain for the application’s CoreWindow:

m_swapChain = dxgi.CreateSwapChainForCoreWindow(device, m_window.Get());

Here, again, the dx.h library makes my life a lot simpler by automatically filling in the DXGI_SWAP_CHAIN_DESC1 structure for me. I’ll then call out to the CreateDeviceSwapChainBitmap method to create a Direct2D bitmap that will represent the swap chain’s back buffer:

void CreateDeviceSwapChainBitmap()
{
  BitmapProperties1 props(BitmapOptions::Target | BitmapOptions::CannotDraw,
    PixelFormat(Dxgi::Format::B8G8R8A8_UNORM, AlphaMode::Ignore));
  auto bitmap =
    m_target.CreateBitmapFromDxgiSurface(m_swapChain, props);
  m_target.SetTarget(bitmap);
}

This method first needs to describe the swap chain’s back buffer in a way that makes sense to Direct2D. BitmapProperties1 is the dx.h version of the Direct2D D2D1_BITMAP_PROPERTIES1 structure. The BitmapOptions::Target constant indicates the bitmap will be used as the target of a device context. The Bitmap­Options::CannotDraw constant relates to the fact that the swap chain’s back buffer can only be used as an output and not as an input to other drawing operations. PixelFormat is the dx.h version of the Direct2D D2D1_PIXEL_FORMAT structure.

With the bitmap properties defined, the CreateBitmapFromDxgiSurface method retrieves the swap chain’s back buffer and creates a Direct2D bitmap to represent it. In this way, the Direct2D device context can render directly to the swap chain simply by targeting the bitmap via the SetTarget method.

Back in the Render method, I just need to tell Direct2D how to scale any drawing commands according to the user’s DPI configuration:

float dpi;
HR(m_displayProperties->get_LogicalDpi(&dpi));
m_target.SetDpi(dpi);

I’ll then call out to the app’s device resource handlers to create any resources as needed. To summarize, Figure 3 provides the complete device initialization sequence for the Render method.

Figure 3 Preparing the Render Target

void Render()
{
  if (!m_target)
  {
    auto device = Direct3D::CreateDevice();
    m_target = m_factory.CreateDevice(device).CreateDeviceContext();
    auto dxgi = device.GetDxgiFactory();
    m_swapChain = dxgi.CreateSwapChainForCoreWindow(device, m_window.Get());
    CreateDeviceSwapChainBitmap();
    float dpi;
    HR(m_displayProperties->get_LogicalDpi(&dpi));
    m_target.SetDpi(dpi);
    CreateDeviceResources();
    CreateDeviceSizeResources();
  }
  // Drawing and presentation ... see Figure 2

Although DPI scaling is properly applied immediately after the Direct2D device context is created, it also needs to be updated whenever this setting is changed by the user. The fact that DPI scaling can change for a running app is new to Windows 8. This is where the IDisplayPropertiesEventHandler interface comes in. I can now simply implement its Invoke method and update the device accordingly. Here’s the LogicalDpiChanged event handler:

auto __stdcall Invoke(IInspectable *) -> HRESULT override
{
  if (m_target)
  {
    float dpi;
    HR(m_displayProperties->get_LogicalDpi(&dpi));
    m_target.SetDpi(dpi);
    CreateDeviceSizeResources();
    Render();
  }
  return S_OK;
}

Assuming the target—the device context—has been created, it retrieves the current logical DPI value and simply forwards it on to Direct2D. It then calls out to the app to recreate any device-size-­specific resources before re-rendering. In this way, my app can dynamically respond to changes in the display’s DPI configuration. The final change that the window must handle dynamically is that of changes to the window’s size. I’ve already hooked up the event registration, so I simply need to add the implementation of the IWindowSizeChangedEventHandler Invoke method, representing the SizeChanged event handler:

auto __stdcall Invoke(ICoreWindow *,
  IWindowSizeChangedEventArgs *) -> HRESULT override
{
  if (m_target)
  {
    ResizeSwapChainBitmap();
    Render();
  }
  return S_OK;
}

The only thing left to do is resize the swap chain bitmap via the ResizeSwapChainBitmap method. Again, this is something that needs to be handled carefully. Resizing the swap chain’s buffers can and should be an efficient operation, but only if done correctly. First, in order for this operation to succeed, I need to ensure all references to these buffers have been released. These may be references the app is holding directly as well as indirectly. In this case, the reference is held by the Direct2D device context. The target image is the Direct2D bitmap I created to wrap the swap chain’s back buffer. Releasing this is easy enough:

m_target.SetTarget();

I can then call the swap chain’s ResizeBuffers method to do all of the heavy lifting, and then call out to the app’s device resource handlers as needed. Figure 4 shows you how this comes together.

Figure 4 Resizing the Swap Chain

void ResizeSwapChainBitmap()
{
  m_target.SetTarget();
  if (S_OK == m_swapChain.ResizeBuffers())
  {
    CreateDeviceSwapChainBitmap();
    CreateDeviceSizeResources();
  }
  else
  {
    ReleaseDevice();
  }
}

You can now add some drawing commands, and they’ll be rendered efficiently by DirectX to the target of the CoreWindow. As a simple example, you might want to create a solid-color brush inside the CreateDeviceResources handler and assign it to a member variable as follows:

SolidColorBrush m_brush;
m_brush = m_target.CreateSolidColorBrush(Color(1.0f, 0.0f, 0.0f));

Inside the window’s Draw method, I can start by clearing the window’s background with white:

m_target.Clear(Color(1.0f, 1.0f, 1.0f));

I can then use the brush and draw a simple red rectangle as follows:

RectF rect (100.0f, 100.0f, 200.0f, 200.0f);
m_target.DrawRectangle(rect, m_brush);

To ensure the app can gracefully recover from device loss, I must ensure that it releases the brush at just the right time:

void ReleaseDeviceResources()
{
  m_brush.Reset();
}

And that’s all it takes to render a CoreWindow-based app with DirectX. Of course, if you compare this to my May 2013 column, you should be pleasantly surprised by how much simpler the DirectX-related code works out to be thanks to the dx.h library. But as it stands, there’s still a good deal of boilerplate code, mainly related to implementing the COM interfaces. This is where C++/CX comes in and simplifies the use of WinRT APIs inside your apps. It hides some of the boilerplate COM code that I’ve shown you in these last two columns.


Kenny Kerr is a computer programmer based in Canada, an author for Pluralsight and a Microsoft MVP. He blogs at kennykerr.ca and you can follow him on Twitter at twitter.com/kennykerr.

 

Rate: