D3D11 Backend
The Windows backend uses Direct3D 11 for GPU rendering and shared handles (HANDLE) for cross-process texture sharing.
Overview
Textures are created with D3D11_RESOURCE_MISC_SHARED flag, enabling them to be shared across process boundaries via Windows HANDLE. A keyed mutex synchronizes access between sender and receiver.
How It Works
Sender Process Receiver Process
───────────────── ─────────────────
D3D11 Device D3D11 Device
│ │
├── Create shared texture │
│ (D3D11_RESOURCE_MISC_SHARED) │
├── Get shared HANDLE │
├── Render into texture │
├── Release keyed mutex │
│ Publish HANDLE │
│ via shared state │
│ ├── OpenSharedResource
│ ├── Acquire keyed mutex
│ ├── Read texture
│ │
└── Ring buffer (N textures) └── Frame acquisition
Thread Safety
IDXGIKeyedMutex provides synchronization. The sender acquires the mutex (key N), writes, releases (key N+1). The receiver acquires (key N+1), reads, releases (key N+2).
Zero-Copy
Texture data is shared via GPU memory. Both processes reference the same D3D11 texture through the shared handle. Transfer mode is zero_copy_shared_texture.
Backend API
#include <nozzle/backends/d3d11.hpp>
namespace nozzle::d3d11 {
Result<device> wrap_device(const DeviceDesc &desc);
Result<texture> wrap_texture(const TextureWrapDesc &desc);
ID3D11Texture2D *get_texture(const texture &tex);
HANDLE get_shared_handle(const texture &tex);
}
Types
struct ID3D11Device;
struct ID3D11DeviceContext;
struct ID3D11Texture2D;
DeviceDesc
struct DeviceDesc {
ID3D11Device *device{nullptr};
ID3D11DeviceContext *context{nullptr};
};
TextureWrapDesc
struct TextureWrapDesc {
ID3D11Texture2D *texture{nullptr};
uint32_t dxgi_format{0}; // DXGI_FORMAT as uint32_t
uint32_t width{0};
uint32_t height{0};
};
wrap_device
Wrap an existing D3D11 device and context.
Result<device> wrap_device(const DeviceDesc &desc);
auto dev = nozzle::d3d11::wrap_device({
.device = d3d_device,
.context = d3d_context
}).value();
wrap_texture
Wrap an existing shared D3D11 texture.
Result<texture> wrap_texture(const TextureWrapDesc &desc);
get_texture / get_shared_handle
Extract native handles from a nozzle texture.
ID3D11Texture2D *get_texture(const texture &tex);
HANDLE get_shared_handle(const texture &tex);
OpenGL Interop
Windows GL interop uses a CPU copy path (no direct GPU interop — WGL_NV_DX_interop2 is NVIDIA-only):
#include <nozzle/backends/opengl.hpp>
nozzle::gl::gl_texture_desc gl_desc{
.name = gl_texture_name,
.target = GL_TEXTURE_2D,
.width = 1920,
.height = 1080,
.format = nozzle::texture_format::bgra8_unorm
};
// Publish GL texture → D3D11 staging texture → shared texture
nozzle::gl::publish_gl_texture(sender, gl_desc);
// Copy received frame → D3D11 → glTexSubImage2D
auto frame = receiver.acquire_frame({.timeout_ms = 1000}).value();
nozzle::gl::copy_frame_to_gl_texture(frame, gl_desc);
Format Mapping
| Nozzle Format | DXGI_FORMAT |
|---|---|
r8_unorm |
DXGI_FORMAT_R8_UNORM |
rg8_unorm |
DXGI_FORMAT_R8G8_UNORM |
rgba8_unorm |
DXGI_FORMAT_R8G8B8A8_UNORM |
bgra8_unorm |
DXGI_FORMAT_B8G8R8A8_UNORM |
rgba8_srgb |
DXGI_FORMAT_R8G8B8A8_UNORM_SRGB |
r16_float |
DXGI_FORMAT_R16_FLOAT |
rgba16_float |
DXGI_FORMAT_R16G16B16A16_FLOAT |
r32_float |
DXGI_FORMAT_R32_FLOAT |
rgba32_float |
DXGI_FORMAT_R32G32B32A32_FLOAT |
depth32_float |
DXGI_FORMAT_D32_FLOAT |
Requirements
- Windows 10+
- D3D11 runtime (included in Windows)
d3d11.lib,dxgi.lib