Skip to main content

minigpu_view 1.9.1 copy "minigpu_view: ^1.9.1" to clipboard
minigpu_view: ^1.9.1 copied to clipboard

Zero-copy Flutter rendering for minigpu SharedOutputTexture and miniav GPU buffers.

minigpu_view #

Zero-copy GPU-to-Flutter rendering for minigpu SharedOutputTexture and miniav GPU buffers.

GPU frames produced by a minigpu compute pipeline or a miniav capture device are handed to Flutter's texture registry as a raw GPU handle, no CPU readback, no pixel copy across the bus on every frame.

How it works #

┌─────────────────────────────────────────────────────────────┐
│  Your Dart code                                             │
│                                                             │
│  SharedOutputTexture ──.asPreviewSource()──► PreviewSource  │
│                                                    │        │
│  MinigpuPreviewController.present(source) ◄─────────┘        │
│           │                                                  │
│           │  platform channel  'minigpu_view'                │
└───────────┼──────────────────────────────────────────────────┘
            ▼
┌─────────────────────────────────────────────────────────────┐
│  Native plugin (Windows)                                    │
│                                                             │
│  IDXGIResource::GetSharedHandle  ──►  ANGLE / EGL           │
│  FlutterTextureRegistrar::RegisterTexture                   │
│         │                                                   │
│         └──► textureId ──► Dart ──► Texture(textureId)      │
└─────────────────────────────────────────────────────────────┘

On Windows the plugin registers a kFlutterDesktopGpuSurfaceTypeDxgiSharedHandle texture. Flutter's ANGLE-backed rasterizer opens the legacy DXGI shared handle on its own D3D11 device — no device synchronisation is needed on the Dart side.

On Web the plugin registers a WebGPU canvas element as an HtmlElementView.

GPU-buffer previews accept tightly packed rgba8 (one little-endian u32 per pixel) and rgba32float (four f32 values per pixel). The producer buffer needs storage usage and must belong to the same WebGPU device as the canvas. A direct render pass reads its pixels without row padding, intermediate textures or CPU readback. The sink caches its pipelines and a 16-byte size uniform; dispose the preview controller to release that uniform and the canvas.

Installation #

dependencies:
  minigpu_view:
    git:
      url: https://github.com/PracticalXR/minigpu.git
      path: minigpu_view

Or with a local path override:

dependencies:
  minigpu_view: ^0.1.0

dependency_overrides:
  minigpu_view:
    path: ../minigpu_view

Quick start #

1 — Create a controller #

final controller = MinigpuPreviewController();

Hold this in your State and dispose it in dispose().

2 — Produce GPU frames #

Use a minigpu SharedOutputTexture as the frame source. Create one once and reuse it across frames:

final gpu = Minigpu();
await gpu.initialize();

final sharedTex = gpu.createSharedOutputTexture(width, height);

Write into it from a compute shader output buffer:

// outputBuffer holds 4 × f32 per pixel (RGBA in [0, 1]).
sharedTex.copyFromBufferF32(outputBuffer);

// Or from a packed RGBA8 u32 buffer (e.g. GpuEffect output):
sharedTex.copyFromBuffer(outputBuffer);

3 — Present the frame #

await controller.present(sharedTex.asPreviewSource());

Call this once per frame — for example in a Timer.periodic loop or after each GPU dispatch.

4 — Display in your widget tree #

MiniavGpuPreview(
  controller: controller,
  fit: BoxFit.contain,            // BoxFit.fill stretches to fill bounds
  placeholder: const Center(     // shown before the first frame arrives
    child: CircularProgressIndicator(),
  ),
)

MiniavGpuPreview is a plain StatelessWidget that listens to the controller via AnimatedBuilder — no setState needed in the parent.

API reference #

MinigpuPreviewController #

Member Description
present(PreviewSource) Pushes a frame to the platform. Returns once the texture registry slot is confirmed.
textureId The Flutter texture id (null before the first present).
size Natural pixel size of the most recent frame.
presentedAtUs Broadcast stream of host-side ack timestamps (µs). Use for backpressure.
retire(PreviewSource) Drops one replaced source. Completes after the engine has let go of its texture.
dispose() Releases platform resources. Must be called when no longer needed.

MinigpuPreviewController is a ChangeNotifier — widgets rebuild automatically when textureId or size changes.

MiniavGpuPreview #

Property Default Description
controller — Required.
fit BoxFit.contain How to fit the frame inside the available space.
alignment Alignment.center Alignment when fit leaves empty space.
filterQuality FilterQuality.medium Passed to Flutter's Texture widget.
placeholder SizedBox.shrink() Shown until the first frame arrives.

Adapters #

Extension Source Notes
SharedOutputTexture.asPreviewSource() minigpu Zero-copy on Windows via DXGI shared handle.
MiniAVBuffer.asPreviewSource() miniav Returns null for CPU buffers and for Windows GPU buffers (their handle closes when the buffer is released). Show those with asGpuFrame(gpu) and a GpuPane.

Exceptions #

Exception When
UnsupportedPreviewException Platform doesn't support the given PreviewSourceKind yet. Catch and fall back to a CPU path.
PreviewPresentException Host plugin rejected the handle (invalid, device lost, format mismatch).

Example #

See example/lib/main.dart for a self-contained runnable demo that drives a WGSL animated gradient through SharedOutputTexture and displays it via MiniavGpuPreview.

cd example
flutter run -d windows

Platform support #

Platform Status Surface type
Windows ✅ kFlutterDesktopGpuSurfaceTypeDxgiSharedHandle (ANGLE cross-device)
Web ✅ HtmlElementView (WebGPU canvas)
macOS 🚧 planned Metal IOSurface
Linux 🚧 planned GBM / DMA-buf
Android 🚧 planned AHardwareBuffer
iOS 🚧 planned Metal IOSurface

Relationship to miniav_tools / FFmpeg encoder #

SharedOutputTexture exposes two handles:

  • d3d11TexturePtr — raw ID3D11Texture2D* on Dawn's D3D11 device. Used by the FFmpeg D3D11VA encoder (miniav_tools) because encoder and producer share the same device — no handle open needed.
  • d3d11Handle — legacy DXGI shared HANDLE (from IDXGIResource::GetSharedHandle). Used by this plugin because Flutter's ANGLE has its own D3D11 device and cannot accept a foreign texture pointer directly.

Both handles remain valid simultaneously, so encoding and display can run concurrently.