Skip to content
Merged
Show file tree
Hide file tree
Changes from 37 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
19 changes: 17 additions & 2 deletions core/libs/prx/libSceAgcDriver/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3127,10 +3127,25 @@ if(BUILD_TESTING)
set_tests_properties(agc_driver_native_depth_retirement PROPERTIES TIMEOUT 30 ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1" SKIP_RETURN_CODE 77 WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
add_test(NAME agc_driver_native_depth_clears COMMAND agc_driver_native_multisample_depth_tests --clears)
add_test(NAME agc_driver_native_depth_clear_refusals COMMAND agc_driver_native_multisample_depth_tests --clear-refusals)
set_tests_properties(agc_driver_native_depth_clears agc_driver_native_depth_clear_refusals PROPERTIES TIMEOUT 30 ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1" SKIP_RETURN_CODE 77 WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
add_test(NAME agc_driver_native_depth_stencil COMMAND agc_driver_native_multisample_depth_tests --stencil)
add_test(NAME agc_driver_native_depth_separate_stencil COMMAND agc_driver_native_multisample_depth_tests --separate-stencil)
add_test(NAME agc_driver_native_depth_aliased_stencil COMMAND agc_driver_native_multisample_depth_tests --aliased-stencil)
add_test(NAME agc_driver_native_depth_expand_gate COMMAND agc_driver_native_multisample_depth_tests --expand-gate)
add_test(NAME agc_driver_native_depth_publication COMMAND agc_driver_native_multisample_depth_tests --publication)
set_tests_properties(agc_driver_native_depth_publication PROPERTIES TIMEOUT 30 ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1" SKIP_RETURN_CODE 77 WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
if(WIN32)
set_tests_properties(agc_driver_native_depth_publication PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
set_tests_properties(agc_driver_native_depth_clears agc_driver_native_depth_clear_refusals agc_driver_native_depth_stencil agc_driver_native_depth_separate_stencil agc_driver_native_depth_aliased_stencil agc_driver_native_depth_expand_gate PROPERTIES TIMEOUT 30 ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1" SKIP_RETURN_CODE 77 WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
if(WIN32)
set_tests_properties(agc_driver_native_depth_retirement PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
set_tests_properties(agc_driver_native_depth_clears agc_driver_native_depth_clear_refusals PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
set_tests_properties(agc_driver_native_depth_clears agc_driver_native_depth_clear_refusals agc_driver_native_depth_stencil agc_driver_native_depth_separate_stencil agc_driver_native_depth_aliased_stencil agc_driver_native_depth_expand_gate PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
add_test(NAME agc_driver_native_depth_driver_path COMMAND agc_driver_native_multisample_depth_tests --driver-path)
add_test(NAME agc_driver_native_depth_driver_queued_fill COMMAND agc_driver_native_multisample_depth_tests --driver-queued-fill)
set_tests_properties(agc_driver_native_depth_driver_path agc_driver_native_depth_driver_queued_fill PROPERTIES TIMEOUT 30 ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1" SKIP_RETURN_CODE 77 WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
if(WIN32)
set_tests_properties(agc_driver_native_depth_driver_path agc_driver_native_depth_driver_queued_fill PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
set_tests_properties(agc_sparse_buffer_holes PROPERTIES TIMEOUT 30)
add_test(NAME agc_pipeline_journal COMMAND agc_driver_graphics_tests --pipeline-journal)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ void Write(std::uint64_t address, std::span<const std::byte> source, std::size_t
// guest writes made meanwhile to untouched bytes survive. Compares in 256-byte blocks.
void WriteChanged(std::uint64_t address, std::span<const std::byte> current, std::span<const std::byte> original);
void WriteChanged(std::uint64_t address, std::span<const std::byte> current, std::span<const std::byte> original, std::uint32_t byteStoreLimit);
std::size_t WriteChangedIfHeld(std::uint64_t address, std::span<const std::byte> current, std::span<const std::byte> original, std::uint64_t unchangedSince);
std::uint32_t ChangedByteStoreLimit();

// Write tracking. The guest arena is reserved with page write watching, so CollectWrites learns which
Expand Down Expand Up @@ -131,6 +132,7 @@ std::uint64_t UntrackedHint(std::uint64_t address, std::size_t bytes);
// the stamps collects make (dirty pages), not the MarkWritten stamps of the driver's own GPU label
// records, which share the title's label blocks. False when the arena is not write-watched.
bool UnchangedSinceCollected(std::uint64_t address, std::size_t bytes, std::uint64_t generation);
bool UnchangedSinceLastCollect(std::uint64_t address, std::size_t bytes, std::uint64_t generation);
// Per 64 KiB tracker block of [address, address + bytes) (block 0 holds `address`): `changed[k]`
// receives whether the block was stamped after generations[k] (UnchangedSince over that block
// alone), `cpu[k]`, when given, whether a collect stamped it after (a CPU store, as
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "prx/libSceAgcDriver/Execution/include/GuestMemory.hpp"
#include "prx/libSceAgcDriver/Execution/include/Pm4.hpp"
#include "prx/libSceAgcDriver/Execution/include/Driver/Synchronization/DeferredLabels.hpp"
#include "prx/libSceAgcDriver/Graphics/include/DepthSurface.hpp"
#include <cstdlib>

namespace AgcDriver::DriverDetail {
Expand Down Expand Up @@ -79,6 +80,10 @@ std::optional<DrawVerdict> Driver::precheckDraw(const QueueState& queue, const S
auto copyDraw = DepthCopyDraw(queue, *copy);
return draw(copyDraw, packet, submission, packetOffset, rejected);
}
if (const auto control = Graphics::DecodeDepthRenderControl(queue); control && ((control->control & 0x60u) != 0u || Graphics::DepthSurfaceAt(control->address))) {
if (DrawPipeline::Active()) DrawPipeline::Queue0().Drain(DrawPipeline::DrainReason::Packet, (packet[0] >> 8u) & 0xffu);
Graphics::NoteDepthRenderControl(*control);
}
if (Graphics::DepthResummarizeDrawsNothing(queue)) return DrawVerdict::Nothing;
rejected = Graphics::DepthMaintenanceRejection(queue);
if (!rejected.empty()) return DrawVerdict::Rejected;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ bool Driver::enqueueDmaPacket(std::span<const std::uint32_t> packet, std::uint32
DrawPipeline::Queue0().Enqueue([this, queue, fillAddress, fillBytes, pattern] {
GuestMemory::SetCurrentPacket(0x50, queue);
if (const auto localDevice = device.Load()) {
storeBufferFill(queue, fillAddress, fillBytes, {pattern, pattern, pattern, pattern}, localDevice, false, false);
storeBufferFill(queue, fillAddress, fillBytes, {pattern, pattern, pattern, pattern}, localDevice, false, true);
++dmaFills;
}
}, {{fillAddress, fillAddress + fillBytes}});
Expand Down
105 changes: 83 additions & 22 deletions core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1307,9 +1307,28 @@ struct WriteTracker {
return false;
}

bool rangeUnchanged(std::uint64_t address, std::size_t bytes, std::uint64_t since) const {
bool rangeUnchanged(std::uint64_t address, std::size_t bytes, std::uint64_t since, bool walked = true) const {
if (refusedSince(address, address + bytes, since)) return false;
return unchanged(blockOf(address), blockOf(address + bytes - 1), since) || !storedOver(address, address + bytes, since, true);
return unchanged(blockOf(address), blockOf(address + bytes - 1), since) || !storedOver(address, address + bytes, since, walked);
}

bool cpuUnchanged(std::uint64_t address, std::size_t bytes, std::uint64_t generation) const {
const auto first = blockOf(address);
const auto last = blockOf(address + bytes - 1u);
const auto end = address + bytes;
for (auto block = first; block <= last; ++block) {
if (const auto found = cpuPages.find(block); found != cpuPages.end()) {
const auto begin = blockBegin(block);
const auto from = std::max(address, begin) - begin;
const auto to = std::min<std::uint64_t>(end, begin + WriteBlockBytes) - begin;
for (auto page = from / WritePageBytes; page <= (to - 1u) / WritePageBytes; ++page) {
if (found->second.pages[page] > generation) return false;
}
} else if (cpuStampOf(block) > generation) {
return false;
}
}
return true;
}

void stamp(std::uint64_t block, std::uint32_t stampGeneration, StampKind kind) {
Expand Down Expand Up @@ -1995,21 +2014,21 @@ static GuestAllocations::Lease::Ranges pinStoreRanges(std::uint64_t address, std
return pinned;
}

std::uint64_t storeOwn(std::uint64_t address, std::size_t bytes, const std::function<std::pair<std::uint64_t, std::uint64_t>()>& store) {
std::uint64_t storeOwn(std::uint64_t address, std::size_t bytes, const std::function<std::pair<std::uint64_t, std::uint64_t>()>& store, bool complete = true) {
if (bytes == 0) return 0;
const auto pinned = pinStoreRanges(address, bytes);
BreakProofs();
const auto stampStored = [](WriteTracker& tracker, std::pair<std::uint64_t, std::uint64_t> stored) -> std::uint64_t {
const auto stampStored = [complete](WriteTracker& tracker, std::pair<std::uint64_t, std::uint64_t> stored) -> std::uint64_t {
if (stored.second <= stored.first) return 0;
++tracker.generation;
if (!tracker.watched || !tracker.covers(stored.first, static_cast<std::size_t>(stored.second - stored.first))) {
tracker.noteUnstampedStore(stored.first, stored.second);
if (tracker.watched) tracker.stampWatchedPart(stored.first, stored.second, tracker.generation, true);
if (tracker.watched) tracker.stampWatchedPart(stored.first, stored.second, tracker.generation, complete);
return 0;
}
for (auto block = tracker.blockOf(stored.first); block <= tracker.blockOf(stored.second - 1); ++block) {
tracker.stamp(block, tracker.generation, StampKind::Driver);
tracker.noteDriverStore(block, stored.first, stored.second, tracker.generation, true);
tracker.noteDriverStore(block, stored.first, stored.second, tracker.generation, complete);
}
return tracker.generation;
};
Expand Down Expand Up @@ -2210,22 +2229,16 @@ bool UnchangedSinceCollected(std::uint64_t address, std::size_t bytes, std::uint
auto& tracker = Tracker();
const auto lock = lockTracker(tracker);
if (!tracker.watched || generation == 0 || bytes == 0 || !tracker.covers(address, bytes)) return false;
const auto first = tracker.blockOf(address);
const auto last = tracker.blockOf(address + bytes - 1);
const auto end = address + bytes;
for (auto block = first; block <= last; ++block) {
if (const auto found = tracker.cpuPages.find(block); found != tracker.cpuPages.end()) {
const auto begin = tracker.blockBegin(block);
const auto from = std::max(address, begin) - begin;
const auto to = std::min<std::uint64_t>(end, begin + WriteBlockBytes) - begin;
for (auto page = from / WritePageBytes; page <= (to - 1) / WritePageBytes; ++page) {
if (found->second.pages[page] > generation) return false;
}
} else if (tracker.cpuStampOf(block) > generation) {
return false;
}
}
return true;
return tracker.cpuUnchanged(address, bytes, generation);
}

bool UnchangedSinceLastCollect(std::uint64_t address, std::size_t bytes, std::uint64_t generation) {
if (bytes == 0u || bytes > std::numeric_limits<std::uint64_t>::max() - address) return false;
auto& tracker = Tracker();
const auto lock = lockTracker(tracker);
tracker.initialize();
if (!tracker.watched || generation == 0u || !tracker.covers(address, bytes)) return false;
return tracker.cpuUnchanged(address, bytes, generation) && tracker.rangeUnchanged(address, bytes, generation, false);
}

bool WrittenSince(std::uint64_t address, std::size_t bytes, std::uint64_t generation) {
Expand Down Expand Up @@ -2906,6 +2919,54 @@ void WriteChanged(std::uint64_t address, std::span<const std::byte> current, std
});
}

static_assert(std::atomic_ref<std::uint64_t>::is_always_lock_free && std::atomic_ref<std::uint8_t>::is_always_lock_free);

std::size_t WriteChangedIfHeld(std::uint64_t address, std::span<const std::byte> current, std::span<const std::byte> original, std::uint64_t unchangedSince) {
require(current.size() == original.size(), "write-back snapshot sizes differ");
BreakProofs();
if (current.empty()) return 0;
const ReadSiteScope site(ReadSite::Store);
FlushGpuWrites(address, current.size());
const TimedAccess timed(CounterChangedWrite, current.size());
auto* destination = reinterpret_cast<std::uint8_t*>(address);
const auto size = current.size();
std::size_t lost = 0;
storeOwn(address, size, [&] {
CheckRange(destination, size, 1, true);
if (unchangedSince != 0u) {
auto& tracker = Tracker();
if (!tracker.cpuUnchanged(address, size, unchangedSince) || tracker.refusedSince(address, address + size, unchangedSince)) {
lost = size;
return std::pair<std::uint64_t, std::uint64_t>{0, 0};
}
}
std::size_t firstChanged = size;
std::size_t lastChanged = 0;
const auto exchange = [&](std::size_t index) {
if (current[index] == original[index]) return;
auto expected = std::to_integer<std::uint8_t>(original[index]);
firstChanged = std::min(firstChanged, index);
lastChanged = std::max(lastChanged, index + 1);
if (!std::atomic_ref<std::uint8_t>(destination[index]).compare_exchange_strong(expected, std::to_integer<std::uint8_t>(current[index]))) ++lost;
};
std::size_t at = 0;
for (; at < size && lost == 0 && ((address + at) & 7u) != 0u; ++at) exchange(at);
for (; at + 8 <= size && lost == 0; at += 8) {
std::uint64_t wanted, held;
std::memcpy(&wanted, current.data() + at, 8);
std::memcpy(&held, original.data() + at, 8);
if (wanted == held) continue;
firstChanged = std::min(firstChanged, at);
lastChanged = std::max(lastChanged, at + 8);
if (std::atomic_ref<std::uint64_t>(*reinterpret_cast<std::uint64_t*>(destination + at)).compare_exchange_strong(held, wanted)) continue;
for (std::size_t index = at; index < at + 8; ++index) exchange(index);
}
for (; at < size && lost == 0; ++at) exchange(at);
return firstChanged < lastChanged ? std::pair<std::uint64_t, std::uint64_t>{address + firstChanged, address + lastChanged} : std::pair<std::uint64_t, std::uint64_t>{0, 0};
}, false);
return lost;
}

void Write(std::uint64_t address, std::span<const std::byte> source, std::size_t alignment) {
if (source.empty()) return;
// A store, as in WriteChanged.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ namespace AgcDriver::Graphics {
class Texture;
class StorageTexture;

VkImageView DepthSurfaceView(const Context& context, const DepthTarget& target, bool depthWritten = true);
VkImageView DepthSurfaceView(const Context& context, const DepthTarget& target, bool depthWritten = true, bool stencilTested = false);
SampleOrderTarget DepthSurfaceOrderTarget(const Context& context, const DepthTarget& target);
DepthSampleLocations DepthSurfaceSampleLocations(const Context& context, const DepthTarget& target);
enum class DepthPlaneScope { Overlapped, Whole };
Expand All @@ -38,8 +38,10 @@ bool DirectDepthCopy(const Context& context, VkFormat depthFormat, VkImageAspect
void SetDirectDepthCopy(bool enabled);
std::uint64_t DirectDepthCopyCount();
std::uint64_t MultisampledDepthBytes_nid_no_patch(VkExtent2D extent, std::uint32_t bytesPerTexel, std::uint32_t samples);
void RequireNoResidentDepthRead_nid_no_patch(std::uint64_t address, std::size_t bytes);
void RequireNoResidentDepthRead_nid_no_patch(std::uint64_t address, std::size_t bytes, bool allowPublication = true);
void AppendHostWrittenDepthRanges(std::uint64_t address, std::size_t bytes, std::vector<std::pair<std::uint64_t, std::uint64_t>>& ranges);
enum class DepthPublicationStage { ReadCaptured, GuestCopied, BeforeCommit, PlaneCommitted };
void SetDepthPublicationHookForTest(void (*hook)(DepthPublicationStage));
bool HasResidentDepthGuards_nid_no_patch();
void RetireUnmappedDepth_nid_no_patch(std::uintptr_t address, std::size_t bytes);
void RequireNoNativeDepthFeedback_nid_no_patch(const Context& context, const DepthTarget& target, std::span<const std::shared_ptr<Texture>> sampled);
Expand Down Expand Up @@ -72,6 +74,7 @@ std::optional<MemoryValueFill> MatchMemoryValueFill(std::span<const std::uint32_
void NoteMemoryValueFill(const MemoryValueFill& fill);
bool DepthSurfaceHolds(const Context& context, const GuestTextureResource& resource, const Texture* texture);
void NoteDepthMetadataFill(std::uint64_t address, std::size_t bytes, std::uint32_t pattern);
void NoteDepthRenderControl(const DepthRenderControl& draw);
VkImageAspectFlags HtileFillClears(std::uint32_t pattern, bool stencilInHtile);
std::uint64_t HtileMetadataBytes(VkExtent2D extent);
bool HtileFillCovers(std::uint64_t htile, VkExtent2D extent, std::uint64_t address, std::size_t bytes);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <array>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <memory>
#include <optional>
#include <span>
Expand Down Expand Up @@ -48,6 +49,8 @@ class MultisampledColorFill {
void NoteFill(std::uint64_t fillAddress, std::size_t fillBytes, std::uint32_t pattern);
std::optional<std::uint32_t> TakeFill();
void Discard();
std::optional<std::uint32_t> Pending() const { return filled; }
const std::string& Refusal() const { return refusal; }

private:
std::uint64_t address = 0;
Expand All @@ -56,6 +59,21 @@ class MultisampledColorFill {
std::string refusal;
};

enum class MultisampleColorContent { Unknown, Fill, FastClear, Clear, Drawn };

struct MultisampleColorAlias {
std::uint64_t address = 0;
std::uint64_t bytes = 0;
VkFormat format = VK_FORMAT_UNDEFINED;
std::uint32_t samples = 0;
MultisampleColorContent newest = MultisampleColorContent::Unknown;
std::optional<VkClearColorValue> value;
std::optional<std::uint8_t> byte;
};

std::optional<MultisampleColorAlias> MultisampleColorTargetOver(std::uint64_t address, std::size_t bytes);
void WithMultisampleTargetsLockForTest(const std::function<void()>& inside);

std::uint64_t MultisampledColorBytes(const ColorTarget& target);
std::size_t MultisampledDccBytes(const ColorTarget& target);
VkClearColorValue MultisampledDccClear(const ColorTarget& target, std::span<const std::byte> dcc, std::span<const std::byte> cmask);
Expand Down Expand Up @@ -89,6 +107,7 @@ void InstallResidentImageReadGuard_nid_no_patch();
void UpdateResidentImageUnmapObserver_nid_no_patch();
void ClaimMultisampleTargetMemory(std::uint64_t address, std::uint64_t bytes);
void GuardOneFragmentFmask(const Context& context, const ColorTarget& target);
bool HoldsMultisampleTargetsLock();
void RequireNoMultisampleTarget(std::uint64_t address, std::size_t bytes, const char* access);
void RequireNoMultisampleColorTarget(std::uint64_t address, std::size_t bytes, const char* access);
std::vector<std::pair<std::uint64_t, std::uint64_t>> HostOnlySampleRanges(std::uint64_t address, std::size_t bytes);
Expand Down
6 changes: 6 additions & 0 deletions core/libs/prx/libSceAgcDriver/Graphics/include/State.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,12 @@ struct DepthCopyPass {
std::optional<DepthCopyPass> DecodeDepthCopyPass(const QueueState& queue);
std::string DepthMaintenanceRejection(const QueueState& queue);
bool DepthResummarizeDrawsNothing(const QueueState& queue);
struct DepthRenderControl {
std::uint64_t address;
std::uint32_t samples;
std::uint32_t control;
};
std::optional<DepthRenderControl> DecodeDepthRenderControl(const QueueState& queue);
// The message DecodeState (or the pixel stage decode after it) would throw for the register rules
// this precheck covers, evaluated without exceptions before the draw is decoded; empty when they
// pass (DecodeState still checks everything). A register a rule needs that is absent is no verdict.
Expand Down
Loading
Loading