feat(render): Vulkan campaign V11 step 2 — delete the OpenGL backend

Vulkan is the sole, user-signed-off backend (V10 landed) and step 1
already removed ImGui/Studio/DevTools. This step deletes the GL
rendering backend itself: every Gpu/Gl/** implementation, the Wb
ManagedGL*/GLHelpers/GLSLShader/GLStateScope/RenderStateCache/
BindlessSupport family, Shader/ShaderProgramConstruction/SamplerCache,
RenderBootstrap, and RenderFrameGlStateController.

GameWindow.cs's Run()/CreateGraphics()/CreateBackbufferReader()/
OnLoad() collapse to their Vulkan-only arm; GameWindowGraphics loses
its OpenGlGameWindowGraphics subclass. RuntimeOptions.RenderBackend and
RenderBackendKind (incl. the Gl member of GpuBackendKind) are gone —
there is nothing left to select between. The five world-draw dual-arm
renderers (WbDrawDispatcher, EnvCellRenderer, TerrainModernRenderer,
ParticleRenderer, SkyRenderer) and the composition roots
(WorldRenderComposition, HostInputCameraComposition,
LivePresentationComposition, FrameRootComposition) collapse to their
RHI-only arm. GL-only diagnostic properties with a live external reader
(DynamicBufferCount and friends) simplify to a documented `=> 0`/no-op
rather than disappearing, since the reader is out of this commit's
scope.

A few GL-flavored mechanisms turned out to be backend-neutral once
isolated: GlConstructionCleanupLedger is renamed
ResourceConstructionCleanupLedger (exception-chain walking has nothing
to do with GL), and GlfwNativePlatformProbe moved out of the otherwise
GL-only GraphicalCapabilityRecord.cs into
GraphicalWindowBackendSelection.cs before the rest of that file was
deleted.

Test files with no surviving subject are deleted outright
(GraphicalCapabilityRequirementsTests, ShaderProgramConstructionTests,
PortalDepthShaderParityTests, TextureCacheBindlessTests,
TextRendererFailureSafetyTests, ClipFrameUploadTests, every
Gpu/Gl/*Tests, GlTextureOwnershipTests, RenderFrameGlStateControllerTests);
others get their dead GL-only members trimmed while their live
assertions stay (ClipFrameLayoutTests' MeshClipSsboBinding check now
reads GpuBindingModel.StorageClipRegions, the same binding index under
its new backend-neutral name; GpuResourceRetirementTransactionTests
drops its OpenGLGraphicsDevice-subclassing test double and the two GL
queue tests it existed for). EnvCellRendererTests' construction helper
now builds a real ObjectMeshManager via VulkanMeshPipelineDevice
instead of passing null through a null-forgiving operator, since the
RHI constructor never tolerated a null mesh manager and the old GL
constructor (which did) is gone.

Deferred to the next two steps, deliberately not touched here: the
Silk.NET.OpenGL/.Extensions.ARB package references, IMeshPipelineDevice.Gl
(WbMeshAdapter's GL? threading stays in place), Chorizite.Core's stale
csproj comment (the package itself is still load-bearing —
TextureFormat and friends are used well beyond the deleted
ManagedGLUniformBuffer), and the CI/gate scripts.

Build: `dotnet build AcDream.slnx -c Release` — 0 warnings, 0 errors.
Tests: full-solution `dotnet test` green across every project
(App.Tests 3937/3940 + 3 skips, Core.Tests 3296/3298 + 2 skips, all
others 100%); the 2 App.Tests names that flake under full-suite
parallel execution (#250-family, documented pre-existing) pass in
isolation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-07-29 02:19:53 +02:00
parent b70b9832ff
commit 8a7a0837e1
121 changed files with 1243 additions and 19840 deletions

View file

@ -1,8 +1,6 @@
using AcDream.App.Rendering.Gpu;
using AcDream.App.Rendering.Gpu.Gl;
using AcDream.Core.Textures;
using AcDream.Core.World;
using Silk.NET.OpenGL;
namespace AcDream.App.Rendering;
@ -163,192 +161,6 @@ internal interface ICompositeTextureArrayBackend
void Delete(CompositeTextureArrayResource resource);
}
/// <summary>
/// Narrow GL backend for composite arrays. Unlike ManagedGLTextureArray it
/// deliberately has one mip level, no PBO, and one resident handle: those are
/// the semantics of the standalone composite textures this pool replaces.
/// </summary>
internal sealed unsafe class GlCompositeTextureArrayBackend : ICompositeTextureArrayBackend
{
private readonly GL _gl;
private readonly Wb.BindlessSupport _bindless;
private readonly GlGpuDevice _device;
public GlCompositeTextureArrayBackend(GL gl, Wb.BindlessSupport bindless, GlGpuDevice device)
{
_gl = gl ?? throw new ArgumentNullException(nameof(gl));
_bindless = bindless ?? throw new ArgumentNullException(nameof(bindless));
_device = device ?? throw new ArgumentNullException(nameof(device));
_gl.GetInteger(GetPName.MaxArrayTextureLayers, out int maximumLayers);
MaximumArrayLayers = Math.Max(1, maximumLayers);
}
public int MaximumArrayLayers { get; }
public CompositeTextureArrayResource Create(int width, int height, int capacity)
{
uint name = _gl.GenTexture();
if (name == 0)
throw new InvalidOperationException("OpenGL did not create a composite texture array.");
bool resident = false;
ulong handle = 0;
long bytes = 0;
bool tracked = false;
try
{
// Composite creation/upload runs in the render thread's pre-draw
// preparation phase. Normalize that phase to texture unit zero
// instead of synchronously reading driver binding state.
_gl.ActiveTexture(TextureUnit.Texture0);
Wb.RenderStateCache.CurrentAtlas = 0;
_gl.BindTexture(TextureTarget.Texture2DArray, name);
_gl.TexStorage3D(
TextureTarget.Texture2DArray,
levels: 1,
SizedInternalFormat.Rgba8,
checked((uint)width),
checked((uint)height),
checked((uint)capacity));
_gl.TexParameter(TextureTarget.Texture2DArray, TextureParameterName.TextureBaseLevel, 0);
_gl.TexParameter(TextureTarget.Texture2DArray, TextureParameterName.TextureMaxLevel, 0);
_gl.TexParameter(TextureTarget.Texture2DArray, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
_gl.TexParameter(TextureTarget.Texture2DArray, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
_gl.TexParameter(TextureTarget.Texture2DArray, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);
_gl.TexParameter(TextureTarget.Texture2DArray, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);
handle = _bindless.GetResidentHandle(name);
resident = true;
Wb.GLHelpers.ThrowOnResourceError(
_gl,
$"creating composite texture array {width}x{height}x{capacity}");
bytes = checked((long)width * height * 4L * capacity);
Wb.GpuMemoryTracker.TrackResourceAllocation(Wb.GpuResourceType.Texture);
Wb.GpuMemoryTracker.TrackAllocation(bytes, Wb.GpuResourceType.Texture);
tracked = true;
// Campaign V slice V4t: intern the resident handle into the device's
// one texture table. A table-exhaustion throw here is caught by the
// same rollback below, and nothing was added to the table if it did.
GpuTextureSlot slot = _device.RegisterWorldTextureHandle(handle);
return new CompositeTextureArrayResource
{
Name = name,
Handle = handle,
Slot = slot,
Width = width,
Height = height,
Capacity = capacity,
Bytes = bytes,
};
}
catch (Exception creationFailure)
{
List<Exception>? cleanupFailures = null;
void Attempt(Action cleanup)
{
try { cleanup(); }
catch (Exception ex) { (cleanupFailures ??= []).Add(ex); }
}
bool residencyReleased = !resident;
if (resident)
{
Attempt(() =>
{
_bindless.MakeNonResident(handle);
Wb.GLHelpers.ThrowOnResourceError(_gl, "rolling back composite texture residency");
residencyReleased = true;
});
}
if (residencyReleased)
{
Attempt(() =>
{
_gl.DeleteTexture(name);
Wb.GLHelpers.ThrowOnResourceError(_gl, "rolling back composite texture array");
if (tracked)
{
Wb.GpuMemoryTracker.TrackDeallocation(bytes, Wb.GpuResourceType.Texture);
Wb.GpuMemoryTracker.TrackResourceDeallocation(Wb.GpuResourceType.Texture);
}
});
}
if (cleanupFailures is not null)
{
cleanupFailures.Insert(0, creationFailure);
throw new AggregateException(
"Composite texture-array construction and rollback both failed.",
cleanupFailures);
}
throw;
}
finally
{
_gl.BindTexture(TextureTarget.Texture2DArray, 0);
_gl.ActiveTexture(TextureUnit.Texture0);
}
}
public void Upload(CompositeTextureArrayResource resource, int layer, byte[] rgba)
{
_gl.ActiveTexture(TextureUnit.Texture0);
Wb.RenderStateCache.CurrentAtlas = 0;
try
{
_gl.BindBuffer(BufferTargetARB.PixelUnpackBuffer, 0);
_gl.BindTexture(TextureTarget.Texture2DArray, resource.Name);
fixed (byte* pixels = rgba)
{
_gl.TexSubImage3D(
TextureTarget.Texture2DArray,
level: 0,
xoffset: 0,
yoffset: 0,
zoffset: layer,
checked((uint)resource.Width),
checked((uint)resource.Height),
depth: 1,
PixelFormat.Rgba,
PixelType.UnsignedByte,
pixels);
}
Wb.GLHelpers.ThrowOnResourceError(
_gl,
$"uploading composite texture layer {layer} ({resource.Width}x{resource.Height})");
}
finally
{
_gl.BindTexture(TextureTarget.Texture2DArray, 0);
_gl.BindBuffer(BufferTargetARB.PixelUnpackBuffer, 0);
_gl.ActiveTexture(TextureUnit.Texture0);
}
}
public void MakeNonResident(CompositeTextureArrayResource resource)
{
Wb.GLHelpers.ThrowOnResourceError(
_gl,
$"releasing composite texture handle {resource.Handle} (precondition)");
// Campaign V slice V4t: retire the table entry before the handle it
// names stops being resident. Idempotent, so this stays correct when
// the retryable release ledger re-runs the operation.
_device.ReleaseWorldTextureHandle(resource.Handle);
_bindless.MakeNonResident(resource.Handle);
Wb.GLHelpers.ThrowOnResourceError(_gl, $"releasing composite texture handle {resource.Handle}");
}
public void Delete(CompositeTextureArrayResource resource)
{
Wb.GLHelpers.ThrowOnResourceError(
_gl,
$"deleting composite texture array {resource.Name} (precondition)");
_gl.DeleteTexture(resource.Name);
Wb.GLHelpers.ThrowOnResourceError(_gl, $"deleting composite texture array {resource.Name}");
Wb.GpuMemoryTracker.TrackDeallocation(resource.Bytes, Wb.GpuResourceType.Texture);
Wb.GpuMemoryTracker.TrackResourceDeallocation(Wb.GpuResourceType.Texture);
}
}
/// <summary>
/// Campaign V slice V6i-2: the backend-neutral composite array backend.
///
@ -525,25 +337,6 @@ internal sealed class CompositeTextureArrayCache : IDisposable
Accounted,
}
public CompositeTextureArrayCache(
GL gl,
Wb.BindlessSupport bindless,
GlGpuDevice device,
IGpuResourceRetirementQueue retirementQueue,
long unownedBudgetBytes = DefaultUnownedBudgetBytes,
long physicalBudgetBytes = DefaultPhysicalBudgetBytes,
int maximumUploadsPerFrame = DefaultMaximumUploadsPerFrame,
long maximumUploadBytesPerFrame = DefaultMaximumUploadBytesPerFrame)
: this(
new GlCompositeTextureArrayBackend(gl, bindless, device),
retirementQueue,
unownedBudgetBytes,
physicalBudgetBytes,
maximumUploadsPerFrame,
maximumUploadBytesPerFrame)
{
}
internal CompositeTextureArrayCache(
ICompositeTextureArrayBackend backend,
IGpuResourceRetirementQueue retirementQueue,