fix(content): S1 review round - retail default sides shape, mask hoist, upload order
Campaign OVERHAUL S1 review fixes (two Opus lens reviews, findings verified by the lead against the decomp): - a raw sides_type outside 0/1/2 constructs retail's default single-side shape (ConstructMesh @0x0059DFA0 loop bounds default to 1) instead of dropping the polygon; still counted as a data anomaly (corpus has none); - the positive-surface stippling mask OR runs once per polygon before the degenerate-fan guard, as retail's count loop does (pseudo-C 426859-426866); - untextured slots keep their mask accounting but bake no texture and no vertices (contract §9 item 3); the dev pak shrinks by 114 KB; - failed surface-override / Surface / texture-dependency lookups are attempted and logged once per slot, not once per candidate; - cell-shell batches upload in ascending source surface index, retail's built-EnvCell subset draw order (ConstructMesh attribute-range scan, DrawMesh @0x0059D4A0); ordinary GfxObj meshes keep storage order; - CellMesh.HasDrawableGeometry documented as the admission rule without texture-dependency resolution (a conservative superset of emission); - the stippling/surface equivalence sweep's cell half is pinned at zero again; InAscendingSurfaceOrder is marked bake/upload/test-only; - plan §5: reviewer findings are verified by the lead, one skeptic at most for a blocking finding, never more than five agents per step. Three new Content tests pin the mask hoist, the vertex-free untextured slot, and the single-side fallback. Content 213/213, Core Meshing and Conformance green, App hermetic 6,757/6,757, Release build 0/0. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
0840d5fb77
commit
e2543d0ef0
10 changed files with 265 additions and 73 deletions
|
|
@ -2041,6 +2041,34 @@ namespace AcDream.App.Rendering.Wb
|
|||
|
||||
#region Private: GPU Upload
|
||||
|
||||
/// <summary>
|
||||
/// Upload-time batch order for one prepared mesh. Ordinary GfxObj
|
||||
/// meshes keep the (Width,Height,Format) storage grouping. A cell-shell
|
||||
/// mesh (any batch with <see cref="TextureBatchData.IsCellShell"/>) is
|
||||
/// ordered by ascending <see cref="TextureBatchData.SourceSurfaceIndex"/>,
|
||||
/// retail's built-EnvCell subset draw order (contract §3.6:
|
||||
/// <c>D3DPolyRender::ConstructMesh</c> @0x0059DFA0 emits one attribute
|
||||
/// range per nonempty surface index ascending, and
|
||||
/// <c>DrawMesh</c> @0x0059D4A0 draws them in that order). Runs once per
|
||||
/// mesh upload; the allocation is not per-frame.
|
||||
/// </summary>
|
||||
private static IEnumerable<((int Width, int Height, TextureFormat Format) Format, TextureBatchData Batch)> OrderedUploadBatches(ObjectMeshData meshData)
|
||||
{
|
||||
var pairs = new List<((int Width, int Height, TextureFormat Format), TextureBatchData)>();
|
||||
bool cellShell = false;
|
||||
foreach (var (format, batches) in meshData.TextureBatches)
|
||||
{
|
||||
foreach (var batch in batches)
|
||||
{
|
||||
pairs.Add((format, batch));
|
||||
cellShell |= batch.IsCellShell;
|
||||
}
|
||||
}
|
||||
if (cellShell)
|
||||
return pairs.OrderBy(p => p.Item2.SourceSurfaceIndex); // stable: OrderBy preserves storage order on ties
|
||||
return pairs;
|
||||
}
|
||||
|
||||
private ObjectRenderData? UploadGfxObjMeshData(ObjectMeshData meshData)
|
||||
{
|
||||
if (meshData.Vertices.Length == 0) return null;
|
||||
|
|
@ -2111,20 +2139,18 @@ namespace AcDream.App.Rendering.Wb
|
|||
}
|
||||
|
||||
// OH2/S1 (docs/research/2026-09-01-overhaul/oh2-cellstruct-surface-contract.md
|
||||
// §3.6 point 5): for a cell-shell mesh (batch.IsCellShell true),
|
||||
// this dictionary walk groups by (Width,Height,Format) for
|
||||
// atlas/storage packing only — it is NOT retail's ascending
|
||||
// source-surface-index subset draw order. The resulting
|
||||
// `renderBatches` list order is what EnvCellRenderer later
|
||||
// replays as draw order. Recovering the exact retail order
|
||||
// requires walking AcDream.Content.CellSurfaceSubsets.
|
||||
// InAscendingSurfaceOrder(meshData) instead; no consumer does
|
||||
// that yet (OH7's ordered draw stream is the intended owner) so
|
||||
// this upload path still does not preserve retail's subset
|
||||
// order for cell shells.
|
||||
foreach (var (format, batches) in meshData.TextureBatches)
|
||||
// §3.6): TextureBatches groups by (Width,Height,Format) for
|
||||
// atlas/storage packing only. For a cell-shell mesh the
|
||||
// `renderBatches` list order is what EnvCellRenderer replays
|
||||
// as subset draw order, and retail draws built-EnvCell
|
||||
// subsets in ascending source surface-array index
|
||||
// (ConstructMesh @0x0059DFA0 attribute-range scan, DrawMesh
|
||||
// @0x0059D4A0 ascending subset loop). So cell-shell batches
|
||||
// are uploaded in that order; ordinary GfxObj meshes keep the
|
||||
// storage order. This runs once per mesh at upload time, not
|
||||
// per frame.
|
||||
foreach (var (format, batch) in OrderedUploadBatches(meshData))
|
||||
{
|
||||
foreach (var batch in batches)
|
||||
{
|
||||
if (batch.Indices.Count == 0) continue;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue