mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-11-07 13:39:55 +00:00
fix(agc): give each slice of a depth array its own HTILE
A slice of a depth array target (DB_DEPTH_VIEW SLICE_START) offset its depth and stencil addresses but kept the array's DB_HTILE_DATA_BASE as its HTILE. Every slice then shared slice 0's metadata: a fast clear of slice 0's HTILE cleared every slice, and a clear of any other slice's HTILE matched none. A slice's HTILE now starts at the base plus the slice times one slice's pipe-aligned metadata, as addrlib addresses it (sliceSize * slice in Gfx10Lib::HwlComputeHtileAddrFromCoord; HtileMetadataBytes is that slice size). The recognized HTILE clear kernel now reports the bytes it clears, 16 per thread (global_store_dwordx4 at the thread index << 4), and a surface takes the clear when that range covers its HTILE, as a DMA_DATA fill does; a clear over a whole array still clears every slice. Uncharted: Legacy of Thieves Collection (PPSA05684) renders its sun shadow cascades into the four slices of a 2048x2048 D16 array; each slice's HTILE lies 256 KiB after the previous one.
This commit is contained in:
1 parent
8ba869d303
commit
8fec47a4b2
6 files changed
+57
-10
No files matched your search
@@ -75,7 +75,10 @@ void Driver::dispatch(QueueState& queue, std::span<const std::uint32_t> packet,
|
||||
if (!partialLastGroup && matchesFillKernel(std::span(snapshot.code).subspan(codeOffset), userData, compute)) return;
|
||||
if (!partialLastGroup && indirectArguments == 0 && matchesCopyKernel(std::span(snapshot.code).subspan(codeOffset), userData, compute)) return;
|
||||
} else {
|
||||
if (const auto htile = Graphics::HtileDepthClearAddress(std::span(snapshot.code).subspan(codeOffset), userData, compute.numThreads); htile != 0) Graphics::NoteHtileDepthClear(htile);
|
||||
if (const auto htile = Graphics::HtileDepthClearAddress(std::span(snapshot.code).subspan(codeOffset), userData, compute.numThreads); htile != 0) {
|
||||
const auto threads = (packet[4] & 0x20u) != 0 ? static_cast<std::uint64_t>(packet[1]) : static_cast<std::uint64_t>(packet[1]) * compute.numThreads[0];
|
||||
Graphics::NoteHtileDepthClear(htile, static_cast<std::size_t>(threads * 16u));
|
||||
}
|
||||
if (indirectArguments == 0 && (packet[4] & 0x20u) == 0) {
|
||||
if (const auto fill = Graphics::MatchMemoryValueFill(std::span(snapshot.code).subspan(codeOffset), userData, compute.numThreads, {packet[1], packet[2], packet[3]})) Graphics::NoteMemoryValueFill(*fill);
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ void ClearDepthSurfaces(VkDevice device);
|
||||
bool DepthSurfaceAt(std::uint64_t address);
|
||||
bool DepthSurfaceAt(const Context& context, std::span<const std::uint32_t> words, const GuestTextureResource& resource);
|
||||
std::uint64_t HtileDepthClearAddress(std::span<const std::uint32_t> code, std::span<const std::uint32_t> userData, const std::array<std::uint32_t, 3>& numThreads);
|
||||
void NoteHtileDepthClear(std::uint64_t htileAddress);
|
||||
void NoteHtileDepthClear(std::uint64_t htileAddress, std::size_t bytes);
|
||||
struct MemoryValueFill {
|
||||
std::uint64_t address;
|
||||
std::size_t bytes;
|
||||
|
||||
@@ -617,11 +617,20 @@ DepthSurface* planeSurface(VkDevice device, std::uint64_t address) {
|
||||
return found == list.rend() ? nullptr : found->get();
|
||||
}
|
||||
|
||||
std::set<std::uint64_t>& clearedHtiles() {
|
||||
static auto* cleared = new std::set<std::uint64_t>();
|
||||
std::map<std::uint64_t, std::size_t>& clearedHtiles() {
|
||||
static auto* cleared = new std::map<std::uint64_t, std::size_t>();
|
||||
return *cleared;
|
||||
}
|
||||
|
||||
bool takeClearedHtile(const DepthTarget& target) {
|
||||
if (target.htileAddress == 0) return false;
|
||||
auto& cleared = clearedHtiles();
|
||||
const auto found = std::find_if(cleared.begin(), cleared.end(), [&](const auto& entry) { return HtileFillCovers(target.htileAddress, target.extent, entry.first, entry.second); });
|
||||
if (found == cleared.end()) return false;
|
||||
cleared.erase(found);
|
||||
return true;
|
||||
}
|
||||
|
||||
const DepthSurface* drawnDepth = nullptr;
|
||||
|
||||
std::atomic<std::uint64_t> surfacesSerial{0};
|
||||
@@ -678,7 +687,7 @@ float HostDepthBiasConstant(const Context& context, VkFormat format, float const
|
||||
|
||||
VkImageView DepthSurfaceView(const Context& context, const DepthTarget& target) {
|
||||
std::lock_guard lock(surfacesMutex());
|
||||
const bool cleared = target.htileAddress != 0 && clearedHtiles().erase(target.htileAddress) != 0;
|
||||
const bool cleared = takeClearedHtile(target);
|
||||
DepthSurface* surface = nullptr;
|
||||
for (const auto& candidate : surfaces()) {
|
||||
if (candidate->context.device == context.device && sameSurface(candidate->target, target)) {
|
||||
@@ -736,15 +745,15 @@ void NoteMemoryValueFill(const MemoryValueFill& fill) {
|
||||
NoteDepthMetadataFill(fill.address, fill.bytes, value);
|
||||
}
|
||||
|
||||
void NoteHtileDepthClear(std::uint64_t htileAddress) {
|
||||
void NoteHtileDepthClear(std::uint64_t htileAddress, std::size_t bytes) {
|
||||
std::lock_guard lock(surfacesMutex());
|
||||
bool existing = false;
|
||||
for (const auto& surface : surfaces()) {
|
||||
if (surface->target.htileAddress != htileAddress) continue;
|
||||
if (!HtileFillCovers(surface->target.htileAddress, surface->target.extent, htileAddress, bytes)) continue;
|
||||
surface->pendingClear |= VK_IMAGE_ASPECT_DEPTH_BIT;
|
||||
existing = true;
|
||||
}
|
||||
if (!existing) clearedHtiles().insert(htileAddress);
|
||||
if (!existing) clearedHtiles()[htileAddress] = bytes;
|
||||
}
|
||||
|
||||
void ClearDepthSurfaces(VkDevice device) {
|
||||
|
||||
@@ -292,7 +292,7 @@ void decodeDepth(const Registers& cx, std::uint32_t depthControl, bool testValEx
|
||||
depth.clearDepth = readFloat(cx, 0x00b);
|
||||
depth.clearStencil = static_cast<std::uint8_t>(read(cx, 0x00a) & 0xffu);
|
||||
if (const auto htile = find(cx, 0x005); zFormat != 0 && htile != cx.end() && htile->second != 0 && (read(cx, 0x010) & (1u << 29u)) != 0) {
|
||||
depth.htileAddress = base(0x005, 0x01e);
|
||||
depth.htileAddress = base(0x005, 0x01e) + static_cast<std::uint64_t>(view & 0x1fffu) * HtileMetadataBytes(depth.extent);
|
||||
depth.htileStencil = stencil && (read(cx, 0x011) & (1u << 29u)) == 0;
|
||||
}
|
||||
result.depth = depth;
|
||||
|
||||
@@ -39,11 +39,14 @@ THandle newHandle() {
|
||||
VkFormat lastImageFormat = VK_FORMAT_UNDEFINED;
|
||||
std::vector<VkImage> createdImages;
|
||||
std::vector<std::tuple<VkImage, VkImage, VkImageAspectFlags>> imageCopies;
|
||||
VkImage lastImage = VK_NULL_HANDLE;
|
||||
std::vector<VkImage> clearedImages;
|
||||
|
||||
VKAPI_ATTR VkResult VKAPI_CALL createImage(VkDevice, const VkImageCreateInfo* info, const VkAllocationCallbacks*, VkImage* image) {
|
||||
lastImageFormat = info->format;
|
||||
*image = newHandle<VkImage>();
|
||||
createdImages.push_back(*image);
|
||||
lastImage = *image;
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -101,7 +104,9 @@ VKAPI_ATTR void VKAPI_CALL destroyImageView(VkDevice, VkImageView, const VkAlloc
|
||||
VKAPI_ATTR void VKAPI_CALL destroyImage(VkDevice, VkImage, const VkAllocationCallbacks*) {}
|
||||
VKAPI_ATTR void VKAPI_CALL freeMemory(VkDevice, VkDeviceMemory, const VkAllocationCallbacks*) {}
|
||||
VKAPI_ATTR void VKAPI_CALL cmdPipelineBarrier(VkCommandBuffer, VkPipelineStageFlags, VkPipelineStageFlags, VkDependencyFlags, std::uint32_t, const VkMemoryBarrier*, std::uint32_t, const VkBufferMemoryBarrier*, std::uint32_t, const VkImageMemoryBarrier*) {}
|
||||
VKAPI_ATTR void VKAPI_CALL cmdClearDepthStencilImage(VkCommandBuffer, VkImage, VkImageLayout, const VkClearDepthStencilValue*, std::uint32_t, const VkImageSubresourceRange*) {}
|
||||
VKAPI_ATTR void VKAPI_CALL cmdClearDepthStencilImage(VkCommandBuffer, VkImage image, VkImageLayout, const VkClearDepthStencilValue*, std::uint32_t, const VkImageSubresourceRange*) {
|
||||
clearedImages.push_back(image);
|
||||
}
|
||||
VKAPI_ATTR void VKAPI_CALL cmdCopyImage(VkCommandBuffer, VkImage source, VkImageLayout, VkImage destination, VkImageLayout, std::uint32_t count, const VkImageCopy* regions) {
|
||||
for (std::uint32_t i = 0; i < count; ++i) imageCopies.emplace_back(source, destination, regions[i].dstSubresource.aspectMask);
|
||||
}
|
||||
@@ -307,6 +312,29 @@ void RunDepthSurfaceReuseTests() {
|
||||
imageCopies.clear();
|
||||
const auto otherWithA = pairing(otherDepth, stencilA);
|
||||
Require(imageCopies.size() == 1 && imageCopies[0] == std::make_tuple(withA, otherWithA, static_cast<VkImageAspectFlags>(VK_IMAGE_ASPECT_STENCIL_BIT)), "a stencil plane paired with another depth plane must carry its stencil over");
|
||||
{
|
||||
constexpr std::uint64_t arrayBase = 0x80000000;
|
||||
constexpr std::uint64_t htile = 0x81000000;
|
||||
constexpr VkExtent2D extent{1024, 512};
|
||||
const auto sliceHtile = HtileMetadataBytes(extent);
|
||||
DepthTarget first{arrayBase, 0, extent, VK_FORMAT_D32_SFLOAT, 1.0f, 0};
|
||||
first.htileAddress = htile;
|
||||
DepthTarget second{arrayBase + DepthSliceBytes(extent, 4u), 0, extent, VK_FORMAT_D32_SFLOAT, 1.0f, 0};
|
||||
second.htileAddress = htile + sliceHtile;
|
||||
DepthSurfaceView(context, first);
|
||||
const auto firstImage = lastImage;
|
||||
DepthSurfaceView(context, second);
|
||||
const auto secondImage = lastImage;
|
||||
const auto clearsAfter = [&](std::size_t bytes) {
|
||||
clearedImages.clear();
|
||||
NoteHtileDepthClear(htile, static_cast<std::size_t>(bytes));
|
||||
DepthSurfaceView(context, first);
|
||||
DepthSurfaceView(context, second);
|
||||
return clearedImages;
|
||||
};
|
||||
Require(clearsAfter(sliceHtile) == std::vector<VkImage>{firstImage}, "an HTILE clear of the first slice must clear that slice alone");
|
||||
Require(clearsAfter(2u * sliceHtile) == std::vector<VkImage>{firstImage, secondImage}, "an HTILE clear over both slices must clear both");
|
||||
}
|
||||
|
||||
ClearDepthSurfaces(context.device);
|
||||
}
|
||||
@@ -1484,6 +1484,13 @@ void DepthStencilTests() {
|
||||
AgcDriver::Graphics::RegisterReadLog() = nullptr;
|
||||
for (const auto read : log) Require(AgcDriver::Graphics::DrawKeyCovers(read), "DrawKeyRegisters lacks an HTILE register the decoder reads: " + std::to_string(read.offset));
|
||||
Require(state.depth->htileAddress == (0x30000ull | (1ull << 40u)), "the HTILE address was not decoded from DB_HTILE_DATA_BASE and its high byte");
|
||||
{
|
||||
const auto view = queue.context[0x002];
|
||||
queue.context[0x002] = 2u | (2u << 13u);
|
||||
const auto slice = AgcDriver::Graphics::DecodeState(queue);
|
||||
Require(slice.depth->htileAddress == (0x30000ull | (1ull << 40u)) + 2u * AgcDriver::Graphics::HtileMetadataBytes(slice.depth->extent), "slice 2 of a depth array must use the third slice of its HTILE");
|
||||
queue.context[0x002] = view;
|
||||
}
|
||||
queue.context[0x010] &= ~0x20000000u;
|
||||
state = AgcDriver::Graphics::DecodeState(queue);
|
||||
Require(state.depth->htileAddress == 0, "a surface without TILE_SURFACE_ENABLE decoded an HTILE address");
|
||||
|
||||
Reference in new issue
Block a user