MFC Support for Direct2D – Lost Render Target

As stated in the Direct2D documentation, the graphics device might become unavailable. If the device is lost, the render target also becomes invalid, along with any device-dependent resources that were associated with the device. That happens infrequently (e.g. if the user removes the display adapter), but once something bad can happen, a good programmer must handle it, except case he/she likes to see happy QA-tester faces. 🙂

Handling render target loss when using raw Direct2D interfaces

Direct2D signals a lost device by returning the error code D2DERR_RECREATE_TARGET from the ID2D1RenderTarget::EndDraw method. This case we have to release the render target and all its associated device-dependent resources (bitmaps, brushes and so on) in order to be further re-created. Here is a simplified example:

HRESULT CSomeWindow::OnRender()
{
    HRESULT hr = S_OK;
    m_pRenderTarget->BeginDraw();

    // perform Direct2D drawing here...

    hr = m_pRenderTarget->EndDraw();
    if (D2DERR_RECREATE_TARGET == hr)
    {
        // The render target has become invalid, along with its 
        // associated resources, so we have to discard all of them in order to be
        // further re-created.
        DiscardDeviceResources();
        hr = S_OK;
    }
    return hr;
}

In a real program it’s a lot of work to do (discard, then re-create the render target and resources) but fortunately, the programmer’s life becomes much easier if he/she is using MFC.

Handling render target loss when using MFC

MFC does the work for you: if CRenderTarget::EndDraw function returns D2DERR_RECREATE_TARGET, the framework calls CRenderTarget::ReCreate which discards the render target, then re-creates all its associated resources. Additionally, it sends AFX_WM_RECREATED2DRESOURCES registered message. So usually, we have nothing to do in this case. At most we can handle AFX_WM_RECREATED2DRESOURCES, for example, in order to write what happened in the application’s log.

///////////////////////////////////////////////////////////////////////////////////////////////////
// Function:    CDemoView::OnAfxRecreated2DResources
// Purpose:     AFX_WM_RECREATED2DRESOURCES message handler, sent by MFC framework to indicate
//              that render target has been lost and re-created.
//
LRESULT CDemoView::OnAfxRecreated2DResources(WPARAM wParam, LPARAM lParam)
{
    // TODO: write in the application's log!
    return 0;
}

A little CD2DBitmapBrush issue

Doing tests on Direct2D resources re-creation, I have discovered a little issue in CD2DBitmapBrush MFC class. It always re-creates the bitmap brush using D2D1_EXTEND_MODE_CLAMP. Usually, we use a bitmap brush to fill an area by repeating its content (i.e. having D2D1_EXTEND_MODE_WRAP extend mode) so I fix it by overriding CD2DBitmapBrush::ReCreate, as shown below.

class CD2DBitmapBrushEx : public CD2DBitmapBrush
{
public:
    using CD2DBitmapBrush::CD2DBitmapBrush;
    virtual HRESULT ReCreate(CRenderTarget* pRenderTarget) override
    {
        // do not call __super::ReCreate!
        CD2DBrush::Destroy();
        return Create(pRenderTarget);
    }
};

Demo project

The demo project is a simple MFC application which uses a Direct2D bitmap brush to fill the window’s background. In case the render target was previously lost and re-created, it also displays a message. Because the render target loss is quite hard to be reproduced, for testing purpose, you can hit the Test/Re-create render target menu item.

Lost Render Target demo
Lost Render Target demo

Download: Lost Render Target Demo (642 downloads)

Resources

Leave a Comment