mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-19 18:36:53 +02:00
graphics: do not arm a fast clear from a read-modify-write dispatch (#428)
* graphics: do not arm a fast clear from a read-modify-write dispatch * tests: cover compute metadata clear classification --------- Co-authored-by: nmzik <Nmzik@mail.ru>
This commit is contained in:
@@ -574,6 +574,8 @@ if(BUILD_TESTING)
|
||||
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --storage-sampled-only)
|
||||
add_test(NAME texture_cache_depth_readback
|
||||
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --depth-readback-only)
|
||||
add_test(NAME compute_meta_clear_classification
|
||||
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --compute-meta-clear-only)
|
||||
add_test(NAME buffer_cache_dirty_gc
|
||||
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --buffer-cache-gc-only)
|
||||
|
||||
|
||||
@@ -54,7 +54,9 @@ bool RenderExecutor::TryConsumeComputeMetaClear(const ShaderComputeInputInfo& in
|
||||
for (uint32_t i = 0; i < program.info.buffers.size(); i++) {
|
||||
const auto& resource = program.info.buffers[i];
|
||||
const auto descriptor = DecodeNativeDescriptor<ShaderBufferResource>(resources.buffers[i]);
|
||||
if (!resource.written && cache.IsMeta(descriptor.Base48())) {
|
||||
// A metadata resource that is also read is not proven to be a full overwrite. Execute it
|
||||
// conservatively instead of replacing the dispatch with a coarse full-surface clear.
|
||||
if (cache.IsMeta(descriptor.Base48()) && (!resource.written || resource.read)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -357,10 +357,23 @@ struct TextureCacheTestAccess {
|
||||
return cache.TryDownloadImage(id);
|
||||
}
|
||||
|
||||
static void RegisterHtileMeta(TextureCache &cache, uint64_t address) {
|
||||
std::lock_guard lock(cache.m_lock);
|
||||
auto &metadata = cache.m_surface_metas[address];
|
||||
metadata.type = TextureCache::MetaDataInfo::Type::HTile;
|
||||
metadata.clear_mask = 0;
|
||||
}
|
||||
|
||||
static TileManager &Tiler(TextureCache &cache) { return cache.m_tiler; }
|
||||
};
|
||||
|
||||
struct RenderExecutorTestAccess {
|
||||
static bool TryConsumeComputeMetaClear(RenderExecutor &executor,
|
||||
const ShaderComputeInputInfo &input,
|
||||
const CommandBuffer &buffer) {
|
||||
return executor.TryConsumeComputeMetaClear(input, buffer);
|
||||
}
|
||||
|
||||
static TextureBinding
|
||||
ResolveTexture(RenderExecutor &executor,
|
||||
const ShaderRecompiler::IR::ImageResource &resource,
|
||||
@@ -3778,6 +3791,92 @@ public:
|
||||
std::printf("[host] %-32s ok\n", name);
|
||||
}
|
||||
|
||||
void CheckComputeMetaClearClassification() {
|
||||
constexpr const char *name = "ComputeMetaClearClassification";
|
||||
constexpr uint64_t read_only_meta = 0x0000000204201f00ull;
|
||||
constexpr uint64_t read_write_meta = 0x0000000204202000ull;
|
||||
constexpr uint64_t write_only_meta = 0x0000000204202100ull;
|
||||
EnsureRuntimeContext();
|
||||
RenderContext context(m_runtime_context);
|
||||
auto &scheduler = context.GetCommandScheduler();
|
||||
HW::Context registers{};
|
||||
HW::UserConfig user_config{};
|
||||
HW::Shader shaders{};
|
||||
scheduler.Begin(registers, user_config, shaders);
|
||||
auto &texture_cache = context.GetTextureCache();
|
||||
TextureCacheTestAccess::RegisterHtileMeta(texture_cache, read_only_meta);
|
||||
TextureCacheTestAccess::RegisterHtileMeta(texture_cache, read_write_meta);
|
||||
TextureCacheTestAccess::RegisterHtileMeta(texture_cache, write_only_meta);
|
||||
Require(name, "HTile fixture",
|
||||
texture_cache.IsMeta(read_only_meta) &&
|
||||
texture_cache.IsMeta(read_write_meta) &&
|
||||
texture_cache.IsMeta(write_only_meta) &&
|
||||
!texture_cache.IsMetaCleared(read_only_meta, 0) &&
|
||||
!texture_cache.IsMetaCleared(read_write_meta, 0) &&
|
||||
!texture_cache.IsMetaCleared(write_only_meta, 0),
|
||||
"test HTile entries were not registered in their initial state");
|
||||
|
||||
const auto MakeInput = [](uint64_t address, bool read, bool written) {
|
||||
auto program = std::make_shared<ShaderRecompiler::IR::Program>();
|
||||
program->stage = ShaderType::Compute;
|
||||
ShaderRecompiler::IR::BufferResource resource{};
|
||||
resource.read = read;
|
||||
resource.written = written;
|
||||
resource.formatted = true;
|
||||
program->info.buffers.push_back(resource);
|
||||
|
||||
ShaderBufferResource descriptor{};
|
||||
descriptor.UpdateAddress48(address);
|
||||
descriptor.fields[1] |= 16u << 16u;
|
||||
descriptor.fields[2] = 8;
|
||||
descriptor.fields[3] =
|
||||
DstSel(4, 5, 6, 7) |
|
||||
(static_cast<uint32_t>(Prospero::BufferFormat::k32_32_32_32UInt)
|
||||
<< 12u);
|
||||
ShaderRecompiler::IR::DescriptorValue value{};
|
||||
value.dword_count = 4;
|
||||
std::copy_n(descriptor.fields, value.dword_count, value.dwords.begin());
|
||||
auto snapshot =
|
||||
std::make_shared<ShaderRecompiler::IR::ResourceSnapshot>();
|
||||
snapshot->buffers.push_back(value);
|
||||
|
||||
ShaderComputeInputInfo input{};
|
||||
input.stage.program = std::move(program);
|
||||
input.stage.resources = std::move(snapshot);
|
||||
return input;
|
||||
};
|
||||
auto &executor = context.GetRenderExecutor();
|
||||
auto &command = scheduler.Current();
|
||||
const auto read_only_input = MakeInput(read_only_meta, true, false);
|
||||
const bool read_only_consumed =
|
||||
RenderExecutorTestAccess::TryConsumeComputeMetaClear(
|
||||
executor, read_only_input, command);
|
||||
Require(name, "read-only dispatch preserved",
|
||||
!read_only_consumed &&
|
||||
!texture_cache.IsMetaCleared(read_only_meta, 0),
|
||||
"a metadata read-only dispatch changed logical clear state");
|
||||
|
||||
const auto read_write_input = MakeInput(read_write_meta, true, true);
|
||||
const bool read_write_consumed =
|
||||
RenderExecutorTestAccess::TryConsumeComputeMetaClear(
|
||||
executor, read_write_input, command);
|
||||
Require(name, "read/write dispatch preserved",
|
||||
!read_write_consumed &&
|
||||
!texture_cache.IsMetaCleared(read_write_meta, 0),
|
||||
"a metadata read/modify/write dispatch was replaced by a clear");
|
||||
|
||||
const auto write_only_input = MakeInput(write_only_meta, false, true);
|
||||
const bool write_only_consumed =
|
||||
RenderExecutorTestAccess::TryConsumeComputeMetaClear(
|
||||
executor, write_only_input, command);
|
||||
Require(name, "write-only fill consumed",
|
||||
write_only_consumed &&
|
||||
texture_cache.IsMetaCleared(write_only_meta, 0),
|
||||
"a metadata write-only fill was not consumed as a clear");
|
||||
scheduler.Finish();
|
||||
std::printf("[host] %-32s ok\n", name);
|
||||
}
|
||||
|
||||
void CheckUnifiedTextureCacheFlow() {
|
||||
constexpr const char *name = "UnifiedTextureCacheFlow";
|
||||
constexpr uintptr_t base = 0x0000000200600000ull;
|
||||
@@ -25443,6 +25542,11 @@ int main(int argc, char **argv) {
|
||||
vulkan.CheckUnifiedTextureCacheFlow();
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--compute-meta-clear-only") == 0) {
|
||||
VulkanHarness vulkan;
|
||||
vulkan.CheckComputeMetaClearClassification();
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--layered-image-only") == 0) {
|
||||
VulkanHarness vulkan;
|
||||
vulkan.CheckUnifiedImageViewCache();
|
||||
|
||||
Reference in New Issue
Block a user