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
5 changes: 3 additions & 2 deletions core/libs/prx/libSceAgcDriver/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1164,8 +1164,9 @@ if(BUILD_TESTING)
add_test(NAME agc_import_settle_first_use COMMAND agc_driver_recorder_tests --settle-first-use-only)
add_test(NAME agc_import_settle_earlier_import COMMAND agc_driver_recorder_tests --settle-earlier-import-only)
add_test(NAME agc_import_settle_retire COMMAND agc_driver_recorder_tests --settle-retire-only)
set_tests_properties(agc_import_settle_deferred agc_import_settle_deferred_other_family agc_import_settle_first_use agc_import_settle_earlier_import agc_import_settle_retire PROPERTIES SKIP_RETURN_CODE 77 TIMEOUT 60)
set_tests_properties(agc_import_watch agc_import_watch_other_family agc_import_settle_deferred agc_import_settle_deferred_other_family agc_import_settle_first_use agc_import_settle_earlier_import agc_import_settle_retire PROPERTIES ENVIRONMENT_MODIFICATION
add_test(NAME agc_import_settle_storage_image COMMAND agc_driver_recorder_tests --settle-storage-image-only)
set_tests_properties(agc_import_settle_deferred agc_import_settle_deferred_other_family agc_import_settle_first_use agc_import_settle_earlier_import agc_import_settle_retire agc_import_settle_storage_image PROPERTIES SKIP_RETURN_CODE 77 TIMEOUT 60)
set_tests_properties(agc_import_watch agc_import_watch_other_family agc_import_settle_deferred agc_import_settle_deferred_other_family agc_import_settle_first_use agc_import_settle_earlier_import agc_import_settle_retire agc_import_settle_storage_image PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
add_test(NAME agc_unchanged_cpu_stamps COMMAND agc_driver_recorder_tests --unchanged-cpu-stamps-only)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ struct HostImportSettleCounts {
std::uint64_t rewatched = 0;
std::uint64_t parked = 0;
std::uint64_t unmapWaits = 0;
std::uint64_t keptCompared = 0;
};
HostImportSettleCounts HostImportSettles();
// Tests: how many imports settleImport has used on the settle queue.
Expand Down
1 change: 1 addition & 0 deletions core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ class StorageTexture : public std::enable_shared_from_this<StorageTexture> {
// Whether an image with results pending, or one a FlushPending is storing right now, overlaps
// any of the [begin, end) ranges: one acquisition of the registry's mutex.
static bool AnyPendingOverlaps(std::span<const std::pair<std::uint64_t, std::uint64_t>> ranges);
static bool AnyLiveOverlaps(std::span<const std::pair<std::uint64_t, std::uint64_t>> ranges);
// Moves with every change of the pending registry (an image marked dirty, stored, flushed or
// dropped; after the store for a flush), bumped under the registry's mutex with the change, so
// a reader that loads it before and after its own reads knows the registry it consulted stayed
Expand Down
19 changes: 8 additions & 11 deletions core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ struct HostImports {
std::uint64_t settlesSubmitted = 0;
std::uint64_t settlesWaited = 0;
std::uint64_t settlesRewatched = 0;
std::uint64_t settlesKeptCompared = 0;
std::uint64_t settlesParked = 0;
std::uint64_t settleUnmapWaits = 0;
#endif
Expand Down Expand Up @@ -1051,24 +1052,20 @@ void reapSettleBatches(const Context& context, HostImports& state) {
void rewatchSettledImports(HostImports& state) {
if (state.settleReady.empty()) return;
std::vector<std::pair<std::uint64_t, std::uint64_t>> ranges;
std::vector<std::uint64_t> deferred;
for (const auto base : state.settleReady) {
const auto settle = state.pendingSettles.find(base);
if (settle == state.pendingSettles.end() || settle->second.phase != SettlePhase::Settled) continue;
state.pendingSettles.erase(settle);
const auto import = state.imports.find(base);
if (import == state.imports.end() || !import->second.unwatched) {
state.pendingSettles.erase(settle);
continue;
}
if (import == state.imports.end() || !import->second.unwatched) continue;
const std::array<std::pair<std::uint64_t, std::uint64_t>, 1> span{{{base, base + import->second.bytes}}};
if (StorageTexture::AnyPendingOverlaps(span)) {
deferred.push_back(base);
if (StorageTexture::AnyLiveOverlaps(span)) {
++state.settlesKeptCompared;
continue;
}
ranges.push_back(span.front());
state.pendingSettles.erase(settle);
}
state.settleReady = std::move(deferred);
state.settleReady.clear();
if (ranges.empty()) return;
GuestMemory::Rewatch(ranges);
for (const auto& [begin, end] : ranges) {
Expand Down Expand Up @@ -2409,7 +2406,7 @@ void reportMirrors() {
#ifdef _WIN32
auto& imports = Imports();
std::lock_guard importsLock(imports.mutex);
if (imports.settlesSubmitted != 0 || imports.settlesWaited != 0 || !imports.settles.empty()) AgcDriver::ProfilePrint_nid_no_patch("[buffers] host imports under %llu KiB: %llu settle reads submitted, %llu back in the write watch, %llu retired during their read (%llu unmaps waited for it), %zu pending; %llu larger imports settled inside their window\n", static_cast<unsigned long long>(settleFloorBytes >> 10u), static_cast<unsigned long long>(imports.settlesSubmitted), static_cast<unsigned long long>(imports.settlesRewatched), static_cast<unsigned long long>(imports.settlesParked), static_cast<unsigned long long>(imports.settleUnmapWaits), imports.settles.size(), static_cast<unsigned long long>(imports.settlesWaited));
if (imports.settlesSubmitted != 0 || imports.settlesWaited != 0 || !imports.settles.empty()) AgcDriver::ProfilePrint_nid_no_patch("[buffers] host imports under %llu KiB: %llu settle reads submitted, %llu back in the write watch, %llu kept compared under a storage image, %llu retired during their read (%llu unmaps waited for it), %zu pending; %llu larger imports settled inside their window\n", static_cast<unsigned long long>(settleFloorBytes >> 10u), static_cast<unsigned long long>(imports.settlesSubmitted), static_cast<unsigned long long>(imports.settlesRewatched), static_cast<unsigned long long>(imports.settlesKeptCompared), static_cast<unsigned long long>(imports.settlesParked), static_cast<unsigned long long>(imports.settleUnmapWaits), imports.settles.size(), static_cast<unsigned long long>(imports.settlesWaited));
#endif
}

Expand Down Expand Up @@ -2985,7 +2982,7 @@ HostImportSettleCounts HostImportSettles() {
#ifdef _WIN32
auto& state = Imports();
std::lock_guard lock(state.mutex);
return {state.settlesSubmitted, state.settlesWaited, state.settlesRewatched, state.settlesParked, state.settleUnmapWaits};
return {state.settlesSubmitted, state.settlesWaited, state.settlesRewatched, state.settlesParked, state.settleUnmapWaits, state.settlesKeptCompared};
#else
return {};
#endif
Expand Down
13 changes: 13 additions & 0 deletions core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3602,6 +3602,19 @@ bool StorageTexture::AnyPendingOverlaps(std::span<const std::pair<std::uint64_t,
return false;
}

bool StorageTexture::AnyLiveOverlaps(std::span<const std::pair<std::uint64_t, std::uint64_t>> ranges) {
auto& live = Live();
std::lock_guard lock(live.mutex);
for (const auto& range : ranges) {
if (range.second <= range.first) continue;
const auto bytes = static_cast<std::size_t>(range.second - range.first);
for (const auto* texture : live.textures) {
if (texture->overlapsLive(range.first, bytes)) return true;
}
}
return false;
}

std::uint64_t StorageTexture::PendingSerial() {
return pendingSerial.load(std::memory_order_acquire);
}
Expand Down
102 changes: 102 additions & 0 deletions core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1972,6 +1972,107 @@ bool settleRetireTests(const Device& device) {
std::cout << "imports retired during their settle read tests passed\n";
return true;
}

bool settleStorageImageTests(const Device& device) {
using namespace AgcDriver::GuestMemory;
const auto& base = device.GetContext();
constexpr std::size_t unit = 65536;
constexpr std::array<std::uint8_t, 4> red{255, 0, 0, 255};
if (base.hostImportAlignment == 0 || base.hostImportAlignment > unit || base.settleQueue == VK_NULL_HANDLE || !WriteWatched()) {
std::cout << "host imports within 64 KiB, a settle queue or write watching unavailable: a settled import under a storage image not tested\n";
return false;
}
std::unique_lock gpu(GpuMutex());
PrepareImportWatch(base);
if (!settledImportsWatchPrivate(base)) {
std::cout << "this device compares every Windows import: a settled import under a storage image not tested\n";
return false;
}
void* block = AllocateWatched(unit, unit);
Require(block != nullptr, "(i) no watched block although write watching is on");
struct Release {
void* block;
~Release() { ReleaseWatched(block, unit); }
} release{block};
const auto address = reinterpret_cast<std::uint64_t>(block);
std::memset(block, 0x5a, unit);
{
GuestAllocations::Mutation mutation;
mutation.Add(block, unit, true, true, true);
}
TextureDetiler detiler(base);
auto context = base;
context.detiler = &detiler;
GuestTextureResource resource{};
resource.baseAddress = address;
resource.width = 128;
resource.height = 128;
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 == unit, "(i) unexpected surface size");
Require(Watched(address, unit) && ExistingHostImportFor(context, address, unit) == nullptr, "(i) the block was imported or out of the write watch before the image was made");
Recorder recorder(context);
recorder.Activate();
const auto before = HostImportSettles();
{
auto image = StorageTexture::Create(context, detiler, resource, 0);
const auto* import = ExistingHostImportFor(context, address, unit);
Require(import != nullptr && import->unwatched && !Watched(address, unit), "(i) the image's first upload did not import its block compared");
const auto decided = [&] {
const auto now = HostImportSettles();
return now.rewatched != before.rewatched || now.keptCompared != before.keptCompared;
};
const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(5);
while (!decided() && std::chrono::steady_clock::now() < deadline) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
HostImportFor(context, address, unit);
}
Require(decided(), "(i) the settle read of the image's import did not finish");
const auto after = HostImportSettles();
const bool returned = Watched(address, unit);
const VkImageSubresourceRange range{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
const VkClearColorValue value{{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, &value, 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();
image->Refresh();
image->WriteBack();
recorder.Submit();
device.WaitQueue();
recorder.Sync();
std::vector<std::byte> read(unit);
AgcDriver::GuestMemory::Read(address, read);
std::size_t lost = 0;
for (std::size_t at = 0; at < unit; at += 4) {
for (std::size_t c = 0; c < 4; ++c) {
if (std::to_integer<std::uint8_t>(read[at + c]) == red[c]) continue;
++lost;
break;
}
}
std::cout << "(i) the image's import " << (returned ? "returned to the write watch" : "stayed compared") << " after its settle read (" << after.rewatched - before.rewatched << " back in the write watch, " << after.keptCompared - before.keptCompared << " kept compared); the refresh after the image's GPU results lost " << lost << " of " << unit / 4 << " texels\n";
Require(lost == 0, "(i) the refresh after the image's GPU results dropped " + std::to_string(lost) + " of " + std::to_string(unit / 4) + " texels: the import's return to the write watch stamped its block as a CPU store after the image took its generation");
Require(!returned && after.keptCompared == before.keptCompared + 1, "(i) the settled import under a live storage image returned to the write watch");
}
recorder.Sync();
recorder.FlushDeferredReleases();
{
GuestAllocations::Mutation mutation;
mutation.Remove(block);
}
HostImportFor(context, address, unit);
std::cout << "a settled import under a storage image tests passed\n";
return true;
}
#endif

void remappedImportTests(const Device& device) {
Expand Down Expand Up @@ -8694,6 +8795,7 @@ int main(int argc, char** argv) {
if (argc == 2 && std::string_view(argv[1]) == "--settle-first-use-only") return settleFirstUseTests(device) ? 0 : 77;
if (argc == 2 && std::string_view(argv[1]) == "--settle-earlier-import-only") return settleEarlierImportTests(device) ? 0 : 77;
if (argc == 2 && std::string_view(argv[1]) == "--settle-retire-only") return settleRetireTests(device) ? 0 : 77;
if (argc == 2 && std::string_view(argv[1]) == "--settle-storage-image-only") return settleStorageImageTests(device) ? 0 : 77;
#endif
if (argc == 2 && std::string_view(argv[1]) == "--host-import-retirement-only") {
const auto alignment = device.GetContext().hostImportAlignment;
Expand Down
Loading
Loading