refactor(app): compose world rendering startup
Move Region/environment, mandatory modern rendering, terrain, WB, texture, and sampler construction behind the typed Phase-4 composition boundary. Give every fallible GL constructor prefix retryable ownership so partial startup failure cannot leak or replay resource deletion while preserving the accepted render path and DAT inputs. Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
parent
cd7b519f78
commit
6a2fe98cc4
22 changed files with 1983 additions and 634 deletions
|
|
@ -1,6 +1,7 @@
|
|||
using Chorizite.Core.Render;
|
||||
using Chorizite.Core.Render.Enums;
|
||||
using Chorizite.Core.Render.Vertex;
|
||||
using AcDream.App.Rendering;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Silk.NET.OpenGL;
|
||||
// IUniformBuffer is in Chorizite.Core.dll but under the Chorizite.OpenGLSDLBackend namespace
|
||||
|
|
@ -149,38 +150,49 @@ namespace AcDream.App.Rendering.Wb {
|
|||
HasBindless = false;
|
||||
}
|
||||
|
||||
GL.GenBuffers(1, out uint instanceVbo);
|
||||
InstanceVBO = instanceVbo;
|
||||
var resources = new ResourceCleanupGroup();
|
||||
try {
|
||||
InstanceVBO = CreateConstructionBuffer(resources, "WB instance buffer");
|
||||
|
||||
// Query this immutable device limit once. Atlas construction can
|
||||
// happen hundreds of times during portal streaming; repeating a
|
||||
// driver GetFloat for every texture serialized the upload burst.
|
||||
if (renderSettings.EnableAnisotropicFiltering) {
|
||||
GL.GetFloat(GLEnum.MaxTextureMaxAnisotropy, out float maxAniso);
|
||||
MaxSupportedAnisotropy = Math.Max(0f, maxAniso);
|
||||
// Query this immutable device limit once. Atlas construction can
|
||||
// happen hundreds of times during portal streaming; repeating a
|
||||
// driver GetFloat for every texture serialized the upload burst.
|
||||
if (renderSettings.EnableAnisotropicFiltering) {
|
||||
MaxSupportedAnisotropy = GlResourceCommand.Execute(
|
||||
GL,
|
||||
"query maximum texture anisotropy",
|
||||
() => {
|
||||
GL.GetFloat(GLEnum.MaxTextureMaxAnisotropy, out float maxAniso);
|
||||
return Math.Max(0f, maxAniso);
|
||||
});
|
||||
}
|
||||
|
||||
WrapSampler = CreateConstructionSampler(
|
||||
resources,
|
||||
TextureWrapMode.Repeat,
|
||||
"WB repeat sampler");
|
||||
ClampSampler = CreateConstructionSampler(
|
||||
resources,
|
||||
TextureWrapMode.ClampToEdge,
|
||||
"WB clamp sampler");
|
||||
|
||||
_sceneDataBuffer = new ManagedGLUniformBuffer(
|
||||
this,
|
||||
BufferUsage.Dynamic,
|
||||
Marshal.SizeOf<SceneData>());
|
||||
ManagedGLUniformBuffer ownedSceneDataBuffer = _sceneDataBuffer;
|
||||
resources.Add(
|
||||
"WB scene-data uniform buffer",
|
||||
ownedSceneDataBuffer.DisposeImmediately);
|
||||
|
||||
InitializeSharedDebugResources(resources);
|
||||
resources.TransferAll();
|
||||
} catch (Exception constructionFailure) {
|
||||
resources.RollbackConstructionAndThrow(
|
||||
"OpenGLGraphicsDevice construction failed and its GL prefix did not cleanly roll back.",
|
||||
constructionFailure);
|
||||
}
|
||||
|
||||
// Create sampler objects for wrap vs clamp
|
||||
WrapSampler = GL.GenSampler();
|
||||
GL.SamplerParameter(WrapSampler, SamplerParameterI.WrapS, (int)TextureWrapMode.Repeat);
|
||||
GL.SamplerParameter(WrapSampler, SamplerParameterI.WrapT, (int)TextureWrapMode.Repeat);
|
||||
GL.SamplerParameter(WrapSampler, SamplerParameterI.MinFilter, (int)TextureMinFilter.LinearMipmapLinear);
|
||||
GL.SamplerParameter(WrapSampler, SamplerParameterI.MagFilter, (int)TextureMagFilter.Linear);
|
||||
if (MaxSupportedAnisotropy > 0)
|
||||
GL.SamplerParameter(WrapSampler, GLEnum.TextureMaxAnisotropy, MaxSupportedAnisotropy);
|
||||
|
||||
ClampSampler = GL.GenSampler();
|
||||
GL.SamplerParameter(ClampSampler, SamplerParameterI.WrapS, (int)TextureWrapMode.ClampToEdge);
|
||||
GL.SamplerParameter(ClampSampler, SamplerParameterI.WrapT, (int)TextureWrapMode.ClampToEdge);
|
||||
GL.SamplerParameter(ClampSampler, SamplerParameterI.MinFilter, (int)TextureMinFilter.LinearMipmapLinear);
|
||||
GL.SamplerParameter(ClampSampler, SamplerParameterI.MagFilter, (int)TextureMagFilter.Linear);
|
||||
if (MaxSupportedAnisotropy > 0)
|
||||
GL.SamplerParameter(ClampSampler, GLEnum.TextureMaxAnisotropy, MaxSupportedAnisotropy);
|
||||
|
||||
_sceneDataBuffer = new ManagedGLUniformBuffer(this, BufferUsage.Dynamic, Marshal.SizeOf<SceneData>());
|
||||
|
||||
InitializeSharedDebugResources();
|
||||
|
||||
// ParticleBatcher is constructed post-ctor by WbMeshAdapter (WbMeshAdapter.cs:78)
|
||||
// after the adapter has wired up all dependencies. The null! here is overridden
|
||||
// immediately after construction; it is not observable as null at runtime.
|
||||
|
|
@ -196,7 +208,49 @@ namespace AcDream.App.Rendering.Wb {
|
|||
internal AcDream.App.Rendering.IGpuResourceRetirementQueue ResourceRetirement =>
|
||||
_resourceRetirement;
|
||||
|
||||
private void InitializeSharedDebugResources() {
|
||||
private uint CreateConstructionBuffer(ResourceCleanupGroup resources, string name) {
|
||||
uint buffer = GlResourceCommand.CreateName(GL, name, GL.GenBuffer, GL.DeleteBuffer);
|
||||
resources.Add(
|
||||
name,
|
||||
() => GlResourceCommand.DeleteBuffer(
|
||||
GL,
|
||||
buffer,
|
||||
$"delete {name} {buffer}"));
|
||||
return buffer;
|
||||
}
|
||||
|
||||
private uint CreateConstructionSampler(
|
||||
ResourceCleanupGroup resources,
|
||||
TextureWrapMode wrapMode,
|
||||
string name) {
|
||||
uint sampler = GlResourceCommand.CreateName(GL, name, GL.GenSampler, GL.DeleteSampler);
|
||||
resources.Add(
|
||||
name,
|
||||
() => GlResourceCommand.Execute(
|
||||
GL,
|
||||
$"delete {name} {sampler}",
|
||||
() => GL.DeleteSampler(sampler)));
|
||||
GlResourceCommand.Execute(GL, $"configure {name}", () => {
|
||||
GL.SamplerParameter(sampler, SamplerParameterI.WrapS, (int)wrapMode);
|
||||
GL.SamplerParameter(sampler, SamplerParameterI.WrapT, (int)wrapMode);
|
||||
GL.SamplerParameter(
|
||||
sampler,
|
||||
SamplerParameterI.MinFilter,
|
||||
(int)TextureMinFilter.LinearMipmapLinear);
|
||||
GL.SamplerParameter(
|
||||
sampler,
|
||||
SamplerParameterI.MagFilter,
|
||||
(int)TextureMagFilter.Linear);
|
||||
if (MaxSupportedAnisotropy > 0)
|
||||
GL.SamplerParameter(
|
||||
sampler,
|
||||
GLEnum.TextureMaxAnisotropy,
|
||||
MaxSupportedAnisotropy);
|
||||
});
|
||||
return sampler;
|
||||
}
|
||||
|
||||
private void InitializeSharedDebugResources(ResourceCleanupGroup resources) {
|
||||
// Unit quad vertices for two triangles (0 to 1 for length, -0.5 to 0.5 for thickness)
|
||||
float[] quadVertices = {
|
||||
0.0f, -0.5f,
|
||||
|
|
@ -207,53 +261,74 @@ namespace AcDream.App.Rendering.Wb {
|
|||
0.0f, 0.5f
|
||||
};
|
||||
|
||||
GL.GenBuffers(1, out uint quadVbo);
|
||||
SharedQuadVBO = quadVbo;
|
||||
GL.BindBuffer(GLEnum.ArrayBuffer, SharedQuadVBO);
|
||||
fixed (float* pQuad = quadVertices) {
|
||||
GL.BufferData(GLEnum.ArrayBuffer, (nuint)(quadVertices.Length * sizeof(float)), pQuad, GLEnum.StaticDraw);
|
||||
}
|
||||
SharedQuadVBO = CreateConstructionBuffer(resources, "WB shared debug quad buffer");
|
||||
SharedDebugInstanceVBO = CreateConstructionBuffer(
|
||||
resources,
|
||||
"WB shared debug instance buffer");
|
||||
SharedDebugVAO = GlResourceCommand.CreateName(
|
||||
GL,
|
||||
"WB shared debug vertex array",
|
||||
GL.GenVertexArray,
|
||||
GL.DeleteVertexArray);
|
||||
uint ownedDebugVao = SharedDebugVAO;
|
||||
resources.Add(
|
||||
"WB shared debug vertex array",
|
||||
() => GlResourceCommand.DeleteVertexArray(
|
||||
GL,
|
||||
ownedDebugVao,
|
||||
$"delete WB shared debug vertex array {ownedDebugVao}"));
|
||||
|
||||
GL.GenBuffers(1, out uint debugInstanceVbo);
|
||||
SharedDebugInstanceVBO = debugInstanceVbo;
|
||||
// Initial capacity for debug instances
|
||||
GL.BindBuffer(GLEnum.ArrayBuffer, SharedDebugInstanceVBO);
|
||||
GL.BufferData(GLEnum.ArrayBuffer, (nuint)(1024 * 44), (void*)0, GLEnum.StreamDraw); // 44 bytes is sizeof(LineInstance)
|
||||
GlResourceCommand.Execute(GL, "configure WB shared debug resources", () => {
|
||||
GL.BindBuffer(GLEnum.ArrayBuffer, SharedQuadVBO);
|
||||
fixed (float* pQuad = quadVertices) {
|
||||
GL.BufferData(
|
||||
GLEnum.ArrayBuffer,
|
||||
(nuint)(quadVertices.Length * sizeof(float)),
|
||||
pQuad,
|
||||
GLEnum.StaticDraw);
|
||||
}
|
||||
|
||||
GL.GenVertexArrays(1, out uint debugVao);
|
||||
SharedDebugVAO = debugVao;
|
||||
GL.BindVertexArray(SharedDebugVAO);
|
||||
// Initial capacity for debug instances.
|
||||
GL.BindBuffer(GLEnum.ArrayBuffer, SharedDebugInstanceVBO);
|
||||
GL.BufferData(
|
||||
GLEnum.ArrayBuffer,
|
||||
(nuint)(1024 * 44),
|
||||
(void*)0,
|
||||
GLEnum.StreamDraw); // 44 bytes is sizeof(LineInstance)
|
||||
|
||||
// Quad Pos attribute (location 0)
|
||||
GL.BindBuffer(GLEnum.ArrayBuffer, SharedQuadVBO);
|
||||
GL.EnableVertexAttribArray(0);
|
||||
GL.VertexAttribPointer(0, 2, GLEnum.Float, false, 2 * sizeof(float), (void*)0);
|
||||
GL.BindVertexArray(SharedDebugVAO);
|
||||
|
||||
// Instance attributes
|
||||
GL.BindBuffer(GLEnum.ArrayBuffer, SharedDebugInstanceVBO);
|
||||
uint lineInstanceSize = 44; // Marshal.SizeOf<LineInstance>() - we'll hardcode or use a constant later
|
||||
// Quad Pos attribute (location 0)
|
||||
GL.BindBuffer(GLEnum.ArrayBuffer, SharedQuadVBO);
|
||||
GL.EnableVertexAttribArray(0);
|
||||
GL.VertexAttribPointer(0, 2, GLEnum.Float, false, 2 * sizeof(float), (void*)0);
|
||||
|
||||
// aStart (location 1)
|
||||
GL.EnableVertexAttribArray(1);
|
||||
GL.VertexAttribPointer(1, 3, GLEnum.Float, false, lineInstanceSize, (void*)0);
|
||||
GL.VertexAttribDivisor(1, 1);
|
||||
// Instance attributes
|
||||
GL.BindBuffer(GLEnum.ArrayBuffer, SharedDebugInstanceVBO);
|
||||
uint lineInstanceSize = 44;
|
||||
|
||||
// aEnd (location 2)
|
||||
GL.EnableVertexAttribArray(2);
|
||||
GL.VertexAttribPointer(2, 3, GLEnum.Float, false, lineInstanceSize, (void*)12); // OffsetOf End
|
||||
GL.VertexAttribDivisor(2, 1);
|
||||
// aStart (location 1)
|
||||
GL.EnableVertexAttribArray(1);
|
||||
GL.VertexAttribPointer(1, 3, GLEnum.Float, false, lineInstanceSize, (void*)0);
|
||||
GL.VertexAttribDivisor(1, 1);
|
||||
|
||||
// aColor (location 3)
|
||||
GL.EnableVertexAttribArray(3);
|
||||
GL.VertexAttribPointer(3, 4, GLEnum.Float, false, lineInstanceSize, (void*)24); // OffsetOf Color
|
||||
GL.VertexAttribDivisor(3, 1);
|
||||
// aEnd (location 2)
|
||||
GL.EnableVertexAttribArray(2);
|
||||
GL.VertexAttribPointer(2, 3, GLEnum.Float, false, lineInstanceSize, (void*)12);
|
||||
GL.VertexAttribDivisor(2, 1);
|
||||
|
||||
// aThickness (location 4)
|
||||
GL.EnableVertexAttribArray(4);
|
||||
GL.VertexAttribPointer(4, 1, GLEnum.Float, false, lineInstanceSize, (void*)40); // OffsetOf Thickness
|
||||
GL.VertexAttribDivisor(4, 1);
|
||||
// aColor (location 3)
|
||||
GL.EnableVertexAttribArray(3);
|
||||
GL.VertexAttribPointer(3, 4, GLEnum.Float, false, lineInstanceSize, (void*)24);
|
||||
GL.VertexAttribDivisor(3, 1);
|
||||
|
||||
GL.BindVertexArray(0);
|
||||
// aThickness (location 4)
|
||||
GL.EnableVertexAttribArray(4);
|
||||
GL.VertexAttribPointer(4, 1, GLEnum.Float, false, lineInstanceSize, (void*)40);
|
||||
GL.VertexAttribDivisor(4, 1);
|
||||
|
||||
GL.BindVertexArray(0);
|
||||
});
|
||||
}
|
||||
|
||||
public void EnsureInstanceBufferCapacity(int count, int stride, bool forceOrphan = false) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue