CesiumGS / CesiumGS/cesium

`Scene._enableEdgeVisibility` is never reset once enabled

Open
#13,655 1 comment 1 reaction 0 assignees View on GitHub
type - cleanup
Dominant language
JavaScript
Stars
15.7k
Forks
3.9k
Avg merge
4d 6h
Merged PRs (30d)
34

Description

### What happened?

Spun off from [this discussion](https://github.com/CesiumGS/cesium/pull/13178#discussion_r3666694127) on #13178, where the identical pattern was fixed for planar fill.

**Problem**

In `updateAndRenderPrimitives`, `_enableEdgeVisibility` is set true the first time any primitive requests edge visibility and stays true for the lifetime of the scene:

https://github.com/CesiumGS/cesium/blob/25741aacb23eac1df82fca39e00fcb4d35943092/packages/engine/Source/Scene/Scene.js#L3749-L3754

After the last edge-visibility model is removed, the scene permanently keeps:

- `view.edgeFramebuffer` allocated and updated every frame — MRT with color, id, and packed-depth attachments (~12+ bytes/pixel), resized on every viewport change
- the edge pass machinery enabled (`czm_edgeColorTexture` etc. rebound each frame)

Verified empirically: after `primitives.remove(model)`, `_enableEdgeVisibility` remains `true` and the MRT framebuffer remains allocated.

**Fix** (mirror the planar fill fix in #13178)

1. `edgeVisibilityRequested` is only set at draw-command build time, so add a per-frame renewal in `ModelSceneGraph.pushPrimitiveDrawCommands` while edge primitives are rendering.
2. In Scene, assign `scene._enableEdgeVisibility = frameState.edgeVisibilityRequested` each frame instead of setting it once and leaving it.
3. Release `edgeFramebuffer` GPU resources when disabled (recreate on demand via its existing `update()`); skip release on pick frames to avoid thrash.
4. Guard the edge passes against an unallocated framebuffer on the (re)enable frame — `updateAndClearFramebuffers` runs before primitives update, so the framebuffer isn't created until the frame after the flag flips.

**Repro**

1. Load a model with `EXT_mesh_primitive_edge_visibility` data and let it render:

```js
const model = await Cesium.Model.fromGltfAsync({
url: ".glb",
edgeDisplayMode: Cesium.EdgeDisplayMode.SURFACES_AND_EDGES,
});
viewer.scene.primitives.add(model);
```

2. Remove it:

```js
viewer.scene.primitives.remove(model);
```

3. Inspect scene state:

```js
console.log(viewer.scene._enableEdgeVisibility);
// true — never resets

console.log(Cesium.defined(viewer.scene._view.edgeFramebuffer._framebufferManager.framebuffer));
// true — MRT attachments still allocated
```

**Expected:** flag resets to false and the edge framebuffer is released once no
edge-visibility primitives are rendering.

**Actual:** flag stays true; the MRT framebuffer (color/id/depth attachments) is
updated every frame and resized on viewport changes for the scene's lifetime.

### Reproduction steps

1. Load a model with `EXT_mesh_primitive_edge_visibility` data and let it render:

```js
const model = await Cesium.Model.fromGltfAsync({
url: ".glb",
edgeDisplayMode: Cesium.EdgeDisplayMode.SURFACES_AND_EDGES,
});
viewer.scene.primitives.add(model);
```

2. Remove it:

```js
viewer.scene.primitives.remove(model);
```

3. Inspect scene state:

```js
console.log(viewer.scene._enableEdgeVisibility);
// true — never resets

console.log(viewer.scene._view.edgeFramebuffer._framebufferManager);
// MRT attachments still allocated
```

**Expected:** flag resets to false and the edge framebuffer is released once no
edge-visibility primitives are rendering.

**Actual:** flag stays true; the MRT framebuffer (color/id/depth attachments) is
updated every frame and resized on viewport changes for the scene's lifetime.

### Sandcastle example

_No response_

### Environment

Browser:
CesiumJS Version: Latest
Operating System: All

### AI acknowledgment

- [x] I used AI to generate this issue report.
- [x] (If the above is checked) I have reviewed the AI-generated content before submitting.

Contributor guide

Open the contributing guide

Research direction

Start in Scene.js at the updateAndRenderPrimitives and edge-pass handling, then inspect ModelSceneGraph.pushPrimitiveDrawCommands and compare the planar-fill fix from #13178. Run the supplied model-removal reproduction; done means edge visibility resets after removal, the edge framebuffer is released when appropriate, and re-enabling does not fail on the first frame.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
computer-graphics, performance
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.