refactor(render): Campaign V slice V6i-2 commit 3 — the mesh pipeline stops naming a backend
Plan §5.5.10 recorded the blocker as a fact about types: "WbMeshAdapter owns an
OpenGLGraphicsDevice, so it is not constructible on Vulkan until slice V4t" —
which is the entire reason NullWbMeshAdapter exists. §5.5.12 item 6 then measured
how wide that dependency really is, and the answer is seven members out of a
760-line class: a GL context, the retirement queue, the shared instance VBO, and
two capability flags.
IMeshPipelineDevice is exactly that surface. OpenGLGraphicsDevice declares it and
every member already existed under a GL-specific name, so the shipping backend
executes not one changed statement — these are aliases, not behaviour.
Two casts moved, and they are what actually blocked construction:
- ObjectMeshManager downcast IGpuDevice to GlGpuDevice in its CONSTRUCTOR, so a
Vulkan-composed pipeline threw before running a statement. V4t put it there
because the class registered bindless handles itself; commit 2 moved that into
the array, leaving the field a pass-through for the raw-GL renderers' handle
table. The cast now lives on that one property and names the backend it was
composed against instead of reporting a failed cast.
- The atlas array factory is selected by IWorldTextureArrayFactory.For, which is
the one place the texture stack branches on a backend.
MeshPipelineDeviceSeamTests proves the decoupling rather than describing it: it
builds ObjectMeshManager against a device whose Gl is null, asserts it constructs,
asserts construction built no GL object, asserts the handle table refuses by name,
and asserts the factory picks the RHI arm. A reflection test pins the seam's
member set so a later slice cannot quietly widen it back out — the whole value
here is that it is narrow.
What this does NOT claim: the mesh pipeline does not RUN on Vulkan. Its upload
bodies are still raw GL — GlobalMeshBuffer, the VAO/IBO construction, the layer
transfers — and they now fail through one RequireGl() accessor that names the
slice that owns porting them, instead of failing at construction. WbMeshAdapter
still creates an OpenGLGraphicsDevice in its GL constructor, because there is no
second implementation to create yet. Those bodies are items 3–5 of §5.5.12's
remainder list, along with RetailPViewPassExecutor and the three world renderers'
submission arms.
§5.5.13 reports the whole of V6i-2 and the slice table gains its V6i row.
Gates: Release build; App tests 4,109 / 3 skips (the 4,086 baseline plus 23 across
the three commits); complete Release suite 9,172 / 5; strict GL offline pixel gate
vs 0ca802cd 1.60e-05 (9 px of 563,200 — the low end of the documented 9–31 px
control band, and fewer than a same-commit control has measured); GL connected
tools/run-repeat-connected-gate.ps1 -Runs 3 at 3/3 RENDERED on the desktop witness
and 3/3 on the client capture; one Vulkan composition-host run with
VK_LAYER_KHRONOS_validation proven inserted by the loader at zero errors, zero
warnings, no [shutdown] diagnostic, and a captured frame.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
c8d0f70bbe
commit
5b3d72a90c
7 changed files with 459 additions and 18 deletions
|
|
@ -116,7 +116,25 @@ namespace AcDream.App.Rendering.Wb
|
|||
/// </summary>
|
||||
public class ObjectMeshManager : IDisposable
|
||||
{
|
||||
private readonly OpenGLGraphicsDevice _graphicsDevice;
|
||||
/// <summary>
|
||||
/// Campaign V slice V6i-2: the graphics device, no longer named by
|
||||
/// backend. See <see cref="IMeshPipelineDevice"/> for the measured
|
||||
/// surface this class actually needs, and for what still has to move
|
||||
/// before there is a second implementation of it.
|
||||
/// </summary>
|
||||
private readonly IMeshPipelineDevice _graphicsDevice;
|
||||
|
||||
/// <summary>
|
||||
/// The GL context this class's still-raw upload bodies write through.
|
||||
/// Null only on a backend with none, where every one of those bodies is
|
||||
/// a programming error rather than a runtime condition — the slice that
|
||||
/// ports them owns deleting this accessor.
|
||||
/// </summary>
|
||||
private GL RequireGl() =>
|
||||
_graphicsDevice.Gl
|
||||
?? throw new InvalidOperationException(
|
||||
"The mesh pipeline's upload bodies are still raw GL and this device has no "
|
||||
+ "context. Campaign V's world-draw slice owns porting them.");
|
||||
private readonly IPreparedAssetSource _preparedAssets;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
|
|
@ -129,9 +147,21 @@ namespace AcDream.App.Rendering.Wb
|
|||
/// composition wire, because a batch's slot and the table that resolves
|
||||
/// it must come from the same device by construction.
|
||||
/// </summary>
|
||||
private readonly AcDream.App.Rendering.Gpu.Gl.GlGpuDevice _worldTextureTable;
|
||||
private readonly AcDream.App.Rendering.Gpu.IGpuDevice _gpuDevice;
|
||||
|
||||
internal AcDream.App.Rendering.Gpu.Gl.GlGpuDevice WorldTextureTable => _worldTextureTable;
|
||||
/// <summary>
|
||||
/// Campaign V slice V6i-2: the downcast moved here from the constructor.
|
||||
/// Only the raw-GL world renderers reach this — the Vulkan backend binds
|
||||
/// set 2 and never touches the handle table — so a Vulkan-composed mesh
|
||||
/// pipeline can now be CONSTRUCTED, and only a caller that genuinely
|
||||
/// needs a GL handle table fails, naming why.
|
||||
/// </summary>
|
||||
internal AcDream.App.Rendering.Gpu.Gl.GlGpuDevice WorldTextureTable =>
|
||||
_gpuDevice as AcDream.App.Rendering.Gpu.Gl.GlGpuDevice
|
||||
?? throw new InvalidOperationException(
|
||||
"The GL bindless handle table was requested from a mesh pipeline composed against "
|
||||
+ $"the {_gpuDevice.Backend} backend. It is GL-only emulation of the Vulkan texture "
|
||||
+ "table and is deleted with the raw-GL world path.");
|
||||
|
||||
/// <summary>
|
||||
/// Campaign V slice V6i-2: how a shared atlas's physical array is made.
|
||||
|
|
@ -451,7 +481,7 @@ namespace AcDream.App.Rendering.Wb
|
|||
// already lives inside AcDream.App or its InternalsVisibleTo test
|
||||
// assemblies.
|
||||
internal ObjectMeshManager(
|
||||
OpenGLGraphicsDevice graphicsDevice,
|
||||
IMeshPipelineDevice graphicsDevice,
|
||||
AcDream.App.Rendering.Gpu.IGpuDevice gpuDevice,
|
||||
IPreparedAssetSource preparedAssets,
|
||||
ILogger<ObjectMeshManager> logger,
|
||||
|
|
@ -461,17 +491,20 @@ namespace AcDream.App.Rendering.Wb
|
|||
_graphicsDevice = graphicsDevice
|
||||
?? throw new ArgumentNullException(nameof(graphicsDevice));
|
||||
ArgumentNullException.ThrowIfNull(gpuDevice);
|
||||
// Slice V4t: this class only ever exists on GL — it takes an
|
||||
// OpenGLGraphicsDevice — so the backend cast states that fact rather
|
||||
// than narrowing anything.
|
||||
_worldTextureTable = (AcDream.App.Rendering.Gpu.Gl.GlGpuDevice)gpuDevice;
|
||||
_gpuDevice = gpuDevice;
|
||||
// Slice V6i-2: which physical array a shared atlas gets is decided
|
||||
// once, here. Everything below — capacity, slot allocation, ref
|
||||
// counting, layer retirement, eviction — is written against
|
||||
// IWorldTextureArray and does not branch on the backend.
|
||||
_atlasArrays = new GlWorldTextureArrayFactory(
|
||||
//
|
||||
// Slice V4t downcast gpuDevice to GlGpuDevice HERE, which is what
|
||||
// made a Vulkan-composed mesh pipeline throw before it had run a
|
||||
// statement. The cast now lives on the one property that genuinely
|
||||
// needs it — the raw-GL world renderers' handle table — so
|
||||
// construction itself no longer names a backend.
|
||||
_atlasArrays = IWorldTextureArrayFactory.For(
|
||||
graphicsDevice,
|
||||
_worldTextureTable,
|
||||
gpuDevice,
|
||||
logger ?? throw new ArgumentNullException(nameof(logger)));
|
||||
_preparedAssets = preparedAssets
|
||||
?? throw new ArgumentNullException(nameof(preparedAssets));
|
||||
|
|
@ -489,7 +522,7 @@ namespace AcDream.App.Rendering.Wb
|
|||
if (_useModernRendering)
|
||||
{
|
||||
GlobalBuffer = new GlobalMeshBuffer(
|
||||
_graphicsDevice.GL,
|
||||
RequireGl(),
|
||||
gpuDevice,
|
||||
_graphicsDevice.ResourceRetirement);
|
||||
}
|
||||
|
|
@ -1951,7 +1984,7 @@ namespace AcDream.App.Rendering.Wb
|
|||
{
|
||||
if (meshData.Vertices.Length == 0) return null;
|
||||
|
||||
var gl = _graphicsDevice.GL;
|
||||
var gl = RequireGl();
|
||||
uint vao = 0, vbo = 0;
|
||||
var modernIndexBatches = meshData.TextureBatches.Values
|
||||
.SelectMany(batches => batches)
|
||||
|
|
@ -2398,7 +2431,7 @@ namespace AcDream.App.Rendering.Wb
|
|||
return null;
|
||||
|
||||
var releases = new List<(string Name, Action Release)>();
|
||||
GL gl = _graphicsDevice.GL;
|
||||
GL gl = RequireGl();
|
||||
if (_useModernRendering)
|
||||
{
|
||||
if (data.GlobalAllocation is { } allocation)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue