diff --git a/src/shader_recompiler/frontend/instruction.h b/src/shader_recompiler/frontend/instruction.h index 7c2e0bd1e..f4e7bc9f2 100644 --- a/src/shader_recompiler/frontend/instruction.h +++ b/src/shader_recompiler/frontend/instruction.h @@ -3,8 +3,6 @@ #pragma once -#include -#include "common/bit_field.h" #include "shader_recompiler/frontend/opcodes.h" namespace Shader::Gcn { diff --git a/src/shader_recompiler/info.h b/src/shader_recompiler/info.h index bb5c88584..11dd9c05e 100644 --- a/src/shader_recompiler/info.h +++ b/src/shader_recompiler/info.h @@ -222,6 +222,7 @@ struct Info { VAddr pgm_base; bool has_storage_images{}; bool has_discard{}; + bool has_bitwise_xor{}; bool has_image_gather{}; bool has_image_query{}; bool uses_buffer_atomic_float_min_max{}; diff --git a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp index e5a4beb8b..2cf39c98e 100644 --- a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp +++ b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp @@ -455,11 +455,12 @@ void PatchImageSharp(IR::Block& block, IR::Inst& inst, Info& info, Descriptors& // Read image sharp. const auto tsharp = TrackSharp(tsharp_handle, info); const auto inst_info = inst.Flags(); - const bool is_written = inst.GetOpcode() == IR::Opcode::ImageWrite; + const bool is_atomic = IsImageAtomicInstruction(inst); + const bool is_written = inst.GetOpcode() == IR::Opcode::ImageWrite || is_atomic; const ImageResource image_res = { .sharp_idx = tsharp, .is_depth = bool(inst_info.is_depth), - .is_atomic = IsImageAtomicInstruction(inst), + .is_atomic = is_atomic, .is_array = bool(inst_info.is_array), .is_written = is_written, .is_r128 = bool(inst_info.is_r128), diff --git a/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp b/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp index 079827866..8f0e61da2 100644 --- a/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp +++ b/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp @@ -95,6 +95,9 @@ void Visit(Info& info, const IR::Inst& inst) { case IR::Opcode::DiscardCond: info.has_discard = true; break; + case IR::Opcode::BitwiseXor32: + info.has_bitwise_xor = true; + break; case IR::Opcode::ImageGather: case IR::Opcode::ImageGatherDref: info.has_image_gather = true; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index b6130e873..c5f894b10 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -503,9 +503,13 @@ bool Rasterizer::IsComputeMetaClear(const Pipeline* pipeline) { return false; } + // Most of the time when a metadata is updated with a shader it gets cleared. It means + // we can skip the whole dispatch and update the tracked state instead. Also, it is not + // intended to be consumed and in such rare cases (e.g. HTile introspection, CRAA) we + // will need its full emulation anyways. const auto& info = pipeline->GetStage(Shader::LogicalStage::Compute); - // Assume if a shader reads and writes metas at the same time, it is a copy shader. + // Assume if a shader reads metadata, it is a copy shader. for (const auto& desc : info.buffers) { const VAddr address = desc.GetSharp(info).base_address; if (!desc.IsSpecial() && !desc.is_written && texture_cache.IsMeta(address)) { @@ -513,10 +517,15 @@ bool Rasterizer::IsComputeMetaClear(const Pipeline* pipeline) { } } - // Most of the time when a metadata is updated with a shader it gets cleared. It means - // we can skip the whole dispatch and update the tracked state instead. Also, it is not - // intended to be consumed and in such rare cases (e.g. HTile introspection, CRAA) we - // will need its full emulation anyways. + // Metadata surfaces are tiled and thus need address calculation to be written properly. + // If a shader wants to encode HTILE, for example, from a depth image it will have to compute + // proper tile address from dispatch invocation id. This address calculation contains an xor + // operation so use it as a heuristic for metadata writes that are probably not clears. + if (info.has_bitwise_xor) { + return false; + } + + // Assume if a shader writes metadata without address calculation, it is a clear shader. for (const auto& desc : info.buffers) { const VAddr address = desc.GetSharp(info).base_address; if (!desc.IsSpecial() && desc.is_written && texture_cache.ClearMeta(address)) { diff --git a/src/video_core/texture_cache/image.h b/src/video_core/texture_cache/image.h index 31b67e021..2dbaff053 100644 --- a/src/video_core/texture_cache/image.h +++ b/src/video_core/texture_cache/image.h @@ -27,10 +27,9 @@ enum ImageFlagBits : u32 { CpuDirty = 1 << 1, ///< Contents have been modified from the CPU GpuDirty = 1 << 2, ///< Contents have been modified from the GPU (valid data in buffer cache) Dirty = MaybeCpuDirty | CpuDirty | GpuDirty, - GpuModified = 1 << 3, ///< Contents have been modified from the GPU - 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 + GpuModified = 1 << 3, ///< Contents have been modified from the GPU + Registered = 1 << 6, ///< True when the image is registered + Picked = 1 << 7, ///< Temporary flag to mark the image as picked }; DECLARE_ENUM_FLAG_OPERATORS(ImageFlagBits) diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index 723b95892..a6657d8d9 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -508,20 +508,16 @@ ImageView& TextureCache::FindRenderTarget(BaseDesc& desc) { UpdateImage(image_id); // Register meta data for this color buffer - if (!(image.flags & ImageFlagBits::MetaRegistered)) { - if (desc.info.meta_info.cmask_addr) { - surface_metas.emplace(desc.info.meta_info.cmask_addr, - MetaDataInfo{.type = MetaDataInfo::Type::CMask}); - image.info.meta_info.cmask_addr = desc.info.meta_info.cmask_addr; - image.flags |= ImageFlagBits::MetaRegistered; - } + if (desc.info.meta_info.cmask_addr) { + surface_metas.emplace(desc.info.meta_info.cmask_addr, + MetaDataInfo{.type = MetaDataInfo::Type::CMask}); + image.info.meta_info.cmask_addr = desc.info.meta_info.cmask_addr; + } - if (desc.info.meta_info.fmask_addr) { - surface_metas.emplace(desc.info.meta_info.fmask_addr, - MetaDataInfo{.type = MetaDataInfo::Type::FMask}); - image.info.meta_info.fmask_addr = desc.info.meta_info.fmask_addr; - image.flags |= ImageFlagBits::MetaRegistered; - } + if (desc.info.meta_info.fmask_addr) { + surface_metas.emplace(desc.info.meta_info.fmask_addr, + MetaDataInfo{.type = MetaDataInfo::Type::FMask}); + image.info.meta_info.fmask_addr = desc.info.meta_info.fmask_addr; } return RegisterImageView(image_id, desc.view_info); @@ -536,15 +532,11 @@ ImageView& TextureCache::FindDepthTarget(BaseDesc& desc) { UpdateImage(image_id); // Register meta data for this depth buffer - if (!(image.flags & ImageFlagBits::MetaRegistered)) { - if (desc.info.meta_info.htile_addr) { - surface_metas.emplace( - desc.info.meta_info.htile_addr, - MetaDataInfo{.type = MetaDataInfo::Type::HTile, - .clear_mask = image.info.meta_info.htile_clear_mask}); - image.info.meta_info.htile_addr = desc.info.meta_info.htile_addr; - image.flags |= ImageFlagBits::MetaRegistered; - } + if (desc.info.meta_info.htile_addr) { + surface_metas.emplace(desc.info.meta_info.htile_addr, + MetaDataInfo{.type = MetaDataInfo::Type::HTile, + .clear_mask = image.info.meta_info.htile_clear_mask}); + image.info.meta_info.htile_addr = desc.info.meta_info.htile_addr; } // If there is a stencil attachment, link depth and stencil. diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index ff8ffb61c..9a9679c0a 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -161,10 +161,12 @@ public: /// Registers an image view for provided image ImageView& RegisterImageView(ImageId image_id, const ImageViewInfo& view_info); + /// Returns true if the specified address is a metadata surface. bool IsMeta(VAddr address) const { return surface_metas.contains(address); } + /// Returns true if a slice of the specified metadata surface has been cleared. bool IsMetaCleared(VAddr address, u32 slice) const { const auto& it = surface_metas.find(address); if (it != surface_metas.end()) { @@ -173,6 +175,7 @@ public: return false; } + /// Clears all slices of the specified metadata surface. bool ClearMeta(VAddr address) { auto it = surface_metas.find(address); if (it != surface_metas.end()) { @@ -182,6 +185,7 @@ public: return false; } + /// Updates the state of a slice of the specified metadata surface. bool TouchMeta(VAddr address, u32 slice, bool is_clear) { auto it = surface_metas.find(address); if (it != surface_metas.end()) {