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.
Download: Lost Render Target Demo (19)
Resources
- MSDN: Drawing with Direct2D
- MSDN: ID2D1RenderTarget::EndDraw method
- MSDN: CRenderTarget Class
- MSDN: Direct2D Error Codes