Merge pull request #1189 from MichonGoddijn231849/fix/cb-resolve-tiled-targets

fix(agc): CB resolve into any tiled color target
This commit is contained in:
Dean authored and GitHub committed 2026-10-15 17:15:57 +13:00
commit d92a8dad39
4 files changed
+121 -2

No files matched your search

+3 -1
View File
@@ -1117,12 +1117,14 @@ if(BUILD_TESTING)
add_test(NAME agc_resident_present COMMAND agc_driver_recorder_tests --resident-present-only)
set_tests_properties(agc_resident_present PROPERTIES SKIP_RETURN_CODE 77)
add_test(NAME agc_queued_write_groups COMMAND agc_driver_recorder_tests --queued-write-groups-only)
add_test(NAME agc_tiled_resolve COMMAND agc_driver_recorder_tests --tiled-resolve-only)
set_tests_properties(agc_tiled_resolve PROPERTIES SKIP_RETURN_CODE 77)
add_test(NAME agc_slice_dirty COMMAND agc_driver_recorder_tests --slice-dirty-only)
set_tests_properties(agc_slice_dirty PROPERTIES SKIP_RETURN_CODE 77)
add_test(NAME agc_metadata_pass COMMAND agc_driver_recorder_tests --metadata-pass-only)
set_tests_properties(agc_metadata_pass PROPERTIES SKIP_RETURN_CODE 77)
if(WIN32)
set_tests_properties(agc_recorder_read_list agc_host_import_unmap agc_parked_import_unmap agc_host_import_retirement agc_sparse_run_import agc_sparse_run_import_copied agc_unit_shadow agc_descriptor_pool_recycling agc_draw_snapshot_cap agc_draw_snapshot_epoch agc_multisample_guest_access agc_draw_inputs_in_place agc_resident_present agc_slice_dirty agc_queued_write_groups agc_metadata_pass PROPERTIES ENVIRONMENT_MODIFICATION
set_tests_properties(agc_recorder_read_list agc_host_import_unmap agc_parked_import_unmap agc_host_import_retirement agc_sparse_run_import agc_sparse_run_import_copied agc_unit_shadow agc_descriptor_pool_recycling agc_draw_snapshot_cap agc_draw_snapshot_epoch agc_multisample_guest_access agc_draw_inputs_in_place agc_resident_present agc_slice_dirty agc_queued_write_groups agc_metadata_pass agc_tiled_resolve PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
add_test(NAME agc_host_import_second_mapping COMMAND agc_driver_recorder_tests --host-import-second-mapping-only)
set_tests_properties(agc_host_import_second_mapping PROPERTIES SKIP_RETURN_CODE 77 ENVIRONMENT_MODIFICATION
@@ -2671,7 +2671,7 @@ void RunColorMetadataPass(const Context& context, const ColorMetadataPass& pass)
if (pass.mode == ColorMetadataPass::Mode::Resolve) {
Require(pass.source.has_value() && pass.targets.size() == 1, "CB resolve without its source and destination");
const auto& destination = pass.targets.front();
Require(destination.tileMode == ColorTileMode::RenderTarget && context.detiler != nullptr, "CB resolve into a target without a resident image is not implemented");
Require(destination.tileMode != ColorTileMode::Linear && context.detiler != nullptr, "CB resolve into a target without a resident image is not implemented");
const auto resident = CachedStorageSurface(context, SurfaceForTarget(destination));
Require(resident->Attachable(), "storage format cannot be a color attachment");
ResolveMultisampleTarget(context, *pass.source, *resident, resident->AttachmentView(destination.format, destination.mip));
@@ -2610,6 +2610,16 @@ void multisampleTests() {
for (std::uint32_t pixel = 0; pixel < 4; ++pixel) queue.context[0x2fe + pixel * 4u] = 0x622ae6aeu;
expectFailure([&] { DecodeColorMetadataPass(queue); }, "differs from the rasterizer's");
queue = rasterizedResolve;
std::vector<std::byte> resolvedBlock(2u * 65536u);
const auto resolvedAddress = (reinterpret_cast<std::uintptr_t>(resolvedBlock.data()) + 65535u) / 65536u * 65536u;
for (const auto mode : {AgcDriver::Graphics::ColorTileMode::Standard4KB, AgcDriver::Graphics::ColorTileMode::Standard64KB, AgcDriver::Graphics::ColorTileMode::RenderTarget}) {
auto tiled = queue;
tiled.context[0x327] = static_cast<std::uint32_t>(resolvedAddress >> 8u);
tiled.context[0x391] = static_cast<std::uint32_t>(resolvedAddress >> 40u);
tiled.context[0x3b9] = (tiled.context[0x3b9] & ~(0x1fu << 14u)) | (static_cast<std::uint32_t>(mode) << 14u);
const auto tiledPass = DecodeColorMetadataPass(tiled);
Require(tiledPass.has_value() && tiledPass->mode == ColorMetadataPass::Mode::Resolve && tiledPass->targets.size() == 1 && tiledPass->targets[0].tileMode == mode && tiledPass->targets[0].samples == 1, "a CB resolve into a tile mode " + std::to_string(static_cast<std::uint32_t>(mode)) + " destination did not decode");
}
queue.context[0x32b] = queue.context[0x31c] ^ (1u << 8u);
expectFailure([&] { DecodeColorMetadataPass(queue); }, "different formats or extents");
queue = rasterizedResolve;
@@ -6435,6 +6435,103 @@ void fmaskDecompressMsaaTests(const Device& device, Recorder& recorder) {
std::cout << "FMASK decompress MSAA clear and resolve tests passed\n";
}
void tiledResolveTests(const Device& device, Recorder& recorder) {
namespace GuestMemory = AgcDriver::GuestMemory;
auto context = device.GetContext();
if ((context.limits.framebufferColorSampleCounts & VK_SAMPLE_COUNT_4_BIT) == 0 || !device.Graphics()) {
std::cout << "4x color targets or graphics unavailable: CB resolves into tiled targets not tested\n";
return;
}
context.hostImportAlignment = 0;
TextureDetiler detiler(context);
context.detiler = &detiler;
constexpr std::uint32_t side = 128;
constexpr std::size_t targetBytes = side * side * 4u;
constexpr std::size_t sourceBytes = targetBytes * 4u;
constexpr std::size_t bytes = targetBytes + sourceBytes + 65536u;
#ifdef _WIN32
void* block = VirtualAlloc(nullptr, bytes, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
#else
void* block = std::aligned_alloc(65536, bytes);
#endif
Require(block != nullptr, "cannot allocate the tiled resolve block");
const auto address = reinterpret_cast<std::uint64_t>(block);
{
GuestAllocations::Mutation mutation;
mutation.Add(block, bytes, true, true, true);
}
struct Release {
const Context& context;
void* block;
~Release() {
ClearMultisampleTargets(context.device);
ClearCachedTextures(context.device);
{
GuestAllocations::Mutation mutation;
mutation.Remove(block);
}
#ifdef _WIN32
VirtualFree(block, 0, MEM_RELEASE);
#else
std::free(block);
#endif
}
} release{context, block};
ColorTarget source{};
source.address = address + targetBytes;
source.surfaceAddress = source.address;
source.extent = {side, side};
source.surfaceExtent = source.extent;
source.format = VK_FORMAT_R8G8B8A8_UNORM;
source.bytes = targetBytes;
source.componentMapping = 0xe4u;
source.tileMode = ColorTileMode::RenderTarget;
source.samples = 4;
source.cmaskAddress = address + targetBytes + sourceBytes;
source.cmaskBytes = 4096;
source.cmaskFastClear = true;
const std::array<std::pair<ColorTileMode, std::uint32_t>, 3> modes{{{ColorTileMode::Standard4KB, 0x80402010u}, {ColorTileMode::Standard64KB, 0x10204080u}, {ColorTileMode::RenderTarget, 0x40801020u}}};
for (const auto& [mode, clear] : modes) {
const auto name = std::to_string(static_cast<std::uint32_t>(mode));
std::memset(block, 0x55, targetBytes);
std::memset(static_cast<std::byte*>(block) + targetBytes + sourceBytes, 0, 4096);
ColorTarget destination{};
destination.address = address;
destination.surfaceAddress = address;
destination.extent = {side, side};
destination.surfaceExtent = destination.extent;
destination.format = VK_FORMAT_R8G8B8A8_UNORM;
destination.bytes = targetBytes;
destination.componentMapping = 0xe4u;
destination.tileMode = mode;
source.clearWords = {clear, 0};
RunColorMetadataPass(context, {ColorMetadataPass::Mode::Resolve, {destination}, source});
std::vector<std::uint32_t> resolved(targetBytes / 4);
GuestMemory::Read(address, std::as_writable_bytes(std::span(resolved)));
const auto texels = std::ranges::count(resolved, clear);
Require(texels == static_cast<std::ptrdiff_t>(resolved.size()), "a CB resolve into a tile mode " + name + " target left " + std::to_string(resolved.size() - static_cast<std::size_t>(texels)) + " texels without the resolved color");
recorder.Sync();
ClearMultisampleTargets(context.device);
ClearCachedTextures(context.device);
}
{
ColorTarget destination{};
destination.address = address;
destination.extent = {side, side};
destination.format = VK_FORMAT_R8G8B8A8_UNORM;
destination.bytes = targetBytes;
destination.tileMode = ColorTileMode::Linear;
std::string message;
try {
RunColorMetadataPass(context, {ColorMetadataPass::Mode::Resolve, {destination}, source});
} catch (const std::exception& error) {
message = error.what();
}
Require(message.find("without a resident image is not implemented") != std::string::npos, "a CB resolve into a linear target did not throw: " + message);
}
recorder.Sync();
}
void depthSurfaceProofTests(const Device& device, Recorder& recorder) {
const auto& base = device.GetContext();
if (!device.NullDescriptors()) {
@@ -7879,6 +7976,15 @@ int main(int argc, char** argv) {
storageUploadLifetimeTests(device, recorder, std::string_view(argv[1]) == "--storage-upload-failure-only");
return 0;
}
if (argc == 2 && std::string_view(argv[1]) == "--tiled-resolve-only") {
if ((device.GetContext().limits.framebufferColorSampleCounts & VK_SAMPLE_COUNT_4_BIT) == 0 || !device.Graphics()) {
std::cout << "skipped, the device lacks 4x color targets or a graphics queue\n";
return 77;
}
tiledResolveTests(device, recorder);
std::cout << "CB resolves into tiled targets tests passed\n";
return 0;
}
if (argc == 2 && std::string_view(argv[1]) == "--cube-only") {
singleCubeTests(device, recorder);
std::cout << "Single cube snapshot and storage sampling tests passed\n";
@@ -8044,6 +8150,7 @@ int main(int argc, char** argv) {
readWrittenStagingTests(device, recorder);
depthSurfaceProofTests(device, recorder);
multisampleGuestAccessTests(device, recorder);
tiledResolveTests(device, recorder);
fmaskDecompressMsaaTests(device, recorder);
misalignedSnapshotTests(device, recorder);
misalignedRegionTests(device, recorder);