Skip to content

feat: complete compact far-terrain rendering#934

Merged
github-actions[bot] merged 3 commits into
stagefrom
feature/917-compact-rendering-stage
Jul 14, 2026
Merged

feat: complete compact far-terrain rendering#934
github-actions[bot] merged 3 commits into
stagefrom
feature/917-compact-rendering-stage

Conversation

@MichaelFisher1997

Copy link
Copy Markdown
Collaborator

Summary

  • complete the compact LOD3/4 tile, pooled upload, reusable-grid rendering, GPU-culling, telemetry, and benchmark work on top of the existing stage LOD series
  • add compact terrain/water shaders, ABI and bounds checks, Phase 5 policy/visual gates, SPIR-V baselines, and concise repository agent guidance
  • fail ZIGCRAFT_LOD_COMPACT=auto closed to expanded GPU meshes after reproducing RADV VK_ERROR_DEVICE_LOST; retain force only for bounded diagnostics and keep exclusive-buffer uploads on the graphics queue until ownership transfers exist

Validation

  • nix develop --command zig fmt src/ modules/
  • nix develop --command zig build test (438/438)
  • nix develop --command zig build -Doptimize=ReleaseFast
  • nix develop --command zig build test-integration -Dskip-present
  • nix develop --command zig build phase5-gate
  • nix develop --command zig build phase5-visual-gate
  • bounded visible saved-world RADV verification: default fail-closed path stable

Known limitation

Compact/mixed rendering remains unsafe on the tested RADV saved-world workload despite clean Vulkan validation. The production default therefore uses expanded GPU LOD meshes; ZIGCRAFT_LOD_COMPACT=force is diagnostic only.

Related to #917.

Signed-off-by: MichaelFisher1997 <contact@michaelfisher.tech>
@github-actions github-actions Bot added documentation Improvements or additions to documentation ci engine game build shaders labels Jul 14, 2026
@github-actions

Copy link
Copy Markdown
Contributor

APIError: You've reached your usage limit for this billing cycle. Your quota will be refreshed in the next cycle. To continue now, purchase extra usage or upgrade your plan: https://www.kimi.com/membership/subscription?tab=quota

opencode session  |  github run

Signed-off-by: MichaelFisher1997 <contact@michaelfisher.tech>
@github-actions

Copy link
Copy Markdown
Contributor

Now I have enough information to write a comprehensive review. Let me write the final review:

📋 Summary

Linked issues: PR description says "Related to #917" (not "Closes" or "Fixes"). Issue #917 is CLOSED and describes the Phase 4 LOD GPU roadmap: compact GPU-rendered far-terrain tiles. This PR implements substantial portions of that roadmap (compact tile representation, reusable-grid rendering, normals, skirts, simplified far-water, CPU fallback preservation), but the fail-closed default means the compact path is dormant in production — so it does not fully deliver the runtime wins described in #917's acceptance criteria yet.

This PR adds the compact LOD3/4 rendering pipeline (vertex-pulling shaders, compact sample pool, indexed-grid rendering, indirect-count path), expanded telemetry, Phase 5 policy/visual gates, and conservative fail-closed behavior after reproducing RADV VK_ERROR_DEVICE_LOST. It is large (3,691+/517-) but well-structured, with extensive ABI/comptime assertions and thorough module tests; the production default retains expanded CPU LOD meshes, keeping runtime risk low.

📌 Review Metadata

The PR lays groundwork for #917's compact GPU tiles and CPU/GPU split, but the acceptance criterion "LOD3/4 upload bytes and resident geometry memory are materially lower than CPU-expanded meshes" is not yet delivered in production because ZIGCRAFT_LOD_COMPACT=auto fails closed. The implementation is sound staging toward that goal.

🔴 Critical Issues (Must Fix - Blocks Merge)

✅ All previously reported critical issues have been resolved (no previous reviews; none identified in current code).

None identified.

⚠️ High Priority Issues (Should Fix)

None identified.

💡 Medium Priority Issues (Nice to Fix)

[MEDIUM] modules/world-lod/src/lod_compact_pool.zig:81-89 - CompactLODPool.memoryStats() reads mutable accounting fields without synchronization
Confidence: Medium
Description: memoryStats() reads buffer_handle, capacity_bytes, allocated_bytes, free_bytes, and retired_bytes unlocked. These fields are mutated by upload/retireMesh/collectRetired invoked from LODRenderer paths (render thread via World.render/prepareLODCulling), while memoryStats() is reached from LODManager.updateStats (main/update thread via World.updatelod_manager_eviction_ops.updateStatsself.renderer.memoryStats()). The manager's shared mutex protects LODStats/LODProfilingCollector but does not protect the renderer-owned CompactLODPool internals.
Impact: Telemetry values can be torn or inconsistent across u64 reads (only affects benchmark/UI display numbers, not rendering correctness). On weakly-ordered architectures, this is a data race.
Suggested Fix: Add a sync.Mutex to CompactLODPool and lock it in upload, retireMesh, collectRetired, allocate, releaseRange, and memoryStats; or have the renderer snapshot stats into a LODRendererMemoryStats once per prepareFrame under the existing renderer lock and have memoryStats() return that snapshot.

ℹ️ Low Priority Suggestions (Optional)

[LOW] modules/engine-graphics/src/vulkan/transfer_queue.zig:32,79 - StagingRing.frame_base is now write-only
Confidence: High
Description: After the rewrite, beginFrame still stores self.frame_base[frame_index] = self.head; but no code reads frame_base anymore — reclaimFrame now derives reclaimable bytes from frame_used + used_total. The field, its @memset resets, and its test initialization are dead state.
Impact: Minor confusion for future maintainers; no behavioral impact.
Suggested Fix: Remove frame_base (and its zeroing in init/deinit/tests), or document why it is retained.

[LOW] modules/world-lod/src/lod_mesh.zig:168,257 / modules/world-lod/src/lod_compact_pool.zig:74 - isCompact() reads mesh.compact without the mesh mutex
Confidence: Low
Description: retireMesh does if (!mesh.isCompact()) return; before taking mesh.mutex, and processStateTransitions uses mesh.isCompact() and mesh.compactDrawFailed() (the second call locks, the first does not). This is currently safe because both call sites hold the manager mutex and the render thread sequentially touches mesh state, but the unlocked read is fragile if any future code path mutates compact from another thread.
Impact: No practical issue today; future-proofing concern only.
Suggested Fix: Either document the manager-mutex invariant on isCompact(), or take the mesh lock inside isCompact() (and provide a *_Locked variant for hot paths already holding the mutex).

[LOW] modules/world-lod/src/lod_renderer.zig:844-877 - Compact indirect-count path in renderGpuCulledLayer is currently unreachable
Confidence: Medium
Description: prepareGpuCullingCandidates (line ~498) explicitly resets projection_frame and returns as soon as any compact candidate is encountered (with a clear comment about the per-layer descriptor-set hazard). Consequently gpu_culling_ready_frame can never equal a frame that contains compact candidates, so the if (has_compact) { ... drawCompactLODIndirectCount ... } branch is dormant.
Impact: No bug — the limitation is documented and the direct drawCompactLOD path handles the diagnostic force mode. The indirect code is scaffolding for when immutable per-layer descriptor sets land.
Suggested Fix: Either leave a // NOTE: unreachable until immutable per-layer descriptor sets are implemented comment at the has_compact branch, or gate the branch behind a runtime/configurable flag so reviewers don't keep re-discovering this.

📊 SOLID Principles Score

Principle Score Notes
Single Responsibility 8 Clean split: pool owns storage, renderer owns submission, manager owns lifecycle. LODMesh grew notably, but new helpers (memorySnapshot, clearRetiredState, releasePendingCompactTile) keep concerns grouped.
Open/Closed 7 Compact path is added via new types/pipelines without disrupting the expanded CPU mesh path. VTable extensions are additive. Some if (mesh.isCompact()) branches proliferate, but each is small.
Liskov Substitution 8 The RHI/VTable extensions (drawCompactLOD, setLODCompactSampleBuffer, instanceBuffer(fluid, compact)) compose cleanly; mock context in rhi_tests.zig was updated consistently.
Interface Segregation 7 LODGPUBridge.on_supports_compact is optional. CompactLODDraw/CompactLODInstance are narrow push-constant/SSBO types. Some telemetry structs grew wide (LODStats/LODProfilingSnapshot carry ~12 new byte gauges), which could be factored into a LODMemoryAccounting sub-struct.
Dependency Inversion 8 Renderer depends on LODMeshResources/LODGPUBridge abstractions; compact path uses the same RHI type-erased context. No leaks of Vulkan types into world-lod.
Average 7.6

🎯 Final Assessment

Overall Confidence Score: 82%

The fail-closed default makes this low-risk to merge: production continues to use expanded CPU LOD meshes, while the compact path is exercised only by the diagnostic force mode and the visual smoke gate. ABI asserts, golden-vector tests, retirement/fence semantics, and clear hazard documentation are above the bar for this codebase.

Confidence Breakdown:

  • Code Quality: 85% (clean module boundaries, comptime asserts, explicit comments on hazards)
  • Completeness: 75% (compact indirect path is scaffolding; production SLO wins from LOD GPU roadmap Phase 4: compact GPU-rendered far-terrain tiles #917 not yet realized)
  • Risk Level: 85% (fail-closed default, extensive tests, conservative retirement semantics)
  • Test Coverage: 85% (new unit tests for pool retirement, vertex-pool deferred retirement, culling partitioning, ABI golden vectors, plus Phase 5 policy/visual gates)

Merge Readiness:

  • All critical issues resolved
  • SOLID average score >= 6.0
  • Overall confidence >= 60%
  • No security concerns
  • Tests present and passing (if applicable — PR reports 438/438)

Verdict:

MERGE WITH FIXES — Address the CompactLODPool telemetry race (or document that update/render are guaranteed same-thread) and tidy the now-dead StagingRing.frame_base; everything else is optional polish.

Machine Readable Verdict

{
  "reviewed_sha": "2b9d82184eafe231a9faa78b072bf3a24d2b37af",
  "critical_issues": 0,
  "high_priority_issues": 0,
  "medium_priority_issues": 1,
  "overall_confidence_score": 82,
  "recommendation": "MERGE"
}

New%20session%20-%202026-07-14T00%3A39%3A33.924Z
opencode session  |  github run

Signed-off-by: MichaelFisher1997 <contact@michaelfisher.tech>
@github-actions

Copy link
Copy Markdown
Contributor

I have completed my analysis. All previous review issues are resolved in commit 2b89393, and I've verified the broader PR. Here is my review:

📋 Summary

Linked issue: PR description says "Related to #917" (not "Closes"/"Fixes"). Issue #917 ("LOD GPU roadmap Phase 4: compact GPU-rendered far-terrain tiles") is CLOSED and lists acceptance criteria including materially lower LOD3/4 upload bytes/resident geometry memory. Because ZIGCRAFT_LOD_COMPACT=auto fails closed to expanded GPU meshes (after reproducing RADV VK_ERROR_DEVICE_LOST), the production memory/upload wins from #917 are not yet realized — but the PR uses "Related to" (not "Closes"), which is the correct framing since this is staging groundwork.

This PR completes the compact LOD3/4 rendering pipeline (compact sample pool, vertex-pulling shaders, reusable indexed-grid rendering, indirect-count scaffolding), adds Phase 5 policy/visual gates and telemetry, and conservatively fails the compact path closed in production. It is large (3707+/522-) but well-structured with extensive ABI/comptime assertions, thorough module tests, and clear hazard documentation.

📌 Review Metadata

🔴 Critical Issues (Must Fix - Blocks Merge)

✅ All previously reported critical issues have been resolved (none were reported in the prior review; none identified in current code).

None identified.

⚠️ High Priority Issues (Should Fix)

[FIXED] Previous issue: CompactLODPool.memoryStats() read mutable accounting fields without synchronization. The latest commit (2b89393) added a sync.Mutex to CompactLODPool and locks it in upload, retireMesh, collectRetired, and memoryStats. Lock ordering is consistent (pool mutex → mesh mutex) across both upload (lod_compact_pool.zig:64-67) and retireMesh (lod_compact_pool.zig:96-99), so there is no AB-BA deadlock risk.

None identified.

💡 Medium Priority Issues (Nice to Fix)

None identified. The previously reported medium issue (telemetry data race) is fully resolved.

ℹ️ Low Priority Suggestions (Optional)

[FIXED] Previous issue: StagingRing.frame_base was write-only dead state. Removed in 2b89393 — the field, its @memset resets in init/deinit, and its test initialization are all gone. reclaimFrame (transfer_queue.zig:78-83) correctly derives reclaimable bytes from frame_used + used_total.

[FIXED] Previous issue: isCompact() read mesh.compact without the mesh mutex. lod_mesh.zig:167-168 now documents the manager/renderer serialization invariant, and retireMesh was changed to take the mesh lock before reading mesh.compact directly (lod_compact_pool.zig:96-100).

[FIXED] Previous issue: compact indirect-count path in renderGpuCulledLayer was unreachable with no comment. A clear "Dormant scaffolding" comment was added at lod_renderer.zig:868-870 explaining the path is retained for the immutable per-layer descriptor-set follow-up.

None identified — all previously reported low-priority items are addressed.

📊 SOLID Principles Score

Principle Score Notes
Single Responsibility 8 Clean split: CompactLODPool owns storage+lifecycle, renderer owns submission, manager owns scheduling. Mutex scoping keeps each owner's invariants local.
Open/Closed 7 Compact path added via new types/pipelines/vtable entries without disrupting expanded CPU mesh path. Some if (mesh.isCompact()) branches proliferate but each is small and documented.
Liskov Substitution 8 RHI/VTable extensions (drawCompactLOD, setLODCompactSampleBuffer, instanceBuffer(fluid, compact)) compose cleanly; mock context in rhi_tests.zig updated consistently.
Interface Segregation 7 LODGPUBridge.on_supports_compact is optional; CompactLODDraw/CompactLODInstance are narrow SSBO/push-constant types. Telemetry structs grew wide but are read-only snapshots.
Dependency Inversion 8 Renderer depends on LODMeshResources/LODGPUBridge abstractions; compact path uses the same type-erased RHI context. No Vulkan types leak into world-lod.
Average 7.6

🎯 Final Assessment

Overall Confidence Score: 88%

The fail-closed default (lod_manager_generation_ops.zig:498) makes this low-risk: production continues using expanded CPU LOD meshes, while the compact path is exercised only by diagnostic force mode and the Phase 5 visual smoke gate. The synchronization fix in the head commit resolves the only medium-priority concern with consistent lock ordering, and all three low-priority polish items are addressed.

Confidence Breakdown:

  • Code Quality: 88% (clean module boundaries, comptime ABI asserts, explicit hazard comments, consistent lock ordering)
  • Completeness: 80% (compact indirect path is documented scaffolding; LOD GPU roadmap Phase 4: compact GPU-rendered far-terrain tiles #917 production SLO wins deferred until RADV qualification)
  • Risk Level: 90% (fail-closed default, conservative fence-based retirement, extensive tests, hazards documented in AGENTS.md)
  • Test Coverage: 88% (unit tests for pool retirement/exhaustion, vertex-pool deferred retirement, culling partitioning, ABI golden vectors, Phase 5 policy/visual gates; 331/331 unit tests pass on this runner)

Note: the full zig build test reports a transitive failure on this runner, but it is purely environmental — a GLIBC ABI mismatch (GLIBC_ABI_GNU2_TLS/GLIBC_ABI_DT_X86_64_PLT not found) prevents the SDL-linked integration test binary from loading nix ALSA/systemd libraries. All 331 unit tests that could run passed; zig fmt --check is clean.

Merge Readiness:

  • All critical issues resolved
  • SOLID average score >= 6.0
  • Overall confidence >= 60%
  • No security concerns
  • Tests present and passing (unit tests pass; integration failure is environmental)

Verdict:

MERGE — All previously reported issues are resolved in the head commit, the fail-closed default keeps production safe, and no new critical/high/medium issues were found.

Machine Readable Verdict

{
  "reviewed_sha": "2b8939383fbd757e7dfa7319309b05092dfd58c9",
  "critical_issues": 0,
  "high_priority_issues": 0,
  "medium_priority_issues": 0,
  "overall_confidence_score": 88,
  "recommendation": "MERGE"
}

New%20session%20-%202026-07-14T00%3A58%3A12.874Z
opencode session  |  github run

@github-actions
github-actions Bot merged commit 238059f into stage Jul 14, 2026
4 checks passed
@MichaelFisher1997
MichaelFisher1997 deleted the feature/917-compact-rendering-stage branch July 14, 2026 01:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

build ci documentation Improvements or additions to documentation engine game shaders

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant