Skip to content

Tidy the Metal seams, name the renderer's literals and test the iOS mirrors - #41

Merged
Xget7 merged 8 commits into
mainfrom
fix-ios-seams
Oct 20, 2026
Merged

Xget7 merged 8 commits into
mainfrom
fix-ios-seams

Conversation

@Xget7

@Xget7 Xget7 commented Oct 20, 2026

Copy link
Copy Markdown
Owner

Summary

The Metal renderer's seams follow the folder rules, its literals have names, and the Swift-facing mirrors have tests.

rendering/metal/layout/
  MetalFrameSlots.h     kFramesInFlight (was kSlots x2 and kFramesInFlight)
  MetalSortKeys.h       KeyBits (was MetalRadixSort::KeyBits; cull/ no longer includes sort/)
  MetalDrawArguments.h  kDrawBatches, kDrawArgumentBytes (raster/ no longer includes cull/)
  • MetalSurface names its render-scale clamp, and MetalSplatRenderer names the residency, LOD error and texture ceilings, matching the Android names where one exists.
  • MetalSplatRenderer.h drops the unused GpuLayout.h include.
  • New tests: SKTypeConversionsTest (every mirror, plus toCppPolicy rejecting a bad sortDepth with out untouched) and SKPngWriterTest (decodes the written PNG, rejects a zero size and a wrong-length buffer).
  • Lifetime hazard carried over from before the restructure: the selection and sort completion handlers wrote through raw pointers into MetalFrameCompute, and ~MetalSplatRenderer waits only on the slots the render buffer's handler returns.
    Apple documents no order between one command buffer's completion handlers and another's (only that waitUntilCompleted covers its own), so a late handler was possible.
    The handlers now hold a share of a GpuReport and of the frame loop's failure latch.
    Observed order here: a 30 ms sleep in the sort handler under AddressSanitizer did not fail the new destroy-in-flight test, so the driver runs the handlers in commit order and the test passes before and after the fix.

Evidence

  • Before: the new destroy-in-flight test passed on the old code on this Mac (commit 156ab48), including with the delayed handler probe.
  • After: ctest on the macOS build passes 385 of 385 (2 fixture tests skipped for missing files); the ASan build passes the renderer, frame compute and LOD tests.
  • Where it ran: Mac only (Apple GPU family 7+, macOS 27), no physical device, no simulator run. Device runs are deferred to an end session.
  • Checks: scripts/lint-cpp.sh clean; scripts/build-ios.sh and scripts/package-ios.sh succeed; the embedded shader text hash is 61ea372e20f1cd4f65eeb83e1a8da7f6327e9261, unchanged. sdk_harness.py, npm run check and npm run lint did not run: nothing they cover changed.

Merge Danger

Door: Two-way.

Pure renaming, includes and a handler-capture change; revert restores the old layout.

Blast Radius: iOS

Only packages/splatkit-ios changes; the iOS mirror export picks up the new tests and headers.

Xget7 added 8 commits October 20, 2026 01:20
…e home in layout/

The slot count was declared three times (MetalVisibilityPass::kSlots,
MetalFrameCompute::kSlots, MetalSplatRenderer::kFramesInFlight) and the
radix sort owned the key width enum, so cull/ included sort/ and raster/
included cull/ only to read constants.

kFramesInFlight, KeyBits and kDrawBatches/kDrawArgumentBytes now live in
layout/MetalFrameSlots.h, MetalSortKeys.h and MetalDrawArguments.h. Every
per-slot array derives from kFramesInFlight, the cull -> sort and
raster -> cull includes are gone, and the test-local copies of the slot
count in MetalSurfaceTest are dropped. MetalFrameLoopTest keeps its own
count because it acquires exactly two slots by hand.

Behaviour and shader text are unchanged.
…ure limits

MetalSurface clamps the render scale with kMinRenderScale and
kMaxRenderScale, the same names VulkanSurface uses. MetalSplatRenderer
reports its residency window as kMinResidencyCapacity and
kMaxResidencyCapacity (the Android names), the LOD error range as
kMinLodErrorPixels and kMaxLodErrorPixels, and the texture ceiling as
kAppleFamily7MaxTextureDimension.

The values are unchanged.
Nothing the header declares comes from GpuLayout.h; the sources that need
its records include it themselves.
The test writes a 3x2 BGRA image, decodes the file with ImageIO and checks
its size and every channel. A zero size, a buffer one byte short or long,
and an unwritable path all return false, and the first two leave no file.
…tructs

Policy round trips both sort depths, and toCppPolicy rejects a sort depth of
24 and leaves the output untouched. Policy support, stats, capabilities,
camera state, camera request and lens settings each check every field
against distinct values.
… flight

Fifty renderers each draw both frame slots on a 4096-leaf hierarchy, so a
selection, a sort and a render buffer are in flight per slot, and are
destroyed straight away. The test is a regression guard for the completion
handlers that capture pointers into MetalFrameCompute; it needs an
AddressSanitizer build to catch a late handler.
The selection and sort completion handlers wrote through raw pointers into
MetalFrameCompute and MetalFrameLoop, and ~MetalSplatRenderer waits only on
the frame slots that the render buffer's handler returns. Apple documents
no order between one command buffer's completion handlers and another's, so
a handler can still be running after the renderer has gone. In practice the
driver ran them in commit order here: a 30 ms sleep in the sort handler
under AddressSanitizer did not fail the regression test, which therefore
passes before and after this change.

The handlers now hold a share of a GpuReport that carries the counters, and
of the frame loop's failure latch, so nothing they touch can be freed.
@Xget7
Xget7 merged commit fe25470 into main Oct 20, 2026
17 checks passed
@Xget7
Xget7 deleted the fix-ios-seams branch October 20, 2026 04:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant