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;
|
|
}
|
|
}
|