diff --git a/src/video_core/buffer_cache/buffer_cache.cpp b/src/video_core/buffer_cache/buffer_cache.cpp index 43d9d10f6..86b6d7b17 100644 --- a/src/video_core/buffer_cache/buffer_cache.cpp +++ b/src/video_core/buffer_cache/buffer_cache.cpp @@ -25,6 +25,24 @@ namespace VideoCore { static constexpr size_t GDS_BUFFER_SIZE = 64_KB; static constexpr size_t NULL_INDEX_BUFFER_SIZE = 16; static constexpr size_t STREAM_BUFFER_SIZE = 128_MB; +constexpr std::optional CmaskColorExpandedValue(u32 num_samples) { + switch (num_samples) { + case 1: + return 0xFFFFFFFF; + case 2: + return 0xDDDDDDDD; + case 4: + return 0xEEEEEEEE; + case 8: + return 0xFFFFFFFF; + default: + return 0; + } +} +static_assert(CmaskColorExpandedValue(1) == 0xFFFFFFFF); +static_assert(CmaskColorExpandedValue(2) == 0xDDDDDDDD); +static_assert(CmaskColorExpandedValue(4) == 0xEEEEEEEE); +static_assert(CmaskColorExpandedValue(8) == 0xFFFFFFFF); static constexpr auto ARENA_USAGE = vk::BufferUsageFlagBits::eTransferSrc | vk::BufferUsageFlagBits::eTransferDst | @@ -361,13 +379,26 @@ bool BufferCache::SynchronizeMemory(const Buffer* arena, VAddr device_addr, u32 } bool BufferCache::SynchronizeMemoryFromImage(const Buffer* arena, VAddr device_addr, u32 size) { - if (auto type = texture_cache.IsMeta(device_addr)) { - if (*type == TextureCache::MetaType::HTile) { + if (const auto meta = texture_cache.GetMetaInfo(device_addr)) { + if (meta->type == TextureCache::MetaType::HTile) { static constexpr u32 ZmaskUncompressed = 0xf; runtime.FillBuffer(arena, arena->Offset(device_addr), size, ZmaskUncompressed); return true; + } else if (meta->type == TextureCache::MetaType::CMask) { + const auto expanded_value = CmaskColorExpandedValue(meta->num_samples); + if (!expanded_value) { + LOG_WARNING(Render_Vulkan, + "Unsupported CMask sample count: address={:#x}, size={:#x}, " + "samples={}, owner={}:{}", + device_addr, size, meta->num_samples, meta->owner_id.index, + meta->owner_uid); + return false; + } + runtime.FillBuffer(arena, arena->Offset(device_addr), size, *expanded_value); + return true; } else { - LOG_WARNING(Render_Vulkan, "Unhandled metadata type {}", magic_enum::enum_name(*type)); + LOG_WARNING(Render_Vulkan, "Unhandled metadata type {}", + magic_enum::enum_name(meta->type)); } } const ImageId image_id = texture_cache.FindImageFromRange(device_addr, size); diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index b48462092..1e7a23de8 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -639,6 +639,31 @@ ImageView& TextureCache::FindTexture(ImageId image_id, const ImageDesc& desc) { return image.FindView(desc.view_info); } +void TextureCache::RegisterMeta(VAddr address, MetaDataInfo info) { + const auto it = surface_metas.find(address); + if (it == surface_metas.end()) { + surface_metas.emplace(address, info); + return; + } + + auto& registered = it->second; + if (registered.type != info.type || registered.owner_uid != info.owner_uid) { + registered = info; + return; + } + + // Preserve the current clear state when the same render target is rebound. + registered.owner_id = info.owner_id; + registered.num_samples = info.num_samples; +} + +void TextureCache::UnregisterMeta(VAddr address, u64 owner_uid) { + const auto it = surface_metas.find(address); + if (it != surface_metas.end() && it->second.owner_uid == owner_uid) { + surface_metas.erase(address); + } +} + ImageView& TextureCache::FindRenderTarget(ImageId image_id, const ImageDesc& desc) { Image& image = slot_images[image_id]; image.flags |= ImageFlagBits::GpuModified; @@ -650,16 +675,32 @@ ImageView& TextureCache::FindRenderTarget(ImageId image_id, const ImageDesc& des UpdateImage(image_id); // Register meta data for this color buffer - if (desc.info.meta_info.cmask_addr) { - surface_metas.emplace(desc.info.meta_info.cmask_addr, - MetaDataInfo{.type = MetaType::CMask}); - image.info.meta_info.cmask_addr = desc.info.meta_info.cmask_addr; + const VAddr cmask_addr = desc.info.meta_info.cmask_addr; + if (image.info.meta_info.cmask_addr != cmask_addr) { + UnregisterMeta(image.info.meta_info.cmask_addr, image.image_uid); + image.info.meta_info.cmask_addr = cmask_addr; + } + if (cmask_addr) { + RegisterMeta(cmask_addr, MetaDataInfo{ + .type = MetaType::CMask, + .owner_id = image_id, + .owner_uid = image.image_uid, + .num_samples = image.info.num_samples, + }); } - if (desc.info.meta_info.fmask_addr) { - surface_metas.emplace(desc.info.meta_info.fmask_addr, - MetaDataInfo{.type = MetaType::FMask}); - image.info.meta_info.fmask_addr = desc.info.meta_info.fmask_addr; + const VAddr fmask_addr = desc.info.meta_info.fmask_addr; + if (image.info.meta_info.fmask_addr != fmask_addr) { + UnregisterMeta(image.info.meta_info.fmask_addr, image.image_uid); + image.info.meta_info.fmask_addr = fmask_addr; + } + if (fmask_addr) { + RegisterMeta(fmask_addr, MetaDataInfo{ + .type = MetaType::FMask, + .owner_id = image_id, + .owner_uid = image.image_uid, + .num_samples = image.info.num_samples, + }); } return image.FindView(desc.view_info, false); @@ -672,11 +713,19 @@ ImageView& TextureCache::FindDepthTarget(ImageId image_id, const ImageDesc& desc UpdateImage(image_id); // Register meta data for this depth buffer - if (desc.info.meta_info.htile_addr) { - surface_metas.emplace(desc.info.meta_info.htile_addr, - MetaDataInfo{.type = MetaType::HTile, - .clear_mask = image.info.meta_info.htile_clear_mask}); - image.info.meta_info.htile_addr = desc.info.meta_info.htile_addr; + const VAddr htile_addr = desc.info.meta_info.htile_addr; + if (image.info.meta_info.htile_addr != htile_addr) { + UnregisterMeta(image.info.meta_info.htile_addr, image.image_uid); + image.info.meta_info.htile_addr = htile_addr; + } + if (htile_addr) { + RegisterMeta(htile_addr, MetaDataInfo{ + .type = MetaType::HTile, + .owner_id = image_id, + .owner_uid = image.image_uid, + .num_samples = image.info.num_samples, + .clear_mask = image.info.meta_info.htile_clear_mask, + }); } AssociateStencil(image_id, desc.info); @@ -1036,13 +1085,13 @@ void TextureCache::DeleteImage(ImageId image_id) { // Remove any registered meta areas. const auto& meta_info = image.info.meta_info; if (meta_info.cmask_addr) { - surface_metas.erase(meta_info.cmask_addr); + UnregisterMeta(meta_info.cmask_addr, image.image_uid); } if (meta_info.fmask_addr) { - surface_metas.erase(meta_info.fmask_addr); + UnregisterMeta(meta_info.fmask_addr, image.image_uid); } if (meta_info.htile_addr) { - surface_metas.erase(meta_info.htile_addr); + UnregisterMeta(meta_info.htile_addr, image.image_uid); } { diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index cee15ea40..9f101b8e4 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -4,8 +4,14 @@ #pragma once #include +#include +#include +#include #include #include +#include +#include +#include #include "common/lru_cache.h" #include "common/multi_level_page_table.h" @@ -198,6 +204,31 @@ public: HTile, }; + struct MetaDataInfo { + MetaType type; + ImageId owner_id{}; + u64 owner_uid{}; + u32 num_samples = 1; + s32 clear_mask = -1; + }; + + /// Returns metadata information if the specified address belongs to a live image. + std::optional GetMetaInfo(VAddr address) const { + const auto it = surface_metas.find(address); + if (it == surface_metas.end()) { + return std::nullopt; + } + const auto& meta = it->second; + if (!meta.owner_id || !slot_images.IsAllocated(meta.owner_id)) { + return std::nullopt; + } + const auto& owner = slot_images[meta.owner_id]; + if (owner.image_uid != meta.owner_uid || False(owner.flags & ImageFlagBits::Registered)) { + return std::nullopt; + } + return meta; + } + /// Returns meta type if the specified address is a metadata surface. std::optional IsMeta(VAddr address) const { auto it = surface_metas.find(address); @@ -330,6 +361,9 @@ private: void MarkAsMaybeDirty(ImageId image_id, Image& image); + void RegisterMeta(VAddr address, MetaDataInfo info); + void UnregisterMeta(VAddr address, u64 owner_uid); + /// Removes the image and any views/surface metas that reference it. void DeleteImage(ImageId image_id); @@ -379,10 +413,6 @@ private: Common::LRUCache sampler_lru_cache; const bool readback_linear_images; std::mutex download_images_mutex; - struct MetaDataInfo { - MetaType type; - s32 clear_mask = -1; - }; absl::flat_hash_map surface_metas; };