#ifndef EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_TEXTURECACHE_H_ #define EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_TEXTURECACHE_H_ #include "common/abi.h" #include "common/common.h" #include "common/lruCache.h" #include "common/slotVector.h" #include "graphics/host_gpu/pageManager.h" #include "graphics/host_gpu/regionManager.h" #include "graphics/host_gpu/renderer/cache/multiLevelPageTable.h" #include "graphics/host_gpu/renderer/image/blitHelper.h" #include "graphics/host_gpu/renderer/image/image.h" #include "graphics/host_gpu/renderer/image/tiler.h" #include #include #include #include namespace Libs::Graphics { struct GraphicContext; class Buffer; class BufferCache; class CommandBuffer; class CommandScheduler; class RenderExecutor; struct TextureCacheTestAccess; class TextureCache { public: enum class BindingType : uint8_t { Texture, Storage, RenderTarget, DepthTarget, VideoOut }; struct ImageDesc { ImageInfo info; ImageViewInfo view_info; BindingType type = BindingType::Texture; }; struct RegionInfo { bool image_pages = false; bool image_bytes = false; bool gpu_image_bytes = false; }; TextureCache(GraphicContext& graphics, CommandScheduler& scheduler, PageManager& page_manager, BufferCache& buffer_cache); ~TextureCache(); KYTY_CLASS_NO_COPY(TextureCache); [[nodiscard]] ImageId FindImage(ImageDesc& desc, bool exact_format = false); void UpdateImage(ImageId id); [[nodiscard]] ImageId FindImageFromRange(uint64_t address, uint64_t size, bool ensure_valid = true); [[nodiscard]] vk::ImageView FindTexture(ImageId id, const ImageDesc& desc); [[nodiscard]] vk::ImageView FindRenderTarget(ImageId id, const ImageDesc& desc); [[nodiscard]] vk::ImageView FindDepthTarget(ImageId id, const ImageDesc& desc); [[nodiscard]] Image& GetImage(ImageId id) { auto& image = m_slot_images[id]; TouchImage(image); return image; } void MarkGpuWritten(ImageId id); [[nodiscard]] bool ClearImageFromBuffer(CommandBuffer& command, uint64_t address, uint64_t size, uint32_t packed_clear); void InvalidateMemory(uint64_t address, uint64_t size); void InvalidateMemoryFromGPU(uint64_t address, uint64_t size); [[nodiscard]] RegionInfo QueryRegion(uint64_t address, uint64_t size); [[nodiscard]] bool IsMeta(uint64_t address); [[nodiscard]] bool IsMetaCleared(uint64_t address, uint32_t slice, uint32_t* fill_value = nullptr); [[nodiscard]] bool ClearMeta(uint64_t address); // Record deferred DCC state while the original guest dispatch writes the metadata. void TrackDccFill(uint64_t address, uint64_t size, uint32_t fill_value); [[nodiscard]] bool TouchMeta(uint64_t address, uint32_t slice, bool is_clear); void UnmapMemory(uint64_t address, uint64_t size); void ProcessDownloadImages(); void RunGarbageCollector(); private: enum class TransferDirection { Upload, Download }; struct TextureTransferPlan; struct DownloadPlan; struct MetaDataInfo { // A guest metadata-fill dispatch may initialize DCC before its render target is bound. // PendingDcc retains that exact fill until an image binding classifies the address, // without exposing an unconfirmed buffer address to the normal metadata heuristics. // Keep all surface metadata in one entry so CMask/FMask can be // registered beside HTile and DCC without introducing parallel tracking paths. enum class Type : uint8_t { PendingDcc, CMask, FMask, HTile, Dcc }; Type type = Type::PendingDcc; uint32_t clear_mask = 0; uint32_t fill_value = 0xffffffffu; uint64_t fill_size = 0; }; struct OverlapResult { ImageId image; int32_t mip = -1; int32_t layer = -1; }; using ImageIds = InlinePageOwnerList; using ImagePageTable = MultiLevelPageTable; [[nodiscard]] ImageId InsertImage(const ImageInfo& info); [[nodiscard]] ImageId GetNullImage(const ImageDesc& desc); void RegisterImage(ImageId id); void UnregisterImage(ImageId id); void DeleteImage(ImageId id); void FreeImage(ImageId id); void TouchImage(Image& image); void TrackImage(ImageId id); void TrackImageHead(ImageId id); void TrackImageTail(ImageId id); void UntrackImage(ImageId id); void UntrackImageHead(ImageId id); void UntrackImageTail(ImageId id); void MarkAsMaybeDirty(ImageId id, Image& image); void TrackImageDownload(ImageId id, Image& image); [[nodiscard]] static bool SameBacking(const ImageInfo& cached, const ImageInfo& requested, bool exact_format); [[nodiscard]] static BindingType UploadBinding(const Image& image); [[nodiscard]] bool SafeToDownload(const Image& image); // Caller holds m_lock; it also serializes the per-image query epoch. [[nodiscard]] ImageIds FindImagesInRegion(uint64_t address, uint64_t size, bool page_overlap) const; [[nodiscard]] OverlapResult ResolveOverlap(const ImageInfo& requested, BindingType binding, ImageId cached, ImageId merged); [[nodiscard]] ImageId ResolveDepthOverlap(const ImageInfo& requested, BindingType binding, ImageId cached); [[nodiscard]] ImageId ExpandImage(const ImageInfo& info, ImageId source); void RefreshImage(ImageId id); void PrepareDccClear(ImageId id, const ImageDesc& desc); void InitializeImage(ImageId id); [[nodiscard]] TextureTransferPlan BuildTextureTransfer(const Image& image, BindingType binding, TransferDirection direction) const; [[nodiscard]] DownloadPlan BuildDownload(const Image& image) const; void UploadImage(Image& image, Buffer& source, uint64_t source_offset); void DownloadImageData(Image& image, Buffer& destination, uint64_t destination_offset, uint64_t destination_size, DownloadPlan plan); void DownloadDepth(Image& image, Buffer& destination, uint64_t destination_offset); void CommitGpuWrite(Image& image); // Caller holds m_lock. Volume layer ranges select depth slices. void ClearImage(CommandBuffer& command, ImageId id, const vk::ImageSubresourceRange& range, const vk::ClearValue& clear); void PrepareImageCopy(Image& image); void RefreshCopySource(ImageId id); [[nodiscard]] bool CopyD16(Image& destination, Image& source); void CopyImage(ImageId destination, ImageId source); void AssociateStencil(ImageId depth, GuestRange stencil); void CopyImageMip(ImageId destination, ImageId source, uint32_t mip, uint32_t layer); void ValidateImageDesc(const ImageDesc& desc) const; void InvalidateCpuAliases(uint64_t address, uint64_t size); [[nodiscard]] bool TryDownloadImage(ImageId id); GraphicContext& m_graphics; CommandScheduler& m_scheduler; TrackingSpinLock m_lock; PageManager& m_page_manager; BlitHelper m_blit_helper; TileManager m_tiler; BufferCache& m_buffer_cache; Common::SlotVector m_slot_images; ImagePageTable m_image_page_table; std::unordered_map m_null_images; Common::LeastRecentlyUsedCache m_lru_cache; std::unordered_set m_download_images; std::map m_surface_metas; uint64_t m_total_used_memory = 0; uint64_t m_trigger_gc_memory = 0; uint64_t m_pressure_gc_memory = 1536ull * 1024 * 1024; uint64_t m_critical_gc_memory = 3ull * 1024 * 1024 * 1024; uint64_t m_gc_tick = 0; mutable uint32_t m_image_query_epoch = 0; bool m_readback_linear_images = false; friend struct TextureCacheTestAccess; friend class BufferCache; friend class RenderExecutor; }; } // namespace Libs::Graphics #endif // EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_TEXTURECACHE_H_