diff options
author | Steven Le Rouzic <steven.lerouzic@gmail.com> | 2024-05-03 00:40:22 +0200 |
---|---|---|
committer | Steven Le Rouzic <steven.lerouzic@gmail.com> | 2024-05-03 00:40:22 +0200 |
commit | 909304e44763c58c0ebbe40068a58784ebaced7b (patch) | |
tree | a5751d3e26f9b5c83b6de9ccf805818e5d0aadbc /deimos | |
parent | 1b5f373f8e913aa948ef0964d26a0082e6563f0a (diff) |
Add Vulkan backend API
Diffstat (limited to 'deimos')
-rw-r--r-- | deimos/core/allocator.h | 27 | ||||
-rw-r--r-- | deimos/vulkan/BUILD | 5 | ||||
-rw-r--r-- | deimos/vulkan/vulkan_backend.cpp | 318 | ||||
-rw-r--r-- | deimos/vulkan/vulkan_backend.h | 33 | ||||
-rw-r--r-- | deimos/vulkan/vulkan_loader.cpp | 12 | ||||
-rw-r--r-- | deimos/vulkan/vulkan_loader.h (renamed from deimos/vulkan/vulkan.h) | 3 |
6 files changed, 388 insertions, 10 deletions
diff --git a/deimos/core/allocator.h b/deimos/core/allocator.h index 07a214e..9e8dd87 100644 --- a/deimos/core/allocator.h +++ b/deimos/core/allocator.h @@ -116,6 +116,33 @@ public: std::forward<A3>(arg3));
}
+ template<typename T, typename A0, typename A1, typename A2, typename A3, typename A4>
+ constexpr gsl::owner<T*> New(
+ A0&& arg0, A1&& arg1, A2&& arg2, A3&& arg3, A4&& arg4,
+ const SourceLocation& source_location = {})
+ {
+ return NewInner<T>(source_location,
+ std::forward<A0>(arg0),
+ std::forward<A1>(arg1),
+ std::forward<A2>(arg2),
+ std::forward<A3>(arg3),
+ std::forward<A4>(arg4));
+ }
+
+ template<typename T, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5>
+ constexpr gsl::owner<T*> New(
+ A0&& arg0, A1&& arg1, A2&& arg2, A3&& arg3, A4&& arg4, A5&& arg5,
+ const SourceLocation& source_location = {})
+ {
+ return NewInner<T>(source_location,
+ std::forward<A0>(arg0),
+ std::forward<A1>(arg1),
+ std::forward<A2>(arg2),
+ std::forward<A3>(arg3),
+ std::forward<A4>(arg4),
+ std::forward<A5>(arg5));
+ }
+
template<typename T>
void Delete(gsl::owner<T*> t, const SourceLocation& source_location = {})
{
diff --git a/deimos/vulkan/BUILD b/deimos/vulkan/BUILD index 5eae9ef..238261f 100644 --- a/deimos/vulkan/BUILD +++ b/deimos/vulkan/BUILD @@ -1,10 +1,13 @@ cc_library(
name = "vulkan",
hdrs = [
- "vulkan.h",
+ "vulkan_backend.h",
+ "vulkan_loader.h",
],
srcs = [
+ "vulkan_backend.cpp",
"vulkan_loader.cpp",
+
"vulkan_bootstrap_functions.inc",
"vulkan_entry_functions.inc",
"vulkan_instance_functions.inc",
diff --git a/deimos/vulkan/vulkan_backend.cpp b/deimos/vulkan/vulkan_backend.cpp new file mode 100644 index 0000000..173e671 --- /dev/null +++ b/deimos/vulkan/vulkan_backend.cpp @@ -0,0 +1,318 @@ +#include "deimos/vulkan/vulkan_backend.h"
+
+#include "deimos/vulkan/vulkan_loader.h"
+
+#include <deimos/core/api_registry.h>
+#include <deimos/core/allocator.h>
+#include <deimos/core/temp_allocator.h>
+#include <deimos/core/log.h>
+#include <deimos/core/os.h>
+
+namespace
+{
+
+using namespace deimos;
+
+LogApi* log_api;
+OsApi* os_api;
+ApiRegistry* api_registry;
+TempAllocatorApi* temp_api;
+VulkanLoaderApi* vulkan_loader_api;
+
+const VkAllocationCallbacks* kVkAlloc = nullptr;
+
+class VulkanBackendImpl : public IVulkanBackend
+{
+ VulkanApi* m_vk;
+ VkInstance m_instance;
+ VkPhysicalDevice m_physical_device;
+ uint32_t m_queue_family;
+ VkSurfaceKHR m_surface;
+ VkDevice m_device;
+ VkQueue m_queue = VK_NULL_HANDLE;
+
+public:
+ VulkanBackendImpl(
+ VulkanApi* vk,
+ VkInstance instance,
+ VkPhysicalDevice physical_device,
+ uint32_t queue_family,
+ VkSurfaceKHR surface,
+ VkDevice device) :
+ m_vk{vk}, m_instance{instance}, m_physical_device{physical_device},
+ m_queue_family{queue_family}, m_surface{surface}, m_device{device}
+ {
+ m_vk->GetDeviceQueue(m_device, m_queue_family, 0, &m_queue);
+ Ensures(m_queue != VK_NULL_HANDLE);
+ }
+};
+
+StatusOr<VkInstance> CreateInstance(VulkanApi* vk)
+{
+ const VkApplicationInfo application_info{
+ .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
+ .pNext = nullptr,
+ .pApplicationName = "Deimos game",
+ .applicationVersion = 0,
+ .pEngineName = "Deimos engine",
+ .engineVersion = 0,
+ .apiVersion = VK_API_VERSION_1_3,
+ };
+
+ const char* extensions[]{
+ VK_KHR_SURFACE_EXTENSION_NAME,
+ VK_KHR_WIN32_SURFACE_EXTENSION_NAME,
+ };
+
+ // @Todo Select layers & extensions based on config
+ const char* layers[]{
+ "VK_LAYER_KHRONOS_validation",
+ "VK_LAYER_LUNARG_monitor",
+ };
+
+ const VkInstanceCreateInfo create_info{
+ .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
+ .pNext = nullptr,
+ .flags = 0,
+ .pApplicationInfo = &application_info,
+ .enabledLayerCount = (uint32_t)ArraySize(layers),
+ .ppEnabledLayerNames = layers,
+ .enabledExtensionCount = (uint32_t)ArraySize(extensions),
+ .ppEnabledExtensionNames = extensions,
+ };
+
+ VkInstance instance{};
+ const VkResult res = vk->CreateInstance(&create_info, kVkAlloc, &instance);
+ if (res != VK_SUCCESS)
+ {
+ return InternalError("vkCreateInstance failed");
+ }
+
+ return instance;
+}
+
+StatusOr<VkSurfaceKHR> CreateSurface(VulkanApi* vk, VkInstance instance, OsWindow* window)
+{
+ const VkWin32SurfaceCreateInfoKHR create_info{
+ .sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR,
+ .pNext = nullptr,
+ .flags = 0,
+ .hinstance = os_api->window->Win32Hinstance(window),
+ .hwnd = os_api->window->Win32Hwnd(window),
+ };
+
+ VkSurfaceKHR surface{};
+ const VkResult res = vk->CreateWin32SurfaceKHR(instance, &create_info, kVkAlloc, &surface);
+ if (res != VK_SUCCESS)
+ {
+ return InternalError("vkCreateWin32SurfaceKHR failed");
+ }
+
+ return surface;
+}
+
+StatusOr<uint32_t> FindQueueFamily(VulkanApi* vk, VkPhysicalDevice physical_device, VkSurfaceKHR surface)
+{
+ auto temp_alloc = temp_api->Acquire();
+
+ uint32_t queue_family_count = 0;
+ vk->GetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_count, nullptr);
+ if (queue_family_count == 0)
+ {
+ return InternalError("No queue on this physical device");
+ }
+
+ log_api->LogInfo("Physical device has $ queue families", queue_family_count);
+
+ auto queue_families = temp_alloc.allocator().NewArray<VkQueueFamilyProperties>(queue_family_count);
+ vk->GetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_count, queue_families.data());
+
+ for (uint32_t index = 0; index < queue_family_count; ++index)
+ {
+ const auto& prps = queue_families[index];
+
+ const bool supports_graphics_compute = (prps.queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0 && (prps.queueFlags & VK_QUEUE_COMPUTE_BIT) != 0;
+
+ VkBool32 vk_supports_presentation{};
+ const VkResult presentation_res = vk->GetPhysicalDeviceSurfaceSupportKHR(physical_device, index, surface, &vk_supports_presentation);
+ const bool supports_presentation = presentation_res == VK_SUCCESS && vk_supports_presentation == VK_TRUE;
+
+ if (supports_graphics_compute && supports_presentation)
+ {
+ return index;
+ }
+ }
+
+ return InternalError("Couldn't find a suitable queue");
+}
+
+Status FindPhysicalDevice(
+ VulkanApi* vk,
+ VkInstance instance,
+ VkSurfaceKHR surface,
+ VkPhysicalDevice* out_physical_device,
+ uint32_t* out_queue_family)
+{
+ auto temp_alloc = temp_api->Acquire();
+
+ uint32_t physical_device_count = 0;
+ vk->EnumeratePhysicalDevices(instance, &physical_device_count, nullptr);
+ if (physical_device_count == 0)
+ {
+ return InternalError("No Vulkan device found");
+ }
+
+ log_api->LogInfo("Found $ physical devices", physical_device_count);
+
+ auto physical_devices = temp_alloc.allocator().NewArray<VkPhysicalDevice>(physical_device_count);
+ vk->EnumeratePhysicalDevices(instance, &physical_device_count, physical_devices.data());
+
+ for (VkPhysicalDevice physical_device: physical_devices)
+ {
+ {
+ VkPhysicalDeviceVulkan12Properties prps12{};
+ prps12.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_PROPERTIES;
+ prps12.pNext = nullptr;
+
+ VkPhysicalDeviceProperties2 prps{};
+ prps.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
+ prps.pNext = &prps12;
+
+ vk->GetPhysicalDeviceProperties2(physical_device, &prps);
+
+ log_api->LogInfo("Trying Vulkan device $ ($, $)",
+ prps.properties.deviceName, prps12.driverName, prps12.driverInfo);
+ }
+
+ auto maybe_queue_family = FindQueueFamily(vk, physical_device, surface);
+ if (maybe_queue_family.ok())
+ {
+ log_api->LogInfo("This device is compatible, choosing it");
+ *out_physical_device = physical_device;
+ *out_queue_family = maybe_queue_family.value();
+ return {};
+ }
+
+ log_api->LogInfo("Incompatible because: $", maybe_queue_family);
+ }
+
+ return InternalError("No suitable device found");
+}
+
+StatusOr<VkDevice> CreateDevice(VulkanApi* vk, VkPhysicalDevice physical_device, uint32_t queue_family)
+{
+ const float queue_priority = 1.0F;
+
+ const VkDeviceQueueCreateInfo queue_create_info{
+ .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
+ .pNext = nullptr,
+ .flags = 0,
+ .queueFamilyIndex = queue_family,
+ .queueCount = 1,
+ .pQueuePriorities = &queue_priority,
+ };
+
+ const char* extensions[]{
+ VK_KHR_SWAPCHAIN_EXTENSION_NAME,
+ };
+
+ const VkDeviceCreateInfo create_info{
+ .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
+ .pNext = nullptr,
+ .flags = 0,
+ .queueCreateInfoCount = 1,
+ .pQueueCreateInfos = &queue_create_info,
+ .enabledLayerCount = 0,
+ .ppEnabledLayerNames = nullptr,
+ .enabledExtensionCount = ArraySize(extensions),
+ .ppEnabledExtensionNames = extensions,
+ .pEnabledFeatures = nullptr,
+ };
+
+ VkDevice device = VK_NULL_HANDLE;
+ const VkResult res = vk->CreateDevice(physical_device, &create_info, kVkAlloc, &device);
+ if (res != VK_SUCCESS)
+ {
+ return InternalError("vkCreateDeviceFailed");
+ }
+
+ return device;
+}
+
+StatusOr<gsl::owner<IVulkanBackend*>> InitializeVulkan(Allocator* allocator, OsWindow* window)
+{
+ auto* vk = vulkan_loader_api->LoadEntry(allocator);
+
+ VkInstance instance{};
+ {
+ StatusOr<VkInstance> s = CreateInstance(vk);
+ if (!s.ok()) { return s.status(); }
+ instance = s.value();
+ log_api->LogInfo("Vulkan instance created");
+ }
+
+ vulkan_loader_api->LoadInstance(vk, instance);
+
+ VkSurfaceKHR surface{};
+ {
+ StatusOr<VkSurfaceKHR> s = CreateSurface(vk, instance, window);
+ if (!s.ok()) { return s.status(); }
+ surface = s.value();
+ log_api->LogInfo("Vulkan surface created");
+ }
+
+ VkPhysicalDevice physical_device{};
+ uint32_t queue_family{};
+ {
+ Status s = FindPhysicalDevice(vk, instance, surface, &physical_device, &queue_family);
+ if (!s.ok()) { return s; }
+ log_api->LogInfo("Vulkan queue family: $", queue_family);
+ }
+
+ VkDevice device = VK_NULL_HANDLE;
+ {
+ StatusOr<VkDevice> s = CreateDevice(vk, physical_device, queue_family);
+ if (!s.ok()) { return s.status(); }
+ device = s.value();
+ log_api->LogInfo("Vulkan device created");
+ }
+
+ vulkan_loader_api->LoadDevice(vk, device);
+
+ return allocator->New<VulkanBackendImpl>(
+ vk, instance, physical_device,
+ queue_family, surface, device);
+}
+
+} // anonymous namespace
+
+namespace deimos
+{
+
+class VulkanBackendApiImpl : public VulkanBackendApi
+{
+public:
+ StatusOr<gsl::owner<IVulkanBackend*>> CreateBackend(Allocator* allocator, OsWindow* window) override
+ {
+ return InitializeVulkan(allocator, window);
+ }
+};
+
+void RegisterVulkanBackendApi(ApiRegistry* registry)
+{
+ api_registry = registry;
+ log_api = registry->Get<LogApi>();
+ os_api = registry->Get<OsApi>();
+ temp_api = registry->Get<TempAllocatorApi>();
+ vulkan_loader_api = registry->Get<VulkanLoaderApi>();
+
+ auto* allocator_api = registry->Get<AllocatorApi>();
+
+ auto* impl = allocator_api->system->New<VulkanBackendApiImpl>();
+ registry->Set(impl);
+
+ log_api->LogInfo("Vulkan backend API registered");
+}
+
+} // namespace deimos
+
diff --git a/deimos/vulkan/vulkan_backend.h b/deimos/vulkan/vulkan_backend.h new file mode 100644 index 0000000..90aaf2a --- /dev/null +++ b/deimos/vulkan/vulkan_backend.h @@ -0,0 +1,33 @@ +#pragma once
+
+#include <deimos/core/id_name.h>
+#include <deimos/core/status.h>
+
+namespace deimos
+{
+
+class ApiRegistry;
+class Allocator;
+struct OsWindow;
+
+class IVulkanBackend
+{
+public:
+};
+
+class VulkanBackendApi
+{
+public:
+ VulkanBackendApi() = default;
+ deimos_NO_COPY_MOVE(VulkanBackendApi);
+ virtual ~VulkanBackendApi() = default;
+
+ static constexpr IdName kApiName{"deimos::VulkanBackendApi"};
+
+ virtual StatusOr<gsl::owner<IVulkanBackend*>> CreateBackend(Allocator*, OsWindow*) = 0;
+};
+
+void RegisterVulkanBackendApi(ApiRegistry*);
+
+} // namespace deimos
+
diff --git a/deimos/vulkan/vulkan_loader.cpp b/deimos/vulkan/vulkan_loader.cpp index b9980af..c4d7d87 100644 --- a/deimos/vulkan/vulkan_loader.cpp +++ b/deimos/vulkan/vulkan_loader.cpp @@ -1,4 +1,4 @@ -#include "deimos/vulkan/vulkan.h"
+#include "deimos/vulkan/vulkan_loader.h"
#include <deimos/core/api_registry.h>
#include <deimos/core/allocator.h>
@@ -13,13 +13,10 @@ namespace deimos class VulkanLoaderImpl : public VulkanLoaderApi
{
- Allocator* m_allocator;
gsl::owner<OsDll*> m_vulkan_dll{};
public:
- explicit VulkanLoaderImpl(Allocator* allocator) : m_allocator{allocator} {}
-
- VulkanApi* LoadEntry() override
+ VulkanApi* LoadEntry(Allocator* allocator) override
{
if (m_vulkan_dll == nullptr)
{
@@ -34,7 +31,7 @@ public: }
}
- VulkanApi* api = m_allocator->New<VulkanApi>();
+ VulkanApi* api = allocator->New<VulkanApi>();
#define FN(NAME) api->NAME = (PFN_vk##NAME)os_api->dll->GetSymbol(m_vulkan_dll, "vk" #NAME);
#include "deimos/vulkan/vulkan_bootstrap_functions.inc"
@@ -76,9 +73,8 @@ void RegisterVulkanLoaderApi(ApiRegistry* registry) log_api = registry->Get<LogApi>();
auto* allocator_api = registry->Get<AllocatorApi>();
- auto* allocator = allocator_api->CreateChild(allocator_api->system, "Vulkan");
- auto* impl = allocator->New<VulkanLoaderImpl>(allocator);
+ auto* impl = allocator_api->system->New<VulkanLoaderImpl>();
registry->Set(impl);
log_api->LogInfo("Vulkan loader API registered");
diff --git a/deimos/vulkan/vulkan.h b/deimos/vulkan/vulkan_loader.h index f116fc0..1339ba4 100644 --- a/deimos/vulkan/vulkan.h +++ b/deimos/vulkan/vulkan_loader.h @@ -16,6 +16,7 @@ namespace deimos {
class ApiRegistry;
+class Allocator;
struct VulkanApi
{
@@ -36,7 +37,7 @@ public: static constexpr IdName kApiName{"deimos::VulkanLoaderApi"};
- virtual VulkanApi* LoadEntry() = 0;
+ virtual VulkanApi* LoadEntry(Allocator* allocator) = 0;
virtual void LoadInstance(VulkanApi*, VkInstance) = 0;
virtual void LoadDevice(VulkanApi*, VkDevice) = 0;
|