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

@ -1,4 +1,5 @@
using Chorizite.Core.Render;
using AcDream.App.Rendering;
using Microsoft.Extensions.Logging;
using Silk.NET.OpenGL;
using System;
@ -161,7 +162,7 @@ namespace AcDream.App.Rendering.Wb {
if (string.IsNullOrWhiteSpace(vertShaderSource) || string.IsNullOrWhiteSpace(fragShaderSource)) {
_log.LogError($"Shader {Name} has no source code!");
return;
throw new InvalidOperationException($"Shader {Name} has no source code.");
}
if (_device.HasOpenGL43 && _device.HasBindless) {
@ -170,41 +171,47 @@ namespace AcDream.App.Rendering.Wb {
fragShaderSource = fragShaderSource.Replace("#version 330 core", replacement);
}
uint vertexShader = CompileShader(ShaderType.VertexShader, Name, vertShaderSource);
uint fragmentShader = CompileShader(ShaderType.FragmentShader, Name, fragShaderSource);
var resources = new ResourceCleanupGroup();
uint prog = 0;
bool accountingPublished = false;
try {
prog = ShaderProgramConstruction.Build(
new GlShaderProgramBuildApi(GL),
vertShaderSource,
fragShaderSource);
uint ownedProgram = prog;
var unpublishedProgramRelease = new RetryableGpuResourceRelease(
() => GlResourceCommand.DeleteProgram(
GL,
ownedProgram,
$"delete unpublished WB shader program {ownedProgram}"),
() => {
if (accountingPublished)
{
GpuMemoryTracker.TrackResourceDeallocation(
GpuResourceType.Shader);
}
});
resources.Add("WB shader program", unpublishedProgramRelease.Run);
var prog = GL.CreateProgram();
GLHelpers.CheckErrors(GL, true);
GL.AttachShader(prog, vertexShader);
GLHelpers.CheckErrors(GL, true);
GL.AttachShader(prog, fragmentShader);
GLHelpers.CheckErrors(GL, true);
GL.LinkProgram(prog);
GLHelpers.CheckErrors(GL, true);
// Bind SceneData uniform block to point 0 if it exists.
GlResourceCommand.Execute(GL, $"configure shader {Name} SceneData binding", () => {
uint sceneDataIndex = GL.GetUniformBlockIndex(prog, "SceneData");
if (sceneDataIndex != uint.MaxValue)
GL.UniformBlockBinding(prog, sceneDataIndex, 0);
});
GL.GetProgram(prog, GLEnum.LinkStatus, out int success);
GLHelpers.CheckErrors(GL);
if (success != 1) {
var infoLog = GL.GetProgramInfoLog(prog);
_log.LogError($"Error: shader {Name} link failed: {infoLog}");
GL.DeleteProgram(prog);
return;
}
else {
_log.LogTrace($"{(Program != 0 ? "Reloaded" : "Loaded")} shader: {Name}");
GpuMemoryTracker.TrackResourceAllocation(GpuResourceType.Shader);
accountingPublished = true;
resources.TransferAll();
} catch (Exception constructionFailure) {
_log.LogError(constructionFailure, "Failed to construct shader {ShaderName}", Name);
resources.RollbackConstructionAndThrow(
$"Shader {Name} construction failed and its GL program did not cleanly roll back.",
constructionFailure);
}
// Bind SceneData uniform block to point 0 if it exists
var sceneDataIndex = GL.GetUniformBlockIndex(prog, "SceneData");
if (sceneDataIndex != uint.MaxValue) {
GL.UniformBlockBinding(prog, sceneDataIndex, 0);
GLHelpers.CheckErrors(GL);
}
GL.DeleteShader(vertexShader);
GLHelpers.CheckErrors(GL);
GL.DeleteShader(fragmentShader);
GLHelpers.CheckErrors(GL);
_log.LogTrace($"{(Program != 0 ? "Reloaded" : "Loaded")} shader: {Name}");
if (Program != 0) {
Unload();
@ -212,32 +219,12 @@ namespace AcDream.App.Rendering.Wb {
_uniformLocations.Clear();
_uniformValues.Clear();
GpuMemoryTracker.TrackResourceAllocation(GpuResourceType.Shader);
Program = prog;
ProgramId = prog;
NeedsLoad = false;
GLHelpers.CheckErrors(GL);
}
private uint CompileShader(ShaderType shaderType, string name, string shaderSource) {
uint shader = GL.CreateShader(shaderType);
GLHelpers.CheckErrors(GL);
GL.ShaderSource(shader, shaderSource);
GLHelpers.CheckErrors(GL);
GL.CompileShader(shader);
GLHelpers.CheckErrors(GL);
GL.GetShader(shader, ShaderParameterName.CompileStatus, out int success);
GLHelpers.CheckErrors(GL);
if (success != 1) {
var infoLog = GL.GetShaderInfoLog(shader);
_log.LogError($"Error: {name}:{shaderType} compilation failed: {infoLog}");
}
return shader;
}
public override void Bind() {
lock (_lock) {
SetActive();