From cefc689ba857b76d2f080716c98b252b55b88127 Mon Sep 17 00:00:00 2001 From: Erik Date: Fri, 10 Apr 2026 18:03:58 +0200 Subject: [PATCH] =?UTF-8?q?feat(app):=20add=20TextureCache=20for=20Surface?= =?UTF-8?q?=E2=86=92GL=20texture=20handle=20caching?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/AcDream.App/Rendering/TextureCache.cs | 92 +++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 src/AcDream.App/Rendering/TextureCache.cs diff --git a/src/AcDream.App/Rendering/TextureCache.cs b/src/AcDream.App/Rendering/TextureCache.cs new file mode 100644 index 0000000..2794038 --- /dev/null +++ b/src/AcDream.App/Rendering/TextureCache.cs @@ -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 _handlesBySurfaceId = new(); + private uint _magentaHandle; + + public TextureCache(GL gl, DatCollection dats) + { + _gl = gl; + _dats = dats; + } + + /// + /// 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. + /// + 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(surfaceId); + if (surface is null) + return DecodedTexture.Magenta; + + var surfaceTexture = _dats.Get((uint)surface.OrigTextureId); + if (surfaceTexture is null || surfaceTexture.Textures.Count == 0) + return DecodedTexture.Magenta; + + var rs = _dats.Get((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; + } + } +}