fix(render) #443: private viewports take the ring transform path
The paperdoll was visible only in portal space. Root cause: the classic WbDrawDispatcher.Draw path appended its transforms into the SHARED world transform frame (WorldTransformFrameArena.Append) with a non-zero base instance, but the default mesh shaders index every parallel per-instance array - clip slots, light sets, indoor, OPACITY, selection lighting, detail category - zero-based; only the packed world submission's shader convention subtracts the shared-arena prefix. With a world frame active the doll drew all instances at per-instance opacity 0 into a cleared target: counted draws, blank pixels, deterministic. Portal space worked because no world transform frame is active there, so the same code took the ring path with base 0. The private viewports are the only production consumers of the classic path, hiding the defect everywhere else. Fix: WbDrawDispatcher.NextClassicDrawIsPrivatePass - the private viewport renderer marks its draw and WriteWorldTransformSection routes private passes onto the plain ring path unconditionally (self-contained render state: the private pass owns its own camera, lighting, and target, and must not depend on the world frame's pose address space). Also landed, each independently justified: - Per-GPU-flight-slot private targets (PrivateViewportFlightTargets), restoring the pre-f6fe0f2a design: that revert's claim that frame submission order protects the single target's write->sample transition is not guaranteed across Vulkan command buffers. Per-slot completed scenes fix the cleared-sibling-after-reveal wart the old attempt had. - Paperdoll resource preparation moved to the frame resource phase (IPrivateEntityViewportResourcePreparation) before world draws consume the bounded composite-upload budget. - The presenter redresses on every dirty edge (an appearance-equal clone can pin retired readiness across generations; the renderer's two-phase promote keeps the last completed image visible during replacement), publishes only non-zero handles, and clears the viewport exactly once at the explicit character-session boundary. Verified live on the clean build: doll visible in the NORMAL world, visible through portal space, and still visible after arrival - the exact reported repro cycle. 26 paperdoll/private-viewport/preparation tests plus 60 renderer-suite tests pass; owner visual gate pending. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
fc30285fd7
commit
cd1cdee0e5
9 changed files with 439 additions and 209 deletions
|
|
@ -924,6 +924,24 @@ public sealed unsafe partial class WbDrawDispatcher
|
|||
return new RhiSection(allocation.Buffer, allocation.OffsetBytes, (uint)byteCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// #443 — the next classic draw is a PRIVATE pass (paperdoll, appraisal,
|
||||
/// chargen preview) and must take the plain ring transform path with
|
||||
/// <c>firstInstance = 0</c>, never an append into the shared world
|
||||
/// transform frame. The default mesh shaders index every parallel
|
||||
/// per-instance array (clip slots, light sets, indoor, opacity, selection,
|
||||
/// detail category) zero-based — only the packed world submission's
|
||||
/// shader convention subtracts a shared-arena prefix — so an
|
||||
/// arena-appended classic draw with a non-zero base reads zeroed
|
||||
/// per-instance data (opacity 0 ⇒ an invisible doll whenever a world
|
||||
/// frame is active; portal space worked only because the arena was
|
||||
/// inactive there). The private pass owns its own camera, lighting, and
|
||||
/// target; per the self-contained-render-state rule it must not depend on
|
||||
/// the world frame's pose address space at all. Consumed and cleared by
|
||||
/// the next <see cref="WriteWorldTransformSection"/>.
|
||||
/// </summary>
|
||||
internal bool NextClassicDrawIsPrivatePass;
|
||||
|
||||
private RhiSection WriteWorldTransformSection(
|
||||
IGpuFrame frame,
|
||||
ReadOnlySpan<float> matrixFloats,
|
||||
|
|
@ -939,7 +957,9 @@ public sealed unsafe partial class WbDrawDispatcher
|
|||
ObserveOrdinaryTransformDemand(
|
||||
frame.Serial,
|
||||
checked((uint)(matrixFloats.Length / 16)));
|
||||
if (!_worldTransformFrames.IsActive)
|
||||
bool privatePass = NextClassicDrawIsPrivatePass;
|
||||
NextClassicDrawIsPrivatePass = false;
|
||||
if (privatePass || !_worldTransformFrames.IsActive)
|
||||
{
|
||||
firstInstance = 0;
|
||||
return WriteRingSection(frame, matrixFloats);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue