Repository navigation
1.1.2-rc
Pre-releasePre-release on 1.1.1. Ships the SpringBone warmup fix, the per-frame joint palette ring, and the outline hull rebuild. Tagged as a pre-release because #427 moves settled SpringBone poses on real avatars — it is promoted to 1.1.2 once Muse validates its assets. SPM stable resolvers skip pre-releases, so from: "1.1.0" stays on 1.1.1 until then.
Behaviour change — SpringBone (#427)
AvatarSample A in VRM 0.0 rendered with hair flared above the forehead after loading. Its hair explicitly authors gravityPower = 0; the loader's 30-step warmup suppressed authored stiffness throughout those steps and then froze the displaced pose.
- Warmup uses authored stiffness and drag from its first step. The interactive startup ramp (
settlingFrames,1 - smoothstep(0, 60, frames)) is no longer applied during warmup — warmup is exactly when settling should finish, so the counter is zeroed on entry instead of being drained per step. Removes the VMK#240/#292 patch pair, which only masked the drain arithmetic. - Collision responses are published even when every chain authors zero gravity. The previous
anyGravitygate let an unrelated chain's gravity decide whether these nodes received their settled pose. - VRM 0.0 sphere collider offsets get the missing handedness conversion. Offsets now reflect Z into the glTF node's local frame before the
Ry180facing rotation, matching three-vrm's VRM0 loader:(x, y, z) → (-x, y, z), previously(-x, y, -z). VRM 1.0 assets are unaffected.
What moves: four trajectory baselines regenerated. 21 of 46 bones change per quality tier; maximum mean-axis shift is 8.65 mm (ultra), 11.36 mm (high), 16.86 mm (medium), 36.78 mm (low). The 1 mm regression tolerance is unchanged. Authored gravity values, warmup step count, and GPU dispatch count are all unchanged; no per-frame work is added.
Renderer (#426)
- Per-frame joint palette ring. With multiple frames in flight the renderer overwrote one CPU-written joint palette while earlier GPU frames could still be reading it. Each existing in-flight slot now owns a persistent palette, selected by the uniform-buffer slot; compositor views share that selection until frame completion.
- Per-skin pose versions. An older slot copies the latest idle pose once instead of re-evaluating matrices, which also prevents a stale pose when animation stops. Slots already holding the current pose skip the copy. The identity-palette debug override invalidates the cached pose when toggled.
- MToon material uniforms are prepared once per render pass and reused across primitives and dedicated outlines. Per-draw values and expression overrides apply to copies; the cache resets each pass, so runtime material edits still take effect.
- Cost: two additional padded palette buffers per renderer —
2 × (totalJointCount + 1024) × 64bytes. No added per-frame GPU wait. Measured on M4 Max (animated AvatarSample_A, 1024×1024, MSAA 1, physics off, 200 warmup + 5,000 frames): CPU median 0.109–0.110 ms → 0.111 ms, GPU p95 0.203–0.205 ms → 0.204–0.205 ms. Timing is essentially unchanged; no speedup is claimed — this is a correctness fix.
Outlines and shaders (#425)
skinned_mtoon_outline_vertexnow reads morphed positions, first-person hidden flags, and the outline width multiply texture, and extrudes through the sharedapplyMToonOutlineExtrusioninstead of a hand-inlined copy.mtoon_outline_vertexreads morphed positions too, andrenderMToonOutlinesbinds all three with the depth-prepass fallbacks.- First-person cull now runs after hull extrusion in both shaders. The world-coordinates branch rebuilt
clipPosand resurrected hidden vertices as outline hulls, which also affected the merged path. skinned_vertex(debug-only) skins throughvrm_skinrather than a third inline copy.OutlineFirstPersonHiddenTestsasserts on rendered pixels; verified to fail with the outline cull removed.- Shader slices rebuilt — macOS, iOS, iOS Simulator, visionOS, visionOS Simulator. The MToon known-good hash is re-baselined. Consumers pick these up only via this tag.
Closes #149, #150, #154, #156, #361, #363.
Docs (#425)
The performance guide, the DocC LoadingVRMModels article, and the API doc comments are resynced with actual behaviour:
- A cancel inside a primitive decode throws Swift's
CancellationError, notGLTFError.loadingCancelled, on both the serial and parallel mesh paths and regardless ofenableCancellation. Callers that special-case cancellation must match both error types. - Three
VRMLoadingOptimizationflags are marked no-effect (below). - Corrected: skinned cull bounds (fixed 50% inflation, then hips translation), decode-limiter scope (every load, not only
.parallelMeshLoading), the SpringBone sleep snippet, depth-prepass coverage (opaque, non-face), and the outline lever (vertex-side, not fragment-side). - Documents the runtime frame-rate levers: pipeline binary archive, frustum culling, morph and sleep gates, sprite cache, depth prepass.
CLAUDE.md's stale ">8 active morph targets switches to GPU" claim is replaced; morph compute always runs, capped at 8 active targets per primitive dispatch.
API
Source-compatible with 1.1.1. Nothing added, nothing removed. The only API-surface changes are doc comments that correct what the existing symbols do:
VRMLoadingOptimization.skipSecondaryUVs— reserved, no effect. OnlyTEXCOORD_0is ever read, so secondary UVs are already skipped on every load.VRMLoadingOptimization.parallelTextureDecoding— reserved, no effect. UseparallelTextureLoadingfor concurrent texture work. Note this flag is inVRMLoadingOptimization.default, so that set does no parallel work.VRMLoadingOptimization.lazyTextureLoading— reserved, no effect. Textures always load eagerly.VRMConstants.Limits.morphComputeThreshold— unused. Morph accumulation always runs on the GPU compute path; there is no CPU evaluation to switch away from. Kept for source compatibility.RendererConfig.enableDepthPrepass— covers opaque, non-face geometry; face materials keep their current depth state.
Default-path rendering changes only through the SpringBone warmup fix above and the outline first-person cull.
Verification
- Full suite on
dbea992: 2080 tests enumerated, exit 0, zero failures (swift test --parallel --num-workers 14 -j 16 --disable-sandbox). - Xcode Cloud green on
dbea992: Build - visionOS, Test - iOS, Test - macOS. CodeQL, Lint, and Shaders green. - Release
VRMRenderbuild with fresh renders of AvatarSample A 0.0, A 1.0, and U 1.0 on #427 — the upward hair flare is gone.
Not yet validated: Muse asset pass over the new SpringBone settled poses. That is the gate for promoting this to 1.1.2.
Full diff: 1.1.1...1.1.2-rc