Skip to content
Closed
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
81 changes: 80 additions & 1 deletion core/libs/prx/libSceAgcDriver/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -559,7 +559,86 @@ set_target_properties(agc_driver_mesh_tests PROPERTIES CXX_EXTENSIONS OFF)
configure_windows_unwind(agc_driver_mesh_tests)

file(GLOB agcExecutionTestSources CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tests/execution/*.cpp)
set(agcExecutionTests)
set(agcDepthStencilShaderSource ${CMAKE_CURRENT_SOURCE_DIR}/tests/shaders/DepthStencilStorage.comp)
set(agcDepthStencilShaderSpv ${agcTestShaderGeneratedRoot}/DepthStencilStorage.spv)
set(agcDepthStencilShaderHeader ${agcTestShaderGeneratedRoot}/DepthStencilStorage_spv.h)
add_custom_command(
OUTPUT "${agcDepthStencilShaderHeader}"
COMMAND ${CMAKE_COMMAND} -E make_directory "${agcTestShaderGeneratedRoot}"
COMMAND "$<TARGET_FILE:glslang-standalone>" -V --target-env vulkan1.1 -o "${agcDepthStencilShaderSpv}" "${agcDepthStencilShaderSource}"
COMMAND ${CMAKE_COMMAND} -DINPUT=${agcDepthStencilShaderSpv} -DOUTPUT=${agcDepthStencilShaderHeader}
-DSYMBOL=DEPTH_STENCIL_STORAGE_SPV -P "${CMAKE_CURRENT_SOURCE_DIR}/embed_spirv.cmake"
DEPENDS "${agcDepthStencilShaderSource}" glslang-standalone
VERBATIM
)
set(agcDepthStencilFormatlessSpv ${agcTestShaderGeneratedRoot}/DepthStencilStorageFormatless.spv)
set(agcDepthStencilFormatlessHeader ${agcTestShaderGeneratedRoot}/DepthStencilStorageFormatless_spv.h)
add_custom_command(
OUTPUT "${agcDepthStencilFormatlessHeader}"
COMMAND ${CMAKE_COMMAND} -E make_directory "${agcTestShaderGeneratedRoot}"
COMMAND "$<TARGET_FILE:glslang-standalone>" -V --target-env vulkan1.1 -DANYPS5_FORMATLESS=1 -o "${agcDepthStencilFormatlessSpv}" "${agcDepthStencilShaderSource}"
COMMAND ${CMAKE_COMMAND} -DINPUT=${agcDepthStencilFormatlessSpv} -DOUTPUT=${agcDepthStencilFormatlessHeader}
-DSYMBOL=DEPTH_STENCIL_STORAGE_FORMATLESS_SPV -P "${CMAKE_CURRENT_SOURCE_DIR}/embed_spirv.cmake"
DEPENDS "${agcDepthStencilShaderSource}" glslang-standalone
VERBATIM
)
set(agcDepthStencilArraySpv ${agcTestShaderGeneratedRoot}/DepthStencilStorageArray.spv)
set(agcDepthStencilArrayHeader ${agcTestShaderGeneratedRoot}/DepthStencilStorageArray_spv.h)
add_custom_command(
OUTPUT "${agcDepthStencilArrayHeader}"
COMMAND ${CMAKE_COMMAND} -E make_directory "${agcTestShaderGeneratedRoot}"
COMMAND "$<TARGET_FILE:glslang-standalone>" -V --target-env vulkan1.1 -DANYPS5_ARRAY=1 -o "${agcDepthStencilArraySpv}" "${agcDepthStencilShaderSource}"
COMMAND ${CMAKE_COMMAND} -DINPUT=${agcDepthStencilArraySpv} -DOUTPUT=${agcDepthStencilArrayHeader}
-DSYMBOL=DEPTH_STENCIL_STORAGE_ARRAY_SPV -P "${CMAKE_CURRENT_SOURCE_DIR}/embed_spirv.cmake"
DEPENDS "${agcDepthStencilShaderSource}" glslang-standalone
VERBATIM
)
set(agcDepthStencilArrayFormatlessSpv ${agcTestShaderGeneratedRoot}/DepthStencilStorageArrayFormatless.spv)
set(agcDepthStencilArrayFormatlessHeader ${agcTestShaderGeneratedRoot}/DepthStencilStorageArrayFormatless_spv.h)
add_custom_command(
OUTPUT "${agcDepthStencilArrayFormatlessHeader}"
COMMAND ${CMAKE_COMMAND} -E make_directory "${agcTestShaderGeneratedRoot}"
COMMAND "$<TARGET_FILE:glslang-standalone>" -V --target-env vulkan1.1 -DANYPS5_ARRAY=1 -DANYPS5_FORMATLESS=1 -o "${agcDepthStencilArrayFormatlessSpv}" "${agcDepthStencilShaderSource}"
COMMAND ${CMAKE_COMMAND} -DINPUT=${agcDepthStencilArrayFormatlessSpv} -DOUTPUT=${agcDepthStencilArrayFormatlessHeader}
-DSYMBOL=DEPTH_STENCIL_STORAGE_ARRAY_FORMATLESS_SPV -P "${CMAKE_CURRENT_SOURCE_DIR}/embed_spirv.cmake"
DEPENDS "${agcDepthStencilShaderSource}" glslang-standalone
VERBATIM
)
set(agcStencilAttachmentHeaders)
foreach(stage IN ITEMS vert frag)
set(shaderSource ${CMAKE_CURRENT_SOURCE_DIR}/tests/shaders/StencilAttachment.${stage})
set(shaderSpv ${agcTestShaderGeneratedRoot}/StencilAttachment_${stage}.spv)
set(shaderHeader ${agcTestShaderGeneratedRoot}/StencilAttachment_${stage}_spv.h)
add_custom_command(
OUTPUT "${shaderHeader}"
COMMAND ${CMAKE_COMMAND} -E make_directory "${agcTestShaderGeneratedRoot}"
COMMAND "$<TARGET_FILE:glslang-standalone>" -V --target-env vulkan1.1 -o "${shaderSpv}" "${shaderSource}"
COMMAND ${CMAKE_COMMAND} -DINPUT=${shaderSpv} -DOUTPUT=${shaderHeader}
-DSYMBOL=STENCIL_ATTACHMENT_${stage}_SPV -P "${CMAKE_CURRENT_SOURCE_DIR}/embed_spirv.cmake"
DEPENDS "${shaderSource}" glslang-standalone
VERBATIM
)
list(APPEND agcStencilAttachmentHeaders ${shaderHeader})
endforeach()
add_test_executable(agc_driver_depth_stencil_storage_tests EXCLUDE_FROM_ALL
tests/DepthStencilStorage.cpp
${agcDepthStencilShaderHeader}
${agcDepthStencilFormatlessHeader}
${agcDepthStencilArrayHeader}
${agcDepthStencilArrayFormatlessHeader}
${agcStencilAttachmentHeaders}
)
target_include_directories(agc_driver_depth_stencil_storage_tests PRIVATE
${LIBS_INCLUDE_DIR}
${agcTestShaderGeneratedRoot}
${CMAKE_SOURCE_DIR}/core/shader/recompiler
${CMAKE_SOURCE_DIR}/3rdparty/Vulkan-Headers/include
)
target_link_libraries(agc_driver_depth_stencil_storage_tests PRIVATE libSceAgcDriver SDL2-static libc)
set_target_properties(agc_driver_depth_stencil_storage_tests PROPERTIES CXX_EXTENSIONS OFF)
configure_windows_unwind(agc_driver_depth_stencil_storage_tests)

set(agcExecutionTests agc_driver_depth_stencil_storage_tests)
foreach(agcExecutionTestSource IN LISTS agcExecutionTestSources)
get_filename_component(agcExecutionTestName ${agcExecutionTestSource} NAME_WE)
string(REGEX REPLACE "([a-z0-9])([A-Z])" "\\1_\\2" agcExecutionTestName "${agcExecutionTestName}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@
namespace AgcDriver::Graphics {

class Texture;
class StorageTexture;

VkImageView DepthSurfaceView(const Context& context, const DepthTarget& target);
std::uint64_t DepthSliceBytes(VkExtent2D extent, std::uint32_t bytesPerTexel);
void ClearDepthSurfaces(VkDevice device);
bool DepthSurfaceAt(std::uint64_t address);
std::shared_ptr<Texture> DepthSurfaceTexture(const Context& context, std::span<const std::uint32_t> words, const GuestTextureResource& resource, VkComponentMapping components);
std::shared_ptr<StorageTexture> DepthSurfaceStorage(const Context& context, const GuestTextureResource& resource, std::uint32_t mip);

}

Expand Down
1 change: 1 addition & 0 deletions core/libs/prx/libSceAgcDriver/Graphics/include/State.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ struct DepthTarget {
VkFormat format;
float clearDepth;
std::uint8_t clearStencil;
bool stencilUncompressed = false;
};

struct State {
Expand Down
6 changes: 5 additions & 1 deletion core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <atomic>
#include <chrono>
#include <cstddef>
#include <functional>
#include <map>
#include <memory>
#include <span>
Expand Down Expand Up @@ -58,7 +59,7 @@ class Texture {
// 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);
Texture(const Context& context, VkImage depthImage, VkFormat depthFormat, VkImageAspectFlags aspect, VkComponentMapping components);
Texture(const Context& context, VkImage depthImage, VkFormat depthFormat, VkImageAspectFlags aspect, VkComponentMapping components, std::shared_ptr<void> depthOwner = {});
static bool CanCopyFrom(const StorageTexture& source, const GuestTextureResource& descriptor);
~Texture();
Texture(const Texture&) = delete;
Expand Down Expand Up @@ -104,6 +105,7 @@ class Texture {
VkFormat viewFormat = VK_FORMAT_UNDEFINED;
std::shared_ptr<ResidentColor> source;
std::shared_ptr<StorageTexture> storageSource;
std::shared_ptr<void> depthOwner;
std::unique_ptr<CommandBatch> upload;
};

Expand All @@ -116,6 +118,7 @@ VkFormat AttachmentProxyFormat(const Context& context, VkFormat format);
// or it leaves the cache. Dispatches reusing the image meanwhile skip the round trip entirely.
class StorageTexture : public std::enable_shared_from_this<StorageTexture> {
public:
StorageTexture(const Context& context, const GuestTextureResource& descriptor, std::function<bool(bool, VkImage)> residentTransfer);
StorageTexture(const Context& context, TextureDetiler& detiler, const GuestTextureResource& descriptor, std::uint32_t mipLevel);
~StorageTexture();
StorageTexture(const StorageTexture&) = delete;
Expand Down Expand Up @@ -472,6 +475,7 @@ class StorageTexture : public std::enable_shared_from_this<StorageTexture> {
// Results are on the GPU only (guarded by the pending-write registry lock).
bool dirty = false;
std::uint64_t version = 0;
std::function<bool(bool, VkImage)> residentTransfer;
// `original` holds the guest bytes; false after a GPU-side clear, which never read them.
bool originalValid = true;
// The storage cache let the image go (Flush): a lookup makes a new image of the surface, so
Expand Down
93 changes: 83 additions & 10 deletions core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
namespace AgcDriver::Graphics {
namespace {

class DepthSurface {
class DepthSurface : public std::enable_shared_from_this<DepthSurface> {
public:
DepthSurface(const Context& context, const DepthTarget& target) : context(context), target(target) {
this->context.bufferPool.reset();
Expand All @@ -35,7 +35,7 @@ class DepthSurface {
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_SAMPLED_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 | VK_IMAGE_USAGE_TRANSFER_SRC_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");
Expand Down Expand Up @@ -87,7 +87,9 @@ class DepthSurface {
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;
if (const auto found = textures.find(key); found != textures.end()) {
if (auto texture = found->second.lock()) return texture;
}
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;
Expand All @@ -100,19 +102,73 @@ class DepthSurface {
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);
auto texture = std::make_shared<Texture>(context, image, target.format, stencil ? VK_IMAGE_ASPECT_STENCIL_BIT : VK_IMAGE_ASPECT_DEPTH_BIT, components, shared_from_this());
textures.insert_or_assign(key, texture);
return texture;
}

std::shared_ptr<StorageTexture> Storage(const GuestTextureResource& resource, std::uint32_t mip) {
if (resource.baseAddress != target.stencilAddress || target.stencilAddress == 0) {
char text[512];
std::snprintf(text, sizeof(text), "AGC graphics: storage image access to the depth plane is not implemented (target format %d, %ux%u; descriptor format %u, %ux%u, dimension %d, levels %u-%u of %u, mip %u, slices %u-%u, tile %d, DCC 0x%llx)",
static_cast<int>(target.format), target.extent.width, target.extent.height, resource.format, resource.width, resource.height, static_cast<int>(resource.dimension), resource.baseLevel, resource.lastLevel, resource.mipCount, mip, resource.baseArray, resource.depthOrLastArray, static_cast<int>(resource.tileMode), static_cast<unsigned long long>(resource.dccAddress));
throw std::runtime_error(text);
}
Require(stencilUncompressed, "storage image access to HTILE stencil is not implemented (DB_STENCIL_INFO TILE_STENCIL_DISABLE must be set)");
if (!(target.format == VK_FORMAT_D32_SFLOAT_S8_UINT && ResolveTextureFormat(resource.format) == VK_FORMAT_R8_UINT && resource.width == target.extent.width && resource.height == target.extent.height && (resource.dimension == TextureDimension::k2D || resource.dimension == TextureDimension::k2DArray) && resource.depthOrLastArray == 0 && resource.baseArray == 0 && resource.baseLevel == 0 && resource.lastLevel == 0 && resource.mipCount == 1 && mip == 0 && resource.dccAddress == 0 && resource.tileMode == TextureTileMode::kZ64KBX)) {
char text[512];
std::snprintf(text, sizeof(text), "AGC graphics: stencil storage requires a matching D32S8 single-level single-layer 2D or 2D-array R8_UINT SW_64KB_Z_X descriptor without metadata (target format %d, %ux%u; descriptor format %u, %ux%u, dimension %d, levels %u-%u of %u, mip %u, slices %u-%u, tile %d, DCC 0x%llx)",
static_cast<int>(target.format), target.extent.width, target.extent.height, resource.format, resource.width, resource.height, static_cast<int>(resource.dimension), resource.baseLevel, resource.lastLevel, resource.mipCount, mip, resource.baseArray, resource.depthOrLastArray, static_cast<int>(resource.tileMode), static_cast<unsigned long long>(resource.dccAddress));
throw std::runtime_error(text);
}
auto texture = storage[resource.dimension].lock();
if (texture == nullptr) {
if (transfer == nullptr) transfer = std::make_unique<DeviceBuffer>(context, static_cast<std::size_t>(target.extent.width) * target.extent.height, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT);
texture = std::make_shared<StorageTexture>(context, resource, [owner = shared_from_this(), seen = ~0ull](bool store, VkImage storageImage) mutable {
if (!store && seen == owner->revision) return false;
owner->Transfer(store, storageImage);
if (store) ++owner->revision;
seen = owner->revision;
return true;
});
storage[resource.dimension] = texture;
}
texture->Refresh();
return texture;
}

void Transfer(bool store, VkImage storageImage) {
auto* recorder = Recorder::Active();
std::unique_ptr<CommandBatch> batch;
if (recorder == nullptr) batch = std::make_unique<CommandBatch>(context);
const auto commands = recorder != nullptr ? recorder->Commands() : batch->Handle();
if (recorder != nullptr) recorder->Keep(shared_from_this());
RecordMemoryBarrier(context, commands, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_MEMORY_WRITE_BIT | VK_ACCESS_MEMORY_READ_BIT, VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT);
VkBufferImageCopy stencilCopy{};
stencilCopy.imageSubresource = {VK_IMAGE_ASPECT_STENCIL_BIT, 0, 0, 1};
stencilCopy.imageExtent = {target.extent.width, target.extent.height, 1};
auto storageCopy = stencilCopy;
storageCopy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
context.Resolved(&DeviceFunctions::cmdCopyImageToBuffer, "vkCmdCopyImageToBuffer")(commands, store ? storageImage : image, VK_IMAGE_LAYOUT_GENERAL, transfer->Handle(), 1, store ? &storageCopy : &stencilCopy);
RecordMemoryBarrier(context, commands, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT);
context.Resolved(&DeviceFunctions::cmdCopyBufferToImage, "vkCmdCopyBufferToImage")(commands, transfer->Handle(), store ? image : storageImage, VK_IMAGE_LAYOUT_GENERAL, 1, store ? &stencilCopy : &storageCopy);
RecordMemoryBarrier(context, commands, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT);
if (batch) batch->SubmitAndWait();
else Recorder::CountBarriers(store ? Recorder::CommandClass::StorageWriteBack : Recorder::CommandClass::StorageUpload, 3);
}

const Context context;
const DepthTarget target;
VkImage image = VK_NULL_HANDLE;
VkImageView view = VK_NULL_HANDLE;
VkDeviceMemory memory = VK_NULL_HANDLE;
bool stencilUncompressed = false;
std::uint64_t revision = 0;

private:
std::map<std::array<std::uint32_t, 12>, std::shared_ptr<Texture>> textures;
std::map<std::array<std::uint32_t, 12>, std::weak_ptr<Texture>> textures;
std::map<TextureDimension, std::weak_ptr<StorageTexture>> storage;
std::unique_ptr<DeviceBuffer> transfer;

void release() noexcept {
textures.clear();
Expand All @@ -134,8 +190,8 @@ std::mutex& surfacesMutex() {
return mutex;
}

std::vector<std::unique_ptr<DepthSurface>>& surfaces() {
static auto* list = new std::vector<std::unique_ptr<DepthSurface>>();
std::vector<std::shared_ptr<DepthSurface>>& surfaces() {
static auto* list = new std::vector<std::shared_ptr<DepthSurface>>();
return *list;
}

Expand All @@ -152,9 +208,17 @@ std::uint64_t DepthSliceBytes(VkExtent2D extent, std::uint32_t bytesPerTexel) {
VkImageView DepthSurfaceView(const Context& context, const DepthTarget& target) {
std::lock_guard lock(surfacesMutex());
for (const auto& surface : surfaces()) {
if (surface->context.device == context.device && sameSurface(surface->target, target)) return surface->view;
if (surface->context.device == context.device && sameSurface(surface->target, target)) {
surface->stencilUncompressed = target.stencilUncompressed;
++surface->revision;
if (auto* recorder = Recorder::Active()) recorder->Keep(surface);
return surface->view;
}
}
surfaces().push_back(std::make_unique<DepthSurface>(context, target));
auto surface = std::make_shared<DepthSurface>(context, target);
surface->stencilUncompressed = target.stencilUncompressed;
surfaces().push_back(surface);
if (auto* recorder = Recorder::Active()) recorder->Keep(surface);
return surfaces().back()->view;
}

Expand All @@ -177,4 +241,13 @@ bool DepthSurfaceAt(std::uint64_t address) {
return std::any_of(surfaces().begin(), surfaces().end(), [&](const auto& surface) { return surface->target.address == address || surface->target.stencilAddress == address; });
}

std::shared_ptr<StorageTexture> DepthSurfaceStorage(const Context& context, const GuestTextureResource& resource, std::uint32_t mip) {
std::lock_guard lock(surfacesMutex());
const auto& list = surfaces();
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)->Storage(resource, mip);
}

}
Loading
Loading