phase(N.4): WbMeshAdapter.Tick — drain WB pipeline queues per frame
Without this, ObjectMeshManager.StagedMeshData and OpenGLGraphicsDevice._glThreadQueue grow unbounded as background workers prep mesh data + queue GL actions. Visual stress test of flag-on at radius 7 showed real FPS drop and rising frame latency from this leak. Tick() drains both queues: 1. _graphicsDevice.ProcessGLQueue() applies pending GL state. 2. Loop _meshManager.StagedMeshData.TryDequeue -> UploadMeshData to materialize VAO/VBO/IBO for each prepared mesh. Wired into GameWindow's render loop before draw work begins. No-op when adapter is uninitialized or disposed. Pattern matches WB's reference ObjectRenderManagerBase.ProcessUploads without the prioritization heuristics (we're not yet drawing the results — Task 22's WbDrawDispatcher will add prioritization when visual budget matters). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
f4f0101d2c
commit
bf53cb4fce
3 changed files with 54 additions and 0 deletions
|
|
@ -6076,6 +6076,12 @@ public sealed class GameWindow : IDisposable
|
|||
|
||||
_gl!.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
|
||||
|
||||
// Phase N.4: drain WB pipeline queues (staged mesh data +
|
||||
// GL thread queue). Must happen before any draw work so that
|
||||
// resources uploaded this frame are available immediately.
|
||||
// No-op when ACDREAM_USE_WB_FOUNDATION is off (_wbMeshAdapter is null).
|
||||
_wbMeshAdapter?.Tick();
|
||||
|
||||
// Phase D.2a — begin ImGui frame. Paired with the Render() call
|
||||
// after the scene draws (below). ImGuiController.Update()
|
||||
// consumes buffered Silk.NET input events and calls ImGui.NewFrame.
|
||||
|
|
|
|||
|
|
@ -94,6 +94,38 @@ public sealed class WbMeshAdapter : IDisposable, IWbMeshAdapter
|
|||
_meshManager.DecrementRefCount(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Per-frame drain of the WB pipeline's main-thread work queues. MUST be
|
||||
/// called once per frame from the render thread. Without this, the staged
|
||||
/// mesh data queue grows unbounded (memory leak) and queued GL actions
|
||||
/// never execute.
|
||||
///
|
||||
/// <para>
|
||||
/// Order matters: <c>ProcessGLQueue</c> runs first to apply any pending GL
|
||||
/// state changes (e.g., texture uploads queued by background workers
|
||||
/// during mesh prep). Then we drain staged mesh data, calling
|
||||
/// <c>UploadMeshData</c> on each item to materialize the actual GL VAO /
|
||||
/// VBO / IBO resources. After Tick, <c>GetRenderData</c> for any id
|
||||
/// previously passed to <c>IncrementRefCount</c> may return non-null.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// No-op when the adapter is uninitialized (e.g., flag is off and the
|
||||
/// adapter was constructed via <c>CreateUninitialized</c>).
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public void Tick()
|
||||
{
|
||||
if (_isUninitialized) return;
|
||||
if (_disposed) return;
|
||||
|
||||
_graphicsDevice!.ProcessGLQueue();
|
||||
while (_meshManager!.StagedMeshData.TryDequeue(out var meshData))
|
||||
{
|
||||
_meshManager.UploadMeshData(meshData);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -46,4 +46,20 @@ public sealed class WbMeshAdapterTests
|
|||
var adapter = WbMeshAdapter.CreateUninitialized();
|
||||
Assert.Null(adapter.GetRenderData(0x01000001ul));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Tick_OnUninitializedAdapter_DoesNotThrow()
|
||||
{
|
||||
var adapter = WbMeshAdapter.CreateUninitialized();
|
||||
adapter.Tick(); // no-op, no throw
|
||||
adapter.Tick(); // idempotent
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Tick_AfterDispose_DoesNotThrow()
|
||||
{
|
||||
var adapter = WbMeshAdapter.CreateUninitialized();
|
||||
adapter.Dispose();
|
||||
adapter.Tick(); // no-op, no throw
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue