fix(rendering): bound portal resource lifetime
Separate logical ownership, render publication, and GPU retirement across live entities, landblocks, particles, textures, mesh arenas, portal/UI teardown, and per-frame scratch storage. Add bounded DAT/texture caches, upload budgets, three-frame fence retirement, exact-incarnation appearance reconciliation, frame pacing, and extensive lifetime conformance coverage.\n\nThe seven-destination connected route now cuts peak working/private memory roughly in half, returns Caul to 125-153 FPS locally, and produces no WER or AMD reset.\n\nCo-authored-by: OpenAI Codex <codex@openai.com>
This commit is contained in:
parent
3971997689
commit
749e8ceeb1
225 changed files with 29107 additions and 3914 deletions
|
|
@ -1,5 +1,7 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using AcDream.App.Rendering.Wb;
|
||||
using Silk.NET.OpenGL;
|
||||
|
||||
namespace AcDream.App.Rendering;
|
||||
|
|
@ -80,8 +82,6 @@ void main() { } // depth-only: color writes are masked off by the caller state
|
|||
|
||||
private readonly GL _gl;
|
||||
private readonly uint _program;
|
||||
private readonly uint _vao;
|
||||
private readonly uint _vbo;
|
||||
private readonly int _locViewProjection;
|
||||
private readonly int _locPlaneCount;
|
||||
private readonly int _locPlanes;
|
||||
|
|
@ -92,6 +92,20 @@ void main() { } // depth-only: color writes are masked off by the caller state
|
|||
private const int MaxFanVerts = 32;
|
||||
private readonly float[] _scratch = new float[MaxFanVerts * 3];
|
||||
|
||||
private sealed class FrameBufferSet
|
||||
{
|
||||
public uint Vao;
|
||||
public uint Vbo;
|
||||
public int CapacityBytes;
|
||||
public int UsedVertices;
|
||||
}
|
||||
|
||||
private readonly FrameBufferSet[] _frameBuffers = new FrameBufferSet[3];
|
||||
private FrameBufferSet? _activeFrameBuffer;
|
||||
|
||||
internal long DynamicBufferCapacityBytes =>
|
||||
_frameBuffers.Sum(set => (long)set.CapacityBytes);
|
||||
|
||||
public PortalDepthMaskRenderer(GL gl)
|
||||
{
|
||||
_gl = gl ?? throw new ArgumentNullException(nameof(gl));
|
||||
|
|
@ -115,19 +129,57 @@ void main() { } // depth-only: color writes are masked off by the caller state
|
|||
_locDepthBias = _gl.GetUniformLocation(_program, "uDepthBias");
|
||||
_locDepthBiasEyeCapN = _gl.GetUniformLocation(_program, "uDepthBiasEyeCapN");
|
||||
|
||||
_vao = _gl.GenVertexArray();
|
||||
_vbo = _gl.GenBuffer();
|
||||
_gl.BindVertexArray(_vao);
|
||||
_gl.BindBuffer(BufferTargetARB.ArrayBuffer, _vbo);
|
||||
unsafe
|
||||
for (int i = 0; i < _frameBuffers.Length; i++)
|
||||
_frameBuffers[i] = CreateFrameBufferSet();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Selects the GPU-fenced frame slot and resets its append cursor. Portal
|
||||
/// fans written during one frame occupy distinct ranges, so later portal
|
||||
/// slices never overwrite vertices still referenced by earlier draws.
|
||||
/// </summary>
|
||||
public void BeginFrame(int frameSlot)
|
||||
{
|
||||
if ((uint)frameSlot >= (uint)_frameBuffers.Length)
|
||||
throw new ArgumentOutOfRangeException(nameof(frameSlot));
|
||||
_activeFrameBuffer = _frameBuffers[frameSlot];
|
||||
_activeFrameBuffer.UsedVertices = 0;
|
||||
}
|
||||
|
||||
private FrameBufferSet CreateFrameBufferSet()
|
||||
{
|
||||
uint vao = 0;
|
||||
uint vbo = 0;
|
||||
try
|
||||
{
|
||||
_gl.BufferData(BufferTargetARB.ArrayBuffer,
|
||||
(nuint)(MaxFanVerts * 3 * sizeof(float)), null, BufferUsageARB.DynamicDraw);
|
||||
_gl.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, 3 * sizeof(float), (void*)0);
|
||||
vao = TrackedGlResource.CreateVertexArray(
|
||||
_gl,
|
||||
"PortalDepthMask frame VAO creation");
|
||||
vbo = TrackedGlResource.CreateBuffer(
|
||||
_gl,
|
||||
"PortalDepthMask frame VBO creation");
|
||||
var set = new FrameBufferSet { Vao = vao, Vbo = vbo };
|
||||
_gl.BindVertexArray(set.Vao);
|
||||
_gl.BindBuffer(BufferTargetARB.ArrayBuffer, set.Vbo);
|
||||
unsafe
|
||||
{
|
||||
_gl.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, 3 * sizeof(float), (void*)0);
|
||||
}
|
||||
_gl.EnableVertexAttribArray(0);
|
||||
_gl.BindVertexArray(0);
|
||||
_gl.BindBuffer(BufferTargetARB.ArrayBuffer, 0);
|
||||
return set;
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (vbo != 0)
|
||||
TrackedGlResource.DeleteBuffer(
|
||||
_gl, vbo, 0, "PortalDepthMask frame VBO rollback");
|
||||
if (vao != 0)
|
||||
TrackedGlResource.DeleteVertexArray(
|
||||
_gl, vao, "PortalDepthMask frame VAO rollback");
|
||||
throw;
|
||||
}
|
||||
_gl.EnableVertexAttribArray(0);
|
||||
_gl.BindVertexArray(0);
|
||||
_gl.BindBuffer(BufferTargetARB.ArrayBuffer, 0);
|
||||
}
|
||||
|
||||
private uint Compile(ShaderType type, string src)
|
||||
|
|
@ -210,8 +262,12 @@ void main() { } // depth-only: color writes are masked off by the caller state
|
|||
{
|
||||
if (worldVerts.Length < 3)
|
||||
return;
|
||||
FrameBufferSet frameBuffer = _activeFrameBuffer
|
||||
?? throw new InvalidOperationException("BeginFrame must be called before drawing portal depth masks.");
|
||||
int n = Math.Min(worldVerts.Length, MaxFanVerts);
|
||||
int planeCount = Math.Min(planes.Length, 8);
|
||||
int firstVertex = frameBuffer.UsedVertices;
|
||||
int requiredBytes = checked((firstVertex + n) * 3 * sizeof(float));
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
|
|
@ -249,10 +305,30 @@ void main() { } // depth-only: color writes are masked off by the caller state
|
|||
_gl.Uniform4(_locPlanes, (uint)planeCount, pp);
|
||||
}
|
||||
|
||||
_gl.BindVertexArray(_vao);
|
||||
_gl.BindBuffer(BufferTargetARB.ArrayBuffer, _vbo);
|
||||
_gl.BindVertexArray(frameBuffer.Vao);
|
||||
_gl.BindBuffer(BufferTargetARB.ArrayBuffer, frameBuffer.Vbo);
|
||||
if (frameBuffer.CapacityBytes < requiredBytes)
|
||||
{
|
||||
int newCapacity = DynamicBufferCapacity.Grow(
|
||||
frameBuffer.CapacityBytes,
|
||||
requiredBytes);
|
||||
TrackedGlResource.AllocateBufferStorage(
|
||||
_gl,
|
||||
GLEnum.ArrayBuffer,
|
||||
frameBuffer.Vbo,
|
||||
frameBuffer.CapacityBytes,
|
||||
newCapacity,
|
||||
GLEnum.DynamicDraw,
|
||||
"PortalDepthMask frame VBO growth");
|
||||
frameBuffer.CapacityBytes = newCapacity;
|
||||
}
|
||||
fixed (float* v = _scratch)
|
||||
_gl.BufferSubData(BufferTargetARB.ArrayBuffer, 0, (nuint)(n * 3 * sizeof(float)), v);
|
||||
_gl.BufferSubData(
|
||||
BufferTargetARB.ArrayBuffer,
|
||||
(nint)(firstVertex * 3 * sizeof(float)),
|
||||
(nuint)(n * 3 * sizeof(float)),
|
||||
v);
|
||||
frameBuffer.UsedVertices += n;
|
||||
|
||||
if (!forceFarZ)
|
||||
{
|
||||
|
|
@ -261,7 +337,7 @@ void main() { } // depth-only: color writes are masked off by the caller state
|
|||
_gl.DepthMask(true);
|
||||
_gl.Uniform1(_locForceFarZ, 0);
|
||||
_gl.Uniform1(_locDepthBias, 0f);
|
||||
_gl.DrawArrays(PrimitiveType.TriangleFan, 0, (uint)n);
|
||||
_gl.DrawArrays(PrimitiveType.TriangleFan, firstVertex, (uint)n);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -276,7 +352,7 @@ void main() { } // depth-only: color writes are masked off by the caller state
|
|||
_gl.Uniform1(_locDepthBias, PunchMarkDepthBias);
|
||||
_gl.Uniform1(_locDepthBiasEyeCapN,
|
||||
PunchMarkBiasEyeCapMeters * CameraNearPlaneMeters);
|
||||
_gl.DrawArrays(PrimitiveType.TriangleFan, 0, (uint)n);
|
||||
_gl.DrawArrays(PrimitiveType.TriangleFan, firstVertex, (uint)n);
|
||||
|
||||
// ── PUNCH pass B: far-Z write on marked pixels only;
|
||||
// zero the stencil as we go (self-cleaning) ──
|
||||
|
|
@ -286,7 +362,7 @@ void main() { } // depth-only: color writes are masked off by the caller state
|
|||
_gl.DepthMask(true);
|
||||
_gl.Uniform1(_locForceFarZ, 1);
|
||||
_gl.Uniform1(_locDepthBias, 0f);
|
||||
_gl.DrawArrays(PrimitiveType.TriangleFan, 0, (uint)n);
|
||||
_gl.DrawArrays(PrimitiveType.TriangleFan, firstVertex, (uint)n);
|
||||
|
||||
_gl.Disable(EnableCap.StencilTest);
|
||||
}
|
||||
|
|
@ -310,7 +386,17 @@ void main() { } // depth-only: color writes are masked off by the caller state
|
|||
public void Dispose()
|
||||
{
|
||||
_gl.DeleteProgram(_program);
|
||||
_gl.DeleteVertexArray(_vao);
|
||||
_gl.DeleteBuffer(_vbo);
|
||||
foreach (FrameBufferSet set in _frameBuffers)
|
||||
{
|
||||
TrackedGlResource.DeleteVertexArray(
|
||||
_gl,
|
||||
set.Vao,
|
||||
"PortalDepthMask frame VAO disposal");
|
||||
TrackedGlResource.DeleteBuffer(
|
||||
_gl,
|
||||
set.Vbo,
|
||||
set.CapacityBytes,
|
||||
"PortalDepthMask frame VBO disposal");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue