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