Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion core/libs/prx/libSceAgcDriver/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1542,6 +1542,8 @@ if(BUILD_TESTING)
set_tests_properties(agc_sparse_run_import_copied PROPERTIES SKIP_RETURN_CODE 77 TIMEOUT 30)
add_test(NAME agc_unit_shadow COMMAND agc_driver_recorder_tests --unit-shadow-only)
set_tests_properties(agc_unit_shadow PROPERTIES SKIP_RETURN_CODE 77 TIMEOUT 30 ENVIRONMENT "APS5_PIN_WAIT_MS=3000")
add_test(NAME agc_idle_publish_wait COMMAND agc_driver_recorder_tests --idle-publish-wait-only)
set_tests_properties(agc_idle_publish_wait PROPERTIES SKIP_RETURN_CODE 77 TIMEOUT 30 ENVIRONMENT "APS5_PIN_WAIT_MS=3000;APS5_WRITE_WATCH_IMPORTS=watch")
if(WIN32)
add_test(NAME agc_storage_cpu_snapshot COMMAND agc_driver_recorder_tests --storage-cpu-snapshot-only)
set_tests_properties(agc_storage_cpu_snapshot PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1" TIMEOUT 30 SKIP_RETURN_CODE 77
Expand Down Expand Up @@ -1575,7 +1577,7 @@ if(BUILD_TESTING)
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_draw_inputs_copied agc_resident_present agc_slice_dirty agc_queued_write_groups agc_metadata_pass agc_tiled_resolve 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_idle_publish_wait agc_descriptor_pool_recycling agc_draw_snapshot_cap agc_draw_snapshot_epoch agc_multisample_guest_access agc_draw_inputs_in_place agc_draw_inputs_copied 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
Expand Down
17 changes: 16 additions & 1 deletion core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3674,6 +3674,18 @@ bool StorageTexture::pendingUnitInside(std::uint64_t address, std::size_t bytes)
return false;
}

namespace {

void waitForIdlePublish(const char* reason) {
if (reason == nullptr || std::strcmp(reason, "idle") != 0 || Recorder::InCompletion()) return;
if (auto* recorder = Recorder::Active()) {
Recorder::CountSync(0);
recorder->Sync();
}
}

}

bool StorageTexture::FlushPending(std::uint64_t address, std::size_t bytes, const StorageTexture* except, const char* reason, PublishScope scope, bool* published) {
static_cast<void>(FlushDepthPlanes(address, bytes));
if (published != nullptr) *published = false;
Expand Down Expand Up @@ -3767,6 +3779,7 @@ bool StorageTexture::FlushPending(std::uint64_t address, std::size_t bytes, cons
// import, so they are published under the same hold, after every store.
if (scope != PublishScope::None) {
const auto units = PublishShadow(address, bytes, scope, PublishReasonFor(reason));
if (units != 0) waitForIdlePublish(reason);
if (published != nullptr) *published = units != 0;
}
return true;
Expand Down Expand Up @@ -3932,7 +3945,9 @@ void StorageTexture::awaitStoredSave(const std::vector<bool>* layers) {
bool StorageTexture::PublishShadowsOnly(std::uint64_t address, std::size_t bytes, PublishScope scope, const char* reason) {
if (scope == PublishScope::None || !AnyShadowedOverlaps(address, bytes)) return false;
std::lock_guard gpu(GuestMemory::GpuMutex());
return PublishShadow(address, bytes, scope, PublishReasonFor(reason)) != 0;
if (PublishShadow(address, bytes, scope, PublishReasonFor(reason)) == 0) return false;
waitForIdlePublish(reason);
return true;
}

namespace {
Expand Down
81 changes: 81 additions & 0 deletions core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4677,6 +4677,79 @@ bool unitShadowTests(const Device& device, Recorder& recorder) {
return true;
}

void idlePublishWaitTests(const Device& device, Recorder& recorder) {
using namespace AgcDriver::GuestMemory;
const auto& context = device.GetContext();
constexpr std::uint64_t unit = 65536;
constexpr std::size_t bytes = 8 * unit;
void* block = AllocateWatched(bytes, 65536);
Require(block != nullptr, "unit shadows are on without write watching");
struct Release {
void* block;
~Release() { ReleaseWatched(block, bytes); }
} release{block};
std::memset(block, 0x11, 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{context, block, address};
const auto* import = HostImportFor(context, address, bytes);
if (import == nullptr || !Watched(address, bytes)) {
std::cout << "host import of the idle publish test block refused or not watched: idle publish wait not tested\n";
return;
}
recorder.Sync();
CollectWritesUncached(address, bytes);
auto* words = static_cast<std::uint8_t*>(block);
const auto copyBuffer = context.Function<PFN_vkCmdCopyBuffer>("vkCmdCopyBuffer");
const auto retile = [&](std::uint64_t begin, std::uint8_t value) {
auto destination = ShadowDestinationFor(context, *import, begin, begin + unit);
Require(destination.has_value() && destination->seedUnits.empty(), "a whole-unit retile piece has no destination");
auto pattern = std::make_shared<Buffer>(context, static_cast<std::size_t>(unit), VK_BUFFER_USAGE_TRANSFER_SRC_BIT);
std::memset(pattern->Bytes().data(), value, pattern->Bytes().size());
const auto commands = recorder.Commands();
recorder.Keep(pattern);
recorder.Keep(destination->slab);
RecordMemoryBarrier(context, commands, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_HOST_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT);
const VkBufferCopy piece{0, destination->offset, unit};
copyBuffer(commands, pattern->Handle(), destination->buffer, 1, &piece);
RecordMemoryBarrier(context, commands, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT);
recorder.MarkCovered(VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT);
const ShadowedRange range{begin, begin + unit, destination->slab};
MarkShadowed(*import, std::span(&range, 1), TrackerGeneration());
Require(AnyShadowedOverlaps(begin, unit), "the retiled unit is not shadowed");
};
retile(address + 2 * unit, 0x22);
retile(address + 3 * unit, 0x33);
StorageTexture::FlushAllPending("idle");
Require(!AnyShadowedOverlaps(address + 2 * unit, static_cast<std::size_t>(2 * unit)), "the idle flush did not publish the shadowed units");
Require(!recorder.PendingWriteOverlaps(address + 2 * unit, static_cast<std::size_t>(2 * unit)), "the idle flush returned before its publish's copies completed");
words[2 * unit] = 0x44;
words[4 * unit - 1] = 0x45;
recorder.Sync();
Require(words[2 * unit] == 0x44 && words[4 * unit - 1] == 0x45, "a CPU store made after the idle flush returned was overwritten by its publish");
Require(words[2 * unit + 1] == 0x22 && words[3 * unit - 1] == 0x22 && words[3 * unit] == 0x33 && words[1 * unit] == 0x11 && words[4 * unit] == 0x11, "the idle publish's bytes are wrong");
retile(address + 5 * unit, 0x55);
StorageTexture::FlushAllPending("label");
Require(recorder.PendingWriteOverlaps(address + 5 * unit, unit), "a publish of a flush other than the idle one was waited");
recorder.Sync();
Require(words[5 * unit] == 0x55 && words[6 * unit - 1] == 0x55, "the label flush's publish did not land");
std::cout << "idle publish wait verified\n";
}

void storageRefreshTests(const Device& device, Recorder& recorder, bool watched) {
const auto& base = device.GetContext();
if (base.hostImportAlignment == 0) {
Expand Down Expand Up @@ -10164,6 +10237,14 @@ int main(int argc, char** argv) {
parkedImportUnmapTests(device);
return 0;
}
if (argc == 2 && std::string_view(argv[1]) == "--idle-publish-wait-only") {
if (!AgcDriver::Graphics::UnitShadowEnabled() || device.GetContext().hostImportAlignment == 0) {
std::cout << "skipped, unit shadows or host imports are unavailable\n";
return 77;
}
idlePublishWaitTests(device, recorder);
return 0;
}
if (argc == 2 && std::string_view(argv[1]) == "--unit-shadow-only") {
if (!AgcDriver::Graphics::UnitShadowEnabled() || device.GetContext().hostImportAlignment == 0) {
std::cout << "skipped, unit shadows or host imports are unavailable\n";
Expand Down
Loading