// src/AcDream.App/Rendering/TextureCache.cs using AcDream.Core.Textures; using AcDream.Core.World; using AcDream.Content; using AcDream.App.Rendering.Gpu; using DatReaderWriter; using DatReaderWriter.DBObjs; using System.Linq; using PixelFormatId = DatReaderWriter.Enums.PixelFormat; using SurfaceType = DatReaderWriter.Enums.SurfaceType; using AcDream.App.Rendering.Residency; namespace AcDream.App.Rendering; public sealed class TextureCache : Wb.IEntityTextureLifetime, IDisposable { private readonly IGpuDevice _device; private readonly IDatReaderWriter _dats; private readonly string _diagnosticsDirectory; private readonly Dictionary<(uint SurfaceId, uint OrigTextureId), (int Width, int Height)> _decodedDimensionsByTexture = new(); /// /// Campaign V slice V4a: one registered plus its /// device texture-table , decoded pixel size, /// and the raw GL name 's classic /// texture-unit binding path still needs (see that class's remarks). /// private readonly record struct GpuUiTextureEntry( IGpuTexture Texture, GpuTextureSlot Slot, uint GlName, int Width, int Height); // Direct-RenderSurface caches for UI sprites: 0x06xxxxxx RenderSurface ids // decoded directly (Portal/HighRes → DecodeRenderSurface), bypassing the // Surface→SurfaceTexture chain that GetOrUpload uses for world materials. // // Review fix round (Slice B, docs/plans/2026-09-06-plugin-shelf-and-dat-icons.md // finding 6): keyed by (id, nearest), not id alone. GetOrUploadRenderSurface's // sampler is chosen at UPLOAD time (UploadUiTexture bakes `nearest` into the // registered IGpuSampler) and a table slot is one fixed (texture, sampler) // pair — the same source id legitimately needs BOTH samplers in the same // session (UiDatFont's glyph atlases already call this nearest:true while // ResolveChrome's background/border art calls it nearest:false with no // `nearest` argument at all). An id-only key made whichever caller asked // FIRST win the sampler for every later caller of the same id; a plugin // markup /