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
34 changes: 27 additions & 7 deletions core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ class DepthSurface {

class DepthPlaneCopy {
public:
DepthPlaneCopy(const Context& context, VkExtent2D extent, VkFormat format, std::uint32_t layers) : context(context), extent(extent), format(format), layers(layers) {
DepthPlaneCopy(const Context& context, VkExtent2D extent, VkFormat format, std::uint32_t layers) : context(context), extent(extent), format(format), layers(layers), guestLayers(layers) {
this->context.bufferPool.reset();
VkImageCreateInfo info{VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO};
info.flags = layers % 6u == 0 && extent.width == extent.height ? VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT : 0u;
Expand Down Expand Up @@ -208,11 +208,23 @@ class DepthPlaneCopy {
const auto geometry = DescribeSurface(resource);
Require(geometry.layers == layers && geometry.sliceLinearBytes == sliceBytes() && !geometry.mips.empty(), "depth plane copy geometry does not match its layers");
std::vector<std::shared_ptr<Buffer>> uploads;
std::vector<std::uint32_t> uploadLayers;
for (std::uint32_t layer = 0; layer < layers; ++layer) {
if (slices[layer] != VK_NULL_HANDLE) continue;
auto& held = guestLayers[layer];
if (slices[layer] != VK_NULL_HANDLE) {
held = {};
continue;
}
const GuestLayer current{resource.baseAddress + geometry.GuestLayerOffset(layer), geometry.layerBytes, resource.tileMode, geometry.thick, GuestMemory::CollectWrites(resource.baseAddress + geometry.GuestLayerOffset(layer), static_cast<std::size_t>(geometry.layerBytes))};
if (held.generation != 0 && held.address == current.address && held.bytes == current.bytes && held.tileMode == current.tileMode && held.thick == current.thick && !Recorder::SnapshotWriteOverlaps(current.address, static_cast<std::size_t>(current.bytes)) && GuestMemory::UnchangedSince(current.address, static_cast<std::size_t>(current.bytes), held.generation)) {
held.generation = current.generation;
continue;
}
auto upload = std::make_shared<Buffer>(context, static_cast<std::size_t>(geometry.layerBytes), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
GuestMemory::ReadCommitted(resource.baseAddress + geometry.GuestLayerOffset(layer), upload->Bytes().first(static_cast<std::size_t>(geometry.layerBytes)));
GuestMemory::ReadCommitted(current.address, upload->Bytes().first(static_cast<std::size_t>(geometry.layerBytes)));
uploads.push_back(std::move(upload));
uploadLayers.push_back(layer);
held = current;
}
if (!uploads.empty()) {
Require(context.detiler != nullptr, "depth plane copy requires a texture detiler");
Expand All @@ -235,10 +247,9 @@ class DepthPlaneCopy {
}
if (!uploads.empty()) {
RecordMemoryBarrier(context, commands, VK_PIPELINE_STAGE_HOST_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_HOST_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT);
std::size_t next = 0;
for (std::uint32_t layer = 0; layer < layers; ++layer) {
if (slices[layer] != VK_NULL_HANDLE) continue;
context.detiler->Dispatch(commands, resource.tileMode, format == VK_FORMAT_D32_SFLOAT ? 4u : 2u, uploads[next++]->Handle(), 0, staging->Handle(), geometry.LinearLayerOffset(layer), geometry.mips.front(), false, layer, geometry.thick);
for (std::size_t upload = 0; upload < uploads.size(); ++upload) {
const auto layer = uploadLayers[upload];
context.detiler->Dispatch(commands, resource.tileMode, format == VK_FORMAT_D32_SFLOAT ? 4u : 2u, uploads[upload]->Handle(), 0, staging->Handle(), geometry.LinearLayerOffset(layer), geometry.mips.front(), false, layer, geometry.thick);
}
if (recorder != nullptr) {
for (auto& upload : uploads) recorder->Keep(upload);
Expand Down Expand Up @@ -280,10 +291,19 @@ class DepthPlaneCopy {
const std::uint32_t layers;

private:
struct GuestLayer {
std::uint64_t address = 0;
std::uint64_t bytes = 0;
TextureTileMode tileMode = TextureTileMode::kLinear;
bool thick = false;
std::uint64_t generation = 0;
};

VkImage image = VK_NULL_HANDLE;
VkDeviceMemory memory = VK_NULL_HANDLE;
std::unique_ptr<DeviceBuffer> staging;
std::map<std::array<std::uint32_t, 5>, std::shared_ptr<Texture>> textures;
std::vector<GuestLayer> guestLayers;

VkDeviceSize sliceBytes() const {
return static_cast<VkDeviceSize>(extent.width) * extent.height * (format == VK_FORMAT_D32_SFLOAT ? 4u : 2u);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
#include "prx/libSceAgcDriver/Execution/include/BdaFeatures.hpp"
#include "prx/libSceAgcDriver/Execution/include/GuestMemory.hpp"
#include "prx/libSceAgcDriver/Graphics/include/DepthSurface.hpp"
#include "prx/libSceAgcDriver/Graphics/include/Texture.hpp"
#include "prx/libSceAgcDriver/Graphics/include/TextureDetiler.hpp"
#include "prx/libc/include/GuestAllocations.hpp"
#include "prx/libc/include/GuestArena.hpp"
#include "prx/libc/include/GuestWriteWatch.hpp"
#include <SDL_loadso.h>
#ifdef _WIN32
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#else
#include <sys/mman.h>
#endif
#include <algorithm>
#include <array>
#include <atomic>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <memory>
#include <optional>
#include <stdexcept>
#include <string>
#include <vector>

namespace {

using namespace AgcDriver::Graphics;

constexpr std::uint32_t Side = 256;
constexpr std::uint32_t Format32Float = 22;
constexpr std::size_t LayerBytes = std::size_t{Side} * Side * 4u;
constexpr std::size_t BlockBytes = 2u * LayerBytes;
constexpr int Skipped = 77;

class Device {
public:
Device() {
#ifdef _WIN32
library = SDL_LoadObject("vulkan-1.dll");
#else
library = SDL_LoadObject("libvulkan.so.1");
#endif
Require(library != nullptr, "cannot load Vulkan");
try {
instanceProc = reinterpret_cast<PFN_vkGetInstanceProcAddr>(SDL_LoadFunction(library, "vkGetInstanceProcAddr"));
Require(instanceProc != nullptr, "missing Vulkan instance resolver");
VkApplicationInfo application{VK_STRUCTURE_TYPE_APPLICATION_INFO};
application.apiVersion = VK_API_VERSION_1_1;
VkInstanceCreateInfo info{VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO};
info.pApplicationInfo = &application;
Check(function<PFN_vkCreateInstance>("vkCreateInstance")(&info, nullptr, &instance), "vkCreateInstance");
std::uint32_t count = 0;
const auto enumerate = function<PFN_vkEnumeratePhysicalDevices>("vkEnumeratePhysicalDevices");
Check(enumerate(instance, &count, nullptr), "vkEnumeratePhysicalDevices");
Require(count != 0, "no Vulkan device");
std::vector<VkPhysicalDevice> devices(count);
Check(enumerate(instance, &count, devices.data()), "vkEnumeratePhysicalDevices");
context.physical = devices.front();
const auto extensions = function<PFN_vkEnumerateDeviceExtensionProperties>("vkEnumerateDeviceExtensionProperties");
Check(extensions(context.physical, nullptr, &count, nullptr), "vkEnumerateDeviceExtensionProperties");
std::vector<VkExtensionProperties> available(count);
Check(extensions(context.physical, nullptr, &count, available.data()), "vkEnumerateDeviceExtensionProperties");
auto bytes = AgcDriver::QueryBdaByteFeatures(context.physical, function<PFN_vkGetPhysicalDeviceFeatures2>("vkGetPhysicalDeviceFeatures2"), available);
auto address = AgcDriver::QueryBdaFeatures(context.physical, function<PFN_vkGetPhysicalDeviceFeatures2>("vkGetPhysicalDeviceFeatures2"), available);
const auto queues = function<PFN_vkGetPhysicalDeviceQueueFamilyProperties>("vkGetPhysicalDeviceQueueFamilyProperties");
queues(context.physical, &count, nullptr);
std::vector<VkQueueFamilyProperties> families(count);
queues(context.physical, &count, families.data());
constexpr VkQueueFlags needed = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT;
std::uint32_t family = 0;
while (family < count && (families[family].queueFlags & needed) != needed) ++family;
Require(family < count, "no Vulkan graphics and compute queue");
const float priority = 1;
VkDeviceQueueCreateInfo queue{VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO};
queue.queueFamilyIndex = family;
queue.queueCount = 1;
queue.pQueuePriorities = &priority;
VkPhysicalDeviceFeatures enabled{};
enabled.shaderInt64 = VK_TRUE;
address.pNext = &bytes;
const std::array<const char*, 2> extensionsEnabled{VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME, VK_KHR_8BIT_STORAGE_EXTENSION_NAME};
VkDeviceCreateInfo device{VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, &address};
device.queueCreateInfoCount = 1;
device.pQueueCreateInfos = &queue;
device.enabledExtensionCount = static_cast<std::uint32_t>(extensionsEnabled.size());
device.ppEnabledExtensionNames = extensionsEnabled.data();
device.pEnabledFeatures = &enabled;
Check(function<PFN_vkCreateDevice>("vkCreateDevice")(context.physical, &device, nullptr, &context.device), "vkCreateDevice");
context.deviceProc = function<PFN_vkGetDeviceProcAddr>("vkGetDeviceProcAddr");
function<PFN_vkGetPhysicalDeviceMemoryProperties>("vkGetPhysicalDeviceMemoryProperties")(context.physical, &context.memory);
VkPhysicalDeviceProperties properties{};
function<PFN_vkGetPhysicalDeviceProperties>("vkGetPhysicalDeviceProperties")(context.physical, &properties);
context.limits = properties.limits;
context.bufferDeviceAddress = true;
context.formatProperties = function<PFN_vkGetPhysicalDeviceFormatProperties>("vkGetPhysicalDeviceFormatProperties");
context.Function<PFN_vkGetDeviceQueue>("vkGetDeviceQueue")(context.device, family, 0, &context.queue);
VkCommandPoolCreateInfo pool{VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO};
pool.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
pool.queueFamilyIndex = family;
Check(context.Function<PFN_vkCreateCommandPool>("vkCreateCommandPool")(context.device, &pool, nullptr, &context.pool), "vkCreateCommandPool");
} catch (...) {
release();
throw;
}
}

~Device() { release(); }
Device(const Device&) = delete;
Device& operator=(const Device&) = delete;
const Context& GetContext() const { return context; }

private:
template<typename TFunction>
TFunction function(const char* name) const {
const auto result = reinterpret_cast<TFunction>(instanceProc(instance, name));
Require(result != nullptr, name);
return result;
}

void release() noexcept {
if (context.device != VK_NULL_HANDLE) ClearDepthSurfaces(context.device);
if (context.pool != VK_NULL_HANDLE) context.Function<PFN_vkDestroyCommandPool>("vkDestroyCommandPool")(context.device, context.pool, nullptr);
context.bufferPool.reset();
if (context.device != VK_NULL_HANDLE) function<PFN_vkDestroyDevice>("vkDestroyDevice")(context.device, nullptr);
if (instance != VK_NULL_HANDLE) function<PFN_vkDestroyInstance>("vkDestroyInstance")(instance, nullptr);
if (library != nullptr) SDL_UnloadObject(library);
}

void* library = nullptr;
PFN_vkGetInstanceProcAddr instanceProc = nullptr;
VkInstance instance = VK_NULL_HANDLE;
Context context{};
};

class WatchedBlock {
public:
WatchedBlock() {
#ifdef _WIN32
block = static_cast<std::uint8_t*>(GuestArena::GuestArenaAllocate_nid_postfix(BlockBytes, 65536));
if (block != nullptr) GuestArena::GuestArenaCommit_nid_postfix(block, BlockBytes, PAGE_READWRITE, BlockBytes);
#else
constexpr std::uintptr_t alignment = 65536;
void* mapped = mmap(nullptr, BlockBytes + alignment, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (mapped != MAP_FAILED) {
const auto begin = reinterpret_cast<std::uintptr_t>(mapped);
const auto aligned = (begin + alignment - 1) & ~(alignment - 1);
if (aligned != begin) munmap(mapped, aligned - begin);
if (aligned + BlockBytes != begin + BlockBytes + alignment) munmap(reinterpret_cast<void*>(aligned + BlockBytes), begin + alignment - aligned);
block = reinterpret_cast<std::uint8_t*>(aligned);
GuestWriteWatch::GuestWriteWatchRegister_nid_postfix(block, BlockBytes);
}
#endif
Require(block != nullptr, "depth plane guest layers: cannot allocate the guest block");
GuestAllocations::Mutation().Add(block, BlockBytes, true, true, true);
}

~WatchedBlock() {
GuestAllocations::Mutation().Remove(block);
#ifdef _WIN32
GuestArena::GuestArenaReset_nid_postfix(block, BlockBytes);
GuestArena::GuestArenaRelease_nid_postfix(block, BlockBytes);
#else
munmap(block, BlockBytes);
GuestWriteWatch::GuestWriteWatchUnregister_nid_postfix(block, BlockBytes);
#endif
}

WatchedBlock(const WatchedBlock&) = delete;
WatchedBlock& operator=(const WatchedBlock&) = delete;

std::uint8_t* Data() const { return block; }
std::uint64_t Address() const { return static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(block)); }

private:
std::uint8_t* block = nullptr;
};

std::atomic<std::uint64_t> watchedBegin{0};
std::atomic<std::uint64_t> watchedEnd{0};
std::atomic<std::uint32_t> watchedReads{0};

void CountReads(std::uint64_t address, std::size_t bytes) {
if (address < watchedEnd.load() && address + bytes > watchedBegin.load()) watchedReads.fetch_add(1);
}

void FillLayer(std::uint8_t* layer, float depth) {
for (std::size_t offset = 0; offset < LayerBytes; offset += sizeof(depth)) std::memcpy(layer + offset, &depth, sizeof(depth));
}

void Run(const Context& base, const WatchedBlock& block) {
TextureDetiler detiler(base);
auto context = base;
context.detiler = &detiler;
const auto address = block.Address();
FillLayer(block.Data(), 1.0f);
FillLayer(block.Data() + LayerBytes, 0.25f);
Require(DepthSliceBytes({Side, Side}, 4u) == LayerBytes, "the depth slice of the test surface is not one layer of the guest block");
DepthSurfaceView(context, DepthTarget{address, 0, {Side, Side}, VK_FORMAT_D32_SFLOAT, 1.0f, 0, 0});
GuestTextureResource resource{};
resource.baseAddress = address;
resource.width = Side;
resource.height = Side;
resource.depthOrLastArray = 1;
resource.mipCount = 1;
resource.tileMode = TextureTileMode::kZ64KBX;
resource.dimension = TextureDimension::k2DArray;
resource.format = Format32Float;
resource.dstSelX = 4;
resource.dstSelY = 5;
resource.dstSelZ = 6;
resource.dstSelW = 7;
const std::array<std::uint32_t, 8> words{};
const VkComponentMapping components{VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY};
watchedBegin = address + LayerBytes;
watchedEnd = address + BlockBytes;
AgcDriver::GuestMemory::SetFlushHook(&CountReads);
const auto sample = [&](const char* when) {
watchedReads = 0;
const auto texture = DepthSurfaceTexture(context, words, resource, components);
Require(texture != nullptr, std::string("the 2D array over the depth surface was not sampled through its depth planes ") + when);
return watchedReads.load();
};
try {
Require(sample("at its first use") != 0, "the first sampling of the depth planes did not read the layer no depth surface holds");
Require(sample("again") == 0, "sampling the depth planes again read the guest layer no depth surface holds although nothing wrote it");
FillLayer(block.Data() + LayerBytes, 0.75f);
Require(sample("after a CPU write") != 0, "sampling the depth planes after a CPU write to the guest layer did not read it again");
Require(sample("after the re-read") == 0, "sampling the depth planes after the re-read of the written layer read it once more");
AgcDriver::GuestMemory::MarkWritten(address + LayerBytes, 4096);
Require(sample("after a driver store") != 0, "sampling the depth planes after a driver store over the guest layer did not read it again");
} catch (...) {
AgcDriver::GuestMemory::SetFlushHook(nullptr);
throw;
}
AgcDriver::GuestMemory::SetFlushHook(nullptr);
}

}

int main() {
try {
if (!AgcDriver::GuestMemory::WriteWatched()) {
std::puts("skipped, guest memory has no write watch");
return Skipped;
}
std::optional<Device> device;
try {
device.emplace();
} catch (const std::exception& error) {
if (std::getenv("ANYPS5_REQUIRE_VULKAN") != nullptr) throw;
std::printf("skipped, no usable Vulkan device: %s\n", error.what());
return Skipped;
}
{
WatchedBlock block;
Require(AgcDriver::GuestMemory::Watched(block.Address(), BlockBytes), "depth plane guest layers: the guest block is not write-watched");
Run(device->GetContext(), block);
}
device.reset();
std::puts("depth plane guest layer tests passed");
return 0;
} catch (const std::exception& error) {
std::cerr << error.what() << '\n';
return 1;
}
}
Loading