Release validation checklist
Release validation is split into three layers:
- MIT addon package — the production runtime that users can ship in commercial/non-commercial projects under MIT.
- Mixed-license demo package — a consumer of that exact addon build plus the CC BY-NC 4.0 CT2Hair/GodotHair demo grooms.
- Demo release media — deterministic videos recorded from the validated demo package.
The production algorithms are already validated on development. Release validation should catch packaging, path, import, license-boundary, and presentation regressions without rerunning every historical numerical/performance benchmark.
Current development validation status
Already validated on development:
- three production quality tiers (Approx, Fast, Cinematic) plus the benchmark-only Reference validation shader;
- Static Bayer, 16-phase TAA Bayer, and A2C coverage behavior;
- viewport-aware Auto coverage policy;
- direct Fast/Cinematic
ImageTexture3DLUT loading and contract binding; - Fast eta/IOR
1.55pinning; - optical wetness interface and runtime propagation across the three production tiers and the benchmark-only Reference shader, in both normal/A2C shader families;
- wetness dry compatibility, visual progression, component ablation, and final film calibration;
- wetness GPU cost on RTX 5090 and AMD integrated graphics;
- shader-parameter documentation comments are present in the production shader sources;
- repository/runtime license decision: MIT;
- demo groom provenance/license: CT2Hair/GodotHair assets remain CC BY-NC 4.0.
The release package therefore does not need another full production benchmark before each RC unless packaging or shader math changes again.
Gate A — standalone MIT addon
A0. Generated shader drift check
The six production .gdshader wrappers and the shared Approx body include are
generated from the canonical templates in tools/templates/. Run the drift
check before any packaging step:
python3 tools/generate_hair_shaders.py --check
Expected success text:
ok: 7 generated shader files match their templates
Any missing: or drift: line means the checked-in addon shaders no longer
match their templates; regenerate with python3 tools/generate_hair_shaders.py
and re-run the check before continuing.
A1. Package from the current production sources
The release addon should contain only the runtime surface:
addons/marschner_hair/
hair_groom_data.gd
hair_material_profile.gd
hair_marschner_lut_adapter.gd
hair_coverage_policy.gd
hair_coverage_controller.gd
internal/
hair_shader_utils.gd
shaders/
hair_approx.gdshader
hair_approx_a2c.gdshader
hair_marschner_unity_fast.gdshader
hair_marschner_unity_fast_a2c.gdshader
hair_marschner_cinematic.gdshader
hair_marschner_cinematic_a2c.gdshader
...shared production includes (hair_common.gdshaderinc, hair_card_common.gdshaderinc, ...)
luts/
unity_azimuthal_64.res
cinematic_longitudinal_kernel_128x128x64.res
README.md
Every addon .gd, .gdshader, and .gdshaderinc file must ship with its
matching Godot .uid sidecar. These sidecars are part of the addon identity
and must be committed and included in the archive.
The standalone addon release should also carry the project MIT LICENSE and applicable MIT third-party notice text at the archive/release level.
Do not ship benchmark raw LUT resources, raw-data reconstruction fixtures, generated result sets, development scenes, experimental shader variants, Windows Zone.Identifier artifacts, demo models, groom maps, or generated demo videos in the MIT addon archive.
A2. Static path audit
After packaging, search the distributed addon for stale development-only paths. The addon should not reference:
res://assets/hair/
res://benchmark/
res://demos/
Every production preload/LUT path should resolve under res://addons/marschner_hair/, while shader #include paths should remain relative inside the packaged shader directory.
A3. Fresh-project import smoke
Create a fresh Godot 4.7 project containing only the release addon plus a tiny validation scene/script. Open it once in the editor before judging import errors.
Check that the global classes register:
HairMaterialProfile
HairGroomData
HairMarschnerLUTAdapter
HairCoveragePolicy
HairCoverageController
There should be no missing preload, missing include, parser, or class-registration errors.
A4. Package-level material smoke
In the fresh project, construct one complete HairGroomData using known-valid groom textures and one HairMaterialProfile.
For each quality tier, create both compiled coverage shader families:
3 quality tiers x 2 compiled coverage families = 6 variants
For every variant verify:
ShaderMaterialcreation succeeds;- the shader resource path is under
res://addons/marschner_hair/shaders/; - shared groom textures bind;
- wetness defaults bind (
0.0, film2.0 / 0.10, remaining calibrated endpoints); - setting
wetness = 1.0propagates to the resulting material; - Fast binds a valid direct 64^3 RGBA16F LUT and eta/IOR stays
1.55; - Cinematic binds a valid direct 128x128x64 R16F LUT;
- A2C requests select
_a2c.gdshadervariants.
Run this with a normal rendering context. On the validated Godot 4.7 setup, do not rely on --headless for direct ImageTexture3D validation.
A5. Force a real draw/compile of all six variants
Material/RID creation alone is not a complete shader-compile test. Render at least one frame with each of the six production variants on a small card/quad mesh under a light.
The goal is not image-quality benchmarking. Confirm only that:
- the renderer compiles the shader;
- no shader/include errors appear;
- the mesh produces visible output;
- Fast/Cinematic do not fall back because of missing LUTs;
- switching normal <-> A2C variants does not drop the wetness parameters.
A small sequential harness is preferable to opening six separate editor windows.
A6. Auto coverage smoke
In Forward+:
MSAA off, TAA off -> Static Bayer
MSAA off, TAA on -> TAA Bayer
MSAA on -> A2C
In Mobile:
MSAA on -> A2C
MSAA off -> Static Bayer
Compatibility should resolve Auto to Static Bayer.
This is a small regression check; the full coverage performance benchmark does not need to be rerun unless the coverage math changes.
A7. Editor tooltip smoke
Open one normal shader material and one A2C shader material from the packaged addon in Godot 4.7 and hover representative parameters:
wetness
wet_film_roughness
longitudinal_roughness
coords_texture
unity_azimuthal_lut (Fast)
cinematic_longitudinal_lut (Cinematic)
bayer_phase_index (normal Bayer variant)
Confirm the Inspector displays the intended descriptions and that repackaging did not strip or break the comments.
A8. Dry/wet visual sanity check
A full recalibration is unnecessary. Render one representative groom at:
wetness = 0.0
wetness = 1.0
under a broad light and a narrower/high-contrast light. Confirm:
- dry still matches the existing dry appearance;
- wet hair has a darker body plus tighter/neutral film response;
- no NaNs, white-out, or tier-specific discontinuity appears after repathing.
A9. Addon metadata
Before publishing:
- bump
VERSIONto the next RC/version; - add a changelog entry for shader-parameter tooltips, optical wetness, and licensing;
- update the release README with calibrated wetness controls and geometry-out-of-scope guidance;
- ensure the release README says LUTs are packaged and requires no LUT-generation step;
- verify all documented addon paths use
res://addons/marschner_hair/; - include the MIT license and applicable third-party MIT notice;
- state explicitly that the standalone addon contains no CC BY-NC demo groom assets.
Gate B — mixed-license demo package
Build the demo only after Gate A passes. The demo should consume the exact validated addon build, not a separately repathed copy.
B1. Addon identity
Compare the embedded demo addons/marschner_hair/** tree against the standalone addon staging tree. File names and bytes must match.
If the demo requires a different runtime source file, that difference belongs in the addon first; do not patch the demo’s private copy.
B2. License boundary audit
The demo archive must contain prominent license/notices:
LICENSE # project/addon MIT license
THIRD_PARTY_NOTICES.md
assets/hair/models/LICENSE.md # demo groom CC BY-NC 4.0 notice
Confirm:
- addon/code files are not incorrectly labeled CC BY-NC;
assets/hair/models/**is not incorrectly labeled MIT;- the demo README describes the archive as mixed-license;
- the NonCommercial restriction is visible before users treat the supplied grooms as production assets;
- no CC BY-NC groom/media file leaked into the standalone addon archive.
B3. Fresh demo import
Extract the demo archive into a new directory and open it in Godot 4.7.
There should be no missing resources, stale development paths, parser errors, shader include errors, or broken imports.
The demo should resolve runtime shader/profile/LUT dependencies through the embedded addons/marschner_hair/ copy.
B4. Demo groom smoke
The source demo contains the following ten CT2Hair/GodotHair hairstyle directories:
bangs
blowout
bob
curly
jewfro
jheri
moptop
pixie
wavy
wings
Cycle each groom once and confirm its mesh and associated groom textures import and render without missing-resource errors. One production tier is sufficient for this all-groom asset smoke; the six-variant shader matrix is already covered by Gate A.
Use Blowout as the standardized release-validation/media groom because the current profile and groom-data resources are already calibrated around it.
B5. Interactive demo controls
Confirm the interactive demo still supports its intended camera/light/hairstyle controls after packaging. In particular, verify the inherited GodotHair-style orbit camera can rotate and zoom around the head without losing framing.
Then confirm the packaged profile workflow can switch quality tiers and move wetness from 0 to 1 on the Blowout groom.
B6. Demo visual regression
With Blowout selected, compare at minimum:
Approx dry
Fast dry
Cinematic dry
Fast wetness 1.0
Cinematic wetness 1.0
This is a presentation sanity check, not a numerical revalidation. The expected direction of change must match the development results.
Gate C — release media
Record media only from the validated demo staging/extracted package, so the videos demonstrate the same addon and resources users receive.
The canonical workflow is documented in release_media.md.
Run:
python benchmark/tools/generate_release_gifs.py \
--godot /path/to/godot \
--project .
bash docs/images/make_composite_mp4s.sh
| The current release media set is the five composite previews, each a horizontal 3-panel strip (Approx | Fast Marschner | Cinematic Marschner): |
demo-video-quality-composite.mp4
demo-video-wetness-000-composite.mp4
demo-video-wetness-033-composite.mp4
demo-video-wetness-067-composite.mp4
demo-video-wetness-100-composite.mp4
The capture harness uses deterministic Movie Maker timing and the original GodotHair camera-orbit convention. Release videos use Static Bayer coverage so camera/shader comparisons are deterministic; coverage behavior itself remains covered by Gate A.
Historical note (pre-composite workflow): earlier releases captured three full-resolution clips (
quality-tiers.mp4,fast-wetness.mp4,cinematic-wetness.mp4) at 1920x1080 / 60 fps with durations of about 18 s and 10 s viabenchmark/tools/capture_release_media.py. Those clips remain attached to the PR13 demo media release as full-resolution downloads but are no longer the current capture set.
C1. Automated media checks
The GIF capture runner must reach:
RELEASE_GIFS_OK
Each composite preview is assembled from three source GIFs by docs/images/make_composite_mp4s.sh. When ffprobe is installed, verify every composite:
resolution = 1440 x 270
fps = 15
frames = 90
duration = 6 s
Source GIFs and composite MP4s live under docs/images/ and are tracked by Git; the MovieWriter OGV intermediates under benchmark/results/release_media/ are intentionally ignored by Git.
C2. Human video review
Review the final MP4s rather than only the MovieWriter intermediate.
Confirm:
- one identical full orbit is shown for every quality tier and wetness state;
- tier labels match the shader actually displayed;
- the four wetness composites show the fixed states
0.00,0.33,0.67,1.00in increasing order; - dry endpoints agree with the quality comparison apart from encoding noise;
- saturated wetness remains finite/stable;
- camera movement is smooth and returns to the starting view;
- there are no black/error frames at shader switches;
- the in-frame
CT2Hair / GodotHair — CC BY-NC 4.0attribution is readable.
Project-published videos that visibly reproduce the supplied demo groom are demo media and should be released under the CC BY-NC 4.0 demo terms, not bundled inside the MIT addon archive.
Tests that do not need to be repeated
Unless the underlying shader/coverage/LUT math changes again, the following expensive investigations are already sufficient evidence and do not need to block the next RC:
- full dual-GPU production tier matrix;
- full coverage-path performance matrix;
- LUT storage/manifest benchmark;
- production LUT byte-equality migration benchmark;
- wetness component ablation and film sweep;
- wetness 300-sample dry/wet GPU comparison.
Final release gate
MIT addon
[ ] tools/generate_hair_shaders.py --check passes ("ok: 7 generated shader files match their templates")
[ ] current production files are canonical under addons/marschner_hair
[ ] no development-only resource paths remain in the addon
[ ] fresh-project import has no parser/preload/include errors
[ ] all 6 quality/coverage variants create and render at least one frame
[ ] Fast and Cinematic direct LUTs validate in the packaged paths
[ ] Auto coverage resolves correctly for Forward+/Mobile/Compatibility smoke
[ ] one normal and one A2C shader show the expected Inspector parameter tooltips
[ ] dry/wet package-level visual sanity check passes
[ ] VERSION / CHANGELOG / release README are current
[ ] MIT LICENSE and applicable third-party MIT notice are present
[ ] no CC BY-NC demo groom/media is inside the addon archive
Demo package
[ ] embedded addon is byte-identical to the validated standalone addon
[ ] mixed-license README / LICENSE / THIRD_PARTY_NOTICES / groom LICENSE are present
[ ] fresh extracted demo project imports without missing resources
[ ] all 10 bundled hairstyle assets can be selected/rendered
[ ] interactive orbit/zoom and demo controls work
[ ] Blowout tier/wetness presentation smoke passes
Release media
[ ] five composite previews (quality + wetness 0.00/0.33/0.67/1.00) built from the validated demo package
[ ] RELEASE_GIFS_OK reached by generate_release_gifs.py
[ ] 1440x270 / 15 fps / 90 frames / 6 s metadata checks pass (when ffprobe is available)
[ ] final MP4 visual review passes
[ ] CC BY-NC attribution is visible in-frame and repeated in release text
When all three sections pass, the matching addon and demo releases are ready to publish.