mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-23 17:13:13 -05:00
The BDA device test was built only on request and never registered, so it ran nowhere. Register it with the other AGC driver tests. Pick the Vulkan device the way VulkanDevice does, a discrete GPU over an integrated one, instead of the first enumerated device, so the test runs on the same GPU as the runtime on hosts with an iGPU. Without a usable device it exits 77, as OpenVulkanTestDevice does, unless ANYPS5_REQUIRE_VULKAN is set. On a CPU device the BDA execution tests are not run: llvmpipe loads through the null pointer of a branch that is not taken and the process segfaults (Mesa #16515, #16217). The contract, allocation and color transfer tests still run there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
177 lines
8.9 KiB
C++
177 lines
8.9 KiB
C++
#include "BdaShader.hpp"
|
|
#include "ColorTransferTests.hpp"
|
|
#include <fstream>
|
|
#include "prx/libSceAgcDriver/Execution/include/BdaFeatures.hpp"
|
|
#include "prx/libSceAgcDriver/Graphics/include/Recorder.hpp"
|
|
#include "prx/libSceAgcDriver/Graphics/include/Resources.hpp"
|
|
#include <SDL_loadso.h>
|
|
#include <array>
|
|
#include <cstdlib>
|
|
#include <iostream>
|
|
#include <memory>
|
|
#include <vector>
|
|
|
|
namespace AgcDriver::Graphics {
|
|
Recorder* Recorder::Active() { return nullptr; }
|
|
void Recorder::Submit() { Require(false, "the device tests have no recorder"); }
|
|
bool Recorder::Reap() { Require(false, "the device tests have no recorder"); return false; }
|
|
}
|
|
|
|
namespace {
|
|
|
|
using namespace AgcDriver::Graphics;
|
|
|
|
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");
|
|
const auto rankDeviceType = [](VkPhysicalDeviceType type) {
|
|
switch (type) {
|
|
case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: return 3;
|
|
case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: return 2;
|
|
case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: return 1;
|
|
default: return 0;
|
|
}
|
|
};
|
|
const auto physicalProperties = function<PFN_vkGetPhysicalDeviceProperties>("vkGetPhysicalDeviceProperties");
|
|
int selectedRank = -1;
|
|
for (const auto physical : devices) {
|
|
VkPhysicalDeviceProperties candidate{};
|
|
physicalProperties(physical, &candidate);
|
|
if (candidate.apiVersion < VK_API_VERSION_1_1) continue;
|
|
const int rank = rankDeviceType(candidate.deviceType);
|
|
if (rank <= selectedRank) continue;
|
|
context.physical = physical;
|
|
selectedRank = rank;
|
|
cpu = candidate.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU;
|
|
}
|
|
Require(context.physical != VK_NULL_HANDLE, "no Vulkan 1.1 device");
|
|
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());
|
|
std::uint32_t family = 0;
|
|
while (family < count && (families[family].queueFlags & VK_QUEUE_COMPUTE_BIT) == 0) ++family;
|
|
Require(family < count, "no Vulkan 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 = 2;
|
|
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.Function<PFN_vkGetDeviceQueue>("vkGetDeviceQueue")(context.device, family, 0, &context.queue);
|
|
VkCommandPoolCreateInfo pool{VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO};
|
|
pool.queueFamilyIndex = family;
|
|
Check(context.Function<PFN_vkCreateCommandPool>("vkCreateCommandPool")(context.device, &pool, nullptr, &context.pool), "vkCreateCommandPool");
|
|
} catch (...) {
|
|
release();
|
|
throw;
|
|
}
|
|
}
|
|
|
|
~Device() { release(); }
|
|
const Context& GetContext() const { return context; }
|
|
bool RunsOnCpu() const { return cpu; }
|
|
|
|
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.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{};
|
|
bool cpu = false;
|
|
};
|
|
|
|
}
|
|
|
|
int main(int argc, char** argv) {
|
|
try {
|
|
RunBdaContractTests();
|
|
if (argc == 2) {
|
|
const auto shader = MakeBdaTestShader(0x7fff12340000ULL, 32);
|
|
std::ofstream file(argv[1], std::ios::binary);
|
|
file.write(reinterpret_cast<const char*>(shader.data()), static_cast<std::streamsize>(shader.size() * sizeof(std::uint32_t)));
|
|
Require(static_cast<bool>(file), "cannot save BDA test SPIR-V");
|
|
return 0;
|
|
}
|
|
std::unique_ptr<Device> device;
|
|
try {
|
|
device = std::make_unique<Device>();
|
|
} catch (const std::exception& error) {
|
|
if (std::getenv("ANYPS5_REQUIRE_VULKAN") != nullptr) throw;
|
|
std::cout << "skipped, no usable Vulkan device: " << error.what() << '\n';
|
|
return 77;
|
|
}
|
|
Buffer buffer(device->GetContext(), 256, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT);
|
|
Require(buffer.DeviceAddress() != 0 && buffer.Bytes().size() == 256, "invalid real BDA buffer");
|
|
buffer.Bytes()[255] = std::byte{0x5a};
|
|
Require(buffer.Bytes()[255] == std::byte{0x5a}, "real BDA buffer mapping failed");
|
|
if (device->RunsOnCpu()) {
|
|
std::cout << "CPU Vulkan device: BDA execution not tested\n";
|
|
} else {
|
|
RunBdaExecutionTests(device->GetContext());
|
|
}
|
|
RunColorTransferTests(device->GetContext());
|
|
std::cout << "Vulkan BDA allocation and execution tests passed\n";
|
|
return 0;
|
|
} catch (const std::exception& error) {
|
|
std::cerr << error.what() << '\n';
|
|
return 1;
|
|
}
|
|
}
|