Skip to content
Open
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
18 changes: 18 additions & 0 deletions core/libs/prx/libSceAgcDriver/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1116,6 +1116,17 @@ if(BUILD_TESTING)
set_tests_properties(agc_unchanged_cpu_stamps PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
foreach(mode IN ITEMS map ring shared uncached)
add_test(NAME agc_collect_mapping_history_${mode} COMMAND agc_driver_recorder_tests --collect-mapping-history-only)
set_tests_properties(agc_collect_mapping_history_${mode} PROPERTIES SKIP_RETURN_CODE 77 TIMEOUT 30)
if(WIN32)
set_tests_properties(agc_collect_mapping_history_${mode} PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
endforeach()
set_tests_properties(agc_collect_mapping_history_ring PROPERTIES ENVIRONMENT "APS5_NO_EPOCH_COLLECT_MAP=1")
set_tests_properties(agc_collect_mapping_history_shared PROPERTIES ENVIRONMENT "APS5_SHARED_COLLECT_MEMO=1")
set_tests_properties(agc_collect_mapping_history_uncached PROPERTIES ENVIRONMENT "APS5_NO_COLLECT_MEMO=1")
add_test(NAME agc_storage_kept_unit_settle COMMAND agc_driver_recorder_tests --kept-unit-settle-only)
set_tests_properties(agc_storage_kept_unit_settle PROPERTIES SKIP_RETURN_CODE 77 TIMEOUT 30)
if(WIN32)
Expand Down Expand Up @@ -1870,6 +1881,13 @@ if(BUILD_TESTING)
endif()
endforeach()
endforeach()
add_test(NAME agc_driver_array_layer_target_cpu COMMAND agc_driver_array_layer_target_tests)
set_tests_properties(agc_driver_array_layer_target_cpu PROPERTIES
ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1;APS5_NO_HOST_IMPORT=1" TIMEOUT 30 SKIP_RETURN_CODE 77)
if(WIN32)
set_tests_properties(agc_driver_array_layer_target_cpu PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
foreach(preparedMode IN ITEMS indirect uncached debug debug_failure deferred deferred_undecodable registered_workgroup registered_workgroup_sync lazy lazy_sync)
set(preparedTest "agc_driver_prepared_shaders_${preparedMode}")
if(preparedMode STREQUAL "indirect")
Expand Down
14 changes: 8 additions & 6 deletions core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -797,6 +797,7 @@ struct WriteTracker {
std::uintptr_t end = 0;
std::uint64_t epoch = 0;
std::uint64_t unwatched = 0;
std::uint64_t mapping = 0;
};
std::array<Memo, 256> memo{};
std::size_t nextMemo = 0;
Expand Down Expand Up @@ -1184,19 +1185,20 @@ std::uint64_t collectWrites(std::uint64_t address, std::size_t bytes, bool memoi
// must not differ between them.
const auto epoch = currentCollectEpoch();
const auto unwatched = unwatchSerial.load(std::memory_order_acquire);
const auto mapping = GuestAllocations::GuestAllocationsGeneration_nid_postfix();
const bool useMemo = memoized && collectMemoEnabled() && bytes != 0;
if (useMemo && !sharedCollectMemo()) {
// An entry exists only for a completed walk of an in-arena range, so the tracker is
// initialized and watched; nothing below the lock needs asking.
if (epochCollectMapEnabled()) {
const auto& map = epochCollectMap(epoch);
if (const auto found = map.ranges.find(first); found != map.ranges.end() && found->second.unwatched == unwatched && stop <= found->second.end) {
if (const auto found = map.ranges.find(first); found != map.ranges.end() && found->second.unwatched == unwatched && found->second.mapping == mapping && stop <= found->second.end) {
collectMemoHits.fetch_add(1, std::memory_order_relaxed);
return tracker.generation.load(std::memory_order_relaxed);
}
}
for (const auto& entry : threadCollectMemo.entries) {
if (entry.epoch == epoch && entry.unwatched == unwatched && entry.begin <= first && stop <= entry.end) {
if (entry.epoch == epoch && entry.unwatched == unwatched && entry.mapping == mapping && entry.begin <= first && stop <= entry.end) {
// The current generation, not the memoized one: blocks stamped since (MarkWritten, an
// overlapping collect) were written before this caller reads, and an older value would
// make every later UnchangedSince fail until the epoch ends.
Expand All @@ -1211,7 +1213,7 @@ std::uint64_t collectWrites(std::uint64_t address, std::size_t bytes, bool memoi
auto cursor = first;
if (useMemo && sharedCollectMemo()) {
for (const auto& entry : tracker.memo) {
if (entry.epoch == epoch && entry.unwatched == unwatchSerial.load(std::memory_order_relaxed) && entry.begin <= cursor && stop <= entry.end) {
if (entry.epoch == epoch && entry.unwatched == unwatchSerial.load(std::memory_order_relaxed) && entry.mapping == mapping && entry.begin <= cursor && stop <= entry.end) {
collectMemoHits.fetch_add(1, std::memory_order_relaxed);
return tracker.generation;
}
Expand All @@ -1232,12 +1234,12 @@ std::uint64_t collectWrites(std::uint64_t address, std::size_t bytes, bool memoi
// Only a completed walk is remembered; a failed one (uncommitted pages) returned 0 above.
if (collectMemoEnabled()) {
const auto serial = unwatchSerial.load(std::memory_order_relaxed);
if (sharedCollectMemo()) tracker.memo[tracker.nextMemo++ % tracker.memo.size()] = {first, stop, epoch, serial};
if (sharedCollectMemo()) tracker.memo[tracker.nextMemo++ % tracker.memo.size()] = {first, stop, epoch, serial, mapping};
else {
threadCollectMemo.entries[threadCollectMemo.next++ % threadCollectMemo.entries.size()] = {first, stop, epoch, serial};
threadCollectMemo.entries[threadCollectMemo.next++ % threadCollectMemo.entries.size()] = {first, stop, epoch, serial, mapping};
if (epochCollectMapEnabled()) {
auto& entry = epochCollectMap(epoch).ranges[first];
if (entry.unwatched != serial || entry.end < stop) entry = {first, stop, epoch, serial};
if (entry.unwatched != serial || entry.mapping != mapping || entry.end < stop) entry = {first, stop, epoch, serial, mapping};
}
}
}
Expand Down
5 changes: 3 additions & 2 deletions core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3038,8 +3038,9 @@ bool VulkanDevice::PresentDisplayBuffer(const DisplayBuffer& buffer) {
// walk: the collect memo is per worker packet, so on this thread a memoized answer could
// miss a CPU write that landed after a worker's walk of the same range.
GuestMemory::CollectWritesUncached(buffer.address, bytes);
if (!GuestMemory::UnchangedSince(buffer.address, bytes, resident->Generation()) || (buffer.dccAddress != 0 && ResidentKeysMoved(*resident))) {
resident->Refresh();
if (!GuestMemory::UnchangedSince(buffer.address, bytes, resident->GenerationFor(buffer.address, bytes)) || (buffer.dccAddress != 0 && ResidentKeysMoved(*resident))) {
if (resident->ImageLayers() > 1) resident->RefreshLayers(layer, 1);
else resident->Refresh();
++refreshedPresents;
}
++residentPresents;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ bool CompletePendingCpuWrites(const Context& context, std::uint64_t address, std
// The cached storage image of a surface (render targets use it as their resident image); brought up
// to date with guest memory before it is returned.
std::shared_ptr<StorageTexture> CachedStorageSurface(const Context& context, const GuestTextureResource& resource);
std::shared_ptr<StorageTexture> CachedStorageLayers(const Context& context, const GuestTextureResource& resource, std::uint32_t firstLayer, std::uint32_t layerCount);
// Whether `image` is still the storage cache's image of its surface (what CachedStorageSurface
// would return); a true answer counts as a use for the cache's eviction order, as a lookup would.
bool StorageImageCached(const Context& context, const StorageTexture* image);
Expand Down
17 changes: 14 additions & 3 deletions core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,7 @@ class StorageTexture : public std::enable_shared_from_this<StorageTexture> {
// validates textures copied from this image.
std::uint64_t Version() const { return version; }
std::uint64_t Generation() const { return generation; }
std::uint64_t GenerationFor(std::uint64_t address, std::uint64_t bytes) const;
// The DCC keys the content was uploaded under and the last scan of the surface's keys
// (ProvedClearKeys): Refresh's own key rule for the fast revalidation. Under
// GuestMemory::GpuMutex only, as Refresh is; never from a build's stage A.
Expand All @@ -346,6 +347,7 @@ class StorageTexture : public std::enable_shared_from_this<StorageTexture> {
// was still current (nothing uploaded).
// Keeps the image current with guest memory (see GuestMemory::CollectWrites).
bool Refresh();
bool RefreshLayers(std::uint32_t first, std::uint32_t count);
static std::uint64_t SinglePassMoves();
static std::uint64_t RefreshesProved();
static std::uint64_t UntrackedCompares();
Expand All @@ -357,9 +359,8 @@ class StorageTexture : public std::enable_shared_from_this<StorageTexture> {
StorageTexture(const Context& context, TextureDetiler& detiler, const GuestTextureResource& descriptor, std::uint32_t mipLevel);
// The regions of every array layer, or of the tracked layers `layers` selects.
std::vector<VkBufferImageCopy> CopyRegions(const std::vector<bool>* layers = nullptr) const;
// Uploads the surface, or only the tracked layers `layers` selects (the direct path; the others
// upload everything).
void upload(const std::vector<bool>* layers = nullptr);
std::vector<bool> cpuUploadLayers(const std::vector<bool>& units) const;
void captureGuestBytes(const std::vector<bool>* layers);
bool originalHolds(std::uint64_t address, std::size_t bytes) const;
bool compareUntracked(std::uint64_t address, std::size_t bytes, std::span<std::uint8_t> changed, bool memoize = false) const;
Expand Down Expand Up @@ -454,7 +455,10 @@ class StorageTexture : public std::enable_shared_from_this<StorageTexture> {
// layer generation (the whole-surface fast check), `dirty` whether any layer is pending.
std::uint64_t layerBegin(std::uint32_t layer) const { return descriptor.baseAddress + static_cast<std::uint64_t>(layer) * trackedLayerBytes; }
bool anyLayerPending() const;
bool refresh(std::uint32_t firstLayer, std::uint32_t lastLayer);
void noteLayersBehind(std::uint32_t firstLayer, std::uint32_t lastLayer, const std::vector<bool>& outside);
void refreshGeneration();
std::uint64_t collectedLayerGeneration(std::uint32_t layer, std::uint64_t whole, bool uncached = false) const;
bool refreshProved();
void takeRefreshProof(bool aliased);
bool otherPendingOverlaps() const;
Expand Down Expand Up @@ -530,7 +534,13 @@ class StorageTexture : public std::enable_shared_from_this<StorageTexture> {
std::uint64_t sliceLinearBytes = 0;
SurfaceGeometry geometry;
std::vector<std::byte> original;
mutable std::array<std::uint64_t, 5> comparedGuestBytes{};
struct ComparedRange {
std::uint64_t address = 0;
std::size_t bytes = 0;
std::array<std::uint64_t, 5> stamp{};
};
mutable std::array<ComparedRange, 4> comparedRanges{};
mutable std::uint32_t nextComparedRange = 0;
std::vector<std::byte> generationBaseline;
// DCC keys the image content was uploaded under: a fast-cleared surface starts as its clear value.
DccKeys uploadedKeys = DccKeys::Uncompressed;
Expand All @@ -556,6 +566,7 @@ class StorageTexture : public std::enable_shared_from_this<StorageTexture> {
std::uint64_t trackedLayerBytes = 0;
std::vector<std::uint64_t> layerGeneration;
std::vector<bool> layerPending;
std::vector<bool> layerBehind;
bool blockUnits = false;
// Write-back hysteresis (block units, see writeBack): partial stores in the current window of
// presents, its first present, and the present until which every pending unit is stored.
Expand Down
5 changes: 3 additions & 2 deletions core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2030,7 +2030,7 @@ void Draw(const Context& context, const State& state, const Pm4::DrawParameters&
Require(color.dccAddress == 0 && color.cmaskAddress == 0 && AttachmentProxyFormat(context, color.format) == VK_FORMAT_UNDEFINED, "a slice of a 2D array color target with DCC, CMASK or an attachment proxy has no resident array image");
const auto array = ArraySurfaceForTarget(color);
Require(color.surfaceAddress == color.arrayAddress + color.arraySlice * DescribeSurface(array).layerBytes, "the array image's layer pitch differs from the color view's slice offset");
auto resident = CachedStorageSurface(context, array);
auto resident = CachedStorageLayers(context, array, color.arraySlice, 1);
Require(resident->Attachable(), "storage format cannot be a color attachment");
return resident;
}
Expand Down Expand Up @@ -2623,7 +2623,8 @@ DrawRecipeOutcome DrawWithRecipe(const Context& context, const State& state, con
auto stored = recipe.targets[index].lock();
if (stored == nullptr || !StorageImageCached(context, stored.get()) || !StorageImageServesKeys(*stored, state.colors[index].dccAddress)) return miss(DrawRecipeMiss::TargetGone);
auto resident = refreshResidentTarget(context, state, state.colors[index], outcome, profile, [&] {
stored->Refresh();
if (state.colors[index].arrayLastSlice != 0) stored->RefreshLayers(state.colors[index].arraySlice, 1);
else stored->Refresh();
return stored;
});
timer.phase(PhaseReadTarget);
Expand Down
Loading
Loading