test(agc): check that a buffer written over pending storage results keeps both

A storage image is written on the GPU and left pending, then a leased
address-based use writes the first 40800 bytes of the same memory as a
buffer and every pending image is flushed: guest memory must hold the
buffer's bytes where it wrote and the image's texels everywhere else.
Runs with watched and with compared imports and returns 77 when host
imports or write watching are unavailable.
This commit is contained in:
Gustavo Ariel Gamarra Rojas 2026-10-12 14:52:44 -03:00
parent f4ff589679
commit 554e615524
2 changed files with 109 additions and 0 deletions

View File

@ -810,6 +810,12 @@ if(BUILD_TESTING)
set_tests_properties(agc_storage_baseline_settled PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
add_test(NAME agc_storage_written_buffer_order COMMAND agc_driver_recorder_tests --written-buffer-order-only)
set_tests_properties(agc_storage_written_buffer_order PROPERTIES SKIP_RETURN_CODE 77 TIMEOUT 30)
if(WIN32)
set_tests_properties(agc_storage_written_buffer_order PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
add_test(NAME agc_host_import_unmap COMMAND agc_driver_recorder_tests --host-import-unmap-only)
set_tests_properties(agc_host_import_unmap PROPERTIES SKIP_RETURN_CODE 77)
add_test(NAME agc_parked_import_unmap COMMAND agc_driver_recorder_tests --parked-import-unmap-only)

View File

@ -2482,6 +2482,103 @@ void storageRefreshTests(const Device& device, Recorder& recorder, bool watched)
recorder.Sync();
}
bool writtenBufferOrderTests(const Device& device, Recorder& recorder) {
const auto& base = device.GetContext();
if (base.hostImportAlignment == 0) {
std::cout << "host imports unavailable: written buffer order not tested\n";
return false;
}
constexpr std::uint32_t side = 256;
constexpr std::size_t bytes = side * side * 4;
void* block = AllocateWatched(bytes, 65536);
if (block == nullptr) {
std::cout << "no write watching: written buffer order not tested\n";
return false;
}
std::memset(block, 0x55, bytes);
const auto address = reinterpret_cast<std::uint64_t>(block);
{
GuestAllocations::Mutation mutation;
mutation.Add(block, bytes, true, true, true);
}
struct Unregister {
const Context& context;
void* block;
std::uint64_t address;
~Unregister() {
{
GuestAllocations::Mutation mutation;
mutation.Remove(block);
}
HostImportFor(context, address, bytes);
}
} unregister{base, block, address};
if (HostImportFor(base, address, bytes) == nullptr) {
std::cout << "host import of the written buffer order block refused: written buffer order not tested\n";
return false;
}
std::cout << "written buffer order: host imports are " << (AgcDriver::GuestMemory::Watched(address, bytes) ? "watched" : "compared") << "\n";
TextureDetiler detiler(base);
auto context = base;
context.detiler = &detiler;
GuestTextureResource resource{};
resource.baseAddress = address;
resource.width = side;
resource.height = side;
resource.mipCount = 1;
resource.tileMode = TextureTileMode::kR64KBX;
resource.dimension = TextureDimension::k2D;
resource.format = 56;
resource.dstSelX = 4;
resource.dstSelY = 5;
resource.dstSelZ = 6;
resource.dstSelW = 7;
Require(DescribeSurface(resource).guestBytes == bytes, "the written buffer order surface has an unexpected size");
const auto settle = [&] {
recorder.Submit();
device.WaitQueue();
recorder.Sync();
};
{
auto image = StorageTexture::Create(context, detiler, resource, 0);
const VkImageSubresourceRange range{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
const VkClearColorValue red{{1.0f, 0.0f, 0.0f, 1.0f}};
{
const auto commands = recorder.Commands();
recorder.Keep(image);
RecordMemoryBarrier(context, commands, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_WRITE_BIT);
context.Function<PFN_vkCmdClearColorImage>("vkCmdClearColorImage")(commands, image->Image(), VK_IMAGE_LAYOUT_GENERAL, &red, 1, &range);
RecordMemoryBarrier(context, commands, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT);
image->MarkDirty();
}
constexpr std::size_t written = 40800;
GuestBufferMemory memory(context);
memory.AcquireRegistered();
memory.AddWritable(address, written, false, false);
memory.Upload(true);
std::uint32_t adjustment = 0;
const auto view = memory.Descriptor(address, written, adjustment);
Require(adjustment == 0, "the written buffer view is not aligned to its element");
{
const auto commands = recorder.Commands();
RecordMemoryBarrier(context, commands, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_WRITE_BIT);
context.Function<PFN_vkCmdFillBuffer>("vkCmdFillBuffer")(commands, view.buffer, view.offset, written, 0xa5a5a5a5u);
RecordMemoryBarrier(context, commands, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT);
}
memory.RecordCopyBacks(recorder);
memory.MarkDirectWrites();
settle();
StorageTexture::FlushAllPending("written buffer order test");
settle();
std::vector<std::byte> read(bytes);
AgcDriver::GuestMemory::Read(address, read);
Require(std::all_of(read.begin(), read.begin() + written, [](std::byte value) { return value == std::byte{0xa5}; }), "storage results pending under a buffer a later use wrote were stored over the buffer's contents");
for (std::size_t i = written; i < bytes; ++i) Require(std::to_integer<std::uint8_t>(read[i]) == (i % 4 == 1 || i % 4 == 2 ? 0 : 255), "the storage results outside the written buffer did not reach guest memory");
}
recorder.Sync();
return true;
}
void hostImportUnmapTests(const Device& device, Recorder& recorder) {
const auto& context = device.GetContext();
if (context.hostImportAlignment == 0) {
@ -5491,6 +5588,11 @@ int main(int argc, char** argv) {
std::cout << "Completion label storage and ordering tests passed\n";
return 0;
}
if (argc == 2 && std::string_view(argv[1]) == "--written-buffer-order-only") {
if (!writtenBufferOrderTests(device, recorder)) return 77;
std::cout << "written buffer order tests passed\n";
return 0;
}
if (argc == 2 && std::string_view(argv[1]) == "--host-import-unmap-only") {
gpu.unlock();
hostImportUnmapTests(device, recorder);
@ -5571,6 +5673,7 @@ int main(int argc, char** argv) {
unitShadowTests(device, recorder);
storageRefreshTests(device, recorder, false);
storageRefreshTests(device, recorder, true);
static_cast<void>(writtenBufferOrderTests(device, recorder));
writeBackPaddingTests(device, recorder, false);
writeBackPaddingTests(device, recorder, true);
singlePassTests(device, recorder, false);