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:
- Format Compatibility: Direct2D requires specific
pixel formats, typically
DXGI_FORMAT_B8G8R8A8_UNORMorDXGI_FORMAT_R8G8B8A8_UNORM. - Bind Flags: The texture must include
D3D11_BIND_RENDER_TARGETso Direct2D can draw to it, and oftenD3D11_BIND_SHADER_RESOURCEso Direct3D can sample from it in subsequent passes. - Miscellaneous Flags for Sharing: When sharing
resources across different Direct3D devices or processes, specify
D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEXorD3D11_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
IDXGISurfaceinterface viaQueryInterface. - Wrap with an ID2D1Bitmap: Using an existing
Direct2D device context (
ID2D1DeviceContext), callCreateBitmapFromDxgiSurface. ProvideD2D1_BITMAP_PROPERTIES1that match the texture's pixel format and specifyD2D1_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:
- Direct3D Writing: The Direct3D pipeline acquires
the keyed mutex using
AcquireSync(0, timeout), performs its draw calls, and releases it usingReleaseSync(1). - Direct2D Writing/Reading: Direct2D (or its hosting
context) queries the
IDXGIKeyedMutexinterface from the shared surface, callsAcquireSync(1, timeout), issues its draw commands, and then releases the lock withReleaseSync(0). - If Direct2D and Direct3D operate on the exact same
ID3D11Device, explicit keyed mutex synchronization is unnecessary; callingFlushorEndDrawensures commands are submitted in the correct sequence.
Rendering Workflow
The standard execution loop follows a strict sequential pipeline:
- Direct3D Pass: The application renders 3D geometry to depth and color buffers, optionally writing intermediate results to the shared texture.
- Context Transition: Direct3D flushes its command list or releases the keyed mutex to ensure pending GPU writes complete.
- Direct2D Pass: Direct2D calls
ID2D1DeviceContext::BeginDraw(). UI components, text, or 2D overlays are rendered directly onto the shared surface. - Finalization: Direct2D calls
ID2D1DeviceContext::EndDraw(). This finalizes 2D command execution. - 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.