diff --git a/src/video_core/amdgpu/liverpool.h b/src/video_core/amdgpu/liverpool.h index c5966305..a170a30c 100644 --- a/src/video_core/amdgpu/liverpool.h +++ b/src/video_core/amdgpu/liverpool.h @@ -352,7 +352,7 @@ struct Liverpool { return u64(z_read_base) << 8; } - [[nodiscard]] size_t GetSizeAligned() const { + size_t GetSizeAligned() const { return depth_slice.tile_max * 8; } }; @@ -688,11 +688,15 @@ struct Liverpool { return u64(base_address) << 8; } - u64 CmaskAddress() const { - return u64(cmask_base_address) << 8; + VAddr CmaskAddress() const { + return VAddr(cmask_base_address) << 8; } - [[nodiscard]] size_t GetSizeAligned() const { + VAddr FmaskAddress() const { + return VAddr(fmask_base_address) << 8; + } + + size_t GetSizeAligned() const { const auto num_bytes_per_element = NumBits(info.format) / 8u; const auto slice_size = (slice.tile_max + 1) * 64u; const auto total_size = slice_size * (view.slice_max + 1) * num_bytes_per_element; @@ -700,11 +704,11 @@ struct Liverpool { return total_size; } - [[nodiscard]] TilingMode GetTilingMode() const { + TilingMode GetTilingMode() const { return attrib.tile_mode_index; } - [[nodiscard]] bool IsTiled() const { + bool IsTiled() const { return !info.linear_general; } diff --git a/src/video_core/texture_cache/image.cpp b/src/video_core/texture_cache/image.cpp index e4678564..19072a46 100644 --- a/src/video_core/texture_cache/image.cpp +++ b/src/video_core/texture_cache/image.cpp @@ -149,6 +149,8 @@ ImageInfo::ImageInfo(const AmdGpu::Liverpool::ColorBuffer& buffer, size.depth = 1; pitch = size.width; guest_size_bytes = buffer.GetSizeAligned(); + meta_info.cmask_addr = buffer.info.fast_clear ? buffer.CmaskAddress() : 0; + meta_info.fmask_addr = buffer.info.compression ? buffer.FmaskAddress() : 0; usage.render_target = true; } diff --git a/src/video_core/texture_cache/image.h b/src/video_core/texture_cache/image.h index 9d9022c6..28429d04 100644 --- a/src/video_core/texture_cache/image.h +++ b/src/video_core/texture_cache/image.h @@ -25,11 +25,12 @@ VK_DEFINE_HANDLE(VmaAllocator) namespace VideoCore { enum ImageFlagBits : u32 { - CpuModified = 1 << 2, ///< Contents have been modified from the CPU - GpuModified = 1 << 3, ///< Contents have been modified from the GPU - Tracked = 1 << 4, ///< Writes and reads are being hooked from the CPU - Registered = 1 << 6, ///< True when the image is registered - Picked = 1 << 7, ///< Temporary flag to mark the image as picked + CpuModified = 1 << 2, ///< Contents have been modified from the CPU + GpuModified = 1 << 3, ///< Contents have been modified from the GPU + Tracked = 1 << 4, ///< Writes and reads are being hooked from the CPU + Registered = 1 << 6, ///< True when the image is registered + Picked = 1 << 7, ///< Temporary flag to mark the image as picked + MetaRegistered = 1 << 8, ///< True when metadata for this surface is known and registered }; DECLARE_ENUM_FLAG_OPERATORS(ImageFlagBits) @@ -49,6 +50,12 @@ struct ImageInfo { bool IsPacked() const; bool IsDepthStencil() const; + struct { + VAddr cmask_addr{}; + VAddr fmask_addr{}; + VAddr htile_addr{}; + } meta_info; + struct { u32 texture : 1; u32 storage : 1; @@ -58,7 +65,6 @@ struct ImageInfo { } usage; // Usage data tracked during image lifetime bool is_tiled = false; - bool is_storage = false; vk::Format pixel_format = vk::Format::eUndefined; vk::ImageType type = vk::ImageType::e1D; SubresourceExtent resources; diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index bb882280..93f69aa2 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -131,6 +131,8 @@ Image& TextureCache::FindImage(const ImageInfo& info, VAddr cpu_address, bool re image_id = image_ids[0]; } + RegisterMeta(info, image_id); + Image& image = slot_images[image_id]; if (True(image.flags & ImageFlagBits::CpuModified) && (!image_ids.empty() || refresh_on_create)) { @@ -283,6 +285,47 @@ void TextureCache::RegisterImage(ImageId image_id) { [this, image_id](u64 page) { page_table[page].push_back(image_id); }); } +void TextureCache::RegisterMeta(const ImageInfo& info, ImageId image_id) { + Image& image = slot_images[image_id]; + + if (image.flags & ImageFlagBits::MetaRegistered) { + return; + } + + bool registered = true; + // Current resource tracking implementation allows us to detect usage of meta only in the last + // moment, so we likely will miss its first clear. To avoid this and make first frame, where + // the meta is encountered, looks correct we set its state to "cleared" at registrations time. + if (info.usage.render_target) { + if (info.meta_info.cmask_addr) { + surface_metas.emplace( + info.meta_info.cmask_addr, + MetaDataInfo{.type = MetaDataInfo::Type::CMask, .is_cleared = true}); + image.info.meta_info.cmask_addr = info.meta_info.cmask_addr; + } + + if (info.meta_info.fmask_addr) { + surface_metas.emplace( + info.meta_info.fmask_addr, + MetaDataInfo{.type = MetaDataInfo::Type::FMask, .is_cleared = true}); + image.info.meta_info.fmask_addr = info.meta_info.fmask_addr; + } + } else if (info.usage.depth_target) { + if (info.meta_info.htile_addr) { + surface_metas.emplace( + info.meta_info.htile_addr, + MetaDataInfo{.type = MetaDataInfo::Type::HTile, .is_cleared = true}); + image.info.meta_info.htile_addr = info.meta_info.htile_addr; + } + } else { + registered = false; + } + + if (registered) { + image.flags |= ImageFlagBits::MetaRegistered; + } +} + void TextureCache::UnregisterImage(ImageId image_id) { Image& image = slot_images[image_id]; ASSERT_MSG(True(image.flags & ImageFlagBits::Registered), diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 0ecd9faf..f50959e0 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -29,6 +29,17 @@ class TextureCache { static constexpr u64 PageBits = 20; static constexpr u64 PageMask = (1ULL << PageBits) - 1; + struct MetaDataInfo { + enum class Type { + CMask, + FMask, + HTile, + }; + + Type type; + bool is_cleared; + }; + public: explicit TextureCache(const Vulkan::Instance& instance, Vulkan::Scheduler& scheduler); ~TextureCache(); @@ -60,6 +71,25 @@ public: return slot_images[id]; } + bool IsMeta(VAddr address) const { + return surface_metas.contains(address); + } + + bool IsMetaCleared(VAddr address) const { + const auto& it = surface_metas.find(address); + if (it != surface_metas.end()) { + return it.value().is_cleared; + } + return false; + } + + void TouchMeta(VAddr address, bool is_clear) { + auto it = surface_metas.find(address); + if (it != surface_metas.end()) { + it.value().is_cleared = is_clear; + } + } + private: ImageView& RegisterImageView(Image& image, const ImageViewInfo& view_info); @@ -123,6 +153,9 @@ private: /// Register image in the page table void RegisterImage(ImageId image); + /// Register meta data surfaces attached to the image + void RegisterMeta(const ImageInfo& info, ImageId image); + /// Unregister image from the page table void UnregisterImage(ImageId image); @@ -145,6 +178,7 @@ private: tsl::robin_map samplers; tsl::robin_pg_map> page_table; boost::icl::interval_map cached_pages; + tsl::robin_map surface_metas; std::mutex mutex; #ifdef _WIN64 void* veh_handle{};