Repository navigation
Sort flag sets so exported C is reproducible - #737
Merged
Merged
Conversation
Dragorn421
reviewed
Sep 19, 2026
GbiMacro.to_c joined set fields in iteration order. Python randomizes string hashes
per process, so exporting the same scene twice could emit
gsSPClearGeometryMode(G_SHADING_SMOOTH | G_LIGHTING),
in one run and
gsSPClearGeometryMode(G_LIGHTING | G_SHADING_SMOOTH),
in the next, with no change anywhere in the blend file. Diffing an export against
what is already committed in a decomp repo then shows noise, and there is no way to
confirm that a change to the exporter left the output alone.
Sorting is safe for these fields: they are flag unions, ORed into one word by
to_binary, so order carries no meaning. Affects SPGeometryMode, SPSetGeometryMode,
SPClearGeometryMode, SPLoadGeometryMode and DPSetOtherMode. DPSetRenderMode keeps
its own to_c, which is positional, and is untouched. Binary output never depended
on the order.
Checked on a generated corpus of 22 meshes exported to C for F3D, F3DEX2 and
F3DEX3, plus a skinned geolayout export: before the change 8 runs of the same
export produced 2 distinct outputs; after it, 8 runs produce 1. Comparing the two
trees, 130 files are identical except for 8 reordered flag commands, and
to_binary output is identical for both orders of the same flags in every affected
macro and microcode.
Taldariner
force-pushed
the
fix-geo-mode-flag-order
branch
from
September 20, 2026 08:36
d2eabbf to
0fd28a3
Compare
Dragorn421
approved these changes
Sep 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Exporting the same scene twice can produce different C files, with nothing changed in the blend file. Only the order of flags inside a command differs:
Reproduction, run it a few times (each run is a fresh process):
On my machine 6 runs printed
G_LIGHTING | G_SHADING_SMOOTHfive times andG_SHADING_SMOOTH | G_LIGHTINGonce. Exporting a corpus of test meshes, 8 runs of the same export produced 2 distinct sets of files.This matters when an export is diffed against what is already committed in a decomp repo: the diff shows changes that are not changes. It also makes it impossible to confirm that a change to the exporter left the output alone, which is what I ran into while working on
f3d_writer.py.Cause
GbiMacro.getattr_virtualjoins iterable fields with" | "in iteration order, and several macros keep their flags in aset. Python randomizes string hashes per process, so the iteration order of a set of flag names changes from run to run.Fix
Sort the values when the field is a
setorfrozenset. Lists keep their order, so nothing positional is affected.This is safe for every field it touches, because they are flag unions that
to_binaryORs into a single word:SPGeometryModeclearFlagList,setFlagListSPSetGeometryModeflagListSPClearGeometryModeflagListSPLoadGeometryModeflagListDPSetOtherModemode0,mode1DPSetRenderModehas its own positionalto_cand is not affected. Binary output never depended on the order.Import is unaffected as well:
f3d_parsersplits flag lists on|and does not care about order.Verification
A corpus of 22 generated meshes (flat and smooth, vertex colors, fog, UV seams, large texture mode, F3DEX3 packed normals and cel shading) exported to C for F3D, F3DEX2 and F3DEX3, plus a skinned geolayout export, on Blender 4.2.16 LTS:
to_binaryoutput is identical for both orders of the same flags, for every affected macro in F3D, F3DEX, F3DEX2 and F3DEX3.