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
10 changes: 10 additions & 0 deletions core/libs/prx/libSceAgcDriver/Graphics/include/DepthSurface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <memory>
#include <optional>
#include <span>
#include <utility>

namespace AgcDriver::Graphics {

Expand All @@ -25,6 +26,15 @@ 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);
void ClearDepthSurfaces(VkDevice device);
void RetireDepthSurfaces(VkDevice device, std::uint64_t address, std::uint64_t bytes);
struct WrittenStorageExtent {
std::uint64_t address;
std::uint32_t width;
std::uint32_t height;
std::uint32_t bytesPerElement;
std::array<std::uint32_t, 3> block;
std::uint32_t blocksPerRow;
};
void RetireCoveredDepthSurfaces(VkDevice device, std::span<const std::pair<std::uint64_t, std::uint64_t>> written, std::span<const WrittenStorageExtent> extents);
bool DepthSurfaceAt(std::uint64_t address);
bool DepthSurfaceOverlaps(VkDevice device, std::uint64_t address, std::uint64_t bytes);
bool DepthSurfaceAt(const Context& context, std::span<const std::uint32_t> words, const GuestTextureResource& resource);
Expand Down
37 changes: 35 additions & 2 deletions core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ namespace {

std::uint64_t depthWrites = 0;

std::pair<std::uint32_t, std::uint32_t> DepthBlockExtent(std::uint32_t bytesPerTexel) {
return {bytesPerTexel == 4 ? 128u : 256u, bytesPerTexel == 1 ? 256u : 128u};
}

Recorder* RecorderFor(const Context& context) {
auto* recorder = Recorder::Active();
return recorder != nullptr && recorder->Device() == context.device ? recorder : nullptr;
Expand Down Expand Up @@ -478,6 +482,29 @@ class DepthSurface {
return target.stencilAddress != 0 && address < target.stencilAddress + stencilBytes() && target.stencilAddress < end;
}

bool CoveredBy(std::span<const std::pair<std::uint64_t, std::uint64_t>> written, std::span<const WrittenStorageExtent> extents) const {
const auto reaches = [&](std::uint64_t address, std::uint32_t bytesPerTexel) {
const auto [blockWidth, blockHeight] = DepthBlockExtent(bytesPerTexel);
const std::array<std::uint32_t, 3> block{65536u, blockWidth, blockHeight};
const auto blocksPerRow = (target.extent.width + blockWidth - 1u) / blockWidth;
return std::any_of(extents.begin(), extents.end(), [&](const auto& extent) {
return extent.address == address && extent.bytesPerElement == bytesPerTexel && extent.block == block && extent.blocksPerRow == blocksPerRow && extent.width >= target.extent.width && extent.height >= target.extent.height;
});
};
const auto covers = [&](std::uint64_t begin, std::uint64_t end) {
for (const auto& [writtenBegin, writtenEnd] : written) {
if (writtenEnd <= begin) continue;
if (writtenBegin > begin) return false;
begin = writtenEnd;
if (begin >= end) return true;
}
return false;
};
const bool d16 = target.format == VK_FORMAT_D16_UNORM || target.format == VK_FORMAT_D16_UNORM_S8_UINT;
if (target.samples == 1u && (reaches(target.address, d16 ? 2u : 4u) || (target.stencilAddress != 0 && reaches(target.stencilAddress, 1u)))) return true;
return covers(target.address, target.address + depthBytes()) || (target.stencilAddress != 0 && covers(target.stencilAddress, target.stencilAddress + stencilBytes()));
}

void NoteMemory() {
depthMemory = GuestMemory::CollectWrites(target.address, static_cast<std::size_t>(depthBytes()));
stencilMemory = target.stencilAddress != 0 ? GuestMemory::CollectWrites(target.stencilAddress, static_cast<std::size_t>(stencilBytes())) : 0;
Expand Down Expand Up @@ -1259,8 +1286,7 @@ std::uint64_t DepthSurfacesSerial() {
}

std::uint64_t DepthSliceBytes(VkExtent2D extent, std::uint32_t bytesPerTexel) {
const std::uint32_t blockWidth = bytesPerTexel == 4 ? 128u : 256u;
const std::uint32_t blockHeight = bytesPerTexel == 1 ? 256u : 128u;
const auto [blockWidth, blockHeight] = DepthBlockExtent(bytesPerTexel);
const auto width = static_cast<std::uint64_t>((extent.width + blockWidth - 1) / blockWidth * blockWidth);
const auto height = static_cast<std::uint64_t>((extent.height + blockHeight - 1) / blockHeight * blockHeight);
return width * height * bytesPerTexel;
Expand Down Expand Up @@ -1804,4 +1830,11 @@ void RetireDepthSurfaces(VkDevice device, std::uint64_t address, std::uint64_t b
}
}

void RetireCoveredDepthSurfaces(VkDevice device, std::span<const std::pair<std::uint64_t, std::uint64_t>> written, std::span<const WrittenStorageExtent> extents) {
std::lock_guard lock(surfacesMutex());
for (const auto& surface : surfaces()) {
if (surface->context.device == device && !surface->retired && surface->CoveredBy(written, extents)) retire(*surface);
}
}

}
31 changes: 30 additions & 1 deletion core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -973,12 +973,41 @@ GuestTextureResource StorageSurface(const Context& context, const GuestTextureRe
return surface;
}

struct WrittenStorage {
std::vector<std::pair<std::uint64_t, std::uint64_t>> bytes;
std::vector<WrittenStorageExtent> extents;
};

static WrittenStorage WrittenStorageBytes(const GuestTextureResource& viewed, std::uint32_t mip) {
WrittenStorage written;
const auto geometry = DescribeSurface(viewed);
if (geometry.thick || mip >= geometry.mips.size()) return written;
const auto& level = geometry.mips[mip];
const auto bytesPerElement = BytesPerElement(viewed.format);
const auto covered = CoveredMipBytes(viewed.tileMode, bytesPerElement, level);
const bool blocks = viewed.tileMode != TextureTileMode::kLinear && !level.tail;
const auto first = geometry.layers > 1u && geometry.imageDepth <= 1u ? std::min(viewed.baseArray, geometry.layers) : 0u;
for (auto layer = first; layer < geometry.layers; ++layer) {
if (!geometry.HasLayer(mip, layer)) continue;
const auto begin = viewed.baseAddress + geometry.GuestLayerOffset(layer) + level.tiledOffset;
for (const auto& [rangeBegin, rangeEnd] : covered) {
if (!written.bytes.empty() && written.bytes.back().second == begin + rangeBegin) written.bytes.back().second = begin + rangeEnd;
else written.bytes.emplace_back(begin + rangeBegin, begin + rangeEnd);
}
if (blocks) written.extents.push_back({begin, level.width, level.height, bytesPerElement, ThinBlockLayout(viewed.tileMode, bytesPerElement), level.blocksPerRow});
}
return written;
}

// `guestBytes` is the surface size when the caller described the surface already (0: described here).
std::shared_ptr<StorageTexture> cachedStorageTexture(const Context& context, std::span<const std::uint32_t> words, const GuestTextureResource& viewed, std::uint32_t mip, std::uint64_t guestBytes, bool written) {
Require(viewed.dimension != TextureDimension::k2DMsaa, "storage image access to a multisampled texture is not implemented");
RequireNoMultisampleTarget(viewed.baseAddress, guestBytes != 0 ? static_cast<std::size_t>(guestBytes) : 1u, "a storage image access");
if (auto depth = DepthSurfaceStorage(context, words, viewed, mip)) return depth;
if (written) RetireDepthSurfaces(context.device, viewed.baseAddress, guestBytes != 0 ? guestBytes : DescribeSurface(viewed).guestBytes);
if (written && DepthSurfaceOverlaps(context.device, viewed.baseAddress, guestBytes != 0 ? guestBytes : DescribeSurface(viewed).guestBytes)) {
const auto storage = WrittenStorageBytes(viewed, mip);
RetireCoveredDepthSurfaces(context.device, storage.bytes, storage.extents);
}
static const bool disabled = std::getenv("APS5_NO_TEXTURE_CACHE") != nullptr;
if (disabled) return StorageTexture::Create(context, *context.detiler, viewed, mip);
static_cast<void>(words);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ using DepthFastClearHarness::Width;

alignas(4096) std::array<float, 65536> Depth{};
alignas(256) std::array<std::uint32_t, 64> Output{};

std::uintptr_t DepthAddress() {
return (reinterpret_cast<std::uintptr_t>(Depth.data()) + 0xffffu) & ~std::uintptr_t{0xffffu};
}
alignas(256) constexpr std::array<std::uint32_t, 13> LoadCode{
0x7e3c0280u, 0x7e3e0280u, 0x7e140280u, 0xf0001108u, 0x00010a1eu,
0xbf8c3f70u, 0xf0201108u, 0x00010a1eu, 0x34060082u, 0xe0701000u, 0x80000a03u, 0xbf810000u, 0xbf810000u,
Expand All @@ -26,14 +30,14 @@ alignas(256) constexpr std::array<std::uint32_t, 10> SampleCode{
0x00610a1eu, 0x34060082u, 0xe0701000u, 0x80000a03u, 0xbf810000u,
};

void Read(AgcDriver::VulkanDevice& device, std::uint32_t format, std::uint32_t width, bool sampled, float expected) {
void Read(AgcDriver::VulkanDevice& device, std::uint32_t format, std::uint32_t width, bool sampled, float expected, std::uint32_t height = Height, std::uint32_t swizzle = 0u) {
Output.fill(0xdeadbeefu);
const auto output = reinterpret_cast<std::uintptr_t>(Output.data());
const auto depth = reinterpret_cast<std::uintptr_t>(Depth.data());
const auto depth = DepthAddress();
const std::array<std::uint32_t, 16> data{
static_cast<std::uint32_t>(output), static_cast<std::uint32_t>(output >> 32u), sizeof(Output), 0x31016facu,
static_cast<std::uint32_t>(depth >> 8u), static_cast<std::uint32_t>(depth >> 40u) | (format << 20u) | (((width - 1u) & 3u) << 30u),
((width - 1u) >> 2u) | ((Height - 1u) << 14u), 0x90000facu, 0u, 0u, 0u, 0u,
((width - 1u) >> 2u) | ((height - 1u) << 14u), 0x90000facu | (swizzle << 20u), 0u, 0u, 0u, 0u,
0x92u, 0u, 0u, 0u,
};
const auto code = sampled ? std::span<const std::uint32_t>(SampleCode) : std::span<const std::uint32_t>(LoadCode);
Expand All @@ -60,15 +64,22 @@ int main() {
const auto device = OpenVulkanTestDevice();
if (!device) return VulkanTestSkipped;
const auto shaders = DepthFastClearHarness::Compile(*device);
const auto address = reinterpret_cast<std::uintptr_t>(Depth.data());
const auto address = DepthAddress();
for (const auto format : {VK_FORMAT_D32_SFLOAT, VK_FORMAT_D16_UNORM}) {
AgcDriver::Graphics::ClearDepthSurfaces(device->Device());
Depth.fill(0.25f);
const DepthFastClearHarness::Surface surface{address, 0, 0, false, format};
Require(DepthFastClearHarness::Draw(*device, shaders, surface, {.depthWrite = true}) == DepthFastClearHarness::Covered, "depth producer did not cover the surface");
Read(*device, format == VK_FORMAT_D16_UNORM ? 7u : 22u, Width, true, 0.5f);
Read(*device, 22u, format == VK_FORMAT_D16_UNORM ? Width : Width / 2u, false, 0.25f);
if (format == VK_FORMAT_D32_SFLOAT) Read(*device, 22u, Width, true, 0.25f);
if (format != VK_FORMAT_D32_SFLOAT) continue;
Read(*device, 22u, Width, true, 0.5f);
Read(*device, 20u, Width, false, 0.25f, Height, 25u);
Read(*device, 22u, Width, true, 0.25f);
Require(DepthFastClearHarness::Draw(*device, shaders, surface, {.depthTest = false, .depthWrite = true}) == DepthFastClearHarness::Covered, "depth producer did not cover the surface");
Read(*device, 22u, Width, true, 0.5f);
Read(*device, 22u, 128u, false, 0.25f, 128u);
Read(*device, 22u, Width, true, 0.25f);
}
std::puts("depth storage reuse GPU tests passed");
return 0;
Expand Down
Loading