Cached Surface Layer
Hs2dCachedSurfaceLayer is a custom world-space cache. It creates one world-sized surface, calls a paint callback until it succeeds, and then uses CachedWorldViewportRenderer to crop and scale the active camera viewport to the screen. Once painted, normal frames are viewport blits.
Use cached surfaces for expensive static world content that does not fit tile-layer semantics. They are the correct alternative to raw draw loops for static custom backgrounds, large boards, generated terrain, or pre-rendered world decoration.
When To Use
Use a cached surface when:
| Good fit | Better alternative |
|---|---|
| Static custom world art that is expensive to draw repeatedly. | Static tile for grid terrain. |
| Generated background or board painted once from many primitives. | Cached chunk tile for large sparse tile maps. |
| Content may be unavailable on the first frame and can retry painting. | Direct/surface parallax for moving background strips. |
If content changes during gameplay, either rebuild the cached layer around explicit dirty events or add a purpose-built dirty region strategy. Do not repaint the whole surface every frame.
Construction Pattern
const terrainCache = world.addCachedSurfaceLayer({
id: 'terrain-cache',
worldWidth,
worldHeight,
surfaceClearColor: 0x00000000,
viewportClearColor: 0x000000ff,
alphaEnable: true,
paint: (surface, helpers) => {
const atlas = this.bitmapCache.getOrCreate(this.terrainAtlasUri);
if (!atlas) return false;
const rock = helpers.createRegion(atlas, 0, 0, 64, 64);
for (let i = 0; i < rocks.length; i++) {
surface.DrawObject(rocks[i].x, rocks[i].y, rock);
}
return true;
},
});
Returning false defers painting. The layer retries on later compose calls until the callback returns true.
Update, Render, And Compose Behavior
| Phase | Behavior |
|---|---|
| Attach | No immediate painting is required. |
| Paint | ensurePainted creates the world surface, clears it, and calls paint(surface, helpers) until it returns true. |
| Update/render | No normal update or render work. |
| Compose | Crops the world surface to the active camera viewport, redraws the viewport surface only when camera/scale/viewport/source dimensions changed, then draws that viewport surface to the screen. |
The same layer also exposes drawViewport(...) for standalone render loops outside an Hs2dWorld.
Performance Notes
- Keep world surface dimensions within target device memory limits.
- Paint once or on explicit dirty events, never as a hidden frame-loop draw routine.
- Use
helpers.createRegionfor source atlas regions. - Watch
getViewportRedraws()to confirm static cameras are reusing the viewport surface. - Prefer tile-specific layers for tile maps because they expose tile stats and builder integration.
Code Sample
protected override buildWorld(): Hs2dWorld {
const world = Hs2dWorld.create({
x: this.cameraX,
y: this.cameraY,
viewportWidth: this.screenWidth,
viewportHeight: this.screenHeight,
worldWidth: this.worldWidth,
worldHeight: this.worldHeight,
});
world.addCachedSurfaceLayer({
id: 'static-decor',
worldWidth: this.worldWidth,
worldHeight: this.worldHeight,
surfaceClearColor: 0x00000000,
paint: (surface, helpers) => this.paintDecorSurface(surface, helpers),
});
return world;
}
Source References
| Source | Why it matters |
|---|---|
../games/hosanna-ui/src/hosanna-game/hosanna2d/Hs2dCachedSurfaceLayer.ts | Cached surface options, paint retry, drawViewport, compose behavior, and custom layer neighbor type. |
../games/hosanna-ui/src/hosanna-game/scene/CachedWorldViewportRenderer.ts | Viewport surface creation, camera crop, scale redraw checks, and viewport redraw counters. |
../games/hosanna-ui/src/hosanna-game/hosanna2d/Hs2dWorld.ts | addCachedSurfaceLayer world API. |
../games/hosanna-ui/src/hosanna-game/hosanna2d/Hs2dStaticTileLayer.ts | Similar whole-world cache approach for tile maps. |
../games/hosanna-ui/src/hosanna-game/hosanna2d/Hs2dChunkedTileLayer.ts | Chunked cache alternative for large tile maps. |