Skip to content

Add SKMesh API for custom vertex mesh drawing with SkSL shaders#3779

Draft
mattleibow wants to merge 2 commits into
mainfrom
mattleibow/dev-skmesh-api
Draft

Add SKMesh API for custom vertex mesh drawing with SkSL shaders#3779
mattleibow wants to merge 2 commits into
mainfrom
mattleibow/dev-skmesh-api

Conversation

@mattleibow

Copy link
Copy Markdown
Contributor

Fixes #3777

Summary

Adds the SKMesh API to SkiaSharp, wrapping Skia's SkMesh for custom vertex mesh drawing with SkSL shaders. This is a major new rendering capability that enables particle systems, deformable geometry, custom terrain rendering, and advanced visual effects.

New API surface

Type Description
SKMeshSpecification Defines vertex attributes, varyings, stride, and SkSL vertex/fragment programs
SKMesh A drawable mesh from spec + vertex/index buffers + optional uniforms
SKMeshVertexBuffer / SKMeshIndexBuffer CPU-backed buffer types
SKCanvas.DrawMesh() Draws the mesh (with optional SKBlender)
SKMeshMode Triangles, TriangleStrip
SKMeshSpecificationAttributeType / SKMeshSpecificationVaryingType Vertex data types

Architecture

C# (SKMesh.cs) → P/Invoke → C API (sk_mesh.h/cpp) → C++ (SkMesh.h)
  • SKMeshSpecification: ISKNonVirtualReferenceCounted (wraps SkNVRefCnt)
  • SKMesh: Owned/heap-allocated, DisposeNative calls sk_mesh_delete
  • Buffers: ISKReferenceCounted (wraps SkRefCnt, uses generic sk_refcnt_safe_unref)

Changes

