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,525 +0,0 @@
|
|||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using AcDream.App.Rendering;
|
||||
using Silk.NET.Input;
|
||||
using Silk.NET.GLFW;
|
||||
using Silk.NET.OpenGL;
|
||||
using Silk.NET.Windowing;
|
||||
|
||||
namespace AcDream.App.Platform;
|
||||
|
||||
internal sealed record GraphicalFramebufferCapabilities(
|
||||
int RedBits,
|
||||
int GreenBits,
|
||||
int BlueBits,
|
||||
int AlphaBits,
|
||||
int DepthBits,
|
||||
int StencilBits,
|
||||
int SampleBuffers,
|
||||
int Samples,
|
||||
bool FramebufferSrgbApi);
|
||||
|
||||
internal sealed record GraphicalInputCapabilities(
|
||||
int KeyboardCount,
|
||||
int MouseCount,
|
||||
int GamepadCount,
|
||||
int JoystickCount);
|
||||
|
||||
internal sealed record GraphicalWindowCapabilities(
|
||||
int LogicalWidth,
|
||||
int LogicalHeight,
|
||||
int FramebufferWidth,
|
||||
int FramebufferHeight,
|
||||
string MonitorName,
|
||||
double RefreshRateHz,
|
||||
bool VSync);
|
||||
|
||||
internal sealed record GraphicalAudioCapabilities(
|
||||
bool Requested,
|
||||
bool Available,
|
||||
bool PlaybackSubmitted,
|
||||
bool DisposalComplete,
|
||||
string Backend);
|
||||
|
||||
internal sealed record GraphicalSmokeLifecycleCapabilities(
|
||||
int OwnedWindowCount,
|
||||
int OwnedGlApiCount,
|
||||
int OwnedInputContextCount,
|
||||
int OwnedAudioEngineCount,
|
||||
bool ShutdownComplete);
|
||||
|
||||
internal sealed record GraphicalFunctionProbeResult(
|
||||
bool BindlessTexture,
|
||||
bool ShaderDrawParameters,
|
||||
bool MultiDrawIndirect,
|
||||
bool ShaderStorageBuffer,
|
||||
bool TimerQuery,
|
||||
bool SrgbFramebuffer,
|
||||
bool? PersistentBufferStorage,
|
||||
IReadOnlyList<string> Failures)
|
||||
{
|
||||
internal static GraphicalFunctionProbeResult NotRun { get; } = new(
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
null,
|
||||
["active OpenGL function probe did not run"]);
|
||||
}
|
||||
|
||||
internal sealed record GraphicalCapabilityRecord(
|
||||
DateTimeOffset CapturedAtUtc,
|
||||
string RuntimeIdentifier,
|
||||
GraphicalHostOperatingSystem OperatingSystem,
|
||||
GraphicalDisplayProtocol RequestedDisplayProtocol,
|
||||
GraphicalDisplayProtocol ActiveDisplayProtocol,
|
||||
string DisplaySelectionReason,
|
||||
string WindowBackend,
|
||||
string WindowBackendVersion,
|
||||
string GlVendor,
|
||||
string GlRenderer,
|
||||
string GlVersion,
|
||||
string GlslVersion,
|
||||
int GlMajorVersion,
|
||||
int GlMinorVersion,
|
||||
int ContextProfileMask,
|
||||
int ContextFlags,
|
||||
bool HasBindlessTexture,
|
||||
bool HasShaderDrawParameters,
|
||||
bool HasMultiDrawIndirect,
|
||||
bool HasShaderStorageBuffer,
|
||||
bool HasBufferStorage,
|
||||
bool HasTimerQuery,
|
||||
int MaximumShaderStorageBufferBindings,
|
||||
int MaximumUniformBufferBindings,
|
||||
int MaximumTextureSize,
|
||||
int MaximumArrayTextureLayers,
|
||||
int MaximumCombinedTextureImageUnits,
|
||||
GraphicalFramebufferCapabilities Framebuffer,
|
||||
GraphicalInputCapabilities Input,
|
||||
GraphicalWindowCapabilities Window,
|
||||
GraphicalAudioCapabilities Audio,
|
||||
GraphicalSmokeLifecycleCapabilities Lifecycle,
|
||||
IReadOnlyList<string> Extensions,
|
||||
GraphicalFunctionProbeResult FunctionProbe,
|
||||
IReadOnlyList<string> SupportFailures)
|
||||
{
|
||||
internal bool IsSupported => SupportFailures.Count == 0;
|
||||
}
|
||||
|
||||
internal static class GraphicalCapabilityRequirements
|
||||
{
|
||||
internal static IReadOnlyList<string> Evaluate(
|
||||
GraphicalCapabilityRecord capabilities)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(capabilities);
|
||||
var failures = new List<string>();
|
||||
|
||||
if (capabilities.GlMajorVersion < 4
|
||||
|| capabilities.GlMajorVersion == 4
|
||||
&& capabilities.GlMinorVersion < 3)
|
||||
{
|
||||
failures.Add(
|
||||
"OpenGL 4.3 core is required; the active context reports " +
|
||||
$"{capabilities.GlMajorVersion}.{capabilities.GlMinorVersion}.");
|
||||
}
|
||||
if (!capabilities.HasBindlessTexture)
|
||||
failures.Add("GL_ARB_bindless_texture is required.");
|
||||
if (!capabilities.HasShaderDrawParameters)
|
||||
failures.Add("GL_ARB_shader_draw_parameters is required.");
|
||||
if (!capabilities.HasMultiDrawIndirect)
|
||||
failures.Add("multi-draw indirect support is required.");
|
||||
if (!capabilities.HasShaderStorageBuffer)
|
||||
failures.Add("shader-storage buffers are required.");
|
||||
if (!capabilities.HasTimerQuery)
|
||||
failures.Add("OpenGL timer queries are required.");
|
||||
if (capabilities.Framebuffer.DepthBits < 24)
|
||||
{
|
||||
failures.Add(
|
||||
"the default framebuffer must provide at least 24 depth bits.");
|
||||
}
|
||||
if (capabilities.Framebuffer.StencilBits < 8)
|
||||
{
|
||||
failures.Add(
|
||||
"the default framebuffer must provide at least 8 stencil bits.");
|
||||
}
|
||||
if (!capabilities.Framebuffer.FramebufferSrgbApi)
|
||||
failures.Add("framebuffer sRGB support is required.");
|
||||
if (capabilities.Input.KeyboardCount < 1)
|
||||
failures.Add("the graphical input backend exposed no keyboard.");
|
||||
if (capabilities.Input.MouseCount < 1)
|
||||
failures.Add("the graphical input backend exposed no mouse.");
|
||||
|
||||
if (capabilities.FunctionProbe.Failures.Count != 0)
|
||||
{
|
||||
failures.AddRange(
|
||||
capabilities.FunctionProbe.Failures.Select(
|
||||
failure => $"OpenGL function probe: {failure}"));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!capabilities.FunctionProbe.BindlessTexture)
|
||||
failures.Add("the bindless texture call probe did not pass.");
|
||||
if (!capabilities.FunctionProbe.ShaderDrawParameters)
|
||||
{
|
||||
failures.Add(
|
||||
"the shader draw-parameters compile probe did not pass.");
|
||||
}
|
||||
if (!capabilities.FunctionProbe.MultiDrawIndirect)
|
||||
failures.Add("the multi-draw indirect call probe did not pass.");
|
||||
if (!capabilities.FunctionProbe.ShaderStorageBuffer)
|
||||
failures.Add("the shader-storage buffer call probe did not pass.");
|
||||
if (!capabilities.FunctionProbe.TimerQuery)
|
||||
failures.Add("the timer-query call probe did not pass.");
|
||||
if (!capabilities.FunctionProbe.SrgbFramebuffer)
|
||||
failures.Add("the sRGB framebuffer call probe did not pass.");
|
||||
if (capabilities.HasBufferStorage
|
||||
&& capabilities.FunctionProbe.PersistentBufferStorage != true)
|
||||
{
|
||||
failures.Add(
|
||||
"GL_ARB_buffer_storage was advertised but the persistent " +
|
||||
"mapping call probe did not pass.");
|
||||
}
|
||||
}
|
||||
|
||||
return failures;
|
||||
}
|
||||
}
|
||||
|
||||
internal static class GraphicalCapabilityProbe
|
||||
{
|
||||
private const int GlSampleBuffers = 0x80A8;
|
||||
private const int GlSamples = 0x80A9;
|
||||
private const int GlMaxShaderStorageBufferBindings = 0x90DD;
|
||||
private const int GlMaxUniformBufferBindings = 0x8A2F;
|
||||
private const int GlMaxCombinedTextureImageUnits = 0x8B4D;
|
||||
|
||||
internal static GraphicalCapabilityRecord Capture(
|
||||
GL gl,
|
||||
IWindow window,
|
||||
IInputContext input,
|
||||
GraphicalHostPlatformServices platform)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(gl);
|
||||
ArgumentNullException.ThrowIfNull(window);
|
||||
ArgumentNullException.ThrowIfNull(input);
|
||||
ArgumentNullException.ThrowIfNull(platform);
|
||||
|
||||
int major = gl.GetInteger(GetPName.MajorVersion);
|
||||
int minor = gl.GetInteger(GetPName.MinorVersion);
|
||||
bool openGl43 = major > 4 || major == 4 && minor >= 3;
|
||||
bool timerQuery =
|
||||
major > 3
|
||||
|| major == 3 && minor >= 3
|
||||
|| gl.IsExtensionPresent("GL_ARB_timer_query");
|
||||
bool framebufferSrgb =
|
||||
major > 3
|
||||
|| major == 3 && minor >= 0
|
||||
|| gl.IsExtensionPresent("GL_ARB_framebuffer_sRGB")
|
||||
|| gl.IsExtensionPresent("GL_EXT_framebuffer_sRGB");
|
||||
|
||||
IReadOnlyList<string> extensions = ReadExtensions(gl);
|
||||
IMonitor? monitor = window.Monitor;
|
||||
Silk.NET.Windowing.VideoMode mode =
|
||||
monitor?.VideoMode ?? Silk.NET.Windowing.VideoMode.Default;
|
||||
var framebuffer = window.FramebufferSize;
|
||||
var logical = window.Size;
|
||||
|
||||
var captured = new GraphicalCapabilityRecord(
|
||||
DateTimeOffset.UtcNow,
|
||||
platform.RuntimeIdentifier,
|
||||
platform.OperatingSystem,
|
||||
platform.WindowBackend.RequestedProtocol,
|
||||
GlfwNativePlatformProbe.GetActiveProtocol(
|
||||
platform.OperatingSystem),
|
||||
platform.WindowBackend.Reason,
|
||||
"Silk.NET.Windowing.Glfw",
|
||||
GlfwNativePlatformProbe.GetVersion(
|
||||
platform.OperatingSystem),
|
||||
gl.GetStringS(GLEnum.Vendor),
|
||||
gl.GetStringS(GLEnum.Renderer),
|
||||
gl.GetStringS(GLEnum.Version),
|
||||
gl.GetStringS(GLEnum.ShadingLanguageVersion),
|
||||
major,
|
||||
minor,
|
||||
GetInteger(gl, (GetPName)GLEnum.ContextProfileMask),
|
||||
GetInteger(gl, (GetPName)GLEnum.ContextFlags),
|
||||
gl.IsExtensionPresent("GL_ARB_bindless_texture"),
|
||||
gl.IsExtensionPresent("GL_ARB_shader_draw_parameters"),
|
||||
openGl43
|
||||
|| gl.IsExtensionPresent("GL_ARB_multi_draw_indirect"),
|
||||
openGl43
|
||||
|| gl.IsExtensionPresent("GL_ARB_shader_storage_buffer_object"),
|
||||
major > 4
|
||||
|| major == 4 && minor >= 4
|
||||
|| gl.IsExtensionPresent("GL_ARB_buffer_storage"),
|
||||
timerQuery,
|
||||
GetInteger(
|
||||
gl,
|
||||
(GetPName)GlMaxShaderStorageBufferBindings),
|
||||
GetInteger(gl, (GetPName)GlMaxUniformBufferBindings),
|
||||
GetInteger(gl, GetPName.MaxTextureSize),
|
||||
GetInteger(gl, GetPName.MaxArrayTextureLayers),
|
||||
GetInteger(
|
||||
gl,
|
||||
(GetPName)GlMaxCombinedTextureImageUnits),
|
||||
new GraphicalFramebufferCapabilities(
|
||||
GetFramebufferAttachmentInteger(
|
||||
gl,
|
||||
(FramebufferAttachment)GLEnum.BackLeft,
|
||||
FramebufferAttachmentParameterName.RedSize),
|
||||
GetFramebufferAttachmentInteger(
|
||||
gl,
|
||||
(FramebufferAttachment)GLEnum.BackLeft,
|
||||
FramebufferAttachmentParameterName.GreenSize),
|
||||
GetFramebufferAttachmentInteger(
|
||||
gl,
|
||||
(FramebufferAttachment)GLEnum.BackLeft,
|
||||
FramebufferAttachmentParameterName.BlueSize),
|
||||
GetFramebufferAttachmentInteger(
|
||||
gl,
|
||||
(FramebufferAttachment)GLEnum.BackLeft,
|
||||
FramebufferAttachmentParameterName.AlphaSize),
|
||||
GetFramebufferAttachmentInteger(
|
||||
gl,
|
||||
(FramebufferAttachment)GLEnum.Depth,
|
||||
FramebufferAttachmentParameterName.DepthSize),
|
||||
GetFramebufferAttachmentInteger(
|
||||
gl,
|
||||
(FramebufferAttachment)GLEnum.Stencil,
|
||||
FramebufferAttachmentParameterName.StencilSize),
|
||||
GetInteger(gl, (GetPName)GlSampleBuffers),
|
||||
GetInteger(gl, (GetPName)GlSamples),
|
||||
framebufferSrgb),
|
||||
new GraphicalInputCapabilities(
|
||||
input.Keyboards.Count,
|
||||
input.Mice.Count,
|
||||
input.Gamepads.Count,
|
||||
input.Joysticks.Count),
|
||||
new GraphicalWindowCapabilities(
|
||||
logical.X,
|
||||
logical.Y,
|
||||
framebuffer.X,
|
||||
framebuffer.Y,
|
||||
monitor?.Name ?? "unknown",
|
||||
mode.RefreshRate ?? 0,
|
||||
window.VSync),
|
||||
new GraphicalAudioCapabilities(
|
||||
Requested: false,
|
||||
Available: false,
|
||||
PlaybackSubmitted: false,
|
||||
DisposalComplete: true,
|
||||
Backend: "not requested"),
|
||||
new GraphicalSmokeLifecycleCapabilities(
|
||||
OwnedWindowCount: 1,
|
||||
OwnedGlApiCount: 1,
|
||||
OwnedInputContextCount: 1,
|
||||
OwnedAudioEngineCount: 0,
|
||||
ShutdownComplete: false),
|
||||
extensions,
|
||||
GraphicalFunctionProbeResult.NotRun,
|
||||
[]);
|
||||
return captured with
|
||||
{
|
||||
SupportFailures =
|
||||
GraphicalCapabilityRequirements.Evaluate(captured),
|
||||
};
|
||||
}
|
||||
|
||||
internal static GraphicalCapabilityRecord WithFunctionProbe(
|
||||
GraphicalCapabilityRecord capabilities,
|
||||
GraphicalFunctionProbeResult functionProbe)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(capabilities);
|
||||
ArgumentNullException.ThrowIfNull(functionProbe);
|
||||
GraphicalCapabilityRecord updated = capabilities with
|
||||
{
|
||||
FunctionProbe = functionProbe,
|
||||
SupportFailures = [],
|
||||
};
|
||||
return updated with
|
||||
{
|
||||
SupportFailures =
|
||||
GraphicalCapabilityRequirements.Evaluate(updated),
|
||||
};
|
||||
}
|
||||
|
||||
private static int GetInteger(GL gl, GetPName name)
|
||||
{
|
||||
return GlResourceCommand.Execute(
|
||||
gl,
|
||||
$"query OpenGL integer 0x{(uint)name:X}",
|
||||
() =>
|
||||
{
|
||||
gl.GetInteger(name, out int value);
|
||||
return value;
|
||||
});
|
||||
}
|
||||
|
||||
private static int GetFramebufferAttachmentInteger(
|
||||
GL gl,
|
||||
FramebufferAttachment attachment,
|
||||
FramebufferAttachmentParameterName name)
|
||||
{
|
||||
return GlResourceCommand.Execute(
|
||||
gl,
|
||||
$"query default framebuffer attachment {attachment}/{name}",
|
||||
() =>
|
||||
{
|
||||
gl.GetFramebufferAttachmentParameter(
|
||||
FramebufferTarget.Framebuffer,
|
||||
attachment,
|
||||
name,
|
||||
out int value);
|
||||
return value;
|
||||
});
|
||||
}
|
||||
|
||||
private static IReadOnlyList<string> ReadExtensions(GL gl)
|
||||
{
|
||||
int count = GetInteger(gl, GetPName.NumExtensions);
|
||||
var extensions = new string[Math.Max(0, count)];
|
||||
for (uint index = 0; index < extensions.Length; index++)
|
||||
extensions[index] = gl.GetStringS(GLEnum.Extensions, index);
|
||||
Array.Sort(extensions, StringComparer.Ordinal);
|
||||
return extensions;
|
||||
}
|
||||
}
|
||||
|
||||
internal static class GraphicalCapabilityGuard
|
||||
{
|
||||
internal static GraphicalCapabilityRecord CaptureVerifyAndWrite(
|
||||
GL gl,
|
||||
IWindow window,
|
||||
IInputContext input,
|
||||
GraphicalHostPlatformServices platform,
|
||||
string reportPath)
|
||||
{
|
||||
GraphicalCapabilityRecord passive =
|
||||
GraphicalCapabilityProbe.Capture(
|
||||
gl,
|
||||
window,
|
||||
input,
|
||||
platform);
|
||||
GraphicalFunctionProbeResult functions =
|
||||
GraphicalGlFunctionProbe.Run(gl, passive);
|
||||
GraphicalCapabilityRecord verified =
|
||||
GraphicalCapabilityProbe.WithFunctionProbe(
|
||||
passive,
|
||||
functions);
|
||||
GraphicalCapabilityReportWriter.Write(reportPath, verified);
|
||||
|
||||
return verified;
|
||||
}
|
||||
|
||||
internal static void ThrowIfUnsupported(
|
||||
GraphicalCapabilityRecord capabilities,
|
||||
string reportPath)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(capabilities);
|
||||
if (!capabilities.IsSupported)
|
||||
{
|
||||
throw new NotSupportedException(
|
||||
FormatUnsupportedMessage(capabilities, reportPath));
|
||||
}
|
||||
}
|
||||
|
||||
internal static string FormatUnsupportedMessage(
|
||||
GraphicalCapabilityRecord capabilities,
|
||||
string reportPath)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(capabilities);
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(reportPath);
|
||||
return
|
||||
"acdream's mandatory modern renderer is unsupported by the " +
|
||||
"active graphical backend.\n" +
|
||||
$"Platform: {capabilities.RuntimeIdentifier}, " +
|
||||
$"{capabilities.ActiveDisplayProtocol}, " +
|
||||
$"{capabilities.GlVendor} / {capabilities.GlRenderer}, " +
|
||||
$"{capabilities.GlVersion}\n" +
|
||||
string.Join(
|
||||
"\n",
|
||||
capabilities.SupportFailures.Select(
|
||||
failure => $" - {failure}")) +
|
||||
$"\nFull capability report: {Path.GetFullPath(reportPath)}";
|
||||
}
|
||||
}
|
||||
|
||||
internal static class GraphicalCapabilityReportWriter
|
||||
{
|
||||
private static readonly JsonSerializerOptions Options = new()
|
||||
{
|
||||
WriteIndented = true,
|
||||
Converters =
|
||||
{
|
||||
new JsonStringEnumConverter(),
|
||||
},
|
||||
};
|
||||
|
||||
internal static void Write(
|
||||
string path,
|
||||
GraphicalCapabilityRecord capabilities)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(path);
|
||||
ArgumentNullException.ThrowIfNull(capabilities);
|
||||
string fullPath = Path.GetFullPath(path);
|
||||
string? directory = Path.GetDirectoryName(fullPath);
|
||||
if (!string.IsNullOrEmpty(directory))
|
||||
Directory.CreateDirectory(directory);
|
||||
|
||||
string temporaryPath = fullPath + ".tmp";
|
||||
File.WriteAllText(
|
||||
temporaryPath,
|
||||
JsonSerializer.Serialize(capabilities, Options));
|
||||
File.Move(temporaryPath, fullPath, overwrite: true);
|
||||
}
|
||||
}
|
||||
|
||||
internal static unsafe class GlfwNativePlatformProbe
|
||||
{
|
||||
private const int GlfwWin32Platform = 0x00060001;
|
||||
private const int GlfwWaylandPlatform = 0x00060003;
|
||||
private const int GlfwX11Platform = 0x00060004;
|
||||
|
||||
internal static GraphicalDisplayProtocol GetActiveProtocol(
|
||||
GraphicalHostOperatingSystem operatingSystem)
|
||||
{
|
||||
if (operatingSystem == GraphicalHostOperatingSystem.Windows)
|
||||
return GraphicalDisplayProtocol.Windows;
|
||||
|
||||
if (!GraphicalWindowBackendConfigurator.TryGetConfiguredApi(
|
||||
out Glfw? glfw)
|
||||
|| glfw is null
|
||||
|| !glfw.Context.TryGetProcAddress(
|
||||
"glfwGetPlatform",
|
||||
out nint export))
|
||||
{
|
||||
return GraphicalDisplayProtocol.Unknown;
|
||||
}
|
||||
|
||||
int platform =
|
||||
((delegate* unmanaged[Cdecl]<int>)export)();
|
||||
return platform switch
|
||||
{
|
||||
GlfwX11Platform => GraphicalDisplayProtocol.X11,
|
||||
GlfwWaylandPlatform => GraphicalDisplayProtocol.Wayland,
|
||||
GlfwWin32Platform => GraphicalDisplayProtocol.Windows,
|
||||
_ => GraphicalDisplayProtocol.Unknown,
|
||||
};
|
||||
}
|
||||
|
||||
internal static string GetVersion(
|
||||
GraphicalHostOperatingSystem operatingSystem)
|
||||
{
|
||||
if (!GraphicalWindowBackendConfigurator.TryGetConfiguredApi(
|
||||
out Glfw? glfw)
|
||||
|| glfw is null)
|
||||
{
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
return glfw.GetVersionString() ?? "unknown";
|
||||
}
|
||||
}
|
||||
|
|
@ -1,513 +0,0 @@
|
|||
using AcDream.App.Rendering;
|
||||
using AcDream.App.Rendering.Wb;
|
||||
using Silk.NET.OpenGL;
|
||||
|
||||
namespace AcDream.App.Platform;
|
||||
|
||||
/// <summary>
|
||||
/// Executes one minimal, ownership-balanced call path for every OpenGL feature
|
||||
/// the mandatory renderer relies upon. Advertisement alone is insufficient:
|
||||
/// several Linux driver failures present an extension string while returning
|
||||
/// a missing entry point, invalid bindless handle, or unusable framebuffer.
|
||||
/// </summary>
|
||||
internal static unsafe class GraphicalGlFunctionProbe
|
||||
{
|
||||
private const string VertexSource = """
|
||||
#version 430 core
|
||||
#extension GL_ARB_shader_draw_parameters : require
|
||||
void main()
|
||||
{
|
||||
float identity =
|
||||
float(gl_DrawIDARB + gl_BaseInstanceARB) * 0.0;
|
||||
gl_Position = vec4(identity, 0.0, 0.0, 1.0);
|
||||
}
|
||||
""";
|
||||
|
||||
private const string FragmentSource = """
|
||||
#version 430 core
|
||||
layout(location = 0) out vec4 outColor;
|
||||
void main()
|
||||
{
|
||||
outColor = vec4(1.0);
|
||||
}
|
||||
""";
|
||||
|
||||
internal static GraphicalFunctionProbeResult Run(
|
||||
GL gl,
|
||||
GraphicalCapabilityRecord capabilities)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(gl);
|
||||
ArgumentNullException.ThrowIfNull(capabilities);
|
||||
var failures = new List<string>();
|
||||
|
||||
bool bindless = capabilities.HasBindlessTexture
|
||||
&& Attempt(
|
||||
"bindless texture handle/residency",
|
||||
() => ProbeBindless(gl),
|
||||
failures);
|
||||
|
||||
bool shaderDraw = capabilities.HasShaderDrawParameters
|
||||
&& Attempt(
|
||||
"shader draw parameters and multi-draw indirect",
|
||||
() => ProbeShaderDrawAndMultiDraw(gl),
|
||||
failures);
|
||||
bool multiDraw = shaderDraw
|
||||
&& capabilities.HasMultiDrawIndirect;
|
||||
|
||||
bool shaderStorage = capabilities.HasShaderStorageBuffer
|
||||
&& Attempt(
|
||||
"shader-storage buffer",
|
||||
() => ProbeShaderStorageBuffer(gl),
|
||||
failures);
|
||||
|
||||
bool timer = capabilities.HasTimerQuery
|
||||
&& Attempt(
|
||||
"timer query",
|
||||
() => ProbeTimerQuery(gl),
|
||||
failures);
|
||||
|
||||
bool srgb = capabilities.Framebuffer.FramebufferSrgbApi
|
||||
&& Attempt(
|
||||
"sRGB depth/stencil framebuffer",
|
||||
() => ProbeSrgbFramebuffer(gl),
|
||||
failures);
|
||||
|
||||
bool? persistent = capabilities.HasBufferStorage
|
||||
? Attempt(
|
||||
"persistent coherent buffer storage",
|
||||
() => ProbePersistentBufferStorage(gl),
|
||||
failures)
|
||||
: null;
|
||||
|
||||
if (!capabilities.HasBindlessTexture)
|
||||
failures.Add("GL_ARB_bindless_texture was not advertised.");
|
||||
if (!capabilities.HasShaderDrawParameters)
|
||||
failures.Add("GL_ARB_shader_draw_parameters was not advertised.");
|
||||
if (!capabilities.HasMultiDrawIndirect)
|
||||
failures.Add("multi-draw indirect was not advertised.");
|
||||
if (!capabilities.HasShaderStorageBuffer)
|
||||
failures.Add("shader-storage buffers were not advertised.");
|
||||
if (!capabilities.HasTimerQuery)
|
||||
failures.Add("timer queries were not advertised.");
|
||||
if (!capabilities.Framebuffer.FramebufferSrgbApi)
|
||||
failures.Add("framebuffer sRGB was not advertised.");
|
||||
|
||||
return new GraphicalFunctionProbeResult(
|
||||
bindless,
|
||||
shaderDraw,
|
||||
multiDraw,
|
||||
shaderStorage,
|
||||
timer,
|
||||
srgb,
|
||||
persistent,
|
||||
failures);
|
||||
}
|
||||
|
||||
private static bool Attempt(
|
||||
string name,
|
||||
Action action,
|
||||
List<string> failures)
|
||||
{
|
||||
try
|
||||
{
|
||||
action();
|
||||
return true;
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
failures.Add($"{name}: {error.GetType().Name}: {error.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static void ProbeBindless(GL gl)
|
||||
{
|
||||
if (!BindlessSupport.TryCreate(gl, out BindlessSupport? bindless)
|
||||
|| bindless is null)
|
||||
{
|
||||
throw new NotSupportedException(
|
||||
"the ARB bindless extension object could not be loaded.");
|
||||
}
|
||||
|
||||
uint texture = GlResourceCommand.CreateTexture(
|
||||
gl,
|
||||
"graphical capability bindless texture");
|
||||
ulong handle = 0;
|
||||
try
|
||||
{
|
||||
byte* pixel = stackalloc byte[4] { 255, 255, 255, 255 };
|
||||
GlResourceCommand.Execute(
|
||||
gl,
|
||||
"initialize graphical capability bindless texture",
|
||||
() =>
|
||||
{
|
||||
gl.BindTexture(TextureTarget.Texture2D, texture);
|
||||
gl.TexImage2D(
|
||||
TextureTarget.Texture2D,
|
||||
0,
|
||||
InternalFormat.Rgba8,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
PixelFormat.Rgba,
|
||||
PixelType.UnsignedByte,
|
||||
pixel);
|
||||
gl.TexParameter(
|
||||
TextureTarget.Texture2D,
|
||||
TextureParameterName.TextureMinFilter,
|
||||
(int)TextureMinFilter.Nearest);
|
||||
gl.TexParameter(
|
||||
TextureTarget.Texture2D,
|
||||
TextureParameterName.TextureMagFilter,
|
||||
(int)TextureMagFilter.Nearest);
|
||||
});
|
||||
handle = bindless.GetResidentHandle(texture);
|
||||
bindless.MakeNonResident(handle);
|
||||
handle = 0;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (handle != 0)
|
||||
bindless.MakeNonResident(handle);
|
||||
gl.BindTexture(TextureTarget.Texture2D, 0);
|
||||
GlResourceCommand.DeleteTexture(
|
||||
gl,
|
||||
texture,
|
||||
"delete graphical capability bindless texture");
|
||||
}
|
||||
}
|
||||
|
||||
private static void ProbeShaderDrawAndMultiDraw(GL gl)
|
||||
{
|
||||
uint program = ShaderProgramConstruction.Build(
|
||||
new GlShaderProgramBuildApi(gl),
|
||||
VertexSource,
|
||||
FragmentSource);
|
||||
uint vertexArray = 0;
|
||||
uint elementBuffer = 0;
|
||||
uint indirectBuffer = 0;
|
||||
try
|
||||
{
|
||||
vertexArray = GlResourceCommand.CreateName(
|
||||
gl,
|
||||
"graphical capability vertex array",
|
||||
gl.GenVertexArray,
|
||||
gl.DeleteVertexArray);
|
||||
elementBuffer = GlResourceCommand.CreateName(
|
||||
gl,
|
||||
"graphical capability element buffer",
|
||||
gl.GenBuffer,
|
||||
gl.DeleteBuffer);
|
||||
indirectBuffer = GlResourceCommand.CreateName(
|
||||
gl,
|
||||
"graphical capability indirect buffer",
|
||||
gl.GenBuffer,
|
||||
gl.DeleteBuffer);
|
||||
|
||||
uint* index = stackalloc uint[1] { 0 };
|
||||
uint* command = stackalloc uint[5]
|
||||
{
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
};
|
||||
GLHelpers.ThrowOnResourceError(
|
||||
gl,
|
||||
"execute graphical capability multi-draw indirect call " +
|
||||
"(precondition)");
|
||||
gl.UseProgram(program);
|
||||
gl.BindVertexArray(vertexArray);
|
||||
gl.BindBuffer(
|
||||
BufferTargetARB.ElementArrayBuffer,
|
||||
elementBuffer);
|
||||
gl.BufferData(
|
||||
BufferTargetARB.ElementArrayBuffer,
|
||||
(nuint)sizeof(uint),
|
||||
index,
|
||||
BufferUsageARB.StaticDraw);
|
||||
gl.BindBuffer(
|
||||
BufferTargetARB.DrawIndirectBuffer,
|
||||
indirectBuffer);
|
||||
gl.BufferData(
|
||||
BufferTargetARB.DrawIndirectBuffer,
|
||||
(nuint)(sizeof(uint) * 5),
|
||||
command,
|
||||
BufferUsageARB.StaticDraw);
|
||||
gl.MultiDrawElementsIndirect(
|
||||
PrimitiveType.Triangles,
|
||||
DrawElementsType.UnsignedInt,
|
||||
null,
|
||||
1,
|
||||
0);
|
||||
GLHelpers.ThrowOnResourceError(
|
||||
gl,
|
||||
"execute graphical capability multi-draw indirect call");
|
||||
}
|
||||
finally
|
||||
{
|
||||
gl.UseProgram(0);
|
||||
gl.BindVertexArray(0);
|
||||
gl.BindBuffer(BufferTargetARB.DrawIndirectBuffer, 0);
|
||||
if (indirectBuffer != 0)
|
||||
{
|
||||
GlResourceCommand.DeleteBuffer(
|
||||
gl,
|
||||
indirectBuffer,
|
||||
"delete graphical capability indirect buffer");
|
||||
}
|
||||
if (elementBuffer != 0)
|
||||
{
|
||||
GlResourceCommand.DeleteBuffer(
|
||||
gl,
|
||||
elementBuffer,
|
||||
"delete graphical capability element buffer");
|
||||
}
|
||||
if (vertexArray != 0)
|
||||
{
|
||||
GlResourceCommand.DeleteVertexArray(
|
||||
gl,
|
||||
vertexArray,
|
||||
"delete graphical capability vertex array");
|
||||
}
|
||||
GlResourceCommand.DeleteProgram(
|
||||
gl,
|
||||
program,
|
||||
"delete graphical capability shader program");
|
||||
}
|
||||
}
|
||||
|
||||
private static void ProbeShaderStorageBuffer(GL gl)
|
||||
{
|
||||
uint buffer = GlResourceCommand.CreateName(
|
||||
gl,
|
||||
"graphical capability shader-storage buffer",
|
||||
gl.GenBuffer,
|
||||
gl.DeleteBuffer);
|
||||
try
|
||||
{
|
||||
uint* value = stackalloc uint[1] { 0xACD0_0001u };
|
||||
GLHelpers.ThrowOnResourceError(
|
||||
gl,
|
||||
"bind graphical capability shader-storage buffer " +
|
||||
"(precondition)");
|
||||
gl.BindBuffer(BufferTargetARB.ShaderStorageBuffer, buffer);
|
||||
gl.BufferData(
|
||||
BufferTargetARB.ShaderStorageBuffer,
|
||||
(nuint)sizeof(uint),
|
||||
value,
|
||||
BufferUsageARB.StaticDraw);
|
||||
gl.BindBufferBase(
|
||||
BufferTargetARB.ShaderStorageBuffer,
|
||||
0,
|
||||
buffer);
|
||||
gl.BindBufferBase(
|
||||
BufferTargetARB.ShaderStorageBuffer,
|
||||
0,
|
||||
0);
|
||||
GLHelpers.ThrowOnResourceError(
|
||||
gl,
|
||||
"bind graphical capability shader-storage buffer");
|
||||
}
|
||||
finally
|
||||
{
|
||||
gl.BindBuffer(BufferTargetARB.ShaderStorageBuffer, 0);
|
||||
GlResourceCommand.DeleteBuffer(
|
||||
gl,
|
||||
buffer,
|
||||
"delete graphical capability shader-storage buffer");
|
||||
}
|
||||
}
|
||||
|
||||
private static void ProbeTimerQuery(GL gl)
|
||||
{
|
||||
uint query = GlResourceCommand.CreateName(
|
||||
gl,
|
||||
"graphical capability timer query",
|
||||
gl.GenQuery,
|
||||
gl.DeleteQuery);
|
||||
try
|
||||
{
|
||||
GlResourceCommand.Execute(
|
||||
gl,
|
||||
"execute graphical capability timer query",
|
||||
() =>
|
||||
{
|
||||
gl.BeginQuery(QueryTarget.TimeElapsed, query);
|
||||
gl.EndQuery(QueryTarget.TimeElapsed);
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
GlResourceCommand.Execute(
|
||||
gl,
|
||||
"delete graphical capability timer query",
|
||||
() => gl.DeleteQuery(query));
|
||||
}
|
||||
}
|
||||
|
||||
private static void ProbeSrgbFramebuffer(GL gl)
|
||||
{
|
||||
uint texture = 0;
|
||||
uint depthStencil = 0;
|
||||
uint framebuffer = 0;
|
||||
try
|
||||
{
|
||||
texture = GlResourceCommand.CreateTexture(
|
||||
gl,
|
||||
"graphical capability sRGB color texture");
|
||||
depthStencil = GlResourceCommand.CreateName(
|
||||
gl,
|
||||
"graphical capability depth/stencil renderbuffer",
|
||||
gl.GenRenderbuffer,
|
||||
gl.DeleteRenderbuffer);
|
||||
framebuffer = GlResourceCommand.CreateName(
|
||||
gl,
|
||||
"graphical capability framebuffer",
|
||||
gl.GenFramebuffer,
|
||||
gl.DeleteFramebuffer);
|
||||
|
||||
GlResourceCommand.Execute(
|
||||
gl,
|
||||
"configure graphical capability sRGB framebuffer",
|
||||
() =>
|
||||
{
|
||||
gl.BindTexture(TextureTarget.Texture2D, texture);
|
||||
gl.TexImage2D(
|
||||
TextureTarget.Texture2D,
|
||||
0,
|
||||
InternalFormat.Srgb8Alpha8,
|
||||
2,
|
||||
2,
|
||||
0,
|
||||
PixelFormat.Rgba,
|
||||
PixelType.UnsignedByte,
|
||||
null);
|
||||
|
||||
gl.BindRenderbuffer(
|
||||
RenderbufferTarget.Renderbuffer,
|
||||
depthStencil);
|
||||
gl.RenderbufferStorage(
|
||||
RenderbufferTarget.Renderbuffer,
|
||||
InternalFormat.Depth24Stencil8,
|
||||
2,
|
||||
2);
|
||||
|
||||
gl.BindFramebuffer(
|
||||
FramebufferTarget.Framebuffer,
|
||||
framebuffer);
|
||||
gl.FramebufferTexture2D(
|
||||
FramebufferTarget.Framebuffer,
|
||||
FramebufferAttachment.ColorAttachment0,
|
||||
TextureTarget.Texture2D,
|
||||
texture,
|
||||
0);
|
||||
gl.FramebufferRenderbuffer(
|
||||
FramebufferTarget.Framebuffer,
|
||||
FramebufferAttachment.DepthStencilAttachment,
|
||||
RenderbufferTarget.Renderbuffer,
|
||||
depthStencil);
|
||||
if (gl.CheckFramebufferStatus(
|
||||
FramebufferTarget.Framebuffer)
|
||||
!= GLEnum.FramebufferComplete)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"the sRGB depth/stencil framebuffer is incomplete.");
|
||||
}
|
||||
|
||||
gl.Enable(EnableCap.FramebufferSrgb);
|
||||
gl.Clear(
|
||||
ClearBufferMask.ColorBufferBit
|
||||
| ClearBufferMask.DepthBufferBit
|
||||
| ClearBufferMask.StencilBufferBit);
|
||||
gl.Disable(EnableCap.FramebufferSrgb);
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
gl.Disable(EnableCap.FramebufferSrgb);
|
||||
gl.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
|
||||
gl.BindRenderbuffer(RenderbufferTarget.Renderbuffer, 0);
|
||||
gl.BindTexture(TextureTarget.Texture2D, 0);
|
||||
if (framebuffer != 0)
|
||||
{
|
||||
GlResourceCommand.Execute(
|
||||
gl,
|
||||
"delete graphical capability framebuffer",
|
||||
() => gl.DeleteFramebuffer(framebuffer));
|
||||
}
|
||||
if (depthStencil != 0)
|
||||
{
|
||||
GlResourceCommand.Execute(
|
||||
gl,
|
||||
"delete graphical capability depth/stencil renderbuffer",
|
||||
() => gl.DeleteRenderbuffer(depthStencil));
|
||||
}
|
||||
if (texture != 0)
|
||||
{
|
||||
GlResourceCommand.DeleteTexture(
|
||||
gl,
|
||||
texture,
|
||||
"delete graphical capability sRGB texture");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void ProbePersistentBufferStorage(GL gl)
|
||||
{
|
||||
uint buffer = GlResourceCommand.CreateName(
|
||||
gl,
|
||||
"graphical capability persistent buffer",
|
||||
gl.GenBuffer,
|
||||
gl.DeleteBuffer);
|
||||
void* mapped = null;
|
||||
try
|
||||
{
|
||||
GlResourceCommand.Execute(
|
||||
gl,
|
||||
"allocate graphical capability persistent buffer",
|
||||
() =>
|
||||
{
|
||||
gl.BindBuffer(BufferTargetARB.ArrayBuffer, buffer);
|
||||
gl.BufferStorage(
|
||||
GLEnum.ArrayBuffer,
|
||||
64,
|
||||
null,
|
||||
(uint)(
|
||||
BufferStorageMask.MapWriteBit
|
||||
| BufferStorageMask.MapPersistentBit
|
||||
| BufferStorageMask.MapCoherentBit
|
||||
| BufferStorageMask.DynamicStorageBit));
|
||||
mapped = gl.MapBufferRange(
|
||||
BufferTargetARB.ArrayBuffer,
|
||||
0,
|
||||
64,
|
||||
MapBufferAccessMask.WriteBit
|
||||
| MapBufferAccessMask.PersistentBit
|
||||
| MapBufferAccessMask.CoherentBit);
|
||||
if (mapped is null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"persistent mapping returned a null pointer.");
|
||||
}
|
||||
*((byte*)mapped) = 0x5A;
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (mapped is not null)
|
||||
{
|
||||
GlResourceCommand.Execute(
|
||||
gl,
|
||||
"unmap graphical capability persistent buffer",
|
||||
() => gl.UnmapBuffer(BufferTargetARB.ArrayBuffer));
|
||||
}
|
||||
gl.BindBuffer(BufferTargetARB.ArrayBuffer, 0);
|
||||
GlResourceCommand.DeleteBuffer(
|
||||
gl,
|
||||
buffer,
|
||||
"delete graphical capability persistent buffer");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -187,3 +187,58 @@ internal static class GraphicalWindowBackendConfigurator
|
|||
DefaultPathResolver.BaseDirectoryResolver);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the GLFW platform actually selected at runtime (as opposed to the one
|
||||
/// requested — GLFW's <c>Automatic</c> hint can resolve to either X11 or
|
||||
/// Wayland). Moved here from the deleted (Campaign V slice V11) raw-GL
|
||||
/// <c>GraphicalCapabilityRecord.cs</c>: <see cref="VulkanGraphicsContext"/>
|
||||
/// depends on this for its own capability report, so it survived the GL arm
|
||||
/// that used to sit alongside it.
|
||||
/// </summary>
|
||||
internal static unsafe class GlfwNativePlatformProbe
|
||||
{
|
||||
private const int GlfwWin32Platform = 0x00060001;
|
||||
private const int GlfwWaylandPlatform = 0x00060003;
|
||||
private const int GlfwX11Platform = 0x00060004;
|
||||
|
||||
internal static GraphicalDisplayProtocol GetActiveProtocol(
|
||||
GraphicalHostOperatingSystem operatingSystem)
|
||||
{
|
||||
if (operatingSystem == GraphicalHostOperatingSystem.Windows)
|
||||
return GraphicalDisplayProtocol.Windows;
|
||||
|
||||
if (!GraphicalWindowBackendConfigurator.TryGetConfiguredApi(
|
||||
out Glfw? glfw)
|
||||
|| glfw is null
|
||||
|| !glfw.Context.TryGetProcAddress(
|
||||
"glfwGetPlatform",
|
||||
out nint export))
|
||||
{
|
||||
return GraphicalDisplayProtocol.Unknown;
|
||||
}
|
||||
|
||||
int platform =
|
||||
((delegate* unmanaged[Cdecl]<int>)export)();
|
||||
return platform switch
|
||||
{
|
||||
GlfwX11Platform => GraphicalDisplayProtocol.X11,
|
||||
GlfwWaylandPlatform => GraphicalDisplayProtocol.Wayland,
|
||||
GlfwWin32Platform => GraphicalDisplayProtocol.Windows,
|
||||
_ => GraphicalDisplayProtocol.Unknown,
|
||||
};
|
||||
}
|
||||
|
||||
internal static string GetVersion(
|
||||
GraphicalHostOperatingSystem operatingSystem)
|
||||
{
|
||||
if (!GraphicalWindowBackendConfigurator.TryGetConfiguredApi(
|
||||
out Glfw? glfw)
|
||||
|| glfw is null)
|
||||
{
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
return glfw.GetVersionString() ?? "unknown";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue