feat(render #53): inject EntityClassificationCache into WbDrawDispatcher

Adds the cache as a constructor parameter on WbDrawDispatcher and a
private field on GameWindow. The cache is passed through but not yet
consumed by Draw — that wires up in Task 9 (cache miss / populate) and
Task 10 (cache hit / fast path).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-05-10 18:05:03 +02:00
parent 60fbfce8bc
commit a65a241981
2 changed files with 28 additions and 3 deletions

View file

@ -159,6 +159,17 @@ public sealed class GameWindow : IDisposable
/// </summary>
private readonly Dictionary<uint, AnimatedEntity> _animatedEntities = new();
/// <summary>
/// Tier 1 cache (#53): per-entity classification results for static
/// entities (those NOT in <see cref="_animatedEntities"/>). Conceptually
/// paired with <see cref="_animatedEntities"/> — that dictionary is the
/// gating predicate, this cache is the lookup that depends on it.
/// Passed to <see cref="AcDream.App.Rendering.Wb.WbDrawDispatcher"/> at
/// construction time. Tasks 9-10 of the cache plan wire the per-entity
/// miss-populate / hit-fast-path through the dispatcher's loop.
/// </summary>
private readonly AcDream.App.Rendering.Wb.EntityClassificationCache _classificationCache = new();
private sealed class AnimatedEntity
{
public required AcDream.Core.World.WorldEntity Entity;
@ -1604,7 +1615,8 @@ public sealed class GameWindow : IDisposable
_worldState = new AcDream.App.Streaming.GpuWorldState(wbSpawnAdapter, wbEntitySpawnAdapter);
_wbDrawDispatcher = new AcDream.App.Rendering.Wb.WbDrawDispatcher(
_gl, _meshShader!, _textureCache!, _wbMeshAdapter!, _wbEntitySpawnAdapter, _bindlessSupport!);
_gl, _meshShader!, _textureCache!, _wbMeshAdapter!, _wbEntitySpawnAdapter, _bindlessSupport!,
_classificationCache);
// A.5 T22.5: apply A2C gate from quality preset.
_wbDrawDispatcher.AlphaToCoverage = _resolvedQuality.AlphaToCoverage;
}

View file

@ -68,6 +68,12 @@ public sealed unsafe class WbDrawDispatcher : IDisposable
private readonly BindlessSupport _bindless;
// Tier 1 cache (#53): per-entity classification results for static
// entities (those NOT in GameWindow._animatedEntities). Wired here in
// Task 7 for plumbing only — Tasks 9-10 wire the per-entity
// miss-populate / hit-fast-path through the loop.
private readonly EntityClassificationCache _cache;
/// <summary>
/// A.5 T22.5: gate for GL_SAMPLE_ALPHA_TO_COVERAGE around the opaque pass.
/// Default true matches T20 behavior. Set false for Low/Medium presets that
@ -139,25 +145,32 @@ public sealed unsafe class WbDrawDispatcher : IDisposable
private int _gpuSampleCursor;
private bool _gpuQueriesInitialized;
public WbDrawDispatcher(
// Constructor accessibility is internal because EntityClassificationCache
// is internal — a public ctor with an internal-typed parameter would be
// an inconsistent-accessibility error. The dispatcher is constructed
// exclusively from GameWindow (same assembly), so internal is fine.
internal WbDrawDispatcher(
GL gl,
Shader shader,
TextureCache textures,
WbMeshAdapter meshAdapter,
EntitySpawnAdapter entitySpawnAdapter,
BindlessSupport bindless)
BindlessSupport bindless,
EntityClassificationCache classificationCache)
{
ArgumentNullException.ThrowIfNull(gl);
ArgumentNullException.ThrowIfNull(shader);
ArgumentNullException.ThrowIfNull(textures);
ArgumentNullException.ThrowIfNull(meshAdapter);
ArgumentNullException.ThrowIfNull(entitySpawnAdapter);
ArgumentNullException.ThrowIfNull(classificationCache);
_gl = gl;
_shader = shader;
_textures = textures;
_meshAdapter = meshAdapter;
_entitySpawnAdapter = entitySpawnAdapter;
_cache = classificationCache;
_bindless = bindless ?? throw new ArgumentNullException(nameof(bindless));
_instanceSsbo = _gl.GenBuffer();