feat(app): Phase A.1 — TerrainRenderer.RemoveLandblock for streaming unloads
TerrainRenderer's internal landblock collection is now a Dictionary keyed by landblock id so the streaming system can release GPU resources per-landblock as the visible window moves. AddLandblock takes the id as its first parameter; if the same id is added twice, the old buffers are freed before the new ones land (defensive but cheap). RemoveLandblock is a no-op for unknown ids and deletes VBO/EBO/VAO for known ones. Single existing caller in GameWindow.cs updated to pass the id. Build green. No unit tests — direct-to-GL methods need a live context. Tasks 5-7 will validate end-to-end. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c5e207a51f
commit
495f87a4ad
2 changed files with 32 additions and 6 deletions
|
|
@ -245,7 +245,7 @@ public sealed class GameWindow : IDisposable
|
||||||
((int)lbY - centerY) * 192f,
|
((int)lbY - centerY) * 192f,
|
||||||
0f);
|
0f);
|
||||||
|
|
||||||
_terrain.AddLandblock(meshData, origin);
|
_terrain.AddLandblock(lb.LandblockId, meshData, origin);
|
||||||
}
|
}
|
||||||
Console.WriteLine($"terrain: {surfaceCache.Count} unique palette codes across {worldView.Landblocks.Count} landblocks");
|
Console.WriteLine($"terrain: {surfaceCache.Count} unique palette codes across {worldView.Landblocks.Count} landblocks");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ public sealed unsafe class TerrainRenderer : IDisposable
|
||||||
private readonly GL _gl;
|
private readonly GL _gl;
|
||||||
private readonly Shader _shader;
|
private readonly Shader _shader;
|
||||||
private readonly TerrainAtlas _atlas;
|
private readonly TerrainAtlas _atlas;
|
||||||
private readonly List<LandblockGpu> _landblocks = new();
|
private readonly Dictionary<uint, LandblockGpu> _landblocks = new();
|
||||||
|
|
||||||
public TerrainRenderer(GL gl, Shader shader, TerrainAtlas atlas)
|
public TerrainRenderer(GL gl, Shader shader, TerrainAtlas atlas)
|
||||||
{
|
{
|
||||||
|
|
@ -31,10 +31,19 @@ public sealed unsafe class TerrainRenderer : IDisposable
|
||||||
_atlas = atlas;
|
_atlas = atlas;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddLandblock(LandblockMeshData meshData, Vector3 worldOrigin)
|
public void AddLandblock(uint landblockId, LandblockMeshData meshData, Vector3 worldOrigin)
|
||||||
{
|
{
|
||||||
|
if (_landblocks.TryGetValue(landblockId, out var existing))
|
||||||
|
{
|
||||||
|
_gl.DeleteBuffer(existing.Vbo);
|
||||||
|
_gl.DeleteBuffer(existing.Ebo);
|
||||||
|
_gl.DeleteVertexArray(existing.Vao);
|
||||||
|
_landblocks.Remove(landblockId);
|
||||||
|
}
|
||||||
|
|
||||||
var gpu = new LandblockGpu
|
var gpu = new LandblockGpu
|
||||||
{
|
{
|
||||||
|
LandblockId = landblockId,
|
||||||
Vao = _gl.GenVertexArray(),
|
Vao = _gl.GenVertexArray(),
|
||||||
WorldOrigin = worldOrigin,
|
WorldOrigin = worldOrigin,
|
||||||
IndexCount = meshData.Indices.Length,
|
IndexCount = meshData.Indices.Length,
|
||||||
|
|
@ -76,7 +85,23 @@ public sealed unsafe class TerrainRenderer : IDisposable
|
||||||
_gl.VertexAttribIPointer(5, 4, VertexAttribIType.UnsignedByte, stride, (void*)(dataOffset + 12));
|
_gl.VertexAttribIPointer(5, 4, VertexAttribIType.UnsignedByte, stride, (void*)(dataOffset + 12));
|
||||||
|
|
||||||
_gl.BindVertexArray(0);
|
_gl.BindVertexArray(0);
|
||||||
_landblocks.Add(gpu);
|
_landblocks[landblockId] = gpu;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Release GPU buffers for a previously-added landblock. No-op if the
|
||||||
|
/// landblock wasn't added. Called by the streaming system when a
|
||||||
|
/// landblock falls outside the visible window.
|
||||||
|
/// </summary>
|
||||||
|
public void RemoveLandblock(uint landblockId)
|
||||||
|
{
|
||||||
|
if (!_landblocks.TryGetValue(landblockId, out var gpu))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_gl.DeleteBuffer(gpu.Vbo);
|
||||||
|
_gl.DeleteBuffer(gpu.Ebo);
|
||||||
|
_gl.DeleteVertexArray(gpu.Vao);
|
||||||
|
_landblocks.Remove(landblockId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Draw(ICamera camera)
|
public void Draw(ICamera camera)
|
||||||
|
|
@ -96,7 +121,7 @@ public sealed unsafe class TerrainRenderer : IDisposable
|
||||||
int alphaLoc = _gl.GetUniformLocation(_shader.Program, "uAlpha");
|
int alphaLoc = _gl.GetUniformLocation(_shader.Program, "uAlpha");
|
||||||
if (alphaLoc >= 0) _gl.Uniform1(alphaLoc, 1);
|
if (alphaLoc >= 0) _gl.Uniform1(alphaLoc, 1);
|
||||||
|
|
||||||
foreach (var lb in _landblocks)
|
foreach (var lb in _landblocks.Values)
|
||||||
{
|
{
|
||||||
var model = Matrix4x4.CreateTranslation(lb.WorldOrigin);
|
var model = Matrix4x4.CreateTranslation(lb.WorldOrigin);
|
||||||
_shader.SetMatrix4("uModel", model);
|
_shader.SetMatrix4("uModel", model);
|
||||||
|
|
@ -108,7 +133,7 @@ public sealed unsafe class TerrainRenderer : IDisposable
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
foreach (var lb in _landblocks)
|
foreach (var lb in _landblocks.Values)
|
||||||
{
|
{
|
||||||
_gl.DeleteBuffer(lb.Vbo);
|
_gl.DeleteBuffer(lb.Vbo);
|
||||||
_gl.DeleteBuffer(lb.Ebo);
|
_gl.DeleteBuffer(lb.Ebo);
|
||||||
|
|
@ -119,6 +144,7 @@ public sealed unsafe class TerrainRenderer : IDisposable
|
||||||
|
|
||||||
private sealed class LandblockGpu
|
private sealed class LandblockGpu
|
||||||
{
|
{
|
||||||
|
public uint LandblockId;
|
||||||
public uint Vao;
|
public uint Vao;
|
||||||
public uint Vbo;
|
public uint Vbo;
|
||||||
public uint Ebo;
|
public uint Ebo;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue