Enabling antialiasing in Direct3D for Windows Phone 8

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

Windows Phone 8 does not support multi-sample anti-aliasing (MSAA) on back buffer surfaces. This limitation exists in all Windows Phone 8 applications as well as all Windows Store applications. If you attempt to create a swap chain with MSAA enabled on Windows Phone, DXGI will return DXGI_ERROR_INVALID_CALL.

However, since this limitation only exists for the back buffer and not for regular off-screen render targets, it is still possible to enable MSAA for your application by using a secondary render target. To do this, you must perform the following steps:

  1. Create a new ID3D11Texture2D with multisampling enabled and with the rendertarget bind flag set. This is your off-screen render target texture.

  2. Create an ID3D11RenderTargetView for your newly-created render target texture.

  3. Draw your scene to your render target.

  4. Call ID3D11DeviceContext::ResolveSubresource with your rendertarget as the source, and the backbuffer as the destination.

Note that not all texture formats support MSAA. For a list of render target formats which support multi-sample anti-aliasing on feature level 9_3 devices like Windows Phone, see Hardware Support for Direct3D 10Level9 Formats.

Modifying the Direct3D app project template to use anti-aliasing

The following steps show you how to modify the Direct3D app template that is included in Windows Phone SDK 8.0 to support anti-aliasing using an offscreen buffer.

First, in Direct3Dbase.h declare variables for a new Texture2D to serve as the off screen buffer and a RenderTargetView, which is needed in order to use the texture as a render target. Also, create a member variable to store the main back buffer. In the default template, this variable is local and will go out of scope, but in this case we need to reference it later.

    Microsoft::WRL::ComPtr<ID3D11Texture2D> m_backBuffer;
    Microsoft::WRL::ComPtr<ID3D11Texture2D> m_offscreenRenderTarget;
    Microsoft::WRL::ComPtr<ID3D11RenderTargetView> m_offscreenRenderTargetView;

In Direct3Dbase.cpp, at the bottom of the CreateWindowSizeDependentResources method, add the following code to create the render target and render target view. Setting the desc.SampleDesc.Count to 4 is what causes the texture to be created with 4x multi-sampling.

    CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, m_renderTargetSize.Width, m_renderTargetSize.Height, 1, 1);
        desc.BindFlags = D3D11_BIND_RENDER_TARGET;
        desc.SampleDesc.Count = 4; // 4x MSAA
        
        DX::ThrowIfFailed(
            m_d3dDevice->CreateTexture2D(
                &desc,
                nullptr,
                &m_offscreenRenderTarget
                )
            );

        DX::ThrowIfFailed(
            m_d3dDevice->CreateRenderTargetView(
                m_offscreenRenderTarget.Get(),
                nullptr,
                &m_offscreenRenderTargetView
                )
            );

Next in CreateWindowSizeDependentResources, you must modify the code that creates the depth stencil with the same level of multi-sampling as the render target. In this code the lines that were changed from the default template are the addition of the line that depthStencilDesc.SampleDesc.Count to 4, and the line that initializes the depthStencilViewDesc variable with the D3D11_DSV_DIMENSION_TEXTURE2DMS constant. The template, by default, uses D3D11_DSV_DIMENSION_TEXTURE2D which does not enable multi-sampling

    // 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
        );

    depthStencilDesc.SampleDesc.Count = 4; // 4x MSAA

    ComPtr<ID3D11Texture2D> depthStencil;
    DX::ThrowIfFailed(
        m_d3dDevice->CreateTexture2D(
            &depthStencilDesc,
            nullptr,
            &depthStencil
            )
        );

    CD3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc(D3D11_DSV_DIMENSION_TEXTURE2DMS);
    DX::ThrowIfFailed(
        m_d3dDevice->CreateDepthStencilView(
            depthStencil.Get(),
            &depthStencilViewDesc,
            &m_depthStencilView
            )
        );

Also in CreateWindowSizeDependentResources you need to update the code that creates the back buffer to use the m_backBuffer member variable instead of the local back buffer variable used in the template.

    // Create a render target view of the swap chain back buffer.
    DX::ThrowIfFailed(
        m_swapChain->GetBuffer(
            0,
            __uuidof(ID3D11Texture2D),
            &m_backBuffer
            )
        );

    DX::ThrowIfFailed(
        m_d3dDevice->CreateRenderTargetView(
            m_backBuffer.Get(),
            nullptr,
            &m_renderTargetView
            )
        );

Now, all that is left to do is to modify the Render method in CubeRenderer.cpp to use the offscreen render target. At the top of the method, add the following code to set the render target.

    const float midnightBlue[] = { 0.098f, 0.098f, 0.439f, 1.000f };
    m_d3dContext->ClearRenderTargetView(
        m_offscreenRenderTargetView.Get(),
        midnightBlue
        );
    m_d3dContext->OMSetRenderTargets(
        1,
        m_offscreenRenderTargetView.GetAddressOf(),
        m_depthStencilView.Get()
        );

Finally, at end of the Render method, call ResolveSubresources to copy contents of the render target to the back buffer.

    m_d3dContext->ResolveSubresource(
        m_backBuffer.Get(),
        0,
        m_offscreenRenderTarget.Get(),
        0,
        DXGI_FORMAT_B8G8R8A8_UNORM);