Partager via


Direct3D with XAML apps for Windows Phone 8

[ This article is for Windows Phone 8 developers. If you’re developing for Windows 10, see the latest documentation. ]

This topic describes the structure of a Direct3D with XAML app, and walks through the project template that’s included in Windows Phone SDK 8.0. This type of app uses the DrawingSurfaceBackgroundGrid control which allows you to use Direct3D to render graphics that are displayed across the entire background of your app, behind any XAML controls in your UI. The DrawingSurfaceBackgroundGrid must be the root element of the XAML tree and is always the full size of the screen.

A different app type is the XAML and Direct3D app which uses theDrawingSurface control. That control can be any size and can be positioned above, below, and inline with other XAML controls. For info about choosing the control that’s right for your app, see Choosing the right project template for your game for Windows Phone 8.

The Marble Maze sample that was created to illustrate game development for Windows Store apps has been ported to use the Direct3D with XAML project template. To download the sample, see Direct3D with XAML Marble Maze.

Structure of a Direct3D with XAML app

A Direct3D with XAML app is made up of two components: a XAML-based Windows Phone app and a component that’s based on the Windows Phone Runtime. The XAML engine creates and maintains a Direct3D graphics device. Once per frame, the XAML engine passes the graphics device and an associated render target to the Windows Phone Runtime component. After the graphics device has been updated, the Windows Phone Runtime component’s Render method is called. This is where your app can make Direct3D calls to draw on the provided render target. Because the graphics device is shared with the XAML engine, the Render method is the only place when your app can use the device for drawing. Windows Phone SDK 8.0 provides a project template that implements the low-level infrastructure required by this type of app. You can create a new app using this project template, then press F5 and immediately see a 3-D cube rendered to the screen of your device or Windows Phone Emulator. The remainder of this topic walks you through the project template for Direct3D with XAML apps.

Updating the Direct3D with XAML app project template

If your app uses the Direct3D with XAML project template and contains more than one page, you should make the following changes to the files included in the project template. This will ensure that memory is managed correctly as the user navigates between pages. These changes will be included in future releases of the project template.

To update the project template

  1. In MainPage.xaml.cs replace the line

    private Direct3DBackground m_d3dBackground = new Direct3DBackground();
    

    with

    private Direct3DBackground m_d3dBackground = null;
    
  2. In MainPage.xaml.cs, replace the Loaded event handler for the DrawingSurface control with the following:

    private void DrawingSurfaceBackground_Loaded(object sender, RoutedEventArgs e)
    {
        if (m_d3dBackground == null)
        {
            m_d3dBackground = new Direct3DBackground();
    
            // Set window bounds in dips
            m_d3dBackground.WindowBounds = new Windows.Foundation.Size(
                (float)Application.Current.Host.Content.ActualWidth,
                (float)Application.Current.Host.Content.ActualHeight
                );
    
            // Set native resolution in pixels
            m_d3dBackground.NativeResolution = new Windows.Foundation.Size(
                (float)Math.Floor(Application.Current.Host.Content.ActualWidth * Application.Current.Host.Content.ScaleFactor / 100.0f + 0.5f),
                (float)Math.Floor(Application.Current.Host.Content.ActualHeight * Application.Current.Host.Content.ScaleFactor / 100.0f + 0.5f)
                );
    
            // Set render resolution to the full native resolution
            m_d3dBackground.RenderResolution = m_d3dBackground.NativeResolution;
    
            // Hook-up native component to DrawingSurfaceBackgroundGrid
            DrawingSurfaceBackground.SetBackgroundContentProvider(m_d3dBackground.CreateContentProvider());
            DrawingSurfaceBackground.SetBackgroundManipulationHandler(m_d3dBackground);
        }
    }
    
  3. In PhoneDirect3DXamlAppComponent.cpp, replace the definition of the CreateContentProvider method with the following:

    IDrawingSurfaceBackgroundContentProvider^ Direct3DBackground::CreateContentProvider()
    {
        ComPtr<Direct3DContentProvider> provider = Make<Direct3DContentProvider>(this);
        return reinterpret_cast<IDrawingSurfaceBackgroundContentProvider^>(provider.Get());
    }
    

The Direct3D with XAML app project template

This section walks you through the files included in the Direct3D with XAML app project template.

If you’re going to create a Direct3D with XAML app, you should start with the Direct3D with XAML app project template that’s included with Windows Phone SDK 8.0. To create a new project, in Visual Studio, on the File menu, click New Project, and then, under Visual C++, click Windows Phone Direct3D with XAML App.

This template creates a new solution that has two projects: a managed XAML-based app and a Windows Phone Runtime component. The XAML app is almost identical to a regular Windows Phone app. In this app you can use XAML and managed code to implement a UI in the same way you would in an app that doesn’t use any Direct3D graphics. The only difference is that a few extra lines of code are added to the project that add a DrawingSurfaceBackgroundGrid control in which the Direct3D graphics will be displayed and sets up the connection to the Windows Phone Runtime component.

The Windows Phone Runtime component project includes some files that are similar to those in the pure native Direct3D app for Windows Phone, and similar to the Direct3D project template for Windows 8. These files handle obtaining the graphics device for you and have some example code that draws a cube to the screen. There are some additional files that are used to communicate between the Windows Phone Runtime component and the main application, as well as the underlying XAML engine that composites the Direct3D graphics into the XAML UI. If you’re anxious to get started building your app and aren’t interested in the low-level technical details that allow these apps to function, you can just read about the MainPage class, which hosts your XAML-based UI and your managed code, the CubeRenderer class, which is where the actual drawing to the shared texture is performed, and the section of the PhoneDirect3DXamlAppComponent.cpp fle that discusses touch input. The rest of this topic is designed to give a more complete explanation of the code that’s provided in the template.

The managed XAML app

As mentioned earlier, the XAML app project that’s created as part of the Direct3D with XAML template is almost identical to a regular Windows Phone app that doesn’t use any Direct3D. However, there’s a little extra code in two of the files.

MainPage.xaml

This file is where XAML-based Windows Phone apps define their UI. With this project template, a DrawingSurfaceBackgroundGrid control is added for you. The control is the root element of the XAML page. You can add other XAML controls as children of the DrawingSurfaceBackgroundGrid, but you can’t place any elements before, after, or containing the DrawingSurfaceBackgroundGrid.

<DrawingSurfaceBackgroundGrid x:Name="DrawingSurfaceBackground" Loaded="DrawingSurfaceBackground_Loaded">
</DrawingSurfaceBackgroundGrid>

MainPage.xaml.cs

The code-behind file for the main page is where the DrawingSurfaceBackgroundGrid control is hooked up to the Windows Phone Runtime component that does the actual Direct3D drawing. At the top of the page, a using directive references the namespace that contains the Windows Phone Runtime component, “PhoneDirect3DXamlAppComponent”.

using PhoneDirect3DXamlAppComponent;

Next, a member variable of type Direct3DBackground is declared. Direct3DBackground is the class exposed by the Windows Phone Runtime component. This class will be discussed further later in this walkthrough.

private Direct3DBackground m_d3dBackground = null;

The only method that the template creates in MainPage.xaml.cs is the Loaded event handler for the DrawingSurfaceBackgroundGrid control. The first part of this method sets the WindowBounds, NativeResolution, and RenderResolution properties of the Direct3DBackground class. These properties allow the Windows Phone Runtime component to create a viewport with the same dimensions as the screen. The WindowBounds property uses dimensions expressed in Device Independent Pixels (dips), which is the same units that the DrawingSurfaceBackgroundGrid, and all controls, use for their ActualWidth and ActualHeight properties. The NativeResolution and RenderResolution properties expect dimensions expressed in physical pixels, so the size is converted by multiplying by the ScaleFactor property divided by 100. This ensures that the size, in pixels, is correct for all supported screen resolutions. The addition of .5 pixels to the formula and the Floor function call rounds up the dimensions to the nearest integer.

After setting the size properties, the SetContentProvider(Object) method of the DrawingSurfaceBackgroundGrid control is called with the object returned by the Direct3DBackground method, CreateContentProvider. This call sets the Windows Phone Runtime component as the provider that will use Direct3D to draw the content for the DrawingSurfaceBackgroundGrid control. As you’ll see later in this walkthrough, the project template is structured so that it uses the CreateContentProvider helper method to help encapsulate the low-level calls to set up the content provider.

Next, SetManipulationHandler(Object) is called and the Direct3DBackground object is passed in. This registers the Direct3DBackground to receive the PointerPressed()()(), PointerMoved()()(), and PointerReleased()()() touch events when the user touches within the DrawingSurfaceBackgroundGrid control. You’ll have better performance using these events directly in your Windows Phone Runtime component than you would by getting touch events in the MainPage code-behind page and then manually passing that data to the component.

private void DrawingSurfaceBackground_Loaded(object sender, RoutedEventArgs e)
{
    if (m_d3dBackground == null)
    {
        m_d3dBackground = new Direct3DBackground();

        // Set window bounds in dips
        m_d3dBackground.WindowBounds = new Windows.Foundation.Size(
            (float)Application.Current.Host.Content.ActualWidth,
            (float)Application.Current.Host.Content.ActualHeight
            );

        // Set native resolution in pixels
        m_d3dBackground.NativeResolution = new Windows.Foundation.Size(
            (float)Math.Floor(Application.Current.Host.Content.ActualWidth * Application.Current.Host.Content.ScaleFactor / 100.0f + 0.5f),
            (float)Math.Floor(Application.Current.Host.Content.ActualHeight * Application.Current.Host.Content.ScaleFactor / 100.0f + 0.5f)
            );

        // Set render resolution to the full native resolution
        m_d3dBackground.RenderResolution = m_d3dBackground.NativeResolution;

        // Hook-up native component to DrawingSurfaceBackgroundGrid
        DrawingSurfaceBackground.SetBackgroundContentProvider(m_d3dBackground.CreateContentProvider());
        DrawingSurfaceBackground.SetBackgroundManipulationHandler(m_d3dBackground);
    }
}

The Windows Runtime component

This section discusses the Windows Runtime component that’s created as part of the Direct3D with XAML app project template. Many of the files in this project handle the low-level details of hooking up the DrawingSurfaceBackgroundGrid control with the component. In most scenarios you won’t need to modify them.

PhoneDirect3DXamlAppComponent.h

This header file declares the Direct3DBackground class. This class serves as a proxy between the XAML engine and your Direct3D code. Note that this class implements the IDrawingSurfaceManipulationHandler interface that includes the PointerPressed()()(), PointerMoved()()(), and PointerReleased()()() events.

PhoneDirect3DXamlAppComponent.cpp

In the PhoneDirect3DXamlAppComponent implementation file, you’ll see the definition of the CreateContentProvider method that’s called from the DrawingSurfaceBackground_Loaded event handler in MainPage.xaml.cs. This method initializes a new instance of the Direct3DContentProvider class and casts it as an IDrawingSurfaceContentProvider. If you look at the definition for the IDrawingSurfaceContentProvider, you’ll see that it doesn’t have any members defined. This is because it’s not an interface that you implement using your code. The Direct3DContentProvider class is implemented using the Windows Runtime C++ Template Library (WRL). This method casts the class as a Windows Phone Runtime interface so that it can be accessed by the XAML engine. You shouldn’t have to worry about any of these implementation details as you develop your app. They are explained here solely for informational purposes.

IDrawingSurfaceBackgroundContentProvider^ Direct3DBackground::CreateContentProvider()
{
    ComPtr<Direct3DContentProvider> provider = Make<Direct3DContentProvider>(this);
    return reinterpret_cast<IDrawingSurfaceBackgroundContentProvider^>(provider.Get());
}

Next, PhoneDirect3DXamlAppComponent.cpp provides an implementation of the SetManipulationHost(DrawingSurfaceManipulationHost) method that’s called from the DrawingSurfaceBackground_Loaded event handler in MainPage.xaml.cs so that the Direct3DBackground class can receive touch input events from the DrawingSurface control. The template also provides stubbed-out handlers for the touch input events that you can add your own code to.

void Direct3DBackground::SetManipulationHost(DrawingSurfaceManipulationHost^ manipulationHost)
{
    manipulationHost->PointerPressed +=
        ref new TypedEventHandler<DrawingSurfaceManipulationHost^, PointerEventArgs^>(this, &Direct3DBackground::OnPointerPressed);

    manipulationHost->PointerMoved +=
        ref new TypedEventHandler<DrawingSurfaceManipulationHost^, PointerEventArgs^>(this, &Direct3DBackground::OnPointerMoved);

    manipulationHost->PointerReleased +=
        ref new TypedEventHandler<DrawingSurfaceManipulationHost^, PointerEventArgs^>(this, &Direct3DBackground::OnPointerReleased);
}
void Direct3DBackground::OnPointerPressed(DrawingSurfaceManipulationHost^ sender, PointerEventArgs^ args)
{
    // Insert your code here.
}

void Direct3DBackground::OnPointerMoved(DrawingSurfaceManipulationHost^ sender, PointerEventArgs^ args)
{
    // Insert your code here.
}

void Direct3DBackground::OnPointerReleased(DrawingSurfaceManipulationHost^ sender, PointerEventArgs^ args)
{
    // Insert your code here.
}

Next, PhoneDirect3DXamlAppComponent.cpp defines Connect and Disconnect methods. Connect is called after the SetContentProvider(Object) call has completed. In this method, a new CubeRenderer is created and its Initialize method is called. During this call is when CubeRenderer creates device-dependent resources like vertex buffers and shaders. Disconnect is called when the associated DrawingSurfaceBackgroundGrid control leaves the visual tree.

HRESULT Direct3DBackground::Connect(_In_ IDrawingSurfaceRuntimeHostNative* host, _In_ ID3D11Device1* device)
{
    m_renderer = ref new CubeRenderer();
    m_renderer->Initialize(device);
    m_renderer->UpdateForWindowSizeChange(WindowBounds.Width, WindowBounds.Height);

    // Restart timer after renderer has finished initializing.
    m_timer->Reset();

    return S_OK;
}

void Direct3DBackground::Disconnect()
{
    m_renderer = nullptr;
}

The PrepareResources method is called by the XAML engine for each frame. This is when the Windows Phone Runtime component can set the requested size of the render target. The template sets the width and height of the render target to the size of the screen that was set by the XAML app in DrawingSurfaceBackground_Loaded. You can set this size to something smaller, for example to a quarter of the actual screen resolution, in order to speed up rendering. The XAML engine will automatically use hardware scaling to efficiently scale the smaller render target up to the size of the full screen.

Warning

The XAML engine uses hardware scaling to scale the render target for Direct3D with XAML apps. Because the MediaElement also uses hardware scaling, the presence or absence of a MediaElement in the XAML visual tree is important for DrawingSurfaceBackgroundGrid performance. Using DrawingSurfaceBackgroundGrid without a MediaElement allows the runtime to make further optimizations resulting in improved performance.

HRESULT Direct3DBackground::PrepareResources(_In_ const LARGE_INTEGER* presentTargetTime, _Inout_ DrawingSurfaceSizeF* desiredRenderTargetSize)
{
    m_timer->Update();
    m_renderer->Update(m_timer->Total, m_timer->Delta);

    desiredRenderTargetSize->width = RenderResolution.Width;
    desiredRenderTargetSize->height = RenderResolution.Height;

    return S_OK;
}

The final method defined in PhoneDirect3DXamlAppComponent.cpp is Draw. The first thing that happens in this method is that UpdateDevice, defined in Direct3Dbase.cpp, is called to update the graphics device, context, and render target. If the user switches away from your app and then returns, it is possible that the XAML engine will lose and then recreate its graphics device. If this happens, this method allows your app to recreate device dependent resources, such as textures and shaders.

The XAML engine will call the Draw method once every time it updates the XAML UI. The call to RequestAdditionalFrame asks the XAML engine to redraw the screen as soon as possible after the current frame is done. The call to RequestAdditionalFrame can be made from anywhere in the Windows Phone Runtime component after Connect has been called and before Disconnect is called. To reduce the power consumption of your app, you can modify the template code so that RequestAdditionalFrame is not called every time the Draw method is called. The XAML engine will still call Draw every time it updates the UI, but it will not try to redraw as soon as possible. This is particularly useful when your app is displaying static content or content that does not need to be redrawn at the highest possible frame rate.

HRESULT Direct3DBackground::Draw(_In_ ID3D11Device1* device, _In_ ID3D11DeviceContext1* context, _In_ ID3D11RenderTargetView* renderTargetView)
{
    m_renderer->UpdateDevice(device, context, renderTargetView);
    m_renderer->Render();

    RequestAdditionalFrame();

    return S_OK;
}

Direct3DBase.cpp

Direct3DBase is a class you can find in many Direct3D project templates, including those for Direct3D apps for Windows Phone, as well as for Windows 8. The implementation of this class is slightly different for the XAML and Direct3D app template, but its structure and functionality are very similar. For most scenarios, you will not need to modify this class. The class exposes an Initialize method that immediately calls CreateDeviceResources. The base implementation of this method doesn’t do anything. The overloaded method in CubeRenderer.cpp uses this method to create device-dependent resources like textures and shaders. Remember that this method can be called multiple times if the graphics device changes while the app is running.

Next, Direct3DBase implements the UpdateDevice method. Earlier, we saw that this method is called from the Direct3DBackground class once per frame. If the graphics device has changed since the previous frame, CreateDeviceResources is called again. If the dimensions of the render target have changed, CreateWindowSizeDependentResources is called. Finally, a viewport object is created from the render target dimensions.

CubeRenderer.cpp

The CubeRenderer class inherits from Direct3Dbase. It provides some example Direct3D code that draws a spinning cube. Again, this file is common to Direct3D templates on different platforms. If you’re just getting started with Direct3D, you may want to spend some time just modifying the code in this file to change what’s drawn to the screen. Eventually, you will most likely replace this class with one that you create specifically for your game, but you should keep the same basic structure. For example, your class should inherit from Direct3DBase.

CubeRenderer.cpp contains a method definition for CreateDeviceResources. This is where you create the resources that are dependent on the device. The example code in the template creates a vertex buffer and an index buffer that define the geometry of the cube, in addition to a vertex shader and a pixel shader that define how the cube is drawn render target.

Next, the method CreateWindowSizeDependentResources is defined. This method creates a perspective matrix using the dimensions of the render target.

Finally, CubeRenderer.cpp implements the Update and Render methods that are called once for each frame. Update usually is where objects in your game are updated to their new position for each frame. In the example code, the view and model matrices are updated to make the cube spin around on the screen. Render is where the Direct3D calls are made to draw to the render target. The code you put in this method will vary widely depending on your game. It is important to note that, because the graphics device is owned by the XAML engine,you can only use the graphics device to draw to the render target during the Render method.

Direct3DContentProvider.cpp

Direct3DContentProvider.cpp handles the low-level details of hooking up the XAML engine with the Windows Phone Runtime component. This behavior was encapsulated in this class because in most cases you won’t need to modify it to implement your game.