Grid Layer Dirty Flags and RenderTexture Caching #148

Closed
opened 2025-11-29 02:21:31 +00:00 by john · 0 comments
Owner

Overview

Apply the #144 dirty flag pattern to Grid layers. Each layer caches its rendered output to a RenderTexture and only re-renders when content changes.

Problem

Current Grid re-renders all content every frame:

  • 100×100 grid = 10,000 tile draws per frame
  • Static tile maps waste 99% of render time
  • Entity movement triggers full grid re-render

Solution

Per-layer dirty tracking and RenderTexture caching:

Layer {
    data: [cell values...]
    render_texture: sf::RenderTexture  // Cached output
    dirty: bool                         // Need re-render?
}

Grid::render() {
    for layer in layers (sorted by z_index):
        if layer.z_index < 0:  // Below entities
            if layer.dirty:
                layer.render_to_texture()
                layer.dirty = false
            blit(layer.render_texture)
    
    render_entities()  // Always rendered (they move)
    
    for layer in layers:
        if layer.z_index >= 0:  // Above entities
            if layer.dirty:
                layer.render_to_texture()
                layer.dirty = false
            blit(layer.render_texture)
}

Dirty Flag Triggers

Mark layer dirty when:

  • layer.set(x, y, value) - cell content changes
  • layer.texture changed (for TileLayers)
  • Layer added/removed from grid

Do NOT mark dirty for:

  • grid.center changes (viewport pan) - just blit different region
  • grid.zoom changes - scale the cached texture
  • Entity movement - entities render separately

Expected Performance

Scenario Before After
Static 100×100 tilemap 2.3ms/frame ~0.1ms/frame
Entity-only movement 2.3ms/frame ~0.2ms/frame (entities only)
Single tile change 2.3ms/frame ~0.1ms + 1 tile

Implementation

  1. Add sf::RenderTexture and bool dirty to layer classes
  2. Add markDirty() method to layers
  3. Modify layer.set() to call markDirty()
  4. Update Grid::render() to use cached textures
  5. Handle viewport (center/zoom) as texture blit, not re-render

Relationship to Other Issues

  • Depends on #147: Layer data structures must exist first
  • Enables #123: Sub-grid chunks are per-layer cached textures at smaller scale
  • Mirrors #144: Same pattern applied to Grid that Frame now uses

Definition of Done

  • Each layer has RenderTexture cache
  • Dirty flag triggers on content modification
  • Viewport changes don't trigger re-render
  • Benchmark shows improvement for static grids
  • Entity movement doesn't trigger layer re-render
## Overview Apply the #144 dirty flag pattern to Grid layers. Each layer caches its rendered output to a RenderTexture and only re-renders when content changes. ## Problem Current Grid re-renders all content every frame: - 100×100 grid = 10,000 tile draws per frame - Static tile maps waste 99% of render time - Entity movement triggers full grid re-render ## Solution Per-layer dirty tracking and RenderTexture caching: ``` Layer { data: [cell values...] render_texture: sf::RenderTexture // Cached output dirty: bool // Need re-render? } Grid::render() { for layer in layers (sorted by z_index): if layer.z_index < 0: // Below entities if layer.dirty: layer.render_to_texture() layer.dirty = false blit(layer.render_texture) render_entities() // Always rendered (they move) for layer in layers: if layer.z_index >= 0: // Above entities if layer.dirty: layer.render_to_texture() layer.dirty = false blit(layer.render_texture) } ``` ## Dirty Flag Triggers **Mark layer dirty when:** - `layer.set(x, y, value)` - cell content changes - `layer.texture` changed (for TileLayers) - Layer added/removed from grid **Do NOT mark dirty for:** - `grid.center` changes (viewport pan) - just blit different region - `grid.zoom` changes - scale the cached texture - Entity movement - entities render separately ## Expected Performance | Scenario | Before | After | |----------|--------|-------| | Static 100×100 tilemap | 2.3ms/frame | ~0.1ms/frame | | Entity-only movement | 2.3ms/frame | ~0.2ms/frame (entities only) | | Single tile change | 2.3ms/frame | ~0.1ms + 1 tile | ## Implementation 1. Add `sf::RenderTexture` and `bool dirty` to layer classes 2. Add `markDirty()` method to layers 3. Modify `layer.set()` to call `markDirty()` 4. Update Grid::render() to use cached textures 5. Handle viewport (center/zoom) as texture blit, not re-render ## Relationship to Other Issues - **Depends on #147**: Layer data structures must exist first - **Enables #123**: Sub-grid chunks are per-layer cached textures at smaller scale - **Mirrors #144**: Same pattern applied to Grid that Frame now uses ## Definition of Done - [ ] Each layer has RenderTexture cache - [ ] Dirty flag triggers on content modification - [ ] Viewport changes don't trigger re-render - [ ] Benchmark shows improvement for static grids - [ ] Entity movement doesn't trigger layer re-render
john closed this issue 2025-11-29 04:28:20 +00:00
Sign in to join this conversation.
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: john/McRogueFace#148
No description provided.