feat(agc-driver): sample depth and stencil surfaces from their GPU images
A depth target lives only in a GPU image (its guest surface is never written), so a texture over its depth or stencil plane now samples that image through a view of the plane's aspect: R32_SFLOAT for D32, R16_UNORM for D16, R8_UINT for stencil, one level and slice at the surface's extent. Other shapes, and storage access to a depth surface, throw. Depth images get the sampled usage, and the barriers after a render pass and before a dispatch include depth attachment writes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
b7eaabbc1e
commit
30c0cf1dfb
|
|
@ -2871,7 +2871,7 @@ void VulkanDevice::recordDispatch(RecordedDispatch& record) {
|
|||
Graphics::Recorder::CountMerged(CommandClass::DispatchLeading);
|
||||
} else {
|
||||
const auto timing = recorder.BeginGpuTiming(CommandClass::DispatchLeading);
|
||||
Graphics::RecordMemoryBarrier(context, commands, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_HOST_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT);
|
||||
Graphics::RecordMemoryBarrier(context, commands, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_HOST_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT);
|
||||
Graphics::Recorder::CountBarriers(CommandClass::DispatchLeading);
|
||||
recorder.EndGpuTiming(timing);
|
||||
++d.preBarriersRecorded;
|
||||
|
|
|
|||
|
|
@ -1,15 +1,20 @@
|
|||
#ifndef CORE_LIBS_PRX_LIBSCEAGCDRIVER_GRAPHICS_INCLUDE_DEPTHSURFACE_HPP
|
||||
#define CORE_LIBS_PRX_LIBSCEAGCDRIVER_GRAPHICS_INCLUDE_DEPTHSURFACE_HPP
|
||||
|
||||
#include "prx/libSceAgcDriver/Graphics/include/GuestTextureResource.hpp"
|
||||
#include "prx/libSceAgcDriver/Graphics/include/State.hpp"
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <span>
|
||||
|
||||
namespace AgcDriver::Graphics {
|
||||
|
||||
class Texture;
|
||||
|
||||
// The attachment view of the device's image for a depth/stencil target, made on first use. The
|
||||
// image lives on the GPU only, always in the general layout: its guest surface (tiled, HTILE
|
||||
// compressed) is never read or written, so a new image starts at the target's clear values, and
|
||||
// texture reads of the surface are refused (DepthSurfaceAt). Images stay until the device goes
|
||||
// shaders sample the image itself (DepthSurfaceTexture). Images stay until the device goes
|
||||
// (ClearDepthSurfaces), so their views are stable for cached framebuffers.
|
||||
// ponytail: never evicted, one image per distinct surface; evict (and keep recorded ones alive) if
|
||||
// titles churn through many.
|
||||
|
|
@ -17,6 +22,10 @@ VkImageView DepthSurfaceView(const Context& context, const DepthTarget& target);
|
|||
void ClearDepthSurfaces(VkDevice device);
|
||||
// Whether the depth or stencil plane of a surface drawn with starts at `address`.
|
||||
bool DepthSurfaceAt(std::uint64_t address);
|
||||
// The texture sampling the depth or stencil plane that starts at the descriptor's base address,
|
||||
// or null when no surface drawn with on this device has a plane there. The plane is read in place,
|
||||
// so the descriptor must describe it (its format, extent, one level and slice); anything else throws.
|
||||
std::shared_ptr<Texture> DepthSurfaceTexture(const Context& context, std::span<const std::uint32_t> words, const GuestTextureResource& resource, VkComponentMapping components);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -57,6 +57,9 @@ public:
|
|||
// compute pass writing it and the next pass sampling it share one image and copy nothing.
|
||||
// CanCopyFrom says whether the two descriptors address the same surface compatibly.
|
||||
Texture(const Context& context, const std::shared_ptr<StorageTexture>& source, const GuestTextureResource& descriptor, VkComponentMapping components);
|
||||
// A view of one plane (aspect) of a depth surface's image, which stays in the general layout;
|
||||
// the surface (DepthSurface.hpp) owns the image and its views.
|
||||
Texture(const Context& context, VkImage depthImage, VkFormat depthFormat, VkImageAspectFlags aspect, VkComponentMapping components);
|
||||
static bool CanCopyFrom(const StorageTexture& source, const GuestTextureResource& descriptor);
|
||||
~Texture();
|
||||
Texture(const Texture&) = delete;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,15 @@
|
|||
#include "prx/libSceAgcDriver/Graphics/include/DepthSurface.hpp"
|
||||
#include "prx/libSceAgcDriver/Graphics/include/Recorder.hpp"
|
||||
#include "prx/libSceAgcDriver/Graphics/include/Resources.hpp"
|
||||
#include "prx/libSceAgcDriver/Graphics/include/Texture.hpp"
|
||||
#include "prx/libSceAgcDriver/Graphics/include/TextureFormat.hpp"
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdio>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
namespace AgcDriver::Graphics {
|
||||
|
|
@ -16,6 +22,7 @@ public:
|
|||
VkFormatProperties properties{};
|
||||
context.formatProperties(context.physical, target.format, &properties);
|
||||
Require((properties.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0, "depth/stencil format " + std::to_string(target.format) + " cannot be an attachment on this device");
|
||||
Require((properties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) != 0, "depth/stencil format " + std::to_string(target.format) + " cannot be sampled on this device");
|
||||
Require(target.extent.width <= context.limits.maxFramebufferWidth && target.extent.height <= context.limits.maxFramebufferHeight, "depth target exceeds framebuffer limits");
|
||||
const VkImageAspectFlags aspects = VK_IMAGE_ASPECT_DEPTH_BIT | (target.stencilAddress != 0 ? VK_IMAGE_ASPECT_STENCIL_BIT : 0u);
|
||||
try {
|
||||
|
|
@ -27,7 +34,7 @@ public:
|
|||
info.arrayLayers = 1;
|
||||
info.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
info.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
info.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
info.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
Check(context.Function<PFN_vkCreateImage>("vkCreateImage")(context.device, &info, nullptr, &image), "vkCreateImage depth");
|
||||
|
|
@ -74,6 +81,31 @@ public:
|
|||
DepthSurface(const DepthSurface&) = delete;
|
||||
DepthSurface& operator=(const DepthSurface&) = delete;
|
||||
|
||||
// The texture sampling the plane at the descriptor's base address, one per descriptor.
|
||||
std::shared_ptr<Texture> Sampled(std::span<const std::uint32_t> words, const GuestTextureResource& resource, VkComponentMapping components) {
|
||||
std::array<std::uint32_t, 12> key{};
|
||||
std::copy_n(words.begin(), std::min<std::size_t>(words.size(), 8), key.begin());
|
||||
key[8] = components.r;
|
||||
key[9] = components.g;
|
||||
key[10] = components.b;
|
||||
key[11] = components.a;
|
||||
if (const auto found = textures.find(key); found != textures.end()) return found->second;
|
||||
const bool stencil = target.stencilAddress != 0 && resource.baseAddress == target.stencilAddress;
|
||||
const bool d16 = target.format == VK_FORMAT_D16_UNORM || target.format == VK_FORMAT_D16_UNORM_S8_UINT;
|
||||
const auto expected = stencil ? VK_FORMAT_R8_UINT : d16 ? VK_FORMAT_R16_UNORM : VK_FORMAT_R32_SFLOAT;
|
||||
const auto format = ResolveTextureFormat(resource.format);
|
||||
if (format != expected || resource.dimension != TextureDimension::k2D || resource.width != target.extent.width || resource.height != target.extent.height || resource.baseLevel != 0 || resource.lastLevel != 0 || resource.baseArray != 0) {
|
||||
char text[448];
|
||||
std::snprintf(text, sizeof(text), "AGC graphics: sampling the %s plane of depth surface 0x%llx (%ux%u, vk format %d) as a %ux%u texture of guest format %u (vk %d), tile mode %u, dimension %d, levels %u-%u, slice %u is not implemented (T# %08x %08x %08x %08x %08x %08x %08x %08x)",
|
||||
stencil ? "stencil" : "depth", static_cast<unsigned long long>(target.address), target.extent.width, target.extent.height, static_cast<int>(target.format), resource.width, resource.height, resource.format, static_cast<int>(format),
|
||||
static_cast<unsigned>(resource.tileMode), static_cast<int>(resource.dimension), resource.baseLevel, resource.lastLevel, resource.baseArray, key[0], key[1], key[2], key[3], key[4], key[5], key[6], key[7]);
|
||||
throw std::runtime_error(text);
|
||||
}
|
||||
auto texture = std::make_shared<Texture>(context, image, target.format, stencil ? VK_IMAGE_ASPECT_STENCIL_BIT : VK_IMAGE_ASPECT_DEPTH_BIT, components);
|
||||
textures.emplace(key, texture);
|
||||
return texture;
|
||||
}
|
||||
|
||||
const Context context;
|
||||
const DepthTarget target;
|
||||
VkImage image = VK_NULL_HANDLE;
|
||||
|
|
@ -81,7 +113,10 @@ public:
|
|||
VkDeviceMemory memory = VK_NULL_HANDLE;
|
||||
|
||||
private:
|
||||
std::map<std::array<std::uint32_t, 12>, std::shared_ptr<Texture>> textures;
|
||||
|
||||
void release() noexcept {
|
||||
textures.clear();
|
||||
if (view) context.Function<PFN_vkDestroyImageView>("vkDestroyImageView")(context.device, view, nullptr);
|
||||
if (image) context.Function<PFN_vkDestroyImage>("vkDestroyImage")(context.device, image, nullptr);
|
||||
if (memory) context.Function<PFN_vkFreeMemory>("vkFreeMemory")(context.device, memory, nullptr);
|
||||
|
|
@ -122,6 +157,16 @@ void ClearDepthSurfaces(VkDevice device) {
|
|||
std::erase_if(surfaces(), [&](const auto& surface) { return surface->context.device == device; });
|
||||
}
|
||||
|
||||
std::shared_ptr<Texture> DepthSurfaceTexture(const Context& context, std::span<const std::uint32_t> words, const GuestTextureResource& resource, VkComponentMapping components) {
|
||||
std::lock_guard lock(surfacesMutex());
|
||||
const auto& list = surfaces();
|
||||
// The newest surface made at the address.
|
||||
const auto found = std::find_if(list.rbegin(), list.rend(), [&](const auto& surface) {
|
||||
return surface->context.device == context.device && (surface->target.address == resource.baseAddress || (surface->target.stencilAddress != 0 && surface->target.stencilAddress == resource.baseAddress));
|
||||
});
|
||||
return found == list.rend() ? nullptr : (*found)->Sampled(words, resource, components);
|
||||
}
|
||||
|
||||
bool DepthSurfaceAt(std::uint64_t address) {
|
||||
std::lock_guard lock(surfacesMutex());
|
||||
return std::any_of(surfaces().begin(), surfaces().end(), [&](const auto& surface) { return surface->target.address == address || surface->target.stencilAddress == address; });
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
#include <cstdio>
|
||||
#include "prx/libSceAgcDriver/Graphics/include/GuestTextureResource.hpp"
|
||||
#include "prx/libSceAgcDriver/Graphics/include/Context.hpp"
|
||||
#include "prx/libSceAgcDriver/Graphics/include/DepthSurface.hpp"
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
|
@ -47,12 +46,6 @@ GuestTextureResource DecodeTextureResource(std::span<const std::uint32_t> words)
|
|||
const auto base40 = (static_cast<std::uint64_t>(words[0]) | (static_cast<std::uint64_t>(words[1]) << 32u)) & 0xffffffffffull;
|
||||
const auto baseAddress = base40 << 8u;
|
||||
Require(baseAddress != 0, "guest texture descriptor has a null base address");
|
||||
// A depth surface's contents live on the GPU only; its guest memory is stale.
|
||||
if (DepthSurfaceAt(baseAddress)) {
|
||||
char text[96];
|
||||
std::snprintf(text, sizeof(text), "AGC graphics: sampling depth/stencil surface 0x%llx is not implemented", static_cast<unsigned long long>(baseAddress));
|
||||
throw std::runtime_error(text);
|
||||
}
|
||||
|
||||
const auto minLod = (words[1] >> 8u) & 0xfffu;
|
||||
const auto format = (words[1] >> 20u) & 0x1ffu;
|
||||
|
|
|
|||
|
|
@ -1304,7 +1304,7 @@ void Recorder::endOpenRenderPass() {
|
|||
context.Resolved(&DeviceFunctions::cmdEndRenderPass, "vkCmdEndRenderPass")(open->commands);
|
||||
// The pass's attachment and shader writes are visible to everything recorded after it (the
|
||||
// host sees them at the batch's fence).
|
||||
recordBarrier(open->commands, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT);
|
||||
recordBarrier(open->commands, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT);
|
||||
CountBarriers(CommandClass::Draw);
|
||||
EndGpuTiming(pass.timing);
|
||||
open->coveredAccess = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#include "prx/libSceAgcDriver/Execution/include/CaptureTrace.hpp"
|
||||
#include "prx/libSceAgcDriver/Graphics/include/DepthSurface.hpp"
|
||||
#include "prx/libSceAgcDriver/Graphics/include/ShaderResources.hpp"
|
||||
#include "prx/libSceAgcDriver/Graphics/include/Recorder.hpp"
|
||||
#include <algorithm>
|
||||
|
|
@ -262,6 +263,8 @@ std::shared_ptr<StorageTexture> sampledStorageSource(const Context& context, con
|
|||
// `guestBytes` is the surface size when the caller described the surface already (0: described here).
|
||||
std::shared_ptr<Texture> cachedTexture(const Context& context, std::span<const std::uint32_t> words, const GuestTextureResource& resource, VkComponentMapping components, std::uint64_t guestBytes = 0) {
|
||||
CaptureTrace::Log("sampled-lookup address=%llx width=%u height=%u dcc=%llx", static_cast<unsigned long long>(resource.baseAddress), resource.width, resource.height, static_cast<unsigned long long>(resource.dccAddress));
|
||||
// A depth surface's contents live in its GPU image; its guest memory is stale.
|
||||
if (auto depth = DepthSurfaceTexture(context, words, resource, components)) return depth;
|
||||
static const bool disabled = std::getenv("APS5_NO_TEXTURE_CACHE") != nullptr;
|
||||
const bool profile = LookupOutcomes::Profiled();
|
||||
const auto start = profile ? std::chrono::steady_clock::now() : std::chrono::steady_clock::time_point{};
|
||||
|
|
@ -474,6 +477,11 @@ std::array<std::uint32_t, 8> SurfaceKey(const Context& context, const GuestTextu
|
|||
|
||||
// `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& resource, std::uint32_t mip, std::uint64_t guestBytes) {
|
||||
if (DepthSurfaceAt(resource.baseAddress)) {
|
||||
char text[112];
|
||||
std::snprintf(text, sizeof(text), "AGC graphics: storage image access to depth/stencil surface 0x%llx is not implemented", static_cast<unsigned long long>(resource.baseAddress));
|
||||
throw std::runtime_error(text);
|
||||
}
|
||||
static const bool disabled = std::getenv("APS5_NO_TEXTURE_CACHE") != nullptr;
|
||||
if (disabled) return std::make_shared<StorageTexture>(context, *context.detiler, resource, mip);
|
||||
static_cast<void>(words);
|
||||
|
|
|
|||
|
|
@ -457,6 +457,17 @@ Texture::Texture(const Context& context, const std::shared_ptr<StorageTexture>&
|
|||
}
|
||||
}
|
||||
|
||||
Texture::Texture(const Context& context, VkImage depthImage, VkFormat depthFormat, VkImageAspectFlags aspect, VkComponentMapping components) : context(context) {
|
||||
layout = VK_IMAGE_LAYOUT_GENERAL;
|
||||
VkImageViewCreateInfo viewInfo{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO};
|
||||
viewInfo.image = depthImage;
|
||||
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
viewInfo.format = depthFormat;
|
||||
viewInfo.components = components;
|
||||
viewInfo.subresourceRange = {aspect, 0, 1, 0, 1};
|
||||
Check(context.Function<PFN_vkCreateImageView>("vkCreateImageView")(context.device, &viewInfo, nullptr, &view), "vkCreateImageView depth plane");
|
||||
}
|
||||
|
||||
Texture::~Texture() {
|
||||
release();
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue