WebGPUExternalSurfaceDemo shows how to render ImageSharp.Drawing content into a UI object owned by an application framework. The sample uses a WinForms Control, but the API shape is intended for any externally-owned native drawable host.
It exists to demonstrate:
- creating a
WebGPUExternalSurfacefrom aWebGPUSurfaceHost - keeping the external surface synchronized with the host control's drawable framebuffer size
- acquiring
WebGPUSurfaceFrameinstances manually - drawing with the normal
DrawingCanvasAPI - presenting by disposing the acquired frame
dotnet run --project samples/WebGPUExternalSurfaceDemo -c DebugRequirements:
- .NET 8.0 SDK or later
- Windows, because this sample is a WinForms app
- a WebGPU-capable desktop backend such as D3D12 or Vulkan
- adapter support for the storage-capable BGRA format selected by the sample
When the sample starts you should see a WinForms window with five tabs:
Clock: a continuously-rendered animated clock sceneTiger: an interactive SVG tiger viewer with pan and zoomApply: a reactive external-surface readback scene usingDrawingCanvas.Apply(...); move the mouse to move the edge-detect and blur regions, and use the mouse wheel to resize themManual Text Flow: prepared text is laid out one line at a time around a selectable path obstacle that follows mouse movementRich Text Editor: a small editor surface that exercises text selection, caret movement, hit testing, font changes, and inline styling
WebGPUWindow owns a top-level native window. WebGPUExternalSurface is different: it attaches WebGPU rendering to something the application already owns, such as a control, view, widget, or native surface.
That makes it the integration path for UI frameworks. The host owns:
- the UI object and its lifecycle
- the platform handle
- layout and resize events
- input events
The external surface owns:
- the WebGPU surface created for that host
- swapchain configuration
- frame acquisition
- presentation
- per-frame texture and texture-view handles
The reusable integration point is WebGPURenderControl.cs.
WebGPURenderControl.OnHandleCreated(...) creates the external surface from the WinForms control handle:
this.surface = new WebGPUExternalSurface(
WebGPUSurfaceHost.Win32(
this.Handle,
Marshal.GetHINSTANCE(typeof(WebGPURenderControl).Module)),
initialFramebufferSize,
new WebGPUExternalSurfaceOptions
{
Format = WebGPUTextureFormat.Bgra8Unorm
});WebGPUSurfaceHost is a small public descriptor for externally-owned native handles. The host keeps ownership of those handles; WebGPUExternalSurface only uses them to create and manage the WebGPU rendering surface.
WebGPUExternalSurface.Resize(...) expects the drawable framebuffer size in pixels:
this.framebufferSize = this.ClientSize;
this.surface.Resize(new ImageSharpSize(this.framebufferSize.Width, this.framebufferSize.Height));The acquired frame exposes the same pixel coordinate space through frame.Canvas.Bounds.
RenderOnce() acquires a frame, invokes user drawing code, and disposes the frame:
if (!this.surface.TryAcquireFrame(out WebGPUSurfaceFrame? frame))
{
return;
}
using (frame)
{
this.PaintFrame?.Invoke(frame.Canvas, delta);
}Disposing the frame renders pending canvas work, presents the surface texture, and releases the per-frame WebGPU handles.
WebGPURenderControl supports on-demand and continuous rendering. On-demand controls render from normal WinForms invalidation, which keeps static scenes idle until input, resize, or another event asks them to repaint. Continuous controls hook Application.Idle and render while the WinForms message queue is empty; the clock scene uses this mode because it animates without input.
Frame pacing is delegated to the present mode. With the default WebGPUPresentMode.Fifo, frame acquisition naturally waits for display presentation instead of using a separate software timer.
MainForm.cs creates independent WebGPURenderControl instances, one per tab. Each control owns its own external surface.
The scenes are deliberately ordinary canvas code:
- ClockScene.cs: animated vector clock
- TigerViewerScene.cs: pan and zoom SVG tiger viewer
- ApplyReadbackScene.cs:
Apply(...)scene that reads the external surface back into CPU processing - ManualTextFlowScene.cs: interactive manual text flow using prepared line layout enumeration and a selectable closed-path obstacle
- RichTextEditorScene.cs: custom rich text editor built from Fonts hit testing, caret, selection, and run APIs
Each scene receives:
DrawingCanvasfor the acquired frame- elapsed time since the previous frame
- WebGPURenderControl.cs: reusable WinForms external-surface control
- MainForm.cs: tabs and scene wiring
- ClockScene.cs: clock scene
- TigerViewerScene.cs: tiger viewer scene
- ApplyReadbackScene.cs: apply readback scene
- ManualTextFlowScene.cs: manual text-flow scene
- RichTextEditorScene.cs: rich text editor scene
- WebGPUExternalSurfaceDemo.csproj: sample project file