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:
parent
b70b9832ff
commit
8a7a0837e1
121 changed files with 1243 additions and 19840 deletions
|
|
@ -1,6 +1,4 @@
|
|||
using AcDream.App.Rendering.Wb;
|
||||
using Silk.NET.OpenGL;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
|
||||
namespace AcDream.App.Diagnostics;
|
||||
|
|
@ -28,39 +26,6 @@ internal sealed class FrameScreenshotController
|
|||
private readonly Dictionary<string, CaptureStatus> _status =
|
||||
new(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
public FrameScreenshotController(
|
||||
GL gl,
|
||||
string directory,
|
||||
Action<string>? log = null)
|
||||
: this(
|
||||
CreateReader(gl),
|
||||
directory,
|
||||
log)
|
||||
{
|
||||
}
|
||||
|
||||
private static Func<int, int, byte[]> CreateReader(GL gl)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(gl);
|
||||
var surface = new GlDefaultFramebufferSurface(gl);
|
||||
return (width, height) => ReadDefaultFramebuffer(surface, width, height);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the default framebuffer through the same resolve-aware path the
|
||||
/// screenshot gates use. Shared so there is exactly one implementation of
|
||||
/// "read the backbuffer" in the process — see
|
||||
/// <see cref="ReadDefaultFramebuffer(IDefaultFramebufferSurface, int, int)"/>.
|
||||
/// </summary>
|
||||
internal static byte[] ReadDefaultFramebuffer(GL gl, int width, int height)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(gl);
|
||||
return ReadDefaultFramebuffer(
|
||||
new GlDefaultFramebufferSurface(gl),
|
||||
width,
|
||||
height);
|
||||
}
|
||||
|
||||
internal FrameScreenshotController(
|
||||
Func<int, int, byte[]> readRgba,
|
||||
string directory,
|
||||
|
|
@ -186,7 +151,7 @@ internal sealed class FrameScreenshotController
|
|||
/// <summary>
|
||||
/// Blits the whole colour buffer from the bound read framebuffer to the
|
||||
/// bound draw framebuffer with <c>GL_NEAREST</c> and identical rectangles
|
||||
/// — the multisample resolve.
|
||||
/// — the multisample resolve.
|
||||
/// </summary>
|
||||
void BlitColorNearest(int width, int height);
|
||||
|
||||
|
|
@ -194,7 +159,7 @@ internal sealed class FrameScreenshotController
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the default framebuffer — framebuffer name 0, the backbuffer —
|
||||
/// Reads the default framebuffer — framebuffer name 0, the backbuffer —
|
||||
/// and nothing else.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
|
|
@ -204,13 +169,13 @@ internal sealed class FrameScreenshotController
|
|||
/// offscreen target the previous renderer left bound. The frame this runs in
|
||||
/// draws several: <c>PrivateEntityViewportRenderer</c> clears its paperdoll
|
||||
/// and appraisal FBOs to exactly RGBA(0,0,0,0), which is what a leaked
|
||||
/// binding writes to disk — a fully transparent PNG that reads as a
|
||||
/// binding writes to disk — a fully transparent PNG that reads as a
|
||||
/// blank-world failure while the backbuffer on screen was correct.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This was latent for as long as something else rebound framebuffer 0 often
|
||||
/// enough to mask it (before Campaign V slice V4c, GL <c>BeginPass</c> did so
|
||||
/// on every pass — see plan §5.4). The capture states its own source instead
|
||||
/// on every pass — see plan §5.4). The capture states its own source instead
|
||||
/// of inheriting one, and restores the caller's binding so a diagnostic
|
||||
/// capture cannot perturb the frame it observes.
|
||||
/// </para>
|
||||
|
|
@ -218,15 +183,15 @@ internal sealed class FrameScreenshotController
|
|||
/// <b>Multisampling.</b> The window is created with the quality preset's
|
||||
/// MSAA sample count, so the default framebuffer is normally 4x multisampled,
|
||||
/// and <c>glReadPixels</c> against a multisampled read framebuffer is
|
||||
/// <i>undefined</i> per the GL spec (GL 4.6 §18.2: an INVALID_OPERATION is
|
||||
/// <i>undefined</i> per the GL spec (GL 4.6 §18.2: an INVALID_OPERATION is
|
||||
/// generated only for framebuffer objects; for the default framebuffer the
|
||||
/// result is simply unspecified, and AMD returns real pixels most of the time
|
||||
/// and something else the rest). Every automated pixel gate and every blank-
|
||||
/// world verdict in Campaign V reads through here, so an unspecified read is
|
||||
/// an unsound instrument, not a cosmetic issue. When the default framebuffer
|
||||
/// is multisampled the capture resolves it first — blit the whole colour
|
||||
/// is multisampled the capture resolves it first — blit the whole colour
|
||||
/// buffer into a single-sampled RGBA8 framebuffer with identical rectangles
|
||||
/// and <c>GL_NEAREST</c>, which is the defined resolve — and reads that.
|
||||
/// and <c>GL_NEAREST</c>, which is the defined resolve — and reads that.
|
||||
/// A single-sampled default framebuffer keeps the original direct read, so
|
||||
/// non-MSAA captures stay byte-for-byte what they were.
|
||||
/// </para>
|
||||
|
|
@ -271,7 +236,7 @@ internal sealed class FrameScreenshotController
|
|||
uint resolve = surface.CreateResolveTarget(width, height);
|
||||
try
|
||||
{
|
||||
// Read is still framebuffer 0 — the multisampled source.
|
||||
// Read is still framebuffer 0 — the multisampled source.
|
||||
surface.BindDrawFramebuffer(resolve);
|
||||
surface.BlitColorNearest(width, height);
|
||||
surface.BindReadFramebuffer(resolve);
|
||||
|
|
@ -283,134 +248,4 @@ internal sealed class FrameScreenshotController
|
|||
}
|
||||
}
|
||||
|
||||
private sealed class GlDefaultFramebufferSurface : IDefaultFramebufferSurface
|
||||
{
|
||||
private readonly GL _gl;
|
||||
private uint _resolveRenderbuffer;
|
||||
|
||||
public GlDefaultFramebufferSurface(GL gl) => _gl = gl;
|
||||
|
||||
public uint ReadFramebufferBinding
|
||||
{
|
||||
get
|
||||
{
|
||||
_gl.GetInteger(GetPName.ReadFramebufferBinding, out int binding);
|
||||
return (uint)binding;
|
||||
}
|
||||
}
|
||||
|
||||
public uint DrawFramebufferBinding
|
||||
{
|
||||
get
|
||||
{
|
||||
_gl.GetInteger(GetPName.DrawFramebufferBinding, out int binding);
|
||||
return (uint)binding;
|
||||
}
|
||||
}
|
||||
|
||||
public int DefaultFramebufferSamples
|
||||
{
|
||||
get
|
||||
{
|
||||
_gl.GetInteger(GetPName.Samples, out int samples);
|
||||
return samples;
|
||||
}
|
||||
}
|
||||
|
||||
public void BindReadFramebuffer(uint framebuffer) =>
|
||||
_gl.BindFramebuffer(FramebufferTarget.ReadFramebuffer, framebuffer);
|
||||
|
||||
public void BindDrawFramebuffer(uint framebuffer) =>
|
||||
_gl.BindFramebuffer(FramebufferTarget.DrawFramebuffer, framebuffer);
|
||||
|
||||
public uint CreateResolveTarget(int width, int height)
|
||||
{
|
||||
// RGBA8 matches the default framebuffer's colour encoding — the app
|
||||
// never enables GL_FRAMEBUFFER_SRGB — so the blit is a pure resolve
|
||||
// with no encoding conversion.
|
||||
uint renderbuffer = _gl.GenRenderbuffer();
|
||||
_gl.BindRenderbuffer(RenderbufferTarget.Renderbuffer, renderbuffer);
|
||||
_gl.RenderbufferStorage(
|
||||
RenderbufferTarget.Renderbuffer,
|
||||
InternalFormat.Rgba8,
|
||||
(uint)width,
|
||||
(uint)height);
|
||||
_gl.BindRenderbuffer(RenderbufferTarget.Renderbuffer, 0u);
|
||||
|
||||
uint framebuffer = _gl.GenFramebuffer();
|
||||
_gl.BindFramebuffer(FramebufferTarget.DrawFramebuffer, framebuffer);
|
||||
_gl.FramebufferRenderbuffer(
|
||||
FramebufferTarget.DrawFramebuffer,
|
||||
FramebufferAttachment.ColorAttachment0,
|
||||
RenderbufferTarget.Renderbuffer,
|
||||
renderbuffer);
|
||||
GLEnum status =
|
||||
_gl.CheckFramebufferStatus(FramebufferTarget.DrawFramebuffer);
|
||||
if (status != GLEnum.FramebufferComplete)
|
||||
{
|
||||
_gl.BindFramebuffer(FramebufferTarget.DrawFramebuffer, 0u);
|
||||
_gl.DeleteFramebuffer(framebuffer);
|
||||
_gl.DeleteRenderbuffer(renderbuffer);
|
||||
throw new InvalidOperationException(
|
||||
$"multisample resolve framebuffer {width}x{height} is "
|
||||
+ $"incomplete: {status}");
|
||||
}
|
||||
|
||||
GLHelpers.ThrowOnResourceError(
|
||||
_gl,
|
||||
$"create screenshot resolve target {width}x{height}");
|
||||
_resolveRenderbuffer = renderbuffer;
|
||||
return framebuffer;
|
||||
}
|
||||
|
||||
public void DeleteResolveTarget(uint framebuffer)
|
||||
{
|
||||
_gl.DeleteFramebuffer(framebuffer);
|
||||
if (_resolveRenderbuffer != 0u)
|
||||
_gl.DeleteRenderbuffer(_resolveRenderbuffer);
|
||||
_resolveRenderbuffer = 0u;
|
||||
}
|
||||
|
||||
public void BlitColorNearest(int width, int height)
|
||||
{
|
||||
// A blit is subject to the scissor test, so a frame that left a
|
||||
// scissor rectangle armed would resolve only part of the image.
|
||||
// This operation states the state it needs and restores it — the
|
||||
// same self-contained-GL-state rule the render passes follow.
|
||||
bool scissor = _gl.IsEnabled(EnableCap.ScissorTest);
|
||||
if (scissor)
|
||||
_gl.Disable(EnableCap.ScissorTest);
|
||||
_gl.BlitFramebuffer(
|
||||
0,
|
||||
0,
|
||||
width,
|
||||
height,
|
||||
0,
|
||||
0,
|
||||
width,
|
||||
height,
|
||||
ClearBufferMask.ColorBufferBit,
|
||||
BlitFramebufferFilter.Nearest);
|
||||
if (scissor)
|
||||
_gl.Enable(EnableCap.ScissorTest);
|
||||
GLHelpers.ThrowOnResourceError(
|
||||
_gl,
|
||||
$"resolve default framebuffer {width}x{height}");
|
||||
}
|
||||
|
||||
public unsafe void ReadRgba(int width, int height, byte[] destination)
|
||||
{
|
||||
fixed (byte* pointer = destination)
|
||||
{
|
||||
_gl.ReadPixels(
|
||||
0,
|
||||
0,
|
||||
(uint)width,
|
||||
(uint)height,
|
||||
PixelFormat.Rgba,
|
||||
PixelType.UnsignedByte,
|
||||
pointer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue