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> /// </summary>
private readonly Dictionary<uint, AnimatedEntity> _animatedEntities = new(); 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 private sealed class AnimatedEntity
{ {
public required AcDream.Core.World.WorldEntity Entity; public required AcDream.Core.World.WorldEntity Entity;
@ -1604,7 +1615,8 @@ public sealed class GameWindow : IDisposable
_worldState = new AcDream.App.Streaming.GpuWorldState(wbSpawnAdapter, wbEntitySpawnAdapter); _worldState = new AcDream.App.Streaming.GpuWorldState(wbSpawnAdapter, wbEntitySpawnAdapter);
_wbDrawDispatcher = new AcDream.App.Rendering.Wb.WbDrawDispatcher( _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. // A.5 T22.5: apply A2C gate from quality preset.
_wbDrawDispatcher.AlphaToCoverage = _resolvedQuality.AlphaToCoverage; _wbDrawDispatcher.AlphaToCoverage = _resolvedQuality.AlphaToCoverage;
} }

View file

@ -68,6 +68,12 @@ public sealed unsafe class WbDrawDispatcher : IDisposable
private readonly BindlessSupport _bindless; 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> /// <summary>
/// A.5 T22.5: gate for GL_SAMPLE_ALPHA_TO_COVERAGE around the opaque pass. /// 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 /// 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 int _gpuSampleCursor;
private bool _gpuQueriesInitialized; 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, GL gl,
Shader shader, Shader shader,
TextureCache textures, TextureCache textures,
WbMeshAdapter meshAdapter, WbMeshAdapter meshAdapter,
EntitySpawnAdapter entitySpawnAdapter, EntitySpawnAdapter entitySpawnAdapter,
BindlessSupport bindless) BindlessSupport bindless,
EntityClassificationCache classificationCache)
{ {
ArgumentNullException.ThrowIfNull(gl); ArgumentNullException.ThrowIfNull(gl);
ArgumentNullException.ThrowIfNull(shader); ArgumentNullException.ThrowIfNull(shader);
ArgumentNullException.ThrowIfNull(textures); ArgumentNullException.ThrowIfNull(textures);
ArgumentNullException.ThrowIfNull(meshAdapter); ArgumentNullException.ThrowIfNull(meshAdapter);
ArgumentNullException.ThrowIfNull(entitySpawnAdapter); ArgumentNullException.ThrowIfNull(entitySpawnAdapter);
ArgumentNullException.ThrowIfNull(classificationCache);
_gl = gl; _gl = gl;
_shader = shader; _shader = shader;
_textures = textures; _textures = textures;
_meshAdapter = meshAdapter; _meshAdapter = meshAdapter;
_entitySpawnAdapter = entitySpawnAdapter; _entitySpawnAdapter = entitySpawnAdapter;
_cache = classificationCache;
_bindless = bindless ?? throw new ArgumentNullException(nameof(bindless)); _bindless = bindless ?? throw new ArgumentNullException(nameof(bindless));
_instanceSsbo = _gl.GenBuffer(); _instanceSsbo = _gl.GenBuffer();