Files
Cesarus85--mjolnir-vr/docs/object_matching_logs/render_contrails_obj.md
T

133 lines
7.2 KiB
Markdown

# `render_contrails.obj` exact-match log
## Outcome
`source/render/render_contrails.obj` is a strict whole-object match for the
January 14, 2002 Xbox build. All six functions match in padded size,
relocation count and identity, relocation-normalized bytes, and SHA-256. The
three target-owned string COMDATs also match exactly. The consolidated XDK
3911 build succeeds, and the shared rasterizer ABI/layout corrections preserve
the already-Matching 131-function `rasterizer.obj`.
In plain English: this file builds and traverses each live contrail, computes
the strip's two vertices per point, shades and fades them, builds its triangle
indices, and submits the completed dynamic geometry. The reconstruction uses
ordinary readable C. It contains no inline assembly, volatile codegen trick,
`__forceinline`, undefined behavior, byte patch, compiler-flag override, or
fuzzy acceptance.
## Strict evidence
| Function | Meaningful bytes | Padded bytes | Relocations | Normalized SHA-256 |
| --- | ---: | ---: | ---: | --- |
| `_contrail_fade` | 143 | 144 | 6 | `7dc3dde6b3c8f5af61facf91de6d51d91ff268eaf8173d9911a94fbc6a867f83` |
| `_code_00177860` | 1945 | 1952 | 57 | `70d28d083222f55ac171cb1aa9676d018fb2bcae9ee54f09facee54755cef5e8` |
| `_code_00178000` | 176 | 176 | 9 | `16ce1e5bee24c8a0f0e79659476d2d619b783419e9ba49d3cb2a2c71bccd94fa` |
| `_render_contrails_ground_mapped` | 9 | 16 | 1 | `1b1a80b01bf5912843c9d879bc793dd8ac269b037598c7afa05a0752095dbb5f` |
| `_render_contrails_media_mapped` | 9 | 16 | 1 | `f27c91ab09dc5b6f75808b4ae85f5e00e23c249e0ad6466b48ef6c5bc216fe06` |
| `_render_contrails_normal` | 9 | 16 | 1 | `07a8bcb483de4cb8bba2c0b5dac4ad15eb04f47cde8cb833f3b376ba44abf179` |
Total code is 2,291 meaningful bytes and 2,320 padded bytes. The target-owned
`.rdata` COMDATs are also exact:
| Data | Bytes | SHA-256 |
| --- | ---: | --- |
| `"contrail %s uses an unsupported render type."` | 45 | `38b10bfb93e324bcd9ff15b429387e6e917890cf2b008360bddc77ce09375249` |
| `"triangles && vertices"` | 22 | `6faca77985ef8484b9433bf5961df25e5c373bbb4a3515ebc9fc5a29ba2e0aa2` |
| January assert file path | 41 | `9a3ff7ea5d44b5f3e438ca53d746335c870e6add38e784766b7d21f22e4cce17` |
The ordinary visual reporter still assigns `_code_00177860` 98.45% because it
does not understand all local-label/section representations. The hardened
COFF comparator proves the 1,952-byte function and all 57 resolved relocation
destinations are identical; this is a known reporter false negative, not a
tolerance or semantic approximation.
## Recovered types and interfaces
- `contrail_point_state` is `0x68` bytes.
- `contrail_shader` places `framebuffer_fade_mode` at `+0x2c`.
- `contrail_definition` is `0x144` bytes and its `states` block is at `+0x138`.
- `contrail_datum` is `0x44` bytes.
- `contrail_point_datum` is `0x38` bytes.
- `rasterizer_globals.current_lock_operation` is a `short` at `+2`; the full
rasterizer-global structure remains `0x68` bytes.
- The corrected unlit draw ABI is `(shader, bitmap, animation,
triangle_buffer_index, vertex_buffer_index, vertex_count, centroid, flags)`.
- Typed access uses `contrail_definition_get`, `object_get`, and
`TAG_BLOCK_GET_ELEMENT`, in line with the project house rules.
## Experiment history
The object began with only the three tiny render-mode wrappers exact. The
following source shapes were accepted:
1. Expressing `contrail_fade` through the existing vector helpers reproduced
the 144-byte function; manual scalar arithmetic produced 160 bytes and was
rejected.
2. The natural `data_next_index` iterator, typed contrail tag getter, and four
instance loop reproduced `_code_00178000` at 176 bytes and nine relocs.
3. Correcting the recovered vertex, point, state, shader, definition, and
rasterizer-global layouts moved the large renderer from 1,888 bytes/58
relocs through 1,904 and 1,920-byte shapes to the target 1,952/57 topology.
4. Direct scalar eye/tangent calculations and the January cross-product
operand order reproduced the viewer-facing branch and both orientation
normalizations.
5. Moving `half_width *= 0.5f` immediately before the four texture-coordinate
stores fixed an otherwise independent three-byte scheduling window.
6. The final mismatch was only two compiler stack slots. Writing the two
loop-carried updates as `texture_u += texture_u_step;` followed by
`previous_point = point;` changed their live-range order and closed every
remaining byte without changing the algorithm.
Measured and rejected shapes included aggregate/union packing of the two loop
locals, declaration-order permutations, declaration initializers, an early
`NULL` initialization, delayed texture initialization, nested scopes, and
several alias forms. These either had no effect, changed broad register
allocation, or regressed already-correct code. They should not be repeated.
The unsupported-render-type arm retains its early `return` because that is the
January control-flow shape. This is the documented house-rule exception to the
preferred single-return style; forcing a common exit changes the exact object.
That arm also preserves an original cleanup bug: it skips the dynamic-buffer
unlock/delete calls and leaves `current_lock_operation` set. The source labels
the bug and describes the non-matching fix (route the arm through common
cleanup) without changing January behavior.
## Shared-header blast radius
The recovered layouts and corrected rasterizer ABI live in shared headers, so
the audit did not rely on Ninja's ordinary header tracking. A detached clean
worktree at campaign tip `e30b1568` and the candidate worktree each rebuilt
the same seven translation units from scratch: `render_contrails`, `contrails`,
`objects`, `rasterizer`, `ai_debug`, `cinematics`, and `main`. Comparing every
defined function with `section_infos_equal` and every non-debug data section
found zero changes outside `render_contrails.obj`.
The raw regression manifest reported debug-record, section-number, and accepted
evidence churn, including an apparent `ai_debug_render_actor` regression. That
was not a code regression: a fresh baseline build already has the same current
nonexact function, and baseline-to-candidate hardened comparison is exact.
This fresh-build cross-check prevents stale `.obj` files from being mistaken
for a patch regression while still failing closed on real code, relocation, or
owned-data changes.
## Verification commands
The final candidate was rebuilt with repository-configured XDK 3911
`CL 13.00.9254.1` and `/O2 /Oy- /DDEBUG /Dxbox`, then checked with:
```text
ninja build/base/source/render/render_contrails.obj
python -m tools.coff_compare build/split/source/render/render_contrails.obj build/base/source/render/render_contrails.obj <all six function symbols>
python -m tools.coff_compare build/split/source/render/render_contrails.obj build/base/source/render/render_contrails.obj <all three target-owned rdata symbols>
ninja halobetacache_build
ninja semantic_progress
ninja progress
python -m unittest discover -s tools -p "test*.py" -v
```
All strict comparisons returned `all_equal: true`; the consolidated build and
semantic report completed with zero errors. The progress gate admitted the
object (262/468 Halo units Matching at this checkout), and all 173 tooling
tests passed.