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
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class Texture;
class StorageTexture;

VkImageView DepthSurfaceView(const Context& context, const DepthTarget& target, bool depthWritten = true, bool stencilTested = false);
VkImageView DepthSurfaceViewWithStencilWriteIntent(const Context& context, const DepthTarget& target, bool depthWritten, bool stencilTested, bool stencilWritten);
VkImageView DepthSurfaceViewWithStencilWriteIntent(const Context& context, const DepthTarget& target, bool depthWritten, bool stencilTested, bool stencilWritten, std::optional<std::uint8_t> overwrittenStencil = std::nullopt);
SampleOrderTarget DepthSurfaceOrderTarget(const Context& context, const DepthTarget& target);
DepthSampleLocations DepthSurfaceSampleLocations(const Context& context, const DepthTarget& target);
enum class DepthPlaneScope { Overlapped, Whole };
Expand Down
181 changes: 127 additions & 54 deletions core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,11 @@ std::atomic<bool>& anyDepthGuard() {
return any;
}

std::atomic<std::uint32_t>& pendingOverwriteProofs() {
static std::atomic<std::uint32_t> count{0u};
return count;
}

struct DepthSamplingState {
std::vector<GuestMemory::UnchangedQuery> initial;
std::optional<float> initialDepth;
Expand All @@ -228,6 +233,8 @@ struct DepthSamplingState {
std::optional<std::uint8_t> aliasStencil;
std::optional<MultisampleColorAlias> unprovenStencil;
std::optional<MultisampleColorAlias> depthAlias;
bool unevenPlanes = false;
std::optional<std::uint8_t> overwriteStencil;

void Refuse(const char* reason) {
if (refusal.empty()) refusal = reason;
Expand Down Expand Up @@ -344,9 +351,11 @@ DepthSamplingState prepareUniformPlaneSampling(const Context& context, const Dep
}
const auto depthWord = uniformPlaneValue<std::uint32_t>(depth.address, depth.bytes);
const auto depthValue = depthWord ? std::bit_cast<float>(*depthWord) : 0.0f;
state.unevenPlanes = true;
if (!depthWord || !(depthValue >= 0.0f && depthValue <= 1.0f)) return refuse("native multisample depth plane without HTILE does not hold one depth value in [0, 1]");
const auto stencilByte = uniformPlaneValue<std::uint8_t>(stencil.address, stencil.bytes);
if (!stencilByte) return refuse("native multisample stencil plane without HTILE does not hold one stencil value");
state.unevenPlanes = false;
for (const auto& range : state.initial) GuestMemory::CollectWritesUncached(range.address, range.bytes);
if (!GuestMemory::UnchangedSinceAll(state.initial)) return refuse("guest writes raced native multisample depth import");
state.initialDepth = depthValue;
Expand Down Expand Up @@ -634,14 +643,19 @@ class DepthSurface {
release();
throw;
}
if (this->sampling.overwriteStencil) pendingOverwriteProofs().fetch_add(1u, std::memory_order_acq_rel);
}

~DepthSurface() { release(); }
~DepthSurface() {
if (sampling.overwriteStencil) pendingOverwriteProofs().fetch_sub(1u, std::memory_order_acq_rel);
release();
}
DepthSurface(const DepthSurface&) = delete;
DepthSurface& operator=(const DepthSurface&) = delete;

void CheckCurrent() {
if (target.samples == 1u) return;
ProveOverwrite();
const auto clearLease = clearGeneration == 0u && stencilClearGeneration == 0u ? GuestAllocations::Lease{} : GuestAllocations::GuestAllocationsAcquireAll_nid_postfix();
Require(active->load(std::memory_order_acquire), "the native multisample depth owner was released");
Require(sampling.refusal.empty(), sampling.refusal);
Expand Down Expand Up @@ -935,28 +949,44 @@ class DepthSurface {
}

void NoteHostWrite() {
if (samplingActive && target.format == VK_FORMAT_D32_SFLOAT_S8_UINT && context.detiler != nullptr && !hostWritten->load(std::memory_order_acquire)) {
try {
const auto lease = GuestAllocations::GuestAllocationsAcquireAll_nid_postfix();
CheckCurrent();
std::vector<PublicationPlane> captured;
for (const auto& range : sampling.initial) captured.push_back(CapturePublication(range));
for (const auto& plane : captured) {
Require(GuestMemory::UnchangedSinceCollected(plane.range.address, plane.range.bytes, plane.range.generation), "guest writes raced native multisample depth publication capture");
Require(GuestMemory::UnchangedSince(plane.range.address, plane.range.bytes, plane.range.generation), "guest writes raced native multisample depth publication capture");
}
for (const auto& range : sampling.initial) Require(GuestMemory::UnchangedSinceCollected(range.address, range.bytes, range.generation), "guest writes raced native multisample depth publication capture");
Require(GuestMemory::UnchangedSinceAll(sampling.initial), "guest writes raced native multisample depth publication capture");
publication = std::move(captured);
} catch (...) {
publicationFailure = std::current_exception();
publication.clear();
}
}
if (samplingActive && !hostWritten->load(std::memory_order_acquire)) capturePublication();
hostEverWritten->store(true, std::memory_order_release);
hostWritten->store(true, std::memory_order_release);
}

void ProveOverwrite() {
if (!sampling.overwriteStencil || !hostEverWritten->load(std::memory_order_acquire) || Recorder::InCompletion()) return;
const auto value = *sampling.overwriteStencil;
sampling.overwriteStencil.reset();
pendingOverwriteProofs().fetch_sub(1u, std::memory_order_acq_rel);
if (!sampleable || context.detiler == nullptr || !active->load(std::memory_order_acquire)) return;
if (auto* recorder = RecorderFor(context)) recorder->Sync();
for (const auto& range : sampling.initial) {
StorageTexture::FlushPending(range.address, range.bytes);
GuestMemory::FlushGpuWrites(range.address, range.bytes);
}
if (useSampleShadow) {
if (sampleShadow == nullptr) createSampleShadow();
refreshSampleShadow(true, true);
if (auto* recorder = RecorderFor(context)) recorder->Sync();
}
const auto samples = static_cast<std::uint64_t>(target.extent.width) * target.extent.height * target.samples;
const auto stencil = readBackSamples(VK_IMAGE_ASPECT_STENCIL_BIT, samples);
const auto bytes = stencil->Bytes().first(static_cast<std::size_t>(samples));
if (std::ranges::any_of(bytes, [&](std::byte stored) { return stored != static_cast<std::byte>(value); })) return;
for (const auto& range : sampling.initial) GuestMemory::CollectWritesUncached(range.address, range.bytes);
if (!GuestMemory::UnchangedSinceAll(sampling.initial)) return;
sampling.refusal.clear();
{
std::unique_lock guardLock(depthGuardMutex());
for (const auto& range : sampling.initial) depthGuards().push_back({context.device, range.address, range.bytes, active, {{range.address, range.bytes, 0u}}, hostWritten, hostEverWritten});
anyDepthGuard().store(true, std::memory_order_release);
}
InstallResidentImageReadGuard_nid_no_patch();
samplingActive = true;
capturePublication();
}

void PublishToGuest();

std::uint64_t written = ++depthWrites;
Expand Down Expand Up @@ -1534,6 +1564,54 @@ class DepthSurface {
bool publicationFailed = false;
std::exception_ptr publicationFailure;

void capturePublication() {
if (target.format != VK_FORMAT_D32_SFLOAT_S8_UINT || context.detiler == nullptr) return;
try {
const auto lease = GuestAllocations::GuestAllocationsAcquireAll_nid_postfix();
CheckCurrent();
std::vector<PublicationPlane> captured;
for (const auto& range : sampling.initial) captured.push_back(CapturePublication(range));
for (const auto& plane : captured) {
Require(GuestMemory::UnchangedSinceCollected(plane.range.address, plane.range.bytes, plane.range.generation), "guest writes raced native multisample depth publication capture");
Require(GuestMemory::UnchangedSince(plane.range.address, plane.range.bytes, plane.range.generation), "guest writes raced native multisample depth publication capture");
}
for (const auto& range : sampling.initial) Require(GuestMemory::UnchangedSinceCollected(range.address, range.bytes, range.generation), "guest writes raced native multisample depth publication capture");
Require(GuestMemory::UnchangedSinceAll(sampling.initial), "guest writes raced native multisample depth publication capture");
publication = std::move(captured);
} catch (...) {
publicationFailure = std::current_exception();
publication.clear();
}
}

std::unique_ptr<Buffer> readBackSamples(VkImageAspectFlagBits aspect, std::uint64_t elements) {
const bool depth = aspect == VK_IMAGE_ASPECT_DEPTH_BIT;
const auto linearBytes = (elements * (depth ? 4u : 1u) + 3u) & ~std::uint64_t{3u};
Require(linearBytes <= context.limits.maxStorageBufferRange, "native multisample depth publication exceeds the device's readback range");
auto linear = std::make_unique<Buffer>(context, static_cast<std::size_t>(linearBytes), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
VkImageView arrayView = VK_NULL_HANDLE;
try {
VkImageViewCreateInfo info{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO};
info.image = useSampleShadow ? sampleShadow->image : image;
info.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
info.format = hostFormat;
info.subresourceRange = {static_cast<VkImageAspectFlags>(aspect), 0u, 1u, 0u, 1u};
Check(context.Function<PFN_vkCreateImageView>("vkCreateImageView")(context.device, &info, nullptr, &arrayView), "vkCreateImageView depth publication");
CommandBatch batch(context);
constexpr VkAccessFlags writes = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_SHADER_WRITE_BIT;
RecordMemoryBarrier(context, batch.Handle(), VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, writes, VK_ACCESS_SHADER_READ_BIT);
context.detiler->DispatchMultisampleReadback(batch.Handle(), depth ? VK_FORMAT_D32_SFLOAT : VK_FORMAT_R8_UINT, linear->Handle(), 0u, linearBytes, arrayView, target.extent.width, target.extent.height, 1u, target.samples, 0u, static_cast<std::uint32_t>(elements));
RecordMemoryBarrier(context, batch.Handle(), VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_HOST_BIT, VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT);
batch.SubmitAndWait();
} catch (...) {
if (arrayView != VK_NULL_HANDLE) context.Function<PFN_vkDestroyImageView>("vkDestroyImageView")(context.device, arrayView, nullptr);
throw;
}
context.Function<PFN_vkDestroyImageView>("vkDestroyImageView")(context.device, arrayView, nullptr);
linear->Invalidate();
return linear;
}

PublicationPlane CapturePublication(const GuestMemory::UnchangedQuery& range) {
PublicationPlane plane;
plane.range = range;
Expand Down Expand Up @@ -2373,7 +2451,9 @@ void DepthSurface::PublishToGuest() {
} scope{publicationInProgress};
publicationInProgress = true;
if (publicationFailure) std::rethrow_exception(publicationFailure);
Require(target.format == VK_FORMAT_D32_SFLOAT_S8_UINT && hostFormat == target.format && publication.size() == 3u, "sample-preserving guest publication of native multisample depth is not implemented for this owner");
const bool htile = !uniformPlaneTarget(target);
Require(target.format == VK_FORMAT_D32_SFLOAT_S8_UINT && hostFormat == target.format && publication.size() == (htile ? 3u : 2u), "sample-preserving guest publication of native multisample depth is not implemented for this owner");
const std::size_t stencilPlane = publication.size() - 1u;
Require(sampling.refusal.empty(), sampling.refusal);
RequireStencilProven();
Require(!clearPending && clearGeneration == 0u && stencilClearGeneration == 0u && pendingClear == 0u, "native multisample depth publication with an unapplied clear is not implemented");
Expand Down Expand Up @@ -2408,44 +2488,23 @@ void DepthSurface::PublishToGuest() {
if (auto* recorder = RecorderFor(context)) recorder->Sync();
}
std::array<std::vector<std::byte>, 3> result;
for (std::size_t index : {0u, 2u}) {
for (const std::size_t index : {std::size_t{0u}, stencilPlane}) {
const auto elementBytes = index == 0u ? 4u : 1u;
const auto aspect = index == 0u ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_STENCIL_BIT;
const auto format = index == 0u ? VK_FORMAT_D32_SFLOAT : VK_FORMAT_R8_UINT;
const MultisampleTextureLayout layout(TextureTileMode::kZ64KBX, elementBytes, target.samples, target.extent.width, target.extent.height, 1u, target.pipeBankXor);
Require(layout.Bytes() == publication[index].range.bytes, "native multisample depth publication layout differs from its guarded range");
const auto linearBytes = (layout.LinearBytes() + 3u) & ~std::uint64_t{3u};
Require(linearBytes <= context.limits.maxStorageBufferRange, "native multisample depth publication exceeds the device's readback range");
Buffer linear(context, static_cast<std::size_t>(linearBytes), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
VkImageView arrayView = VK_NULL_HANDLE;
try {
VkImageViewCreateInfo info{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO};
info.image = useSampleShadow ? sampleShadow->image : image;
info.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
info.format = hostFormat;
info.subresourceRange = {static_cast<VkImageAspectFlags>(aspect), 0u, 1u, 0u, 1u};
Check(context.Function<PFN_vkCreateImageView>("vkCreateImageView")(context.device, &info, nullptr, &arrayView), "vkCreateImageView depth publication");
CommandBatch batch(context);
constexpr VkAccessFlags writes = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_SHADER_WRITE_BIT;
RecordMemoryBarrier(context, batch.Handle(), VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, writes, VK_ACCESS_SHADER_READ_BIT);
context.detiler->DispatchMultisampleReadback(batch.Handle(), format, linear.Handle(), 0u, linearBytes, arrayView, target.extent.width, target.extent.height, 1u, target.samples, 0u, static_cast<std::uint32_t>(layout.LinearBytes() / elementBytes));
RecordMemoryBarrier(context, batch.Handle(), VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_HOST_BIT, VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT);
batch.SubmitAndWait();
} catch (...) {
if (arrayView != VK_NULL_HANDLE) context.Function<PFN_vkDestroyImageView>("vkDestroyImageView")(context.device, arrayView, nullptr);
throw;
}
context.Function<PFN_vkDestroyImageView>("vkDestroyImageView")(context.device, arrayView, nullptr);
linear.Invalidate();
const auto linear = readBackSamples(aspect, layout.LinearBytes() / elementBytes);
result[index] = publication[index].Expand();
layout.Tile(linear.Bytes().first(static_cast<std::size_t>(layout.LinearBytes())), result[index]);
}
result[1] = publication[1].Expand();
for (std::size_t offset = 0u; offset < result[1].size(); offset += 4u) {
std::uint32_t word;
std::memcpy(&word, result[1].data() + offset, 4u);
word = expandedHtileWord(word, !target.htileDepthOnly, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT);
std::memcpy(result[1].data() + offset, &word, 4u);
layout.Tile(linear->Bytes().first(static_cast<std::size_t>(layout.LinearBytes())), result[index]);
}
if (htile) {
result[1] = publication[1].Expand();
for (std::size_t offset = 0u; offset < result[1].size(); offset += 4u) {
std::uint32_t word;
std::memcpy(&word, result[1].data() + offset, 4u);
word = expandedHtileWord(word, !target.htileDepthOnly, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT);
std::memcpy(result[1].data() + offset, &word, 4u);
}
}
std::array<std::vector<std::byte>, 3> current;
std::array<bool, 3> tookGuest{};
Expand Down Expand Up @@ -2950,7 +3009,16 @@ std::uint64_t MultisampledDepthBytes_nid_no_patch(VkExtent2D extent, std::uint32
return width * height * bytesPerTexel * samples;
}

void proveOverwrittenPlanes(std::uint64_t address, std::size_t bytes) {
std::lock_guard gpu(GuestMemory::GpuMutex());
std::lock_guard lock(surfacesMutex());
for (const auto& surface : surfaces()) {
if (surface->sampling.overwriteStencil && surface->active->load(std::memory_order_acquire) && surface->Overlaps(address, bytes)) surface->ProveOverwrite();
}
}

void RequireNoResidentDepthRead_nid_no_patch(std::uint64_t address, std::size_t bytes, std::optional<std::pair<std::uint64_t, std::size_t>> htileClear, bool allowPublication) {
if (bytes != 0u && pendingOverwriteProofs().load(std::memory_order_acquire) != 0u && allowPublication && !HoldsMultisampleTargetsLock()) proveOverwrittenPlanes(address, bytes);
if (bytes == 0u || !anyDepthGuard().load(std::memory_order_acquire)) return;
Require(address <= std::numeric_limits<std::uint64_t>::max() - bytes, "native depth read range overflows");
const auto overlaps = [&](const DepthGuard& range) { return address < range.address + range.bytes && range.address < address + bytes; };
Expand Down Expand Up @@ -3088,7 +3156,7 @@ VkImageView DepthSurfaceView(const Context& context, const DepthTarget& target,
return DepthSurfaceViewWithStencilWriteIntent(context, target, depthWritten, stencilTested, true);
}

VkImageView DepthSurfaceViewWithStencilWriteIntent(const Context& context, const DepthTarget& target, bool depthWritten, bool stencilTested, bool stencilWritten) {
VkImageView DepthSurfaceViewWithStencilWriteIntent(const Context& context, const DepthTarget& target, bool depthWritten, bool stencilTested, bool stencilWritten, std::optional<std::uint8_t> overwrittenStencil) {
const auto planeBytes = [&](std::uint32_t bytesPerTexel) { return target.samples == 1u ? DepthSliceBytes(target.extent, bytesPerTexel) : MultisampledDepthBytes_nid_no_patch(target.extent, bytesPerTexel, target.samples); };
ClaimMultisampleTargetMemory(target.address, planeBytes(target.format == VK_FORMAT_D16_UNORM || target.format == VK_FORMAT_D16_UNORM_S8_UINT ? 2u : 4u));
if (target.stencilAddress != 0u) ClaimMultisampleTargetMemory(target.stencilAddress, planeBytes(1u));
Expand All @@ -3106,6 +3174,7 @@ VkImageView DepthSurfaceViewWithStencilWriteIntent(const Context& context, const
}
}
const bool created = surface == nullptr;
if (!created) surface->ProveOverwrite();
GuestAllocations::Lease creationLease;
if (created) {
const bool d16 = target.format == VK_FORMAT_D16_UNORM || target.format == VK_FORMAT_D16_UNORM_S8_UINT;
Expand All @@ -3125,6 +3194,10 @@ VkImageView DepthSurfaceViewWithStencilWriteIntent(const Context& context, const
}
}
auto sampling = target.samples == 1u ? DepthSamplingState{} : prepareDepthSampling(context, target);
if (sampling.unevenPlanes && overwrittenStencil) {
sampling.overwriteStencil = overwrittenStencil;
sampling.initialStencil = static_cast<std::uint8_t>(~*overwrittenStencil);
}
creationLease = target.samples == 1u || !sampling.refusal.empty() ? GuestAllocations::Lease{} : GuestAllocations::GuestAllocationsAcquireAll_nid_postfix();
lock.lock();
releaseRetired(context);
Expand Down
Loading
Loading