// 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; } } }