feat(app): add TextureCache for Surface→GL texture handle caching

This commit is contained in:
Erik 2026-04-10 18:03:58 +02:00
parent 5d35f4fe46
commit cefc689ba8

View file

@ -0,0 +1,92 @@
// src/AcDream.App/Rendering/TextureCache.cs
using AcDream.Core.Textures;
using DatReaderWriter;
using DatReaderWriter.DBObjs;
using Silk.NET.OpenGL;
namespace AcDream.App.Rendering;
public sealed unsafe class TextureCache : IDisposable
{
private readonly GL _gl;
private readonly DatCollection _dats;
private readonly Dictionary<uint, uint> _handlesBySurfaceId = new();
private uint _magentaHandle;
public TextureCache(GL gl, DatCollection dats)
{
_gl = gl;
_dats = dats;
}
/// <summary>
/// Get or upload the GL texture handle for a Surface id. Returns a
/// 1x1 magenta fallback if the Surface or its RenderSurface chain is
/// missing or uses an unsupported format.
/// </summary>
public uint GetOrUpload(uint surfaceId)
{
if (_handlesBySurfaceId.TryGetValue(surfaceId, out var h))
return h;
var decoded = DecodeFromDats(surfaceId);
h = UploadRgba8(decoded);
_handlesBySurfaceId[surfaceId] = h;
return h;
}
private DecodedTexture DecodeFromDats(uint surfaceId)
{
var surface = _dats.Get<Surface>(surfaceId);
if (surface is null)
return DecodedTexture.Magenta;
var surfaceTexture = _dats.Get<SurfaceTexture>((uint)surface.OrigTextureId);
if (surfaceTexture is null || surfaceTexture.Textures.Count == 0)
return DecodedTexture.Magenta;
var rs = _dats.Get<RenderSurface>((uint)surfaceTexture.Textures[0]);
if (rs is null)
return DecodedTexture.Magenta;
return SurfaceDecoder.DecodeRenderSurface(rs);
}
private uint UploadRgba8(DecodedTexture decoded)
{
uint tex = _gl.GenTexture();
_gl.BindTexture(TextureTarget.Texture2D, tex);
fixed (byte* p = decoded.Rgba8)
_gl.TexImage2D(
TextureTarget.Texture2D,
0,
InternalFormat.Rgba8,
(uint)decoded.Width,
(uint)decoded.Height,
0,
PixelFormat.Rgba,
PixelType.UnsignedByte,
p);
_gl.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
_gl.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
_gl.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);
_gl.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);
_gl.BindTexture(TextureTarget.Texture2D, 0);
return tex;
}
public void Dispose()
{
foreach (var h in _handlesBySurfaceId.Values)
_gl.DeleteTexture(h);
_handlesBySurfaceId.Clear();
if (_magentaHandle != 0)
{
_gl.DeleteTexture(_magentaHandle);
_magentaHandle = 0;
}
}
}