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

@ -16,7 +16,6 @@ using AcDream.Runtime.Session;
using DatReaderWriter;
using Silk.NET.Input;
using Silk.NET.Maths;
using Silk.NET.OpenGL;
using Silk.NET.Windowing;
namespace AcDream.App.Rendering;
@ -56,15 +55,11 @@ public sealed class GameWindow :
AcDream.UI.Abstractions.Settings.QualityPreset.High);
private IInputContext? _input;
private TerrainModernRenderer? _terrain;
/// <summary>Phase N.5b: terrain_modern.vert/.frag program. Owned by
/// <see cref="_terrain"/> at draw time but allocated + disposed here.</summary>
private Shader? _terrainModernShader;
private CameraController? _cameraController;
private IDatReaderWriter? _dats;
private IPreparedAssetSource? _preparedAssets;
private readonly AcDream.App.Input.PointerPositionState _pointerPosition = new();
private AcDream.App.Input.CameraPointerInputController? _cameraPointerInput;
private Shader? _meshShader;
private TextureCache? _textureCache;
/// <summary>Phase N.4+: WB-backed rendering pipeline adapter. Always non-null
/// after <c>OnLoad</c> completes (modern path is mandatory as of N.5).</summary>
@ -77,11 +72,6 @@ public sealed class GameWindow :
private AcDream.App.Rendering.Selection.RetailSelectionScene? _retailSelectionScene;
private AcDream.App.Interaction.WorldSelectionQuery? _worldSelectionQuery;
private AcDream.App.Interaction.SelectionInteractionController? _selectionInteractions;
/// <summary>Phase N.5: ARB_bindless_texture + ARB_shader_draw_parameters
/// support. Required at startup — missing bindless throws
/// <see cref="NotSupportedException"/> in <c>OnLoad</c>.</summary>
private AcDream.App.Rendering.Wb.BindlessSupport? _bindlessSupport;
private SamplerCache? _samplerCache;
private DebugLineRenderer? _debugLines;
// K-fix4 (2026-04-26): default OFF. The orange BSP / green cylinder
// wireframes are noisy outdoors and confuse first-time users into
@ -128,8 +118,8 @@ public sealed class GameWindow :
private readonly AcDream.App.Rendering.GameFrameGraphSlot _frameGraphs = new();
private readonly AcDream.App.Rendering.GameRenderResourceLifetime
_renderResourceLifetime = new();
private readonly AcDream.App.Rendering.GlConstructionCleanupLedger
_glConstructionCleanup = new();
private readonly AcDream.App.Rendering.ResourceConstructionCleanupLedger
_constructionCleanup = new();
private readonly AcDream.App.World.WorldEnvironmentController _worldEnvironment;
private readonly GameWindowLifetime _lifetime = new();
private readonly DisplayFramePacingController _displayFramePacing;
@ -518,7 +508,6 @@ public sealed class GameWindow :
private readonly AcDream.UI.Abstractions.Input.KeyBindings _keyBindings;
private readonly GraphicalHostPlatformServices _platformServices;
private readonly ApplicationPathSet _applicationPaths;
private GraphicalCapabilityRecord? _graphicalCapabilities;
private static AcDream.UI.Abstractions.Input.KeyBindings LoadStartupKeyBindings(
string path)
@ -687,8 +676,7 @@ public sealed class GameWindow :
// attribute, so it must come from this same snapshot rather than a
// second settings load during OnLoad.
RuntimeSettingsSnapshot startup = _runtimeSettings.Startup;
if (_options.RenderBackend == RenderBackendKind.Vulkan
&& _options.VulkanCapabilityProbe)
if (_options.VulkanCapabilityProbe)
{
// Campaign V slice V6h reduced the V5 bring-up host to what its name
// says: a capability probe. It opens its own window, runs the gate,
@ -706,39 +694,17 @@ public sealed class GameWindow :
FramePacingPolicy startupPacing =
_displayFramePacing.InitializeStartup(startup.Display.VSync);
// Campaign V slice V6h: the ONLY startup difference between the two
// backends. Vulkan needs a client-API-less window (the surface comes from
// VK_KHR_surface), and neither MSAA nor the stencil bit count is a window
// Vulkan needs a client-API-less window (the surface comes from
// VK_KHR_surface); neither MSAA nor the stencil bit count is a window
// attribute there — both are attachment properties the RHI device
// configures, so they are passed to VulkanGraphicsContext instead.
var options = _options.RenderBackend == RenderBackendKind.Vulkan
? WindowOptions.DefaultVulkan with
{
Size = new Vector2D<int>(1280, 720),
Title = "acdream — Vulkan",
VSync = startupPacing.UseVSync,
}
: WindowOptions.Default with
{
Size = new Vector2D<int>(1280, 720),
Title = "acdream — phase 1",
API = new GraphicsAPI(
ContextAPI.OpenGL,
ContextProfile.Core,
ContextFlags.ForwardCompatible,
new APIVersion(4, 3)),
VSync = startupPacing.UseVSync,
// A.5 T22.5: MSAA from quality preset (0 = disabled, 2/4/8 = multisample).
// Silk.NET passes this to SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES).
// Cannot be changed at runtime; Quality changes mid-session that would
// alter MsaaSamples are logged as a restart-required warning.
Samples = startup.Quality.MsaaSamples,
// #117 (2026-06-11): the aperture punch's depth gate needs a
// stencil buffer (PortalDepthMaskRenderer two-pass mark+punch).
// GLFW defaults to 8 stencil bits, but make the requirement
// explicit rather than platform-implicit.
PreferredStencilBufferBits = 8,
};
// configures instead. The raw-GL window options this used to fork to
// were deleted at Campaign V slice V11.
var options = WindowOptions.DefaultVulkan with
{
Size = new Vector2D<int>(1280, 720),
Title = "acdream — Vulkan",
VSync = startupPacing.UseVSync,
};
_startupPacing = startupPacing;
_startupQuality = startup.Quality;
@ -766,7 +732,7 @@ public sealed class GameWindow :
}
catch (Exception failure)
{
_glConstructionCleanup.RetainFrom(failure);
_constructionCleanup.RetainFrom(failure);
throw;
}
}
@ -912,19 +878,6 @@ public sealed class GameWindow :
_audioSink = value.HookSink;
}
void IGameWindowWorldRenderPublication.PublishBindlessSupport(
BindlessSupport value) =>
PublishCompositionOwner(
ref _bindlessSupport,
value,
"bindless support");
void IGameWindowWorldRenderPublication.PublishTerrainShader(Shader value) =>
PublishCompositionOwner(
ref _terrainModernShader,
value,
"terrain shader");
void IGameWindowWorldRenderPublication.PublishSceneLighting(
SceneLightingUboBinding value) =>
PublishCompositionOwner(
@ -975,9 +928,6 @@ public sealed class GameWindow :
_surfaceCache = surfaceCache;
}
void IGameWindowWorldRenderPublication.PublishMeshShader(Shader value) =>
PublishCompositionOwner(ref _meshShader, value, "mesh shader");
void IGameWindowWorldRenderPublication.PublishWbMeshAdapter(
WbMeshAdapter value) =>
PublishCompositionOwner(ref _wbMeshAdapter, value, "WB mesh adapter");
@ -986,10 +936,6 @@ public sealed class GameWindow :
TextureCache value) =>
PublishCompositionOwner(ref _textureCache, value, "texture cache");
void IGameWindowWorldRenderPublication.PublishSamplerCache(
SamplerCache value) =>
PublishCompositionOwner(ref _samplerCache, value, "sampler cache");
void IGameWindowInteractionRetainedUiPublication.PublishInteractionRetainedUi(
InteractionRetainedUiResult result)
{
@ -1192,41 +1138,31 @@ public sealed class GameWindow :
}
/// <summary>
/// Campaign V slice V6h: how the UI probe reads a completed frame.
///
/// <para><see cref="FrameScreenshotController"/> applies the bottom-up flip
/// <c>glReadPixels</c> needs, so GL hands it the raw read and Vulkan — whose
/// <see cref="IGpuDevice.CaptureBackbuffer"/> is documented top-left-origin —
/// pre-flips so the two cancel. Routing GL through the same
/// <c>CaptureBackbuffer</c> seam would double-flip, which is exactly the kind
/// of "usually right" instrument §5.5 of the campaign plan spent three
/// slices removing.</para>
/// How the UI probe reads a completed frame.
/// <see cref="IGpuDevice.CaptureBackbuffer"/> is documented top-left-origin,
/// so <see cref="FrameScreenshotController"/> flips it to match the PNG's
/// row order. The raw-GL read this used to fork from — whose rows already
/// come out bottom-up, needing no flip — was deleted at Campaign V slice
/// V11.
/// </summary>
private static Func<int, int, byte[]> CreateBackbufferReader(
GameWindowGraphics graphics,
IGpuDevice device) =>
graphics.Gl is { } gl
? (width, height) =>
AcDream.App.Diagnostics.FrameScreenshotController
.ReadDefaultFramebuffer(gl, width, height)
: (width, height) =>
AcDream.App.Diagnostics.FrameScreenshotController.FlipRows(
device.CaptureBackbuffer(width, height),
width,
height);
(width, height) =>
AcDream.App.Diagnostics.FrameScreenshotController.FlipRows(
device.CaptureBackbuffer(width, height),
width,
height);
/// <summary>
/// Campaign V slice V6h: the backend fork, in one expression. Vulkan's
/// context acquisition runs its own capability gate inside
/// Vulkan's context acquisition runs its own capability gate inside
/// <see cref="AcDream.App.Rendering.Gpu.Vk.VulkanGraphicsContext.Acquire"/>,
/// throwing <see cref="NotSupportedException"/> into the same exit-code-4
/// contract <c>Program.cs</c> publishes for GL.
/// contract <c>Program.cs</c> publishes. The raw-GL fork this used to make
/// was deleted at Campaign V slice V11.
/// </summary>
private GameWindowGraphics CreateGraphics()
{
if (_options.RenderBackend != RenderBackendKind.Vulkan)
return new OpenGlGameWindowGraphics(GL.GetApi(_window!));
AcDream.App.Rendering.Gpu.Vk.VulkanGraphicsContext vulkan =
AcDream.App.Rendering.Gpu.Vk.VulkanGraphicsContext.Acquire(
_window!,
@ -1245,32 +1181,10 @@ public sealed class GameWindow :
// run narrow-phase BSP tests during FindObjCollisions.
GameWindowPlatformResult<GameWindowGraphics, IInputContext> platform = AcquirePlatform();
// The GL capability gate reads GL extension strings, so it runs only on
// the GL arm. Vulkan's equivalent gate already ran inside
// VulkanGraphicsContext.Acquire and wrote its own report.
if (platform.Graphics.Gl is { } capabilityGl)
{
string capabilityReportPath = Path.Combine(
_applicationPaths.DiagnosticsDirectory,
"graphical-capabilities.json");
_graphicalCapabilities =
GraphicalCapabilityGuard.CaptureVerifyAndWrite(
capabilityGl,
_window!,
platform.Input,
_platformServices,
capabilityReportPath);
GraphicalCapabilityGuard.ThrowIfUnsupported(
_graphicalCapabilities,
capabilityReportPath);
Console.WriteLine(
"graphics: capability gate passed " +
$"({_graphicalCapabilities.ActiveDisplayProtocol}, " +
$"{_graphicalCapabilities.GlVendor}, " +
$"{_graphicalCapabilities.GlRenderer}, " +
$"{_graphicalCapabilities.GlVersion}); " +
$"report={capabilityReportPath}");
}
// The raw-GL capability gate that used to run here (reading GL
// extension strings) was deleted at Campaign V slice V11. Vulkan's
// equivalent gate already ran inside VulkanGraphicsContext.Acquire and
// wrote its own report.
GameWindowCompositionPipeline.Run<
GameWindowPlatformResult<GameWindowGraphics, IInputContext>,
@ -1727,12 +1641,9 @@ public sealed class GameWindow :
_clipFrame,
_skyRenderer,
_particleRenderer,
_samplerCache,
_textureCache,
_wbMeshAdapter,
_meshShader,
_terrain,
_terrainModernShader,
_sceneLightingUbo,
_debugLines,
_textRenderer,
@ -1740,7 +1651,7 @@ public sealed class GameWindow :
_displayFramePacing,
_frameProfiler,
_renderResourceLifetime,
_glConstructionCleanup),
_constructionCleanup),
new PlatformShutdownRoots(
_dats,
_preparedAssets,