Skip to content

fix(rendering): retire mesh geometry slot on actor delete - #640

Merged
JeanPhilippeKernel merged 4 commits into
developfrom
fix/mesh-geometry-retirement
Aug 21, 2026
Merged

fix(rendering): retire mesh geometry slot on actor delete#640
JeanPhilippeKernel merged 4 commits into
developfrom
fix/mesh-geometry-retirement

Conversation

@JeanPhilippeKernel

Copy link
Copy Markdown
Owner

Closes #639

What

When a mesh actor was deleted from the outliner, RemoveMeshInstance removed the draw-indirect entry but never freed the MeshSlot in the RRM. The slot index was permanently consumed for the session — repeated import-and-delete cycles would silently exhaust MAX_BUFFERS.

Changes

  • RenderResourceManager::ReleaseMeshGeometry(uuid) — frees the MeshSlot (sets Generation=0 so AllocMeshSlot can reuse it) and removes the entry from m_uuid_to_buffer. The VB/IB bytes are not reclaimed (append-only buffer) but the slot is available for future UploadMesh calls.

  • RenderScene::RemoveMeshInstance — checks if any remaining instance shares the same MeshUUID before releasing; only calls ReleaseMeshGeometry when the deleted instance was the last reference to that mesh (handles multiple instances of the same asset).

  • HierarchyViewUIComponent — passes ctx->RenderResourceManager to RemoveMeshInstance at delete time.

  • RenderScene.h — forward-declares RenderResourceManager to avoid pulling the full header into every TU that includes RenderScene.h.

Known limitation

VB/IB bytes are not reclaimed — the global geometry buffer is append-only. Freed slots can be reused for new uploads but the original byte range in the buffer remains occupied. A future compaction pass would be needed to fully reclaim that space (tracked separately).

Test plan

  • Import a mesh, delete actor from outliner — [RRM] Released mesh geometry slot log appears
  • Import same mesh again after delete — uploads to a new slot without capacity error
  • Import two instances of same mesh, delete one — geometry is NOT released (second instance still exists)
  • Delete second instance — geometry IS released

… rewrite

Rendering:
- Register GbufferPass in render graph; was outputting black via dead BasePass
- Fix magenta background: shaderInt64 device feature not enabled, GbufferPass
  Execute was returning before BeginRenderPass when no mesh in scene
- Split vertex_common.glsl / fragment_common.glsl into geometry_bindings.glsl,
  texture_bindings.glsl, material.glsl, draw.glsl, draw_types.glsl, surface.glsl
  to stop Int64/sampler extensions leaking into geometry-only shader stages
- Redesign RenderPass API: binding-number reflection, BindingsByName O(1) map,
  arena-safe SetName/SetPipelineName, pipeline+shader name in error messages
- Fix ImGUIRenderer: remove stale SetSampler("LinearWrapSampler") call

Asset reload (second launch):
- IsAssetExtension: add .zemesh and .zematerial so VFSScanner processes them
- InferTypeFromExtension: map .zematerial -> MATERIAL (was falling through to MESH)
- ImguiLayer: set Scanner.SetAssetRegistry and post startup TriggerScan
- Add AssetManager::ReloadFromDisk — deserializes each unloaded MATERIAL/MESH
  record from disk and calls IngestMaterial/IngestMesh
- Fix pre-existing buffer overflow in IngestMesh: sizeof(AssetNodeHierarchy)
  used instead of sizeof(NodeHierarchy) for the Hierarchies memcpy, overflowing
  308 KB past the allocated block and corrupting the NodeNames hash map

Containers:
- Rewrite UnorderedHashMap: power-of-2 capacity, linear probing with bitmask,
  Array::init(cap,cap) + secure_memset(0), guard_load assertion at 75%
- Fix secure_memset arg order: (dst, value, count, dst_size) was swapped,
  leaving all entry state bytes as garbage instead of EntryState::Empty
- Rewrite HashMap (ordered): same fixes; per-slot default construction since
  prev/next must be size_type(-1), not 0
- 55 edge-case tests: power-of-2, tombstone reuse, collision cluster, large
  batch, ordered insertion invariants, sort_keys idempotent
When a mesh actor was deleted from the outliner RemoveMeshInstance removed
the draw-indirect entry but never freed the MeshSlot in the RRM, so the
slot index was permanently consumed for the session.

- RenderResourceManager::ReleaseMeshGeometry(uuid): frees the MeshSlot
  (sets Generation=0 so AllocMeshSlot can reuse it) and removes the entry
  from m_uuid_to_buffer. The VB/IB bytes are not reclaimed — the global
  geometry buffer is append-only — but the slot is available for reuse.

- RenderScene::RemoveMeshInstance: checks if any remaining instance shares
  the same MeshUUID before releasing; only calls ReleaseMeshGeometry when
  the deleted instance was the last reference to that mesh.

- HierarchyViewUIComponent: passes ctx->RenderResourceManager to
  RemoveMeshInstance so the slot is freed at delete time.

- RenderScene.h: forward-declares RenderResourceManager to avoid pulling
  the full header into every TU that includes RenderScene.h.
@JeanPhilippeKernel JeanPhilippeKernel self-assigned this Aug 21, 2026
@JeanPhilippeKernel JeanPhilippeKernel added area-rendering area-linux Work on Linux system area-window Work on Window system area-macOS Work on macOS system labels Aug 21, 2026
@JeanPhilippeKernel
JeanPhilippeKernel merged commit 7340303 into develop Aug 21, 2026
33 of 34 checks passed
@JeanPhilippeKernel
JeanPhilippeKernel deleted the fix/mesh-geometry-retirement branch August 21, 2026 05:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-linux Work on Linux system area-macOS Work on macOS system area-rendering area-window Work on Window system

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(rendering): retire mesh geometry from RRM global buffers on actor delete

1 participant