feat(rendering): port retail one-pass detail material

Replace the building and EnvCell detail replay with retail's exact single-pass stage result, including authored surface opacity, squared detail alpha, final-alpha clipping, and the original subset pipeline/order. Arm the ordered walk command in place to close #471, delete the replay pipelines/shaders, and advance prepared content to recipe 9.

Mutation witnesses (each restored before commit):
- X=a*qA: RetailDetailTextureContractTests.BothShaderFamiliesUseTheSharedOnePassSourceAndDebugPrecedesDetailSample line 174, missing materialAlpha * detail.a * detail.a.
- X*=base alpha: same test line 175, forbidden baseTexel.a found.
- CLIP against base alpha: EnvCellAlphaDrawSourceTests.ClipShaders_UseGreaterEqualForThePerRangeReference line 260, final-X conditional missing.
- second detail draw: EnvCellAlphaDrawSourceTests.DetailOn_EveryEnvCellFamilyDrawsOnceInPlaceWithAuthoredOpacity line 105, Assert.Single saw 2 MDI calls.
- straight-alpha substitution: WalkStaticStreamPopulatorTests.ImmediateBuildingDetail_RetainsOriginalFramebufferFamily line 1244, Additive first failed (only wb-mesh-alpha-1x recorded; InvAlpha also failed).
- omit ordered arm: OrderPreservingSubmitterTests.PrepareThenDraw_OrdinaryBuildingClipBuildingOrdinary_ArmsOnePassInPlace line 305, expected (77,3.5), got (0,0).
- omit atmospheric combine: RetailDetailTextureContractTests.BothShaderFamiliesUseTheSharedOnePassSourceAndDebugPrecedesDetailSample line 173, atmospheric shared include missing.
- drop serialized opacity: ObjectMeshDataSerializerTests.SurfaceOpacity_RoundTripsBitExactlyAndDeterministically line 288, first reported 0.5 bits 1056964608 vs 1065353216.
- stale detail arm: ordered adjacency test line 307, expected following ordinary (0,0), got (77,3.5).
- per-frame surface map: EnvCellAlphaDrawSourceTests.ProductionWholeLeaf_WarmedScanSubmitRhiAndFilteredReplayDoNotAllocate line 178, expected 0 B, got 147456 B.

Verification before commit: shader compiler 23/23; focused App 213/213; Content 75/75; Core Wb 10/10; launcher migration 6/6; Release solution build 0 warnings / 0 errors; git diff --check clean.
This commit is contained in:
Erik 2026-09-04 23:45:26 +02:00
parent 13dbb79eeb
commit 75664805f8
61 changed files with 1003 additions and 1016 deletions

View file

@ -19,7 +19,7 @@ struct InstanceData {
// CPU writer's layout is unchanged (GpuBindingModel.GpuBatchDataStrideBytes).
struct BatchData {
uint textureIndex; // slot into the global texture table
uint _pad; // keeps textureLayer/flags at offsets 8/12
float surfaceOpacity; // authored material alpha (not base texture alpha)
uint textureLayer; // layer in the shared WB or pooled composite array
uint flags; // reserved — N.5 dispatcher owns all blend state
// (glBlendFunc per pass). If a future phase wants
@ -112,6 +112,10 @@ layout(std430, binding = 8) readonly buffer InstanceSelectionLightingBuf {
vec2 instanceSelectionLighting[];
};
layout(std430, binding = 9) readonly buffer InstanceDetailCategoryBuf {
uint instanceDetailCategory[];
};
uniform mat4 uViewProjection;
// Phase Post-A.5 (ISSUE #52, 2026-05-10): per-pass offset into Batches[].
@ -272,6 +276,9 @@ out flat uint vTextureIndex;
out flat uint vTextureLayer;
out flat float vOpacityMultiplier; // #188
out flat vec2 vSelectionLighting;
out flat float vSurfaceOpacity;
out flat uint vBatchFlags;
out flat uint vDetailCategory;
void main() {
int instanceIndex = gl_BaseInstanceARB + gl_InstanceID;
@ -295,4 +302,7 @@ void main() {
// site in mesh_modern.frag, which is the only form Vulkan can express.
vTextureIndex = b.textureIndex;
vTextureLayer = b.textureLayer;
vSurfaceOpacity = b.surfaceOpacity;
vBatchFlags = b.flags;
vDetailCategory = instanceDetailCategory[instanceIndex];
}