From e02f9fd89b059919baf3a8d8bf8b783470976a27 Mon Sep 17 00:00:00 2001 From: Steven Le Rouzic Date: Sat, 27 Apr 2024 01:16:21 +0200 Subject: Some work on Vulkan initialization --- .clang-tidy | 1 + deimos/core/allocator.h | 21 +++++ deimos/core/base.h | 19 ++++- deimos/core/log.cpp | 8 +- deimos/core/status.h | 14 +++- deimos/core/std.h | 1 + deimos/core/temp_allocator.cpp | 12 +-- deimos/vulkan/BUILD | 2 + deimos/vulkan/vulkan.h | 4 + deimos/vulkan/vulkan_bootstrap_functions.inc | 3 + deimos/vulkan/vulkan_entry_functions.inc | 1 - deimos/vulkan/vulkan_instance_functions.inc | 6 ++ deimos/vulkan/vulkan_loader.cpp | 18 +++- main/main.cpp | 119 +++++++++++++++++++++++++-- 14 files changed, 202 insertions(+), 27 deletions(-) create mode 100644 deimos/vulkan/vulkan_bootstrap_functions.inc create mode 100644 deimos/vulkan/vulkan_instance_functions.inc diff --git a/.clang-tidy b/.clang-tidy index be78fe7..3cedbdc 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -26,3 +26,4 @@ Checks: - "-*-noexcept-move-operations" - "-*-bounds-array-to-pointer-decay" - "-*-no-array-decay" + - "-*-signed-bitwise" diff --git a/deimos/core/allocator.h b/deimos/core/allocator.h index 26374bd..5afb824 100644 --- a/deimos/core/allocator.h +++ b/deimos/core/allocator.h @@ -125,6 +125,27 @@ public: } Free(t, sizeof(T), source_location); } + + template + gsl::owner> NewArray(int64_t count, const SourceLocation& source_location = {}) + requires std::is_default_constructible_v + { + Expects(count > 0); + + auto* raw = Allocate((int64_t)sizeof(T) * count, source_location); + if constexpr (std::is_trivially_default_constructible_v) + { + MemoryZero(raw, (int64_t)sizeof(T) * count); + } + else + { + for (int64_t i = 0; i < count; ++i) + { + new((T*)raw + i) T{}; + } + } + return Span{ (T*)raw, count }; + } }; class AllocatorApi diff --git a/deimos/core/base.h b/deimos/core/base.h index 5cd3a40..70958a5 100644 --- a/deimos/core/base.h +++ b/deimos/core/base.h @@ -5,7 +5,13 @@ #define deimos_StaticAssert(...) static_assert(__VA_ARGS__, #__VA_ARGS__) -#define deimos_Panic(MSG) do { __builtin_trap(); } while (0) +[[noreturn]] +inline void deimos_Trap() +{ + __builtin_trap(); +} + +#define deimos_Panic(MSG) do { deimos_Trap(); } while (0) #define deimos_NO_COPY(TYPE) \ TYPE(const TYPE&) = delete; \ @@ -78,6 +84,11 @@ constexpr void MemoryCopy(void* dst, const void* src, int64_t size) __builtin_memcpy(dst, src, (size_t)size); } +inline void MemoryZero(void* dst, int64_t size) +{ + __builtin_memset(dst, 0, (size_t)size); +} + template constexpr int64_t ArraySize(const T (&)[N]) { return N; } @@ -117,6 +128,12 @@ public: constexpr T* end() const { return m_begin + m_size; } // NOLINT constexpr int64_t size() const { return m_size; } constexpr bool empty() const { return m_size == 0; } + + constexpr T& operator[](int64_t i) const + { + Expects(i >= 0 && i < m_size); + return m_begin[i]; // NOLINT + } }; template diff --git a/deimos/core/log.cpp b/deimos/core/log.cpp index e05c360..b8e7805 100644 --- a/deimos/core/log.cpp +++ b/deimos/core/log.cpp @@ -35,10 +35,12 @@ public: m_writer(os_console_api, OsConsoleType::kStdOut) {} - void Log(LogSeverity severity, const SourceLocation& location, StringView msg) override + void Log(LogSeverity severity, const SourceLocation& /* location */, StringView msg) override { - Format(&m_writer, "$[ $ ] $:$: $\033[0m\n", SeverityToColor(severity), - SeverityToStr(severity), location.file, location.line, msg); + // Format(&m_writer, "$[ $ ] $:$: $\033[0m\n", SeverityToColor(severity), + // SeverityToStr(severity), location.file, location.line, msg); + Format(&m_writer, "$[ $ ] $\033[0m\n", SeverityToColor(severity), + SeverityToStr(severity), msg); } }; diff --git a/deimos/core/status.h b/deimos/core/status.h index 912d4c8..8141dad 100644 --- a/deimos/core/status.h +++ b/deimos/core/status.h @@ -147,18 +147,18 @@ public: StatusOr(const Status& status) : m_status{status} // NOLINT { + Expects(!m_status.ok()); if (m_status.ok()) { - deimos_Panic("Cannot construct a StatusOr from OK"); m_status = InternalError("StatusOr constructed from OK"); } } StatusOr(Status&& status) : m_status{std::move(status)} // NOLINT { + Expects(!m_status.ok()); if (m_status.ok()) { - deimos_Panic("Cannot construct a StatusOr from OK"); m_status = InternalError("StatusOr constructed from OK"); } } @@ -229,6 +229,16 @@ public: } constexpr bool ok() const { return m_status.ok(); } + + constexpr StatusCode code() const { return m_status.code(); } + + constexpr const Status& status() const { return m_status; } + + constexpr const T& value() const & { Expects(m_status.ok()); return m_value; } + + constexpr T& value() & { Expects(m_status.ok()); return m_value; } + + constexpr T&& value() && { Expects(m_status.ok()); return std::move(m_value); } friend void DeimosFormat(IWriter* writer, const StatusOr& status) { diff --git a/deimos/core/std.h b/deimos/core/std.h index 47f161a..27b5bc4 100644 --- a/deimos/core/std.h +++ b/deimos/core/std.h @@ -26,6 +26,7 @@ template concept unsigned_integral = integral && __is_unsigned(T) template constexpr bool is_trivially_destructible_v = __is_trivially_destructible(T); template constexpr bool is_constructible_v = __is_constructible(T, Args...); template constexpr bool is_default_constructible_v = __is_constructible(T); +template constexpr bool is_trivially_default_constructible_v = __is_trivially_constructible(T); template constexpr bool is_copy_constructible_v = __is_constructible(T, const T&); template constexpr bool is_move_constructible_v = __is_constructible(T, T&&); template constexpr bool is_copy_assignable_v = __is_assignable(T, const T&); diff --git a/deimos/core/temp_allocator.cpp b/deimos/core/temp_allocator.cpp index d2b7900..99ee630 100644 --- a/deimos/core/temp_allocator.cpp +++ b/deimos/core/temp_allocator.cpp @@ -35,7 +35,6 @@ public: if (new_current > m_reserve_end) { deimos_Panic("Ran out of temporary memory"); - return nullptr; } if (new_current > m_commit_end) @@ -73,15 +72,8 @@ public: { void* rewind_base = std::bit_cast(tag.tag); Expects(rewind_base >= m_base && rewind_base <= m_commit_end); - - if (rewind_base < m_current) - { - m_current = rewind_base; - } - else - { - deimos_Panic("Invalid temporary allocator rewind"); - } + Expects(rewind_base <= m_current); + m_current = rewind_base; } }; diff --git a/deimos/vulkan/BUILD b/deimos/vulkan/BUILD index a773fa1..8f0a79e 100644 --- a/deimos/vulkan/BUILD +++ b/deimos/vulkan/BUILD @@ -5,7 +5,9 @@ cc_library( ], srcs = [ "vulkan_loader.cpp", + "vulkan_bootstrap_functions.inc", "vulkan_entry_functions.inc", + "vulkan_instance_functions.inc", ], deps = [ "//deimos/core", diff --git a/deimos/vulkan/vulkan.h b/deimos/vulkan/vulkan.h index 48d53b3..4315230 100644 --- a/deimos/vulkan/vulkan.h +++ b/deimos/vulkan/vulkan.h @@ -20,7 +20,9 @@ class ApiRegistry; struct VulkanApi { #define FN(NAME) PFN_vk##NAME NAME{}; +#include "deimos/vulkan/vulkan_bootstrap_functions.inc" #include "deimos/vulkan/vulkan_entry_functions.inc" +#include "deimos/vulkan/vulkan_instance_functions.inc" #undef FN }; @@ -34,6 +36,8 @@ public: static constexpr IdName kApiName{"deimos::VulkanLoaderApi"}; virtual VulkanApi* LoadEntry() = 0; + + virtual void LoadInstance(VulkanApi*, VkInstance) = 0; }; void RegisterVulkanLoaderApi(ApiRegistry*); diff --git a/deimos/vulkan/vulkan_bootstrap_functions.inc b/deimos/vulkan/vulkan_bootstrap_functions.inc new file mode 100644 index 0000000..a69c5a6 --- /dev/null +++ b/deimos/vulkan/vulkan_bootstrap_functions.inc @@ -0,0 +1,3 @@ +// NOLINTBEGIN +FN(GetInstanceProcAddr) +// NOLINTEND diff --git a/deimos/vulkan/vulkan_entry_functions.inc b/deimos/vulkan/vulkan_entry_functions.inc index fe5f9ea..761cb0f 100644 --- a/deimos/vulkan/vulkan_entry_functions.inc +++ b/deimos/vulkan/vulkan_entry_functions.inc @@ -1,4 +1,3 @@ // NOLINTBEGIN -FN(GetInstanceProcAddr) FN(CreateInstance) // NOLINTEND diff --git a/deimos/vulkan/vulkan_instance_functions.inc b/deimos/vulkan/vulkan_instance_functions.inc new file mode 100644 index 0000000..34535ad --- /dev/null +++ b/deimos/vulkan/vulkan_instance_functions.inc @@ -0,0 +1,6 @@ +// NOLINTBEGIN +FN(EnumeratePhysicalDevices) +FN(GetPhysicalDeviceProperties2) +FN(GetPhysicalDeviceQueueFamilyProperties) +FN(DestroyInstance) +// NOLINTEND diff --git a/deimos/vulkan/vulkan_loader.cpp b/deimos/vulkan/vulkan_loader.cpp index ddd5cf0..0760c67 100644 --- a/deimos/vulkan/vulkan_loader.cpp +++ b/deimos/vulkan/vulkan_loader.cpp @@ -31,16 +31,32 @@ public: else { deimos_Panic("Couldn't load Vulkan DLL"); - return nullptr; } } VulkanApi* api = m_allocator->New(); + #define FN(NAME) api->NAME = (PFN_vk##NAME)os_api->dll->GetSymbol(m_vulkan_dll, "vk" #NAME); +#include "deimos/vulkan/vulkan_bootstrap_functions.inc" +#undef FN + +#define FN(NAME) api->NAME = (PFN_vk##NAME)api->GetInstanceProcAddr(VK_NULL_HANDLE, "vk" #NAME); #include "deimos/vulkan/vulkan_entry_functions.inc" #undef FN + return api; } + + void LoadInstance(VulkanApi* api, VkInstance instance) override + { + Expects(api != nullptr); + Expects(instance != VK_NULL_HANDLE); + Expects(api->GetInstanceProcAddr != nullptr); + +#define FN(NAME) api->NAME = (PFN_vk##NAME)api->GetInstanceProcAddr(instance, "vk" #NAME); +#include "deimos/vulkan/vulkan_instance_functions.inc" +#undef FN + } }; void RegisterVulkanLoaderApi(ApiRegistry* registry) diff --git a/main/main.cpp b/main/main.cpp index ca1e063..34d0276 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -7,6 +7,9 @@ using namespace deimos; static LogApi* log_api; +static TempAllocatorApi* temp_api; + +static const VkAllocationCallbacks* kVkAlloc = nullptr; [[nodiscard]] StatusOr CreateInstance(VulkanApi* vk) @@ -26,6 +29,7 @@ StatusOr CreateInstance(VulkanApi* vk) "VK_KHR_win32_surface", }; + // @Todo Select layers & extensions based on config const char* layers[]{ "VK_LAYER_KHRONOS_validation", "VK_LAYER_LUNARG_monitor", @@ -43,7 +47,7 @@ StatusOr CreateInstance(VulkanApi* vk) }; VkInstance instance{}; - const VkResult res = vk->CreateInstance(&create_info, nullptr, &instance); + const VkResult res = vk->CreateInstance(&create_info, kVkAlloc, &instance); if (res != VK_SUCCESS) { return InternalError("vkCreateInstance failed"); @@ -52,21 +56,118 @@ StatusOr CreateInstance(VulkanApi* vk) return instance; } +StatusOr FindPhysicalDevice(VulkanApi* vk, VkInstance instance) +{ + 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 devices = temp_alloc.allocator().NewArray(physical_device_count); + vk->EnumeratePhysicalDevices(instance, &physical_device_count, devices.data()); + + // @Todo Physical device selection + return devices[0]; +} + +StatusOr FindQueueFamily(VulkanApi* vk, VkPhysicalDevice device) +{ + auto temp_alloc = temp_api->Acquire(); + + uint32_t queue_family_count = 0; + vk->GetPhysicalDeviceQueueFamilyProperties(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(queue_family_count); + vk->GetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, queue_families.data()); + + for (uint32_t index = 0; index < queue_family_count; ++index) + { + const auto& prps = queue_families[index]; + if ((prps.queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0 && (prps.queueFlags & VK_QUEUE_COMPUTE_BIT) != 0) + { + return index; + } + } + + return InternalError("Couldn't find a suitable queue"); +} + +Status InitializeVulkan(ApiRegistry* api_registry) +{ + RegisterVulkanLoaderApi(api_registry); + auto* loader_api = api_registry->Get(); + auto* vk = loader_api->LoadEntry(); + + VkInstance instance{}; + { + StatusOr s = CreateInstance(vk); + if (!s.ok()) { return s.status(); } + instance = s.value(); + log_api->LogInfo("Vulkan instance created"); + } + + loader_api->LoadInstance(vk, instance); + + VkPhysicalDevice physical_device{}; + { + StatusOr s = FindPhysicalDevice(vk, instance); + if (!s.ok()) { return s.status(); } + physical_device = s.value(); + } + + { + + 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("Vulkan device: $", prps.properties.deviceName); + log_api->LogInfo("Vulkan driver: $, $", prps12.driverName, prps12.driverInfo); + } + + uint32_t queue_family = 0; + { + StatusOr s = FindQueueFamily(vk, physical_device); + if (!s.ok()) { return s.status(); } + queue_family = s.value(); + } + + log_api->LogInfo("Vulkan queue family: $", queue_family); + + vk->DestroyInstance(instance, kVkAlloc); + return {}; +} + int main(int /* argc */, char* /* argv */[]) { auto* api_registry = InitializeGlobalApiRegistry(); log_api = api_registry->Get(); - log_api->LogInfo("Base APIs registered"); - - RegisterVulkanLoaderApi(api_registry); - auto* vulkan_loader_api = api_registry->Get(); - auto* vk = vulkan_loader_api->LoadEntry(); + temp_api = api_registry->Get(); + log_api->LogInfo("Base APIs registered"); - const StatusOr s = CreateInstance(vk); + const Status s = InitializeVulkan(api_registry); if (!s.ok()) { - log_api->LogError("$", s); - } + log_api->LogError("Vulkan initializaiton error: $", s); + } + log_api->LogInfo("OK"); -- cgit