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
242
src/AcDream.App/Rendering/Wb/TrackedGlResource.cs
Normal file
242
src/AcDream.App/Rendering/Wb/TrackedGlResource.cs
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
using Silk.NET.OpenGL;
|
||||
using AcDream.App.Rendering;
|
||||
|
||||
namespace AcDream.App.Rendering.Wb;
|
||||
|
||||
/// <summary>
|
||||
/// Always-on transaction boundary and accounting for raw dynamic GL objects.
|
||||
/// Growth keeps the published CPU capacity unchanged until BufferData succeeds;
|
||||
/// OpenGL leaves the previous data store intact when allocation reports an
|
||||
/// error, so the caller can continue using the old capacity or unwind.
|
||||
/// </summary>
|
||||
internal static unsafe class TrackedGlResource
|
||||
{
|
||||
public static RetryableGpuResourceRelease CreateRetryableBufferDeletion(
|
||||
GL gl,
|
||||
uint buffer,
|
||||
long capacityBytes,
|
||||
string context)
|
||||
{
|
||||
if (buffer == 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(buffer));
|
||||
ArgumentOutOfRangeException.ThrowIfNegative(capacityBytes);
|
||||
return new RetryableGpuResourceRelease(
|
||||
() => GLHelpers.ThrowOnResourceError(gl, $"{context} (precondition)"),
|
||||
() =>
|
||||
{
|
||||
gl.DeleteBuffer(buffer);
|
||||
// Per the GL error contract, a command which generates an
|
||||
// error does not change object state. Keep mutation and its
|
||||
// validation in one retryable stage so that failed deletion
|
||||
// is issued again, while later accounting remains untouched.
|
||||
GLHelpers.ThrowOnResourceError(gl, context);
|
||||
},
|
||||
() =>
|
||||
{
|
||||
if (capacityBytes != 0)
|
||||
GpuMemoryTracker.TrackDeallocation(capacityBytes, GpuResourceType.Buffer);
|
||||
},
|
||||
() => GpuMemoryTracker.TrackResourceDeallocation(GpuResourceType.Buffer));
|
||||
}
|
||||
|
||||
public static RetryableGpuResourceRelease CreateRetryableVertexArrayDeletion(
|
||||
GL gl,
|
||||
uint vertexArray,
|
||||
string context,
|
||||
Action? trackDeallocation = null)
|
||||
{
|
||||
if (vertexArray == 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(vertexArray));
|
||||
return new RetryableGpuResourceRelease(
|
||||
() => GLHelpers.ThrowOnResourceError(gl, $"{context} (precondition)"),
|
||||
() =>
|
||||
{
|
||||
gl.DeleteVertexArray(vertexArray);
|
||||
GLHelpers.ThrowOnResourceError(gl, context);
|
||||
},
|
||||
trackDeallocation
|
||||
?? (() => GpuMemoryTracker.TrackResourceDeallocation(GpuResourceType.VAO)));
|
||||
}
|
||||
|
||||
public static void AllocateBufferStorage(
|
||||
GL gl,
|
||||
BufferTargetARB target,
|
||||
uint buffer,
|
||||
long previousBytes,
|
||||
long newBytes,
|
||||
BufferUsageARB usage,
|
||||
string context)
|
||||
=> AllocateBufferStorage(
|
||||
gl,
|
||||
(GLEnum)target,
|
||||
buffer,
|
||||
previousBytes,
|
||||
newBytes,
|
||||
(GLEnum)usage,
|
||||
context);
|
||||
|
||||
public static void AllocateBufferStorage(
|
||||
GL gl,
|
||||
BufferTargetARB target,
|
||||
uint buffer,
|
||||
long previousBytes,
|
||||
long newBytes,
|
||||
BufferUsageARB usage,
|
||||
void* data,
|
||||
string context)
|
||||
=> AllocateBufferStorage(
|
||||
gl,
|
||||
(GLEnum)target,
|
||||
buffer,
|
||||
previousBytes,
|
||||
newBytes,
|
||||
(GLEnum)usage,
|
||||
data,
|
||||
context);
|
||||
|
||||
public static uint CreateBuffer(GL gl, string context)
|
||||
{
|
||||
GLHelpers.ThrowOnResourceError(gl, $"{context} (precondition)");
|
||||
uint name = gl.GenBuffer();
|
||||
try
|
||||
{
|
||||
if (name == 0)
|
||||
throw new InvalidOperationException($"OpenGL returned buffer name zero. Context: {context}");
|
||||
GLHelpers.ThrowOnResourceError(gl, context);
|
||||
GpuMemoryTracker.TrackResourceAllocation(GpuResourceType.Buffer);
|
||||
return name;
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (name != 0)
|
||||
gl.DeleteBuffer(name);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public static uint CreateVertexArray(GL gl, string context)
|
||||
{
|
||||
GLHelpers.ThrowOnResourceError(gl, $"{context} (precondition)");
|
||||
uint name = gl.GenVertexArray();
|
||||
try
|
||||
{
|
||||
if (name == 0)
|
||||
throw new InvalidOperationException($"OpenGL returned vertex-array name zero. Context: {context}");
|
||||
GLHelpers.ThrowOnResourceError(gl, context);
|
||||
GpuMemoryTracker.TrackResourceAllocation(GpuResourceType.VAO);
|
||||
return name;
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (name != 0)
|
||||
gl.DeleteVertexArray(name);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public static void AllocateBufferStorage(
|
||||
GL gl,
|
||||
GLEnum target,
|
||||
uint buffer,
|
||||
long previousBytes,
|
||||
long newBytes,
|
||||
GLEnum usage,
|
||||
string context)
|
||||
=> AllocateBufferStorage(
|
||||
gl,
|
||||
target,
|
||||
buffer,
|
||||
previousBytes,
|
||||
newBytes,
|
||||
usage,
|
||||
null,
|
||||
context);
|
||||
|
||||
public static void AllocateBufferStorage(
|
||||
GL gl,
|
||||
GLEnum target,
|
||||
uint buffer,
|
||||
long previousBytes,
|
||||
long newBytes,
|
||||
GLEnum usage,
|
||||
void* data,
|
||||
string context)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfNegative(previousBytes);
|
||||
ArgumentOutOfRangeException.ThrowIfLessThan(newBytes, 1);
|
||||
if (buffer == 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(buffer));
|
||||
|
||||
GLHelpers.ThrowOnResourceError(gl, $"{context} (precondition)");
|
||||
gl.BindBuffer(target, buffer);
|
||||
gl.BufferData(target, checked((nuint)newBytes), data, usage);
|
||||
GLHelpers.ThrowOnResourceError(gl, context);
|
||||
|
||||
long delta = checked(newBytes - previousBytes);
|
||||
if (delta > 0)
|
||||
GpuMemoryTracker.TrackAllocation(delta, GpuResourceType.Buffer);
|
||||
else if (delta < 0)
|
||||
GpuMemoryTracker.TrackDeallocation(-delta, GpuResourceType.Buffer);
|
||||
}
|
||||
|
||||
public static void UpdateBufferSubData(
|
||||
GL gl,
|
||||
BufferTargetARB target,
|
||||
uint buffer,
|
||||
nint byteOffset,
|
||||
long byteCount,
|
||||
void* data,
|
||||
string context)
|
||||
=> UpdateBufferSubData(
|
||||
gl,
|
||||
(GLEnum)target,
|
||||
buffer,
|
||||
byteOffset,
|
||||
byteCount,
|
||||
data,
|
||||
context);
|
||||
|
||||
public static void UpdateBufferSubData(
|
||||
GL gl,
|
||||
GLEnum target,
|
||||
uint buffer,
|
||||
nint byteOffset,
|
||||
long byteCount,
|
||||
void* data,
|
||||
string context)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfNegative(byteOffset);
|
||||
ArgumentOutOfRangeException.ThrowIfLessThan(byteCount, 1);
|
||||
if (buffer == 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(buffer));
|
||||
ArgumentNullException.ThrowIfNull(data);
|
||||
|
||||
GLHelpers.ThrowOnResourceError(gl, $"{context} (precondition)");
|
||||
gl.BindBuffer(target, buffer);
|
||||
gl.BufferSubData(target, byteOffset, checked((nuint)byteCount), data);
|
||||
GLHelpers.ThrowOnResourceError(gl, context);
|
||||
}
|
||||
|
||||
public static void DeleteBuffer(GL gl, uint buffer, long capacityBytes, string context)
|
||||
{
|
||||
if (buffer == 0)
|
||||
return;
|
||||
ArgumentOutOfRangeException.ThrowIfNegative(capacityBytes);
|
||||
GLHelpers.ThrowOnResourceError(gl, $"{context} (precondition)");
|
||||
gl.DeleteBuffer(buffer);
|
||||
GLHelpers.ThrowOnResourceError(gl, context);
|
||||
if (capacityBytes != 0)
|
||||
GpuMemoryTracker.TrackDeallocation(capacityBytes, GpuResourceType.Buffer);
|
||||
GpuMemoryTracker.TrackResourceDeallocation(GpuResourceType.Buffer);
|
||||
}
|
||||
|
||||
public static void DeleteVertexArray(GL gl, uint vertexArray, string context)
|
||||
{
|
||||
if (vertexArray == 0)
|
||||
return;
|
||||
GLHelpers.ThrowOnResourceError(gl, $"{context} (precondition)");
|
||||
gl.DeleteVertexArray(vertexArray);
|
||||
GLHelpers.ThrowOnResourceError(gl, context);
|
||||
GpuMemoryTracker.TrackResourceDeallocation(GpuResourceType.VAO);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue