fix(render): pair cell-shell index segments with their reordered batches
The S1 review round ordered cell-shell batches by source surface index in ObjectMeshManager.UploadGfxObjMeshData but left the index segments handed to the arena in TextureBatches dictionary order. FirstIndex is positional over the segments, so every cell shell's batch pointed at another batch's index range: magenta placeholder walls, stretched textures, and missing faces in every dungeon and house (G1 FAIL 2026-09-02). One upload order is now computed once and used by the count, fill, and batch loops alike. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
e2543d0ef0
commit
8c6563cada
1 changed files with 26 additions and 23 deletions
|
|
@ -2052,9 +2052,9 @@ namespace AcDream.App.Rendering.Wb
|
||||||
/// <c>DrawMesh</c> @0x0059D4A0 draws them in that order). Runs once per
|
/// <c>DrawMesh</c> @0x0059D4A0 draws them in that order). Runs once per
|
||||||
/// mesh upload; the allocation is not per-frame.
|
/// mesh upload; the allocation is not per-frame.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static IEnumerable<((int Width, int Height, TextureFormat Format) Format, TextureBatchData Batch)> OrderedUploadBatches(ObjectMeshData meshData)
|
internal static List<((int Width, int Height, TextureFormat Format) Format, TextureBatchData Batch)> OrderedUploadBatches(ObjectMeshData meshData)
|
||||||
{
|
{
|
||||||
var pairs = new List<((int Width, int Height, TextureFormat Format), TextureBatchData)>();
|
var pairs = new List<((int Width, int Height, TextureFormat Format) Format, TextureBatchData Batch)>();
|
||||||
bool cellShell = false;
|
bool cellShell = false;
|
||||||
foreach (var (format, batches) in meshData.TextureBatches)
|
foreach (var (format, batches) in meshData.TextureBatches)
|
||||||
{
|
{
|
||||||
|
|
@ -2065,7 +2065,7 @@ namespace AcDream.App.Rendering.Wb
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (cellShell)
|
if (cellShell)
|
||||||
return pairs.OrderBy(p => p.Item2.SourceSurfaceIndex); // stable: OrderBy preserves storage order on ties
|
return pairs.OrderBy(p => p.Batch.SourceSurfaceIndex).ToList(); // stable: OrderBy preserves storage order on ties
|
||||||
return pairs;
|
return pairs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2081,26 +2081,30 @@ namespace AcDream.App.Rendering.Wb
|
||||||
// every batch twice (a per-batch ToArray plus an unsized
|
// every batch twice (a per-batch ToArray plus an unsized
|
||||||
// SelectMany growth) — megabytes of transient garbage per mesh on
|
// SelectMany growth) — megabytes of transient garbage per mesh on
|
||||||
// the render thread.
|
// the render thread.
|
||||||
|
// ONE upload order for this mesh. The index segments handed to the
|
||||||
|
// arena, the per-batch FirstIndex, and the renderBatches list are
|
||||||
|
// all positional over this same sequence, so it is computed once
|
||||||
|
// and used by every loop below. (The S1 review round ordered only
|
||||||
|
// the batch loop and desynchronized every cell shell's geometry
|
||||||
|
// from its texture; G1 failed on it. Never split these again.)
|
||||||
|
List<((int Width, int Height, TextureFormat Format) Format, TextureBatchData Batch)> uploadOrder =
|
||||||
|
OrderedUploadBatches(meshData);
|
||||||
|
|
||||||
int totalIndexCount = 0;
|
int totalIndexCount = 0;
|
||||||
int nonEmptyBatchCount = 0;
|
int nonEmptyBatchCount = 0;
|
||||||
foreach (List<TextureBatchData> formatBatches in meshData.TextureBatches.Values)
|
foreach (var (_, formatBatch) in uploadOrder)
|
||||||
{
|
|
||||||
foreach (TextureBatchData formatBatch in formatBatches)
|
|
||||||
{
|
{
|
||||||
if (formatBatch.Indices.Count == 0) continue;
|
if (formatBatch.Indices.Count == 0) continue;
|
||||||
totalIndexCount = checked(totalIndexCount + formatBatch.Indices.Count);
|
totalIndexCount = checked(totalIndexCount + formatBatch.Indices.Count);
|
||||||
nonEmptyBatchCount++;
|
nonEmptyBatchCount++;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
var cpuIndices = new ushort[totalIndexCount];
|
var cpuIndices = new ushort[totalIndexCount];
|
||||||
var indexSegments = new (int Offset, int Count)[nonEmptyBatchCount];
|
var indexSegments = new (int Offset, int Count)[nonEmptyBatchCount];
|
||||||
{
|
{
|
||||||
int fillOffset = 0;
|
int fillOffset = 0;
|
||||||
int segmentIndex = 0;
|
int segmentIndex = 0;
|
||||||
foreach (List<TextureBatchData> formatBatches in meshData.TextureBatches.Values)
|
foreach (var (_, formatBatch) in uploadOrder)
|
||||||
{
|
|
||||||
foreach (TextureBatchData formatBatch in formatBatches)
|
|
||||||
{
|
{
|
||||||
int count = formatBatch.Indices.Count;
|
int count = formatBatch.Indices.Count;
|
||||||
if (count == 0) continue;
|
if (count == 0) continue;
|
||||||
|
|
@ -2110,7 +2114,6 @@ namespace AcDream.App.Rendering.Wb
|
||||||
fillOffset = checked(fillOffset + count);
|
fillOffset = checked(fillOffset + count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
GlobalMeshAllocation? globalAllocation = null;
|
GlobalMeshAllocation? globalAllocation = null;
|
||||||
var renderBatches = new List<ObjectRenderBatch>(nonEmptyBatchCount);
|
var renderBatches = new List<ObjectRenderBatch>(nonEmptyBatchCount);
|
||||||
|
|
@ -2147,9 +2150,9 @@ namespace AcDream.App.Rendering.Wb
|
||||||
// (ConstructMesh @0x0059DFA0 attribute-range scan, DrawMesh
|
// (ConstructMesh @0x0059DFA0 attribute-range scan, DrawMesh
|
||||||
// @0x0059D4A0 ascending subset loop). So cell-shell batches
|
// @0x0059D4A0 ascending subset loop). So cell-shell batches
|
||||||
// are uploaded in that order; ordinary GfxObj meshes keep the
|
// are uploaded in that order; ordinary GfxObj meshes keep the
|
||||||
// storage order. This runs once per mesh at upload time, not
|
// storage order. `uploadOrder` is the SAME sequence the index
|
||||||
// per frame.
|
// segments above were filled from; the two must never diverge.
|
||||||
foreach (var (format, batch) in OrderedUploadBatches(meshData))
|
foreach (var (format, batch) in uploadOrder)
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
if (batch.Indices.Count == 0) continue;
|
if (batch.Indices.Count == 0) continue;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue