Skip to content

Improve automated Skia update analysis and validation - #4573

Merged
mattleibow merged 82 commits into
mainfrom
mattleibow-dev-vulkan-sync-validation
Aug 4, 2026
Merged

Improve automated Skia update analysis and validation#4573
mattleibow merged 82 commits into
mainfrom
mattleibow-dev-vulkan-sync-validation

Conversation

@mattleibow

@mattleibow mattleibow commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Description

Make Skia upstream synchronization a clear four-part pipeline:

  1. deterministic pre-agent setup computes the exact branches, fork base, old upstream, and target upstream commits;
  2. the Opus agent owns only the engineering work—merge, conflict/fork-patch decisions, dependency decisions, source build, tests, and fixes;
  3. a PAT-free deterministic finalizer derives version/provenance metadata from workflow state and the completed git graph;
  4. a push-only PAT step validates committed state, pushes guarded branches, and opens paired draft PRs.

Chrome m152 is the blind validation case, not an encoded answer. The agent independently discovered the required VMA compatibility roll and Ganesh allocator adaptation through normal compiler/test failures.

Related issues

N/A.

Required skia PR

None.

Areas affected

  • Managed API (binding/)
  • Native / C API (externals/skia/src/c, include/c)
  • Generated P/Invoke bindings
  • Native dependency or Skia update
  • Views & integrations
  • Rendering output / visual behavior
  • Performance
  • Tests
  • Build, packaging, or CI
  • Documentation or samples

Changes

None — workflow/skill only; no public API or product runtime behavior changes.

Key behavior:

  • Resolve exact immutable sync facts before the agent starts and expose them through SKIA_SYNC_*.
  • Use a genuine two-parent mono/skia merge and a fingerprinted fork-patch audit.
  • Classify dependencies against the fork base and preserve deliberate fork/security pins.
  • Source-build the merged target; never substitute downloaded old natives.
  • Regenerate bindings deterministically and require the full unfiltered maintained-host test solution.
  • Provision deterministic Xvfb + Mesa softpipe OpenGL and pinned lavapipe Vulkan.
  • Keep the hard 2,000-credit ceiling and Opus 4.8 primary model.
  • Remove the redundant agent-authored SHA handoff. Automation metadata finalization reads exact workflow env and git state without SHA arguments.
  • Record both upstream_ref and upstream_merge_commit, so milestone and upstream-main reviews use the correct provenance.
  • Isolate manual-validation head branches from production skia-sync/* branches.
  • Keep the publication credential exclusively in the final push/PR step. Executed publisher/finalizer assets are staged under runner temp outside the agent-writable sandbox mount.
  • Formal review independently verifies recorded upstream commits belong to their recorded upstream refs.

Testing

Final simplified architecture

Workflow run 30776352709 completed successfully from exact PR head 0fa6952f251:

  • Opus 4.8: 1,157.976 AI credits
  • genuine two-parent m151→m152 merge
  • source build reproduced the VMA 3.2.1 incompatibility and accepted only the required VMA 3.4.0 roll
  • initial full test run reproduced 9 Ganesh Vulkan failures
  • agent added the fork-owned C-shim allocator fallback and reran source build/tests
  • final tests:
    • Core: 6084 passed, 33 skipped, 0 failed
    • Singleton init: 1 passed, 0 failed
    • Vulkan: 23 passed, 2 platform-policy skips, 0 failed
    • Direct3D host: 2 passed, 3 platform-policy skips, 0 failed
  • no generated binding changes
  • no SHA handoff artifact or agent-typed automation SHA/ref arguments
  • PAT-free finalizer recorded fork head d840f796fd, upstream ref chrome/m152, and exact upstream SHA 2a9b593bab
  • push-only PAT step created isolated draft PRs #4636 and mono/skia #331
  • independent exact-SHA formal review Resize returns null for PNGs #331 validated and persisted
  • all generated-PR Linux/macOS/Windows generator, benchmark, and source checks passed

The workflow PR itself is also fully green.

Additional validation

  • Three earlier consecutive clean Opus m152 runs: 30704074629, 30707226660, 30712484027
  • Update-skill helper suite: 17 passed
  • Exact-SHA review helper suite: 5 passed
  • PR-template suite: 3 passed
  • Publisher/detector shell syntax passed
  • Strict gh aw compile auto-skia-sync --validate: 0 errors, 0 warnings

A deep manual review also caught a prior run that conflated the fork head with upstream_merge_commit. That pair was closed, formal blocked report #328 was persisted, and the failure led to the deterministic finalizer/provenance design validated above.

Checklist

  • Tests added or updated where relevant
  • Changes above lists all public API and behavioral changes or states that none changed
  • New/changed public API documentation issue — N/A, no public API changes
  • Native companion PR and regenerated bindings — N/A for this workflow PR; generated validation PRs [skia-sync] Update skia to milestone 152 #4636 and mono/skia Resize returns null for PNGs #331 were reviewed separately

Provision and pin Mesa lavapipe, verify it before the agent starts, and require passing named Ganesh and Graphite Vulkan results before sync branches can be pushed.

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

Copilot-Session: f66474bf-0d92-48db-a924-6ca2b009d6b2
@github-actions

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 -- 4573

PowerShell / Windows:

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

Step 2 — Add the local NuGet source

dotnet nuget add source ~/.skiasharp/hives/pr-4573/packages --name skiasharp-pr-4573
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-4573

Keep deterministic lavapipe installation and verification while relying on the test suite itself to enforce Vulkan coverage.

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

Copilot-Session: f66474bf-0d92-48db-a924-6ca2b009d6b2
@mattleibow mattleibow changed the title Run Vulkan tests in automated Skia sync Provision software Vulkan for automated Skia sync Jul 28, 2026
@github-actions

github-actions Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

📊 SkiaSharp benchmarks — PR #4573

this PR (full source build) vs 🌙 nightly · Linux · Windows · macOS

Informational only — this never blocks the PR. 🟢 faster / less allocation · 🔴 slower / more allocation; moves under 5% are hidden as noise.

⏱️ Times are raw BenchmarkDotNet means, and the ⭐ PR and baseline legs run on separate CI runners, so microbenchmarks can swing run-to-run — treat small time deltas as noise. Allocations are deterministic and the reliable signal. The interactive perf-dashboard (linked below) applies smoothing for the trend view.

Highlights

⏱️ Time — 🔴 34 slower · 🟢 31 faster

  • 🔴 RuntimeEffectShaderBenchmark.DrawFrame · Linux · 3.03 ms → 4.96 ms (+64%)
  • 🔴 SceneRenderBenchmark.RenderFrame(Complexity: 4) · Linux · 19.16 ms → 26.97 ms (+41%)
  • 🔴 SceneRenderBenchmark.RenderFrame(Complexity: 1) · Linux · 4.13 ms → 5.75 ms (+39%)
  • 🔴 RasterImageLifecycleBenchmark.CreateDataWithReleaseProc(Count: 256) · Windows · 98.68 µs → 136.63 µs (+38%)
  • 🟢 ColorMathBenchmark.UnPreMultiply(Colors: 4096) · Linux · 12.86 µs → 7.96 µs (-38%)
  • …and 60 more (see details below)

📦 Allocations — 🔴 1 more alloc · 🟢 0 less alloc

  • 🔴 RuntimeEffectShaderBenchmark.DrawFrame · Linux · 683 B → 730 B (+7%)
Full per-OS benchmark deltas

Linux

⏱️ Time (vs 🌙 nightly 4.151.0-nightly.120)

Benchmark baseline this PR Δ
RuntimeEffectShaderBenchmark.DrawFrame 3.03 ms 4.96 ms 🔴 +64%
SceneRenderBenchmark.RenderFrame(Complexity: 4) 19.16 ms 26.97 ms 🔴 +41%
SceneRenderBenchmark.RenderFrame(Complexity: 1) 4.13 ms 5.75 ms 🔴 +39%
ColorMathBenchmark.UnPreMultiply(Colors: 4096) 12.86 µs 7.96 µs 🟢 -38%
ColorMathBenchmark.PreMultiply(Colors: 4096) 14.51 µs 9.12 µs 🟢 -37%
ColorMathBenchmark.ToColor(Colors: 4096) 15.88 µs 10.54 µs 🟢 -34%
MatrixOpsBenchmark.Invert(Count: 4096) 149.55 µs 105.60 µs 🟢 -29%
MatrixMapPointsBenchmark.MapPoint(Points: 4096) 80.56 µs 59.08 µs 🟢 -27%
MatrixMapPointsBenchmark.MapPoint(Points: 256) 4.99 µs 3.67 µs 🟢 -26%
MatrixOpsBenchmark.Concat(Count: 4096) 244.49 µs 187.23 µs 🟢 -23%
ColorParseBenchmark.Parse(Iterations: 1000) 76.18 µs 58.79 µs 🟢 -23%
PathBoundsBenchmark.TightBounds(Points: 1024) 11.23 µs 8.96 µs 🟢 -20%
MatrixMapPointsBenchmark.MapRadius(Points: 256) 15.39 µs 12.36 µs 🟢 -20%
MatrixMapPointsBenchmark.MapRadius(Points: 4096) 244.87 µs 197.64 µs 🟢 -19%
LargeImageScaleBenchmark.UpscaleOpaque(Size: 2048) 2.86 ms 2.34 ms 🟢 -18%
PathBoundsBenchmark.TightBounds(Points: 64) 1.08 µs 886.7 ns 🟢 -18%
LargeImageScaleBenchmark.UpscaleOpaque(Size: 1024) 741.91 µs 618.49 µs 🟢 -17%
MatrixMapPointsBenchmark.MapVector(Points: 256) 11.81 µs 9.86 µs 🟢 -16%
MatrixMapPointsBenchmark.MapVector(Points: 4096) 189.87 µs 163.77 µs 🟢 -14%
BitmapDrawBenchmark.DrawScaledTiles(Tiles: 64) 376.91 µs 326.20 µs 🟢 -13%
MatrixMapPointsBenchmark.MapRect(Points: 256) 12.86 µs 11.17 µs 🟢 -13%
BitmapDrawBenchmark.DrawScaledTiles(Tiles: 256) 1.42 ms 1.23 ms 🟢 -13%
MatrixMapPointsBenchmark.MapRect(Points: 4096) 205.01 µs 179.53 µs 🟢 -12%
LargeImageScaleBenchmark.UpscaleCrossfade(Size: 2048) 8.08 ms 7.16 ms 🟢 -11%
LargeImageScaleBenchmark.UpscaleCrossfade(Size: 1024) 2.07 ms 1.86 ms 🟢 -10%
…and 6 more

📦 Allocations (vs 🌙 nightly 4.151.0-nightly.120)

Benchmark baseline this PR Δ
RuntimeEffectShaderBenchmark.DrawFrame 683 B 730 B 🔴 +7%

Windows

⏱️ Time (vs 🌙 nightly 4.151.0-nightly.120)

Benchmark baseline this PR Δ
RasterImageLifecycleBenchmark.CreateDataWithReleaseProc(Count: 256) 98.68 µs 136.63 µs 🔴 +38%
PathBoundsBenchmark.TightBounds(Points: 64) 1.50 µs 1.97 µs 🔴 +31%
PathBoundsBenchmark.TightBounds(Points: 1024) 15.85 µs 20.26 µs 🔴 +28%
MatrixMapPointsBenchmark.MapRadius(Points: 4096) 135.94 µs 164.63 µs 🔴 +21%
MatrixMapPointsBenchmark.MapRadius(Points: 256) 9.04 µs 10.57 µs 🔴 +17%
SceneRenderBenchmark.RenderFrame(Complexity: 4) 10.09 ms 11.72 ms 🔴 +16%
RasterImageLifecycleBenchmark.CreateRasterImage(Count: 256) 248.53 µs 288.23 µs 🔴 +16%
MatrixMapPointsBenchmark.MapVector(Points: 4096) 121.02 µs 140.23 µs 🔴 +16%
BitmapDrawBenchmark.DrawUnscaledTiles(Tiles: 256) 909.13 µs 1.03 ms 🔴 +14%
MatrixMapPointsBenchmark.MapPoints(Points: 4096) 2.13 µs 2.41 µs 🔴 +13%
ColorParseBenchmark.Parse(Iterations: 1000) 75.90 µs 85.14 µs 🔴 +12%
MatrixMapPointsBenchmark.MapRect(Points: 4096) 117.80 µs 130.58 µs 🔴 +11%
MatrixOpsBenchmark.Concat(Count: 4096) 224.15 µs 247.35 µs 🔴 +10%
ColorMathBenchmark.PreMultiply(Colors: 4096) 13.21 µs 14.50 µs 🔴 +10%
MatrixMapPointsBenchmark.MapRect(Points: 256) 7.59 µs 8.32 µs 🔴 +10%
CanvasDrawBenchmark.Draw(Shapes: 64) 2.65 ms 2.89 ms 🔴 +9%
CanvasDrawBenchmark.Draw(Shapes: 512) 21.40 ms 23.28 ms 🔴 +9%
SceneRenderBenchmark.RenderFrame(Complexity: 1) 2.09 ms 2.25 ms 🔴 +8%
MatrixMapPointsBenchmark.MapVector(Points: 256) 8.24 µs 8.84 µs 🔴 +7%
MatrixMapPointsBenchmark.MapPoints(Points: 256) 163.1 ns 173.8 ns 🔴 +7%

macOS

⏱️ Time (vs 🌙 nightly 4.151.0-nightly.120)

Benchmark baseline this PR Δ
BitmapDrawBenchmark.DrawScaledTiles(Tiles: 64) 329.27 µs 246.31 µs 🟢 -25%
ColorMathBenchmark.PreMultiply(Colors: 4096) 9.06 µs 10.88 µs 🔴 +20%
BitmapDrawBenchmark.DrawUnscaledTiles(Tiles: 64) 242.26 µs 193.98 µs 🟢 -20%
RasterImageLifecycleBenchmark.CreateDataWithReleaseProc(Count: 256) 81.85 µs 94.06 µs 🔴 +15%
LargeImageScaleBenchmark.UpscaleOpaque(Size: 2048) 2.95 ms 3.39 ms 🔴 +15%
LargeImageScaleBenchmark.UpscaleOpaque(Size: 1024) 739.71 µs 848.72 µs 🔴 +15%
LargeImageScaleBenchmark.UpscaleCrossfade(Size: 1024) 1.60 ms 1.80 ms 🔴 +12%
LargeImageScaleBenchmark.UpscaleCrossfade(Size: 2048) 6.39 ms 7.14 ms 🔴 +12%
CanvasDrawBenchmark.Draw(Shapes: 64) 1.78 ms 1.62 ms 🟢 -9%
BitmapDrawBenchmark.DrawScaledTiles(Tiles: 256) 921.11 µs 841.87 µs 🟢 -9%
ColorMathBenchmark.UnPreMultiply(Colors: 4096) 8.23 µs 8.88 µs 🔴 +8%
MatrixMapPointsBenchmark.MapPoints(Points: 4096) 1.73 µs 1.85 µs 🔴 +7%
MatrixMapPointsBenchmark.MapVector(Points: 4096) 46.78 µs 43.74 µs 🟢 -7%
SceneRenderBenchmark.RenderFrame(Complexity: 4) 6.78 ms 7.13 ms 🔴 +5%

📈 Full interactive perf-dashboard & run details →

@github-actions

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.

web-flow and others added 2 commits July 29, 2026 02:36
Every GPU code path in the test suite was written as "try to bring up the
backend; if anything throws, Assert.Skip". That made a genuine regression -- a
broken P/Invoke, a GRContext factory that starts returning null, a CI
provisioning step that silently stopped working -- indistinguishable from "this
agent has no GPU", so CI stayed green while coverage evaporated.

The committed goldens are the receipt: ganesh-gl had a golden for macOS only
(Windows and Linux had been skipping), and graphite-dawn had none at all.

Replace the catch-and-skip with a declared four-state model in GpuPolicy. Only
one state can fail, and only one is configured:

  Required     built and expected here    -> any failure is a test failure
  Disabled     opted out for this agent   -> SKIASHARP_TEST_SKIP_GPU
  Unsupported  API does not exist here    -> compiled in, never configured
  NotBuilt     API exists, we don't ship  -> compiled in, never configured

The matrix describes platforms; the environment variable describes agents.
Nothing about a platform's inherent capabilities is expressed as configuration:
Metal on Windows and Vulkan on macOS need no setup to skip. Each backend
declares ExistsOn and BuiltOn, with BuiltOn mirroring the gn args in
native/*/build.cake, so enabling macOS Vulkan later is a one-token change.

IRenderer.IsAvailable/UnavailableReason become a single Backend property and
RendererUnavailableException is deleted -- it existed only to launder failures
into skips. SKTest.CreateGlContext, VKTest, Direct3DTest and
SKGraphiteReleaseTestsBase now gate on the policy and let exceptions escape.
Failure messages carry GpuPolicy.OptOutHint so a red test names the exact
directive that would legitimise a skip.

The hidden TF_BUILD + x64 Metal sniff in the renderers is replaced by a visible
uname -m check in the macOS, iOS and Mac Catalyst CI legs. It is the only
pre-set opt-out, and it exists to avoid a two-hour shutdown hang on the
virtualized x64 Metal driver, not to hide a failure.

Device and browser hosts cannot read the agent environment, so the same value
reaches them as -p:SkiaSharpTestSkipGpu -> runtimeconfig -> AppContext.

GpuPolicyTests emits a ##SKIA-GPU-POLICY## line per backend into every TRX, so
each leg records which backends it required and why the rest were skipped. That
report is what makes a skip auditable.

Expect several legs to go red on the first run -- that is the point. Each
failure is coverage that was already missing and is now visible, and each comes
with the opt-out directive if the gap turns out to be legitimate.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 03c0625a-14f0-4a6c-a1f6-b02ac5f8701a
Correct Phase 10 to run the full test solution instead of only the base console project. Run the Vulkan satellite explicitly with TRX evidence and document the m152 Ganesh allocator requirement.

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

Copilot-Session: f66474bf-0d92-48db-a924-6ca2b009d6b2
@mattleibow mattleibow changed the title Provision software Vulkan for automated Skia sync Provision and validate Vulkan in automated Skia sync Jul 29, 2026
web-flow and others added 5 commits July 29, 2026 03:00
iOSExtensionsTests.GRContextDisposeDoesNotCrash still skipped when
MTLDevice.SystemDefault was null -- the exact pattern this change removes, just
in the iOS device host rather than the shared harness. Metal is Required on
every Apple platform, so a missing device there is a red test.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 03c0625a-14f0-4a6c-a1f6-b02ac5f8701a
Keep the Vulkan execution and evidence requirements without documenting the known m152 allocator diagnosis or fix.

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

Copilot-Session: f66474bf-0d92-48db-a924-6ca2b009d6b2
Remove individual test project commands and the duplicate Vulkan invocation. Collapse solution dependencies to net10.0 and require the solution output to show executed Vulkan tests.

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

Copilot-Session: f66474bf-0d92-48db-a924-6ca2b009d6b2
Nano Server is now its own platform in the matrix. It runs a different native
build -- native/nanoserver/build.cake passes supportVulkan=false and
supportDirect3D=false -- so Vulkan and Direct3D are absent from libSkiaSharp.dll
there even though the OS is Windows. The matrix previously inherited the plain
Windows row and would have marked both Required. Not reachable today (the Nano
leg runs only the Console suite, which has no Vulkan/D3D tests), but the matrix
is the load-bearing artifact of this change and has to be factually right.
OpenGL stays Required there: it IS built for Nano, so a container with no
driver is an agent problem, which is the tier-3 case.

Normalise the opt-out list to commas before forwarding it. GpuPolicy documents
comma, semicolon or whitespace separators, but neither cake forwarding path
survived the last two: a space splits the /p: into two process arguments, and a
semicolon is MSBuild's own property-list separator. Only commas reached the test
host, so anyone following the documented contract by hand would have got a
broken build.

Drop the Trim metadata from the RuntimeHostConfigurationOption. ILLink collects
every option marked Trim=true as a feature switch and parses its value as a
bool; this one holds a backend-id list. Dormant today (no test project enables
trimming) but a latent landmine, and Trim metadata does not affect
runtimeconfig.json generation, so it bought nothing.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 03c0625a-14f0-4a6c-a1f6-b02ac5f8701a
The macOS test leg failed before running a single test:

    MSBUILD : error MSB1006: Property is not valid.
    Switch: graphite-metal

MSBuild's -p: switch splits on commas as well as semicolons, so the
/p:SkiaSharpTestSkipGpu=ganesh-metal,graphite-metal that RunDotNetTest built by
hand parsed as two properties. Confirmed directly: `-p:Foo=a,b` gives MSB1006
while `-p:Foo=a` evaluates fine.

RunDotNetTest never needed the property. Its test process is a child of the cake
process, so it already inherits SKIASHARP_TEST_SKIP_GPU and GpuPolicy reads it
straight from the environment; and with NoBuild=true no runtimeconfig.json is
regenerated, so the property was a no-op even when it parsed. Only the device and
browser hosts need it, and that path goes through Cake's DotNetMSBuildSettings,
which quotes the value properly -- proven by the iOS and Mac Catalyst legs, which
both applied the two-backend opt-out correctly.

Also drop the generic Vulkan sentence from the SharpVk not-built reason. On Linux
it read "Vulkan is not built for this host: the Apple builds have no MoltenVK..."
when Vulkan is in fact built there and only the SharpVk vehicle is Windows-only.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 03c0625a-14f0-4a6c-a1f6-b02ac5f8701a
@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

📦 Artifact size report

Packages from this PR (build 1538058) vs the latest nightly baseline 4.151.0-nightly.120 (observed 2026-08-03).

Total .nupkg size: 569.2 MB → 568.8 MB (−417.1 KB, -0.1%)

Packages

⚠️ marks growth over 500.0 KB or 2%. Changes under 50.0 KB are treated as noise.

Package baseline this PR Δ Δ%
SkiaSharp.NativeAssets.WinUI 109.0 MB 108.4 MB 🟢 −559.4 KB -0.5%
HarfBuzzSharp.NativeAssets.Win32 20.7 MB 20.8 MB 🔴 +91.5 KB +0.4%

+38 package(s) unchanged (< 50.0 KB).

Per-file changes

HarfBuzzSharp.NativeAssets.Win32

File Size
runtimes/win-x86/native/libHarfBuzzSharp.pdb 21.9 MB → 22.2 MB (🔴 +272.0 KB)
runtimes/win-arm64/native/libHarfBuzzSharp.pdb 22.3 MB → 22.5 MB (🔴 +184.0 KB)

SkiaSharp.NativeAssets.WinUI

File Size
runtimes/win-x64/native/libGLESv2.pdb 66.6 MB → 66.6 MB (🟢 −64.0 KB)
runtimes/win-arm64/native/libGLESv2.pdb 64.5 MB → 64.4 MB (🟢 −32.0 KB)
runtimes/win-x86/native/libGLESv2.pdb 67.2 MB → 67.2 MB (🟢 −32.0 KB)

Informational only — this never blocks the PR. Native binaries are labelled by os/arch.

web-flow and others added 14 commits July 29, 2026 15:48
Replaces the uname -m sniff added earlier with a declarative mechanism. The
script was the same mistake as the TF_BUILD sniff it replaced -- inferring a
policy at runtime, and silently adapting if the pool changed instead of failing
loudly. What a host can do is now stated up front in the pipeline definition.

Adds an `env:` parameter to azure-templates-jobs-bootstrapper.yml that flows to
all four bootstrapper invocations: the Windows and non-Windows direct runs (via
the step env) and both Docker runs. Containers inherit nothing from the agent,
so the env is materialised into an env-file and passed to `docker run
--env-file`; the file is always written, empty when nothing is declared, so the
run command stays uniform. This is generic infrastructure -- nothing about it is
GPU-specific.

Corrects the Metal opt-out scope. macOS and Mac Catalyst drive real Metal on the
x64 pool and passed CI, so they are no longer opted out. Only the iOS leg is,
because it runs the *simulator*, whose virtualized Metal leaves dispatch-queue
state that hangs the test host on shutdown.

Declares ganesh-gl opt-outs for the five container legs. Those images have no X
server, no Mesa and no ICD, so OpenGL genuinely cannot come up there.

Fixes two P/Invoke library names that were the actual cause of the Linux and
container GL failures. DllImport("libGL") and DllImport("libX11") probe the
unversioned .so, which is a linker symlink shipped only in the -dev packages; a
runtime-only host has libGL.so.1 and libX11.so.6 and nothing else. So the Linux
leg was failing to load Mesa even though CI provisions it correctly, and had
been silently skipping that cell for as long as the harness existed.

Provisions the software Vulkan ICD for x86 as well as x64. The .NET Framework
leg runs the suite once per bitness and a 32-bit process cannot load a 64-bit
vulkan-1.dll, so x86 Vulkan had no loader at all: CI showed 25/25 passing on x64
and 21/25 failing on x86. Bitness resolution is delegated to Windows the way the
official Vulkan Runtime installer does it -- loader into System32 and SysWOW64,
each ICD manifest registered in the matching registry view -- because PATH
cannot disambiguate two files with one name.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 03c0625a-14f0-4a6c-a1f6-b02ac5f8701a
The Windows agents have no GPU driver, so Windows falls back to the GDI generic
OpenGL 1.1 implementation, which advertises no WGL_ARB_* extensions and cannot
satisfy WglContext. Declared as an opt-out rather than left red, since there is
no fix available to land today.

This is a provisioning gap, not a platform limit, and the gap is documented so
it does not quietly become permanent: Mesa's llvmpipe would fix it with no
test-code change (its WGL extension string statically advertises both
WGL_ARB_pixel_format and WGL_ARB_pbuffer, and it reports
WGL_FULL_ACCELERATION_ARB), but there is no trustworthy feed for Mesa's Windows
binaries yet. Silk.NET ships no desktop-GL package, and its ANGLE package is
unusable -- every published version has 32-bit binaries in runtimes/win-x64.

Vulkan and Direct3D stay required on Windows: CI showed both working, 25/25 and
5/5 respectively.

Also documents the complete set of declared CI opt-outs in gpu-test-policy.md,
so what each leg can and cannot do is discoverable without reading the pipeline.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 03c0625a-14f0-4a6c-a1f6-b02ac5f8701a
The policy is a lookup table and one Assert.Skip; it did not need 611 lines
across four files. Now 280 across two, with no behaviour change.

Collapses GpuBackend.cs, TestPlatforms.cs and GpuPolicy.cs into one file. Drops
the GpuAvailability enum in favour of the state token the report already needed,
which removes the enum, its doc block and the token mapper. Replaces the Entry
class and its constructor with a tuple array, so a backend row is one line.

Generates the skip reasons from the id and platform name rather than storing a
hand-written paragraph per backend. The detail those paragraphs carried -- which
gn arg to flip, which file to edit -- belongs in gpu-test-policy.md, where it is
already written, not repeated in every skip message.

Trims the call-site comments. "No catch: the policy already decided" appeared at
seven sites; the rule is stated once on IRenderer and once in the doc, so the
repetitions were noise next to a self-evident RequireOrSkip call. Also drops a
stale <see cref="GpuAvailability.Required"/> and the duplicated policy paragraph
from the two Vulkan renderers.

Verified identical behaviour: same policy report, same typo-guard error, and the
same 6092 tests with the same 2 pre-existing failures.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 03c0625a-14f0-4a6c-a1f6-b02ac5f8701a
Make Phase 10 unconditional: rebuild native, build managed, run the full solution, and fix every failure before staging outputs or creating PRs. Remove platform and human-attention escape hatches.

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

Copilot-Session: f66474bf-0d92-48db-a924-6ca2b009d6b2
The policy still carried more machinery than the decision it makes. A GPU test
either skips with a reason, fails, or passes; everything else was ceremony.

Replaces the GpuBackend enum with GpuBackends string consts. The enum and the id
string were the same fact twice, with Id() and Find() only to map between them --
and every renderer declared both, since IRenderer.Name was documented as equal to
the id. So IRenderer.Backend is gone entirely and the matrix gates on Name.

Replaces the ExistsOn/BuiltOn pair with a single Dictionary<string, TestPlatforms>
of where each backend must work. ExistsOn was read in exactly one place, to
choose between "does not exist on" and "is not built for" -- a whole platform set
per row to pick a verb. The surviving reason, "'ganesh-metal' is not required on
windows", is complete; why it is not required belongs in the doc's table, is not
actionable at a skip, and was the same per-row prose the previous pass deleted.

Moves TestPlatforms and platform detection to TestConfig, which already owns
IsWindows, IsApple and IsNanoServer. Neither is a GPU concept, and this removes a
third copy of the same #if ladder: VisualPlatform hand-wrote the identical probe
order to build golden directory tags, and its strings map exactly onto the enum
names, so it now derives from TestConfig. Golden tags verified byte-identical
before and after -- that path decides which reference PNG a cell compares to.

Drops the messaging helpers. OptOutHint appended "here is how to opt out" to
eleven failure messages; the exception already says what broke. Describe() and
the ##SKIA-GPU-POLICY## marker duplicated what xUnit already publishes, since a
skipped test carries its reason into the same TRX. OptOutSource, the state token
and the Cpu special case go with them.

Drops Lazy for plain static readonly fields. The runtime already gives the
guarantee the Lazy was hand-rolling: a throwing static initializer caches a
TypeInitializationException that every later access rethrows, so a malformed
opt-out list still fails every GPU test identically. Verified the message is
still legible through the wrapper. Startup cost is irrelevant here.

Public surface is now two consts, RequireOrSkip and Validate. GpuPolicy.cs goes
from 241 lines to 131, and a new test asserts every renderer name is a known id,
catching drift the enum could not express.

Behaviour verified unchanged: 5927 non-GPU tests still pass with 16 declared
skips, the visual matrix still reports 25/5/5/15, golden tags are identical,
=all still spares raster, and the typo guard still names the valid ids.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 03c0625a-14f0-4a6c-a1f6-b02ac5f8701a
Deletes documentation/dev/gpu-test-policy.md. The policy is a table and one
skip; it does not need a page of its own, and the visual matrix is the main
consumer, so the table, the opt-out syntax, the CI opt-out list and the Windows
software-GL gap now live in golden-image-tests.md.

Merges SkipReason into RequireOrSkip -- SkipReason had no other caller once the
report was gone.

Replaces Validate() with a public Disabled(). Validate read disabled.Count to
force a static initializer, which reads as counting a set that obviously cannot
throw; the throw came from class init. Disabled() parses on each call and throws
directly, so the typo guard is now a plain InvalidOperationException instead of
one wrapped in TypeInitializationException, and the static-init ordering
constraint is gone. Tests do not need the cache.

Uses the GpuBackends consts for every renderer Name, which were still literals.

Rewrites comments that told a story rather than describing the code as it is:
the simulator Snap() note said what it "used to be", the Vulkan ICD header
narrated the x86 gap as a discovery, test-shared.cake explained what it was
deliberately not doing, and Directory.Build.targets did the same for Trim
metadata. Also drops comments that only restated the call below them, and fixes
two that referenced the removed Unsupported state.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 03c0625a-14f0-4a6c-a1f6-b02ac5f8701a
660 lines across one file becomes 281 across two, and the content now matches
the code.

Splits the GPU policy into its own 77-line page. It gates five test families --
the visual matrix, the GL, Vulkan and Direct3D tests, and the Graphite release
tests -- so scoping it under "golden image tests" was wrong. The previous policy
page was deleted for length, not for existing; this one is a table, an opt-out
syntax and a pointer to the pipeline.

Presents raster, Ganesh and Graphite as three equal families. The old text
described Graphite as in-flight, including a whole "Graphite seam" section that
was a rebase plan for a PR that has since landed, and per-host tables that listed
Graphite renderers as follow-ups.

Corrects claims that no longer held:

  * "every cell emits its PNG on pass and fail" was wrong in the case that
    matters -- RenderAsync runs before the marker is emitted, so a render failure
    emits nothing. The lifecycle section now says which outcomes carry an image.
  * "compared strictly" -- comparison is tolerance-based, always.
  * golden lookup is three-deep on Nano Server (nanoserver, windows, shared),
    not two.
  * GpuRenderGate does not exist; serialization is an xUnit collection.
  * there is no Direct3D visual renderer, so the coverage table invented one.
  * VisualPlatform.Tag is Tags, the tag list omitted nanoserver, and Linux GL is
    GLX rather than "GLX/EGL".
  * the Desktop/ folder rule claimed to cover Metal, which is shared.

Replaces the host coverage matrix, which conflated compiled, required and seeded
and drifted immediately, with the renderer list plus a pointer to the policy
table and the goldens directory. Merges three overlapping seeding sections into
one, and drops the storage, tolerance and CI rationale essays.

Also removes a dead "direct3d" key from GoldenTolerance -- no renderer has that
name -- and the Ganesh entries that only restated the fallback, leaving raster as
the one special case.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 03c0625a-14f0-4a6c-a1f6-b02ac5f8701a
The TRX markers used three inconsistent names and one was actively wrong:
##SKIA-GOLDEN-IMAGE## carried the *actual* rendered image, not the golden,
and ##SKIA-VISUAL-IMAGE## conflated the golden and the diff, distinguishing
them by a suffix baked into the path.

Rename to one role per marker, all sharing the golden key as their path:

  ##SKIA-VISUAL-ACTUAL## path=... outcome={pass|mismatch|unseeded} size=... base64=...
  ##SKIA-VISUAL-GOLDEN## path=... size=... base64=...
  ##SKIA-VISUAL-DIFF##   path=... size=... base64=...

This drops ##SKIA-VISUAL-CELL## by folding the verdict onto the actual image.
The verdict cannot be derived from the images alone -- a pass and an unseeded
test both emit only an actual -- so it still has to be stated, but it no longer
needs a marker of its own. Emitting it means the actual image now goes out
after the comparison rather than before.

The harvest script loses a regex, a dict and the key-mangling that rebuilt
'{scene}.golden.png' to look images up; the three roles are now just three
dicts keyed by the same path.

Vocabulary: 'cell' -> 'test', 'seam' -> 'interfaces', and 'lifecycle' /
'discipline' headings replaced with plain descriptions.

Verified: visual suite 25 total / 5 failed / 5 succeeded / 15 skipped
(unchanged); a forced mismatch emits all three markers and the script writes
actual+golden+diff into mismatch/ with correct bytes; seeding dry-run yields
the same 10 goldens; full non-GPU suite 5927 / 0 failed / 16 skipped.
The outcome= field on the actual-image marker duplicated what the markers
already imply. No golden marker means no golden was committed; a diff only
exists when there was something to diff against. The test result and its
message say whether a comparison passed -- the harvest script does not need
to re-derive that.

So drop the field, and emit the golden and diff whenever a golden was found
rather than only on a mismatch. A pass at 99.5% now ships its diff too, which
is the case actually worth eyeballing.

This deletes the outcome grouping in extract-visual-goldens.py: it no longer
sorts into unseeded/ and mismatch/, it just writes what it found, mirroring
the goldens tree as {renderer}.{platform}/{scene}.{actual,golden,diff}.png.
A scene with only an .actual.png had no golden. Renamed --failures-out to
--images-out to match, since it was never only failures.

Verified: with a forced mismatch, ganesh-gl.windows extracts 5 actual-only
images (unseeded) and raster.windows extracts actual+golden+diff for all 5,
the mismatch showing golden 4323 B against actual 4711 B. Seeding dry-run
unchanged at 10 goldens. Visual suite 25/5/5/15, full non-GPU suite
5927 / 0 failed / 16 skipped. YAML parses.
Two dictionaries, a composite string key and a three-branch lookup existed to
express 'raster is strict, everything else is not'. ByRendererScene was empty
and ByRenderer held a single entry, since the ganesh-* rows only restated the
fallback and the direct3d row named a renderer that does not exist.

Now it is a ternary, and sceneName leaves the signature because nothing read
it. If a scene ever needs its own tolerance, special-case it then.

Visual suite unchanged at 25/5/5/15.
Both said a missing display or ICD makes the GPU tests skip and that the
provisioning 'never turns the build red'. Under this policy ganesh-gl and
ganesh-vulkan are required on Linux, so missing provisioning fails.
A review against the code turned up five statements this change falsifies:

  * AGENTS.md said tests never skip 'except missing hardware' -- missing
    hardware is now precisely what fails.
  * native-dependency-update and update-skia (twice) told the reader that
    backends self-skip when unavailable.
  * golden-image-tests.md suggested special-casing a single scene in
    GoldenTolerance.For, which takes only the renderer name.
Graphite landed in #3968 and skia_enable_graphite=true on every platform, with
Vulkan on Windows/Linux/Android, Metal on Apple and Dawn on WASM. Six skills
still told the reader to filter those changes out:

  * security-audit classed Graphite, Vulkan and Dawn as NOT_REACHABLE compile
    flags 'we don't use', which would dismiss a live CVE. It now points at the
    gn args in native/*/build.cake instead of naming a fixed list.
  * update-skia scored Graphite changes as SKIP, marked skgpu::graphite:: and
    Dawn/wgpu as irrelevant, and framed shared GpuTypes.h changes as a
    Graphite-only trap rather than a change reaching both families.
  * skia-analyst excluded Graphite and Dawn changes outright.
  * sample-scout classified any file naming Graphite or Dawn as low and called
    Graphite 'not SkiaSharp'. Backend-internal GMs are still low; the test is
    now whether it exercises internals, not which backend it names.
  * api-docs used 'Vulkan (not yet supported)' as a formatting example.

Also documents the container legs' ganesh-gl opt-out, which this branch added
to five legs without saying so in containerized-testing.md.
The previous pass flipped 'exclude Graphite, we use Ganesh' into 'include
Graphite', which is the same mistake with the sign changed: naming one backend
as included implies the others are not, and the guidance still had to be
rewritten every time a backend lands. It also introduced three false claims --
skia_enable_graphite is not set on Tizen and is conditional on WASM, Nano Server
builds with Vulkan and Direct3D off, and Vulkan is not deprecated.

Rewritten around the real criteria instead:

  * update-skia's risk table categorises kinds of change, so the backend row is
    simply gone -- a GPU change is already Removed/Renamed/New/Behavior.
  * the relevance table now says relevance is whether our C API or a shipped
    build touches the code, and folds the four GPU prefixes into one row that
    points at native/*/build.cake rather than asserting a platform map.
  * skia-analyst just drops the exclusions: what is not excluded is included.
  * security-audit's NOT_REACHABLE names no backend at all. It asks for the
    platform and the flag, and notes a flag off in one build is often on in
    another -- which is the actual trap.
  * sample-scout keeps 'is it backend internals?' and drops the platform list.

Also fixes a pre-existing scope error: the 'full test suite' command ran
SkiaSharp.Tests.Console.csproj, which references neither the Vulkan nor the
Direct3D project, while claiming to run all three. It now runs the solution.
mattleibow and others added 24 commits July 31, 2026 17:19
Mark command lines, paths, globs, and source symbols as inline code so the Phase 3 prompt is unambiguous and readable.

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

Copilot-Session: 4f137c11-1837-4184-af2b-c25767fcb781
Use the deterministic binding generator directly, removing the region-order compatibility pass. Provision Xvfb with Mesa softpipe alongside lavapipe, remove the ganesh-gl opt-out, and require agents to fix every GpuPolicy backend instead of introducing skips.

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

Copilot-Session: 4f137c11-1837-4184-af2b-c25767fcb781
Keep Phase 3 read-only, remove sanctioned build and feature bypasses, distinguish initial diagnostics from final validation, pin manifests to the exact merged upstream SHA, and make the post-step verify workflow-resolved state plus the final test exit code.

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

Copilot-Session: 4f137c11-1837-4184-af2b-c25767fcb781
Export detector results from the job that actually resolves them, require final dependency decisions to match the built DEPS file, and make generated summaries distinguish required coverage from platform-policy skips.

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

Copilot-Session: 4f137c11-1837-4184-af2b-c25767fcb781
Require Component Governance versions to match authoritative source metadata whenever a tracked dependency revision or enabled state changes, while allowing verified same-version revision rolls. Use Claude Opus 5 for the primary sync agent.

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

Copilot-Session: 4f137c11-1837-4184-af2b-c25767fcb781
Keep Claude Opus 4.8 because the pinned AWF v0.83.4 runtime rejects claude-opus-5 before agent execution.

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

Copilot-Session: 4f137c11-1837-4184-af2b-c25767fcb781
Keep long builds and test suites in a single foreground shell invocation so the agent does not spend turns polling progress. Add modest per-run credit headroom for Opus variance.

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

Copilot-Session: 4f137c11-1837-4184-af2b-c25767fcb781
Keep the long-command no-poll guidance, which returned the workflow to 1,305 credits, but retain 2,000 as the absolute per-run failure ceiling.

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

Copilot-Session: 4f137c11-1837-4184-af2b-c25767fcb781
Run the same deterministic update workflow with Claude Sonnet 5 to compare correctness and cost against the validated Opus 4.8 baseline.

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

Copilot-Session: 4f137c11-1837-4184-af2b-c25767fcb781
Prevent whole-update delegation after the Sonnet trial handed mutating work to a fallback model, and require automation handoff values to remain the exact resolved inputs.

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

Copilot-Session: 4f137c11-1837-4184-af2b-c25767fcb781
Require every behavior in a removed patch to be verified upstream and require final DEPS revisions, decisions, and Component Governance metadata to agree exactly.

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

Copilot-Session: 4f137c11-1837-4184-af2b-c25767fcb781
Sonnet 5 reduced cost on a successful run but failed to establish three consecutive clean updates within the retry limit. Keep the general guardrail improvements and restore the proven Opus 4.8 primary model.

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

Copilot-Session: 4f137c11-1837-4184-af2b-c25767fcb781
Reject fork-head substitutions for the target upstream SHA in version generation, publication, and formal review. Keep the tested mono/skia head and exact upstream merge commit as distinct manifest values.

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

Copilot-Session: 4f137c11-1837-4184-af2b-c25767fcb781
Keep merge/build/test/fix work with the agent while deriving branch, provenance, version, and publication facts from workflow state and the completed git graph. Finalize metadata before exposing the push-only PAT step.

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

Copilot-Session: 4f137c11-1837-4184-af2b-c25767fcb781
The pre-agent tool bootstrap may modify externals/depot_tools; exclude that environmental path while still rejecting uncommitted product and workflow changes.

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

Copilot-Session: 4f137c11-1837-4184-af2b-c25767fcb781
Apply the existing depot-tools bootstrap exclusion to the final worktree assertion after deterministic metadata finalization.

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

Copilot-Session: 4f137c11-1837-4184-af2b-c25767fcb781
Compile the final depot-tools clean-check exclusion into the executable agentic workflow.

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

Copilot-Session: 4f137c11-1837-4184-af2b-c25767fcb781
Make work detection the detector default with explicit resolve-only and output modes. Consolidate checkout preparation, and keep PR rendering and templates inside the trusted publisher using jq.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4f137c11-1837-4184-af2b-c25767fcb781
Keep each PR template beside the values that render it, and reduce AGENTS.md to durable automation policy without publisher implementation details.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4f137c11-1837-4184-af2b-c25767fcb781
Track exact Skia DEPS identities in cgmanifest and make the metadata helper emit a deterministic change signal. Require source-backed agent review for every changed tracked dependency, reject version-only drift, and retain initial Vulkan test evidence before publishing sync PRs.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4f137c11-1837-4184-af2b-c25767fcb781
Populate authoritative version sources for every tracked Skia dependency, including explicit canonical-version and placeholder exceptions. Require source evidence for unchanged baselines as well as dependency rolls.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4f137c11-1837-4184-af2b-c25767fcb781
Make the agent refresh fork-patch fingerprints immediately before completion so late DEPS and C-shim adaptations cannot leave stale evidence for the trusted publisher to reject.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4f137c11-1837-4184-af2b-c25767fcb781
Run metadata and fork-patch validation as an explicit final Phase 10 section against the tested mono/skia tree. Prevent delivery from starting until stale fingerprints and dependency evidence are fully reconciled.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4f137c11-1837-4184-af2b-c25767fcb781
@mattleibow
mattleibow merged commit 7fd1aca into main Aug 4, 2026
122 checks passed
@mattleibow
mattleibow deleted the mattleibow-dev-vulkan-sync-validation branch August 4, 2026 13:37
@mattleibow mattleibow added this to the 4.152.0-preview.1 milestone Aug 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants