#include "BdaShader.hpp" #include "BdaAbi.hpp" #include "prx/libSceAgcDriver/Graphics/include/Resources.hpp" #include #include #include namespace { using namespace AgcDriver::Graphics; class Pipeline { public: Pipeline(const Context& context, std::span code, const std::array& buffers) : context(context) { try { std::array bindings{}; for (std::uint32_t i = 0; i < bindings.size(); ++i) bindings[i] = {i, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr}; VkDescriptorSetLayoutCreateInfo setInfo{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO}; setInfo.bindingCount = bindings.size(); setInfo.pBindings = bindings.data(); Check(context.Function("vkCreateDescriptorSetLayout")(context.device, &setInfo, nullptr, &setLayout), "vkCreateDescriptorSetLayout"); const VkDescriptorPoolSize size{VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 3}; VkDescriptorPoolCreateInfo poolInfo{VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO}; poolInfo.maxSets = 1; poolInfo.poolSizeCount = 1; poolInfo.pPoolSizes = &size; Check(context.Function("vkCreateDescriptorPool")(context.device, &poolInfo, nullptr, &pool), "vkCreateDescriptorPool"); VkDescriptorSetAllocateInfo allocation{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, nullptr, pool, 1, &setLayout}; Check(context.Function("vkAllocateDescriptorSets")(context.device, &allocation, &set), "vkAllocateDescriptorSets"); for (std::uint32_t i = 0; i < buffers.size(); ++i) { const VkDescriptorBufferInfo buffer{buffers[i]->Handle(), 0, buffers[i]->Bytes().size()}; VkWriteDescriptorSet write{VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET}; write.dstSet = set; write.dstBinding = i; write.descriptorCount = 1; write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; write.pBufferInfo = &buffer; context.Function("vkUpdateDescriptorSets")(context.device, 1, &write, 0, nullptr); } VkPipelineLayoutCreateInfo layoutInfo{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO}; layoutInfo.setLayoutCount = 1; layoutInfo.pSetLayouts = &setLayout; Check(context.Function("vkCreatePipelineLayout")(context.device, &layoutInfo, nullptr, &layout), "vkCreatePipelineLayout"); VkShaderModuleCreateInfo moduleInfo{VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO}; moduleInfo.codeSize = code.size_bytes(); moduleInfo.pCode = code.data(); Check(context.Function("vkCreateShaderModule")(context.device, &moduleInfo, nullptr, &module), "vkCreateShaderModule"); VkComputePipelineCreateInfo pipelineInfo{VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO}; pipelineInfo.stage = {VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, nullptr, 0, VK_SHADER_STAGE_COMPUTE_BIT, module, "main", nullptr}; pipelineInfo.layout = layout; Check(context.Function("vkCreateComputePipelines")(context.device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &pipeline), "vkCreateComputePipelines"); } catch (...) { release(); throw; } } ~Pipeline() { release(); } void Run(std::uint32_t groups) { CommandBatch batch(context); const auto commands = batch.Handle(); VkMemoryBarrier upload{VK_STRUCTURE_TYPE_MEMORY_BARRIER, nullptr, VK_ACCESS_HOST_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT}; context.Function("vkCmdPipelineBarrier")(commands, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 1, &upload, 0, nullptr, 0, nullptr); context.Function("vkCmdBindPipeline")(commands, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline); context.Function("vkCmdBindDescriptorSets")(commands, VK_PIPELINE_BIND_POINT_COMPUTE, layout, 0, 1, &set, 0, nullptr); context.Function("vkCmdDispatch")(commands, groups, 1, 1); VkMemoryBarrier download{VK_STRUCTURE_TYPE_MEMORY_BARRIER, nullptr, VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT}; context.Function("vkCmdPipelineBarrier")(commands, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0, 1, &download, 0, nullptr, 0, nullptr); batch.SubmitAndWait(); } private: void release() noexcept { if (pipeline) context.Function("vkDestroyPipeline")(context.device, pipeline, nullptr); if (module) context.Function("vkDestroyShaderModule")(context.device, module, nullptr); if (layout) context.Function("vkDestroyPipelineLayout")(context.device, layout, nullptr); if (pool) context.Function("vkDestroyDescriptorPool")(context.device, pool, nullptr); if (setLayout) context.Function("vkDestroyDescriptorSetLayout")(context.device, setLayout, nullptr); } const Context& context; VkDescriptorSetLayout setLayout = VK_NULL_HANDLE; VkDescriptorPool pool = VK_NULL_HANDLE; VkDescriptorSet set = VK_NULL_HANDLE; VkPipelineLayout layout = VK_NULL_HANDLE; VkShaderModule module = VK_NULL_HANDLE; VkPipeline pipeline = VK_NULL_HANDLE; }; } void RunBdaExecutionTests(const Context& context) { namespace Abi = ShaderRecompiler::BdaAbi; Buffer first(context, 3, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT); Buffer second(context, 2, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT); first.Bytes()[0] = std::byte{0x11}; first.Bytes()[1] = std::byte{0x22}; first.Bytes()[2] = std::byte{0x33}; second.Bytes()[0] = std::byte{0x44}; second.Bytes()[1] = std::byte{0x55}; constexpr std::uint64_t guest = 0x7fff12340001ULL; std::array ranges{{{guest, guest + 3, first.DeviceAddress(), Abi::Read, 0}, {guest + 3, guest + 5, second.DeviceAddress(), Abi::Read, 0}}}; Buffer table(context, sizeof(Abi::Header) + sizeof(ranges), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT); Buffer fault(context, sizeof(Abi::Fault), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT); Buffer output(context, 4, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT); const auto run = [&](std::uint64_t address, std::uint32_t bits, std::uint32_t expected, Abi::FaultReason reason, std::uint32_t count = 2, std::uint32_t groups = 1, std::int64_t offset = 0) { const Abi::Header header{Abi::Version, count, sizeof(Abi::Range), 0}; std::memcpy(table.Bytes().data(), &header, sizeof(header)); std::memcpy(table.Bytes().data() + sizeof(header), ranges.data(), sizeof(ranges)); std::memset(fault.Bytes().data(), 0, fault.Bytes().size()); const std::uint32_t sentinel = 0xdeadbeef; std::memcpy(output.Bytes().data(), &sentinel, sizeof(sentinel)); Pipeline pipeline(context, MakeBdaTestShader(address, bits, offset), {&table, &fault, &output}); pipeline.Run(groups); Abi::Fault report{}; std::uint32_t result = 0; std::memcpy(&report, fault.Bytes().data(), sizeof(report)); std::memcpy(&result, output.Bytes().data(), sizeof(result)); if (static_cast(reason) == 0) { Require(report.state == Abi::FaultState::Empty && result == expected, "BDA GPU read produced incorrect data or a fault"); } else { Require(report.state == Abi::FaultState::Ready && report.reason == reason && report.instruction == 0x1234, "BDA GPU fault was not published correctly"); Require(result == sentinel, "faulting BDA shader continued to output a substitute value"); } }; run(guest, 8, 0x11, static_cast(0)); run(guest + 1, 16, 0x3322, static_cast(0)); run(guest + 1, 32, 0x55443322, static_cast(0)); run(guest + 5, 8, 0, Abi::FaultReason::Unmapped, 2, 64); run(guest - 1, 8, 0, Abi::FaultReason::Unmapped); run(std::numeric_limits::max() - 1, 32, 0, Abi::FaultReason::Overflow); run(guest, 8, 0, Abi::FaultReason::InvalidTable, 3); run(std::numeric_limits::max() - 2, 8, 0, Abi::FaultReason::Overflow, 2, 1, 4); run(1, 8, 0, Abi::FaultReason::Overflow, 2, 1, -4); ranges[0].permissions = 0; run(guest, 8, 0, Abi::FaultReason::Permission); }