Direct2D and Direct3D Interop with Shared Textures

Direct2D and Direct3D interoperability allows developers to combine high-performance 2D vector graphics and DirectWrite typography with complex 3D rendering pipelines. This article explains how Direct2D interoperates with Direct3D surfaces using shared DirectX Graphics Infrastructure (DXGI) textures, covering resource creation, surface wrapping, synchronization mechanisms, and the rendering workflow required to achieve seamless graphics integration.

The Role of DXGI

DirectX Graphics Infrastructure (DXGI) serves as the common denominator between Direct2D and Direct3D. Because Direct2D is built on top of Direct3D, it does not communicate directly with Direct3D rendering pipelines. Instead, interoperability relies on DXGI surfaces. A Direct3D 2D texture (ID3D11Texture2D or Direct3D 12 equivalent resource) that implements the IDXGISurface interface can be referenced by Direct2D, enabling both APIs to write to and read from the same underlying video memory.

Configuring the Direct3D Texture

To enable sharing, the Direct3D texture must be created with explicit flags compatible with both Direct2D rendering and cross-API sharing:

  1. Format Compatibility: Direct2D requires specific pixel formats, typically DXGI_FORMAT_B8G8R8A8_UNORM or DXGI_FORMAT_R8G8B8A8_UNORM.
  2. Bind Flags: The texture must include D3D11_BIND_RENDER_TARGET so Direct2D can draw to it, and often D3D11_BIND_SHADER_RESOURCE so Direct3D can sample from it in subsequent passes.
  3. Miscellaneous Flags for Sharing: When sharing resources across different Direct3D devices or processes, specify D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX or D3D11_RESOURCE_MISC_SHARED_NTHANDLE. Using a keyed mutex provides thread and device-safe synchronization.

Creating the Direct2D Target

Once the Direct3D texture is instantiated, it must be mapped into Direct2D's object model:

  • Query the DXGI Surface: Query the Direct3D texture for its IDXGISurface interface via QueryInterface.
  • Wrap with an ID2D1Bitmap: Using an existing Direct2D device context (ID2D1DeviceContext), call CreateBitmapFromDxgiSurface. Provide D2D1_BITMAP_PROPERTIES1 that match the texture's pixel format and specify D2D1_BITMAP_OPTIONS_TARGET.
  • Set the Target: Assign the created Direct2D bitmap as the active target using ID2D1DeviceContext::SetTarget.

Synchronization via Keyed Mutex

When sharing textures across devices or between independent Direct3D and Direct2D contexts, concurrent access to the GPU memory will cause race conditions and visual artifacts. A Keyed Mutex (IDXGIKeyedMutex) coordinates execution:

  1. Direct3D Writing: The Direct3D pipeline acquires the keyed mutex using AcquireSync(0, timeout), performs its draw calls, and releases it using ReleaseSync(1).
  2. Direct2D Writing/Reading: Direct2D (or its hosting context) queries the IDXGIKeyedMutex interface from the shared surface, calls AcquireSync(1, timeout), issues its draw commands, and then releases the lock with ReleaseSync(0).
  3. If Direct2D and Direct3D operate on the exact same ID3D11Device, explicit keyed mutex synchronization is unnecessary; calling Flush or EndDraw ensures commands are submitted in the correct sequence.

Rendering Workflow

The standard execution loop follows a strict sequential pipeline:

  1. Direct3D Pass: The application renders 3D geometry to depth and color buffers, optionally writing intermediate results to the shared texture.
  2. Context Transition: Direct3D flushes its command list or releases the keyed mutex to ensure pending GPU writes complete.
  3. Direct2D Pass: Direct2D calls ID2D1DeviceContext::BeginDraw(). UI components, text, or 2D overlays are rendered directly onto the shared surface.
  4. Finalization: Direct2D calls ID2D1DeviceContext::EndDraw(). This finalizes 2D command execution.
  5. Presentation: The shared texture is either presented directly to the screen via the DXGI swap chain or bound as a shader resource in Direct3D for post-processing and composite rendering.