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:
Erik 2026-07-22 16:46:05 +02:00
parent cd7b519f78
commit 6a2fe98cc4
22 changed files with 1983 additions and 634 deletions

View file

@ -221,35 +221,46 @@ public sealed unsafe class ParticleRenderer : IDisposable
handle => _particleGfxInfoByEmitter.Remove(handle),
handle => _textures?.ReleaseParticleTextureOwner(handle),
error => Console.Error.WriteLine($"[particles] {error}"));
_shader = new Shader(_gl,
System.IO.Path.Combine(shadersDir, "particle.vert"),
System.IO.Path.Combine(shadersDir, "particle.frag"));
if (_meshAdapter?.MeshManager?.GlobalBuffer is not null)
{
_meshShader = new Shader(_gl,
System.IO.Path.Combine(shadersDir, "particle_mesh.vert"),
System.IO.Path.Combine(shadersDir, "particle_mesh.frag"));
_meshTextureHandleLoc = _gl.GetUniformLocation(_meshShader.Program, "uTextureHandle");
_meshTextureLayerLoc = _gl.GetUniformLocation(_meshShader.Program, "uTextureLayer");
}
float[] quadVerts =
{
-0.5f, -0.5f, 0f, 0f,
0.5f, -0.5f, 1f, 0f,
0.5f, 0.5f, 1f, 1f,
-0.5f, 0.5f, 0f, 1f,
};
uint[] quadIdx = { 0, 1, 2, 0, 2, 3 };
uint quadVbo = 0;
uint quadEbo = 0;
uint uploadVao = 0;
bool vboAllocated = false;
bool eboAllocated = false;
var constructionResources = new ResourceCleanupGroup();
try
{
quadVbo = TrackedGlResource.CreateBuffer(_gl, "creating particle quad VBO");
_shader = new Shader(_gl,
System.IO.Path.Combine(shadersDir, "particle.vert"),
System.IO.Path.Combine(shadersDir, "particle.frag"));
constructionResources.Add("particle shader", _shader.Dispose);
if (_meshAdapter?.MeshManager?.GlobalBuffer is not null)
{
_meshShader = new Shader(_gl,
System.IO.Path.Combine(shadersDir, "particle_mesh.vert"),
System.IO.Path.Combine(shadersDir, "particle_mesh.frag"));
constructionResources.Add(
"particle mesh shader",
_meshShader.Dispose);
_meshTextureHandleLoc = _gl.GetUniformLocation(_meshShader.Program, "uTextureHandle");
_meshTextureLayerLoc = _gl.GetUniformLocation(_meshShader.Program, "uTextureLayer");
}
float[] quadVerts =
{
-0.5f, -0.5f, 0f, 0f,
0.5f, -0.5f, 1f, 0f,
0.5f, 0.5f, 1f, 1f,
-0.5f, 0.5f, 0f, 1f,
};
uint[] quadIdx = { 0, 1, 2, 0, 2, 3 };
bool vboAllocated = false;
bool eboAllocated = false;
uint quadVbo = TrackedGlResource.CreateBuffer(
_gl,
"creating particle quad VBO");
RetryableGpuResourceRelease quadVboRelease =
TrackedGlResource.CreateRetryableBufferDeletion(
_gl,
quadVbo,
() => vboAllocated ? quadVerts.Length * sizeof(float) : 0,
"rolling back particle quad VBO");
constructionResources.Add("particle quad VBO", quadVboRelease.Run);
fixed (void* p = quadVerts)
{
TrackedGlResource.AllocateBufferStorage(
@ -264,9 +275,31 @@ public sealed unsafe class ParticleRenderer : IDisposable
}
vboAllocated = true;
quadEbo = TrackedGlResource.CreateBuffer(_gl, "creating particle quad EBO");
uploadVao = TrackedGlResource.CreateVertexArray(_gl, "creating particle upload VAO");
_gl.BindVertexArray(uploadVao);
uint quadEbo = TrackedGlResource.CreateBuffer(
_gl,
"creating particle quad EBO");
RetryableGpuResourceRelease quadEboRelease =
TrackedGlResource.CreateRetryableBufferDeletion(
_gl,
quadEbo,
() => eboAllocated ? quadIdx.Length * sizeof(uint) : 0,
"rolling back particle quad EBO");
constructionResources.Add("particle quad EBO", quadEboRelease.Run);
uint uploadVao = TrackedGlResource.CreateVertexArray(
_gl,
"creating particle upload VAO");
RetryableGpuResourceRelease uploadVaoRelease =
TrackedGlResource.CreateRetryableVertexArrayDeletion(
_gl,
uploadVao,
"rolling back particle upload VAO");
constructionResources.Add("particle upload VAO", uploadVaoRelease.Run);
GlResourceCommand.Execute(
_gl,
"bind particle upload VAO",
() => _gl.BindVertexArray(uploadVao));
fixed (void* p = quadIdx)
{
TrackedGlResource.AllocateBufferStorage(
@ -280,44 +313,26 @@ public sealed unsafe class ParticleRenderer : IDisposable
"uploading particle quad EBO");
}
eboAllocated = true;
_gl.BindVertexArray(0);
TrackedGlResource.DeleteVertexArray(_gl, uploadVao, "deleting particle upload VAO");
uploadVao = 0;
_gl.BindBuffer(BufferTargetARB.ArrayBuffer, 0);
GlResourceCommand.Execute(_gl, "finish particle static-buffer upload", () =>
{
_gl.BindVertexArray(0);
_gl.BindBuffer(BufferTargetARB.ArrayBuffer, 0);
});
uploadVaoRelease.Run();
_quadVbo = quadVbo;
_quadEbo = quadEbo;
}
catch (Exception creationFailure)
{
List<Exception>? cleanupFailures = null;
void Attempt(Action action)
{
try { action(); }
catch (Exception ex) { (cleanupFailures ??= []).Add(ex); }
}
if (uploadVao != 0)
Attempt(() => TrackedGlResource.DeleteVertexArray(_gl, uploadVao, "rolling back particle upload VAO"));
if (quadEbo != 0)
Attempt(() => TrackedGlResource.DeleteBuffer(
_gl,
quadEbo,
eboAllocated ? quadIdx.Length * sizeof(uint) : 0,
"rolling back particle quad EBO"));
if (quadVbo != 0)
Attempt(() => TrackedGlResource.DeleteBuffer(
_gl,
quadVbo,
vboAllocated ? quadVerts.Length * sizeof(float) : 0,
"rolling back particle quad VBO"));
if (cleanupFailures is not null)
{
cleanupFailures.Insert(0, creationFailure);
throw new AggregateException("Particle static-buffer creation and rollback failed.", cleanupFailures);
}
throw;
}
_particles.EmitterDied += OnEmitterDied;
_particles.EmitterDied += OnEmitterDied;
constructionResources.TransferAll();
}
catch (Exception constructionFailure)
{
constructionResources.RollbackConstructionAndThrow(
"ParticleRenderer construction failed and its shader prefix did not cleanly roll back.",
constructionFailure);
throw new System.Diagnostics.UnreachableException();
}
}
/// <summary>