From 98c8fd5d39ee645922f071b6433308a813245500 Mon Sep 17 00:00:00 2001 From: Steven Le Rouzic <steven.lerouzic@gmail.com> Date: Wed, 24 Apr 2024 00:29:16 +0200 Subject: Add custom formatter & use it on Status --- .clang-tidy | 2 ++ deimos/core/base.h | 6 ++++- deimos/core/format.cpp | 3 +++ deimos/core/format.h | 39 ++++++++++++++++++++++------ deimos/core/log.h | 6 ++--- deimos/core/status.cpp | 30 +++++++++++++++++++-- deimos/core/status.h | 10 +++++++ deimos/core/std.h | 1 + deimos/core/temp_allocator.cpp | 2 +- deimos/vulkan/vulkan.h | 11 +++++++- main/main.cpp | 59 +++++++++++++++++++++++++++++++++++++++--- 11 files changed, 149 insertions(+), 20 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index c8c8c55..be78fe7 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -24,3 +24,5 @@ Checks: - "-*-noexcept-move" - "-*-noexcept-move-constructor" - "-*-noexcept-move-operations" + - "-*-bounds-array-to-pointer-decay" + - "-*-no-array-decay" diff --git a/deimos/core/base.h b/deimos/core/base.h index f24991d..5cd3a40 100644 --- a/deimos/core/base.h +++ b/deimos/core/base.h @@ -78,6 +78,9 @@ constexpr void MemoryCopy(void* dst, const void* src, int64_t size) __builtin_memcpy(dst, src, (size_t)size); } +template<typename T, int64_t N> +constexpr int64_t ArraySize(const T (&)[N]) { return N; } + template<typename T> class Span { @@ -113,6 +116,7 @@ public: constexpr T* begin() const { return m_begin; } 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; } }; template<typename T> @@ -141,8 +145,8 @@ public: {} constexpr const char* data() const { return m_begin; } - constexpr int64_t size() const { return m_size; } + constexpr bool empty() const { return m_size == 0; } }; inline Span<const std::byte> AsBytes(StringView span) diff --git a/deimos/core/format.cpp b/deimos/core/format.cpp index af0a851..5f420e9 100644 --- a/deimos/core/format.cpp +++ b/deimos/core/format.cpp @@ -51,6 +51,9 @@ public: case FormatArg::kUnsignedInteger: PushUnsignedInteger(arg.integer); break; + case FormatArg::kCustom: + arg.custom.callback(m_writer, arg.custom.payload); + break; } } }; diff --git a/deimos/core/format.h b/deimos/core/format.h index 3424dc2..ba1a3e2 100644 --- a/deimos/core/format.h +++ b/deimos/core/format.h @@ -7,6 +7,14 @@ namespace deimos class IWriter; +struct CustomFormatter +{ + using Callback = void (*)(IWriter*, const void* payload); + + const void* payload; + Callback callback; +}; + struct FormatArg { enum Type : uint8_t @@ -14,42 +22,57 @@ struct FormatArg kInteger, kUnsignedInteger, kString, + kCustom, }; Type type; union { - int64_t integer; - uint64_t unsigned_integer; - StringView string; + int64_t integer; + uint64_t unsigned_integer; + StringView string; + CustomFormatter custom; }; - explicit FormatArg(std::signed_integral auto value) : + constexpr explicit FormatArg(std::signed_integral auto value) : type{kInteger}, integer{value} {} - explicit FormatArg(std::unsigned_integral auto value) : + constexpr explicit FormatArg(std::unsigned_integral auto value) : type{kUnsignedInteger}, unsigned_integer{value} {} - explicit FormatArg(StringView value) : + constexpr explicit FormatArg(StringView value) : type{kString}, string{value} {} + + template<typename T> + constexpr explicit FormatArg(const T& payload, CustomFormatter::Callback callback) : + type{kCustom}, + custom{CustomFormatter{&payload, callback}} + {} }; template<typename T> -concept Formattable = requires (T value) { FormatArg(value); }; +deimos::FormatArg DeimosMakeFormatArg(T&& value) + requires std::is_constructible_v<deimos::FormatArg, T> +{ + return deimos::FormatArg(value); +} + +template<typename T> +concept Formattable = std::same_as<deimos::FormatArg, decltype(DeimosMakeFormatArg(std::declval<T>()))>; void FormatVa(IWriter*, gsl::czstring fmt, Span<const FormatArg>); template<Formattable... Args> void Format(IWriter* writer, gsl::czstring fmt, Args&&... args) { - FormatVa(writer, fmt, { FormatArg(std::forward<Args>(args))... }); + FormatVa(writer, fmt, { DeimosMakeFormatArg(std::forward<Args>(args))... }); } } // namespace deimos diff --git a/deimos/core/log.h b/deimos/core/log.h index 9bb4dbb..260d9a4 100644 --- a/deimos/core/log.h +++ b/deimos/core/log.h @@ -50,19 +50,19 @@ public: template<Formattable... Args> void LogInfo(const LogSourceLocation& fmt_source_location, Args&&... args) { - LogVa(LogSeverity::kInfo, fmt_source_location.source_location, fmt_source_location.fmt, { FormatArg(std::forward<Args>(args))... }); + LogVa(LogSeverity::kInfo, fmt_source_location.source_location, fmt_source_location.fmt, { DeimosMakeFormatArg(std::forward<Args>(args))... }); } template<Formattable... Args> void LogDebug(const LogSourceLocation& fmt_source_location, Args&&... args) { - LogVa(LogSeverity::kDebug, fmt_source_location.source_location, fmt_source_location.fmt, { FormatArg(std::forward<Args>(args))... }); + LogVa(LogSeverity::kDebug, fmt_source_location.source_location, fmt_source_location.fmt, { DeimosMakeFormatArg(std::forward<Args>(args))... }); } template<Formattable... Args> void LogError(const LogSourceLocation& fmt_source_location, Args&&... args) { - LogVa(LogSeverity::kError, fmt_source_location.source_location, fmt_source_location.fmt, { FormatArg(std::forward<Args>(args))... }); + LogVa(LogSeverity::kError, fmt_source_location.source_location, fmt_source_location.fmt, { DeimosMakeFormatArg(std::forward<Args>(args))... }); } }; diff --git a/deimos/core/status.cpp b/deimos/core/status.cpp index f472aac..efdd6df 100644 --- a/deimos/core/status.cpp +++ b/deimos/core/status.cpp @@ -9,6 +9,18 @@ static deimos::AllocatorApi* allocator_api; namespace deimos { +StringView StatusCodeToString(StatusCode code) +{ + switch (code) + { + case StatusCode::kOk: return StringView("OK"); + case StatusCode::kUnknown: return StringView("Unknown error"); + case StatusCode::kInvalidArgument: return StringView("Invalid argument"); + case StatusCode::kUnimplemented: return StringView("Unimplemented"); + case StatusCode::kInternal: return StringView("Internal error"); + } +} + struct StatusRep { Atomic<int32_t> ref_count; @@ -18,9 +30,9 @@ struct StatusRep Status::Status(StatusCode code, StringView message) { - if (code == StatusCode::kOk) + if (code == StatusCode::kOk || message.empty()) { - m_rep = CodeToRep(StatusCode::kOk); + m_rep = CodeToRep(code); } else { @@ -74,6 +86,20 @@ void Status::Unref() const } } +void Status::FormatStatus(IWriter* writer, const void* payload) +{ + const auto& status = *(const Status*)payload; // NOLINT + if (status.IsInline()) + { + Format(writer, "[$]", StatusCodeToString(status.code())); + } + else + { + auto* rep = std::bit_cast<StatusRep*>(status.m_rep); + Format(writer, "[$: $]", StatusCodeToString(rep->code), rep->message); + } +} + void InitializeStatus(ApiRegistry* api_registry) { allocator_api = api_registry->Get<AllocatorApi>(); diff --git a/deimos/core/status.h b/deimos/core/status.h index bf34833..7350cc5 100644 --- a/deimos/core/status.h +++ b/deimos/core/status.h @@ -1,6 +1,7 @@ #pragma once #include "deimos/core/base.h" +#include "deimos/core/format.h" namespace deimos { @@ -15,6 +16,8 @@ enum class StatusCode : uint32_t kInternal, }; +StringView StatusCodeToString(StatusCode code); + class Status { uintptr_t m_rep; @@ -79,6 +82,13 @@ public: if (IsInline()) { return (StatusCode)(m_rep >> 1U); } return RepCode(); } + + static void FormatStatus(IWriter* writer, const void* payload); + + friend constexpr deimos::FormatArg DeimosMakeFormatArg(const Status& status) + { + return deimos::FormatArg(status, FormatStatus); + } }; inline Status UnknownError(StringView message = {}) diff --git a/deimos/core/std.h b/deimos/core/std.h index 4bb2983..0e0336e 100644 --- a/deimos/core/std.h +++ b/deimos/core/std.h @@ -24,6 +24,7 @@ template<typename T> concept signed_integral = integral<T> && __is_signed(T); template<typename T> concept unsigned_integral = integral<T> && __is_unsigned(T); template<typename T> constexpr bool is_trivially_destructible_v = __is_trivially_destructible(T); +template<typename T, typename... Args> constexpr bool is_constructible_v = __is_constructible(T, Args...); template<typename T> constexpr bool is_trivially_copyable_v = __is_trivially_copyable(T); template<typename U, typename V> constexpr bool _is_same_helper = false; diff --git a/deimos/core/temp_allocator.cpp b/deimos/core/temp_allocator.cpp index 2f07bcc..d2b7900 100644 --- a/deimos/core/temp_allocator.cpp +++ b/deimos/core/temp_allocator.cpp @@ -41,7 +41,7 @@ public: if (new_current > m_commit_end) { const int64_t new_commit_size = AlignUp( - (uint64_t)(std::bit_cast<uintptr_t>(new_current) - std::bit_cast<uintptr_t>(m_base)), // NOLINT + (int64_t)(std::bit_cast<uintptr_t>(new_current) - std::bit_cast<uintptr_t>(m_base)), // NOLINT kPageSize); os_api->virtual_memory->Commit(m_base, new_commit_size); m_commit_end = OffsetBytes(m_base, new_commit_size); diff --git a/deimos/vulkan/vulkan.h b/deimos/vulkan/vulkan.h index 549deac..48d53b3 100644 --- a/deimos/vulkan/vulkan.h +++ b/deimos/vulkan/vulkan.h @@ -1,7 +1,16 @@ #pragma once #include <deimos/core/id_name.h> -#include <vulkan.h> + +#define VK_NO_STDINT_H +#define VK_NO_STDDEF_H +#include <vk_platform.h> + +#include <vulkan_core.h> + +using HINSTANCE = void*; +using HWND = void*; +#include <vulkan_win32.h> namespace deimos { diff --git a/main/main.cpp b/main/main.cpp index 9fb4755..00e7d71 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -1,22 +1,73 @@ #include <deimos/core/api_registry.h> #include <deimos/core/log.h> #include <deimos/core/temp_allocator.h> +#include <deimos/core/status.h> #include <deimos/vulkan/vulkan.h> using namespace deimos; +static LogApi* log_api; + +[[nodiscard]] +Status CreateInstance(VulkanApi* vk, VkInstance* instance) +{ + 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", + "VK_KHR_win32_surfaceu", + }; + + const char* layers[]{ + "VK_LAYER_KHRONOS_validation", + "VK_LAYER_LUNARG_monitor", + }; + + 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, + }; + + VkResult res = vk->CreateInstance(&create_info, nullptr, instance); + if (res != VK_SUCCESS) + { + return UnknownError(); + } + + return {}; +} + int main(int /* argc */, char* /* argv */[]) { auto* api_registry = InitializeGlobalApiRegistry(); - auto* log_api = api_registry->Get<LogApi>(); + log_api = api_registry->Get<LogApi>(); log_api->LogInfo("Base APIs registered"); RegisterVulkanLoaderApi(api_registry); auto* vulkan_loader_api = api_registry->Get<VulkanLoaderApi>(); - auto* vk = vulkan_loader_api->LoadEntry(); + auto* vk = vulkan_loader_api->LoadEntry(); + + VkInstance instance{}; + Status s = CreateInstance(vk, &instance); + if (!s.ok()) + { + log_api->LogError("Couldn't create Vulkan instance: $", s); + } - Ensures(vk->GetInstanceProcAddr != nullptr); - Ensures(vk->CreateInstance != nullptr); log_api->LogInfo("OK"); return 0; -- cgit