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