Skia submodule (companion PR: mono/skia#203)

  • include/c/sk_mesh.h — 15 C API function declarations
  • src/c/sk_mesh.cpp — implementations
  • include/c/sk_types.h — opaque types, 3 enums, 2 structs
  • sk_canvas.h/cppsk_canvas_draw_mesh
  • sk_types_priv.h — DEF_CLASS_MAP + As/To helpers for nested buffer types
  • SkiaKeeper.c — symbol retention for Xcode linking
  • gn/core.gni — build entries

SkiaSharp

  • binding/SkiaSharp/SKMeshSpecification.cs — spec wrapper with Make() factories
  • binding/SkiaSharp/SKMesh.cs — mesh wrapper with Make() and MakeIndexed() factories
  • binding/SkiaSharp/SKMeshVertexBuffer.cs / SKMeshIndexBuffer.cs — buffer wrappers
  • binding/SkiaSharp/SKCanvas.csDrawMesh() overloads
  • binding/libSkiaSharp.json — type mappings
  • tests/Tests/SkiaSharp/SKMeshTest.cs — 18 tests
  • samples/Gallery/Shared/Samples/MeshSample.cs — gallery sample with 4 visual modes

Tests

18 tests covering specification creation, buffer creation, mesh creation (indexed and non-indexed), drawing, validation, error handling, and triangle strip mode. All pass on macOS arm64.

Known issues

  • WASM: SKMeshSpecification.Make() exhausts the WASM heap during SkSL mesh shader compilation. The mesh API works correctly on all native platforms. This appears to be the Skia SkSL compiler's memory usage exceeding what the WASM GC can handle. The gallery sample currently crashes on Blazor WASM — needs investigation into whether initial heap size or GC tuning can resolve this.
  • Gallery sample on WASM: The 4 visual modes (Ripple Pond, Silk Fabric, Terrain Map, Aurora Borealis) work on native but the WASM gallery shows a GC OOM. May need a SKVertices-based fallback for WASM until the SkSL compiler memory issue is resolved.
  • P/Invoke parameter mapping: Struct pointer parameters (SKMeshSpecificationAttributeNative*) had to be mapped to IntPtr in libSkiaSharp.json for WASM interpreter compatibility. This is a workaround for a mono WASM interpreter limitation with complex P/Invoke signatures.

@github-actions

github-actions Bot commented Apr 28, 2026

Copy link
Copy Markdown
Contributor

📦 Try the packages from this PR

Warning

Do not run these scripts without first reviewing the code in this PR.

Step 1 — Download the packages

bash / macOS / Linux:

curl -fsSL https://raw.githubusercontent.com/mono/SkiaSharp/main/scripts/get-skiasharp-pr.sh | bash -s -- 3779

PowerShell / Windows:

iex "& { $(irm https://raw.githubusercontent.com/mono/SkiaSharp/main/scripts/get-skiasharp-pr.ps1) } 3779"

Step 2 — Add the local NuGet source

dotnet nuget add source ~/.skiasharp/hives/pr-3779/packages --name skiasharp-pr-3779
More options
Option Description
--successful-only / -SuccessfulOnly Only use successful builds
--force / -Force Overwrite previously downloaded packages
--list / -List List available artifacts without downloading
--build-id ID / -BuildId ID Download from a specific build

Or download manually from Azure Pipelines — look for the nuget artifact on the build for this PR.

Remove the source when you're done:

dotnet nuget remove source skiasharp-pr-3779

@github-actions

github-actions Bot commented Apr 28, 2026

Copy link
Copy Markdown
Contributor

📖 Documentation Preview

The documentation for this PR has been deployed and is available at:

🔗 View Staging Site
🔗 View Staging Docs
🔗 View Staging Gallery (Blazor)
🔗 View Staging Gallery (Uno Platform)
🔗 View Staging SkiaFiddle

This preview will be updated automatically when you push new commits to this PR.


This comment is automatically updated by the documentation staging workflow.

@mattleibow mattleibow force-pushed the mattleibow/dev-skmesh-api branch from 59d5240 to bb0a20e Compare May 5, 2026 17:52
@mattleibow mattleibow added this to the 4.x RC 1 milestone May 8, 2026
@mattleibow mattleibow moved this to In Progress in SkiaSharp Backlog May 8, 2026
@mattleibow mattleibow moved this from In Progress to Changes Requested in SkiaSharp Backlog May 8, 2026
@mattleibow mattleibow moved this from Changes Requested to In Progress in SkiaSharp Backlog May 8, 2026
mattleibow and others added 2 commits May 16, 2026 02:06
Implements custom vertex mesh drawing with SkSL shaders, mirroring the
SKRuntimeEffect API pattern with Create/Build factories, Uniforms/Children
properties, and a builder class.

New types: SKMeshSpecification, SKMesh, SKMeshVertexBuffer, SKMeshIndexBuffer,
SKMeshBuilder, SKMeshMode, SKMeshSpecificationAttributeType/VaryingType.

C API uses a builder pattern (sk_mesh_new + setters + sk_mesh_validate)
with all P/Invoke calls ≤4 params for WASM interpreter compatibility.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- SKMeshSpecification.Build() now returns SKMeshBuilder (was SKMeshSpecification),
  matching SKRuntimeEffect.BuildShader/BuildColorFilter/BuildBlender pattern
- SKMeshBuilder.Dispose() now disposes Specification (builder owns it),
  matching SKRuntimeEffectBuilder.Dispose() which disposes Effect
- Nullable annotations on Create/ToMesh/ToMeshIndexed out parameters and
  nullable SKData/SKRuntimeEffect params — eliminates all CS8604/CS8625 warnings
- Add BuildReturnsMeshBuilder test to cover the new return type
- Update builder tests to not double-using spec (builder owns it)
- Rewrite MeshSample to use Build() factory, cache builder across frames,
  and animate with a cycling color uniform over an indexed quad mesh
- Rebase onto origin/main (skia submodule rebased onto origin/skiasharp)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@mtmattei

Copy link
Copy Markdown

We took this PR for a spin on a real workload and it held up really well — sharing results and a few findings.

Setup: managed SkiaSharp.dll built from this branch (d618da5), native win-x64 from the PR's CI artifacts, repacked locally and consumed from an Uno Platform desktop app (GPU canvas, win-x64). The workload is a Gaussian-splat viewer: 270k splats rendered as 17 chunked indexed meshes — 6 attributes (60–72 B stride), 2 varyings, 5 uniforms — with EWA covariance projection moved into the SkSL vertex shader and meshes rebuilt on every camera change.

Results: mesh build cost is ~0.2 ms per camera change for all 17 meshes, and per-frame CPU drops to near zero versus ~26 ms/frame for our equivalent DrawVertices path (which projects on the CPU). Both paths sustain 24 fps under continuous orbit at 270k splats; at 25k splats the SKMesh path reaches 48–60 fps vs ~45 for the CPU path. The API surface (specification → builder → buffers → BuildIndexedDrawMesh) mapped onto this without friction.

A few findings from getting there:

1. Default paint renders meshes black — including MeshSample

The fragment shader output is modulated by the paint color (consistent with DrawVertices), but new SKPaint() defaults to black, so DrawMesh with a default paint renders solid black. MeshSample.OnDrawSample uses a default paint over a black clear, so the gallery sample currently appears to draw nothing. Suggestions: set Color = SKColors.White in the sample, and add a remark to the DrawMesh docs — this one cost us a long "why are all the splats black" hunt.

2. DrawMesh on a raster surface silently draws nothing

Isolated from finding 1 (white paint; DrawVertices control on the same raster setup draws fine):

using var surface = SKSurface.Create(new SKImageInfo(256, 256)); // raster
surface.Canvas.Clear(SKColors.DarkBlue);
using var paint = new SKPaint { Color = SKColors.White };
surface.Canvas.DrawMesh(mesh, paint); // mesh.IsValid == true, FS outputs green
// center pixel inside the triangle: #ff00008b (the clear color)
// DrawVertices control on an identical raster surface: draws as expected

If this matches upstream (SkMesh being GPU-only), a docs remark on DrawMesh would save users a confusing debugging session. GPU canvases work fine.

3. Nullability warnings in the new files

Building the binding (net8.0, Release) produces 9 warnings in the SKMesh additions:

  • CS8603 (possible null reference return): SKMesh.cs:18, SKMeshIndexBuffer.cs:36, SKMeshVertexBuffer.cs:36, SKMeshSpecification.cs:291,422,430
  • CS8618 (non-nullable fields childNames/uniformNames): SKMeshSpecification.cs:12
  • CS8625 (null literal to non-nullable): SKMeshSpecification.cs:139

4. API observation: uniforms are baked at build time

Since uniform values are captured by BuildIndexed/Build, any uniform change means rebuilding the mesh objects. That was cheap for our workload, but it might be worth a doc note so users don't cache meshes expecting SKRuntimeEffect-style late-bound uniforms.

Happy to re-test future revisions of the branch — this API unlocks a lot for us.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

[api] Add SKMesh API for custom vertex mesh drawing with SkSL shaders

2 participants