diff --git a/CMakeLists.txt b/CMakeLists.txt index c7289c9..85ca8d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,3 @@ - cmake_minimum_required(VERSION 3.10) project(xgpu VERSION 1.0.0 LANGUAGES CXX) diff --git a/examples/triangle/triangle.cc b/examples/triangle/triangle.cc index c1ee872..bb71ab7 100644 --- a/examples/triangle/triangle.cc +++ b/examples/triangle/triangle.cc @@ -11,14 +11,15 @@ #include #include #include +#include +#include + #include #include #include #include #include #include -#include -#include #include #include diff --git a/include/xgpu/constants.h b/include/xgpu/constants.h new file mode 100644 index 0000000..f9e7d53 --- /dev/null +++ b/include/xgpu/constants.h @@ -0,0 +1,11 @@ +#ifndef XGPU_CONSTANTS_H +#define XGPU_CONSTANTS_H + +#include + +namespace xgpu +{ + constexpr std::size_t BackendObjectBufferSize { 256 }; +} // namespace xgpu + +#endif // XGPU_CONSTANTS_H diff --git a/include/xgpu/device.h b/include/xgpu/device.h index bead99c..273443e 100644 --- a/include/xgpu/device.h +++ b/include/xgpu/device.h @@ -82,7 +82,6 @@ namespace xgpu private: explicit Device() noexcept = default; - private: std::unique_ptr _handle { nullptr }; }; diff --git a/include/xgpu/instance.h b/include/xgpu/instance.h index dcfd0d1..6cfba78 100644 --- a/include/xgpu/instance.h +++ b/include/xgpu/instance.h @@ -1,39 +1,93 @@ #ifndef RHI_INSTANCE_H #define RHI_INSTANCE_H +#include "types.h" +#include "xgpu/constants.h" #include "xgpu/instance_context.h" #include "xgpu/expected.h" #include "xgpu/error.h" #include "xgpu/platform.h" +#include +#include +#include #include +#include +#include namespace xgpu { - class IInstance + // class Instance : public IInstance + class Instance { + // VTable info for type erasure + private: + struct VTable + { + std::functionvoid> destroy; + std::functionBackend> backend; + }; + + alignas(std::max_align_t) char8_t _buffer[BackendObjectBufferSize]; + const VTable* _vtable { nullptr }; + + // Getters for underlying pointer to buffer + auto ptr() -> void* { return _buffer; } + auto ptr() const -> const void* { return _buffer; } + public: - // Destroy the handle - virtual auto destroy() noexcept -> void = 0; - virtual auto backend() const noexcept -> Backend = 0; - }; - - class [[nodiscard]] Instance : public IInstance - { + template + static auto get_vtable() -> const VTable& + { + static const VTable& table = { + .destroy = [](void* obj) { static_cast(obj)->destroy(); }, + .backend = [](const void* obj) { return static_cast(obj)->backend(); }, + }; + + return table; + } + + + template + auto get_as() -> T* { return reinterpret_cast(_buffer); } + public: - [[nodiscard("This returns a status code depending on if failed")]] - static auto create(const InstanceContext& ctx) noexcept -> expected; + auto destroy() noexcept -> void + { + _vtable->destroy(ptr()); + } - auto destroy() noexcept -> void override { _handle->destroy(); } + auto backend() const noexcept -> Backend + { + return _vtable->backend(ptr()); + } - [[nodiscard]] auto backend() const noexcept -> Backend override { return _handle->backend(); } + // Special members + public: + Instance(Instance&&) = default; + Instance(const Instance&) = default; + + auto operator=(Instance&&) -> Instance& = default; + auto operator=(const Instance&) -> Instance& = default; + + // Private conversion constructor from backend object instance + private: + template , xgpu::Instance>>> + Instance(T&& backend_instance) + { + using DecayT = std::decay_t; + static_assert(sizeof(DecayT) <= BackendObjectBufferSize, "Backend object T is too large to store in xgpu::Instance"); + static_assert(alignof(DecayT) <= alignof(std::max_align_t), "Backend object T has incompatible alignment"); + + new (_buffer) DecayT(std::forward(backend_instance)); + _vtable = &get_vtable(); + } - [[nodiscard]] auto handle() const noexcept -> IInstance* { return _handle.get(); } + public: + [[nodiscard("This returns a status code depending on if failed")]] + static auto create(const InstanceContext& ctx) noexcept -> expected; protected: explicit Instance() noexcept = default; - - private: - std::unique_ptr _handle { nullptr }; }; } // namespace xgpu diff --git a/include/xgpu/vk/device.h b/include/xgpu/vk/device.h index ccbc0ef..83ebfc0 100644 --- a/include/xgpu/vk/device.h +++ b/include/xgpu/vk/device.h @@ -1,5 +1,7 @@ #ifndef RHI_VK_DEVICE_H #define RHI_VK_DEVICE_H +#include "xgpu/constants.h" +#include "xgpu/platform.h" #ifdef RHI_COMPILE_VULKAN_BACKEND #include diff --git a/include/xgpu/vk/instance.h b/include/xgpu/vk/instance.h index d1ea313..9f76fc4 100644 --- a/include/xgpu/vk/instance.h +++ b/include/xgpu/vk/instance.h @@ -4,15 +4,16 @@ #ifdef RHI_COMPILE_VULKAN_BACKEND #include "xgpu/platform.h" -#include "xgpu/instance.h" #include "xgpu/vk/debug.h" +#include "xgpu/vk/instance_context.h" +#include "xgpu/error.h" #include "xgpu/vk/core.h" #include namespace xgpu::vk { - class [[nodiscard]] Instance : public xgpu::IInstance + class Instance { // Factory public: @@ -20,15 +21,16 @@ namespace xgpu::vk // API public: - auto destroy() noexcept -> void override; + auto destroy() noexcept -> void; - auto backend() const noexcept -> Backend override { return Backend::Vulkan; } + auto backend() const noexcept -> Backend { return Backend::Vulkan; } auto native_handle() const noexcept -> VkInstance { return _handle; } + // Private fields private: std::optional _debug_messenger { std::nullopt }; - VkInstance _handle { VK_NULL_HANDLE }; + VkInstance _handle { VK_NULL_HANDLE }; }; } // namespace xgpu::vk diff --git a/include/xgpu/vulkan.h b/include/xgpu/vulkan.h index ba040d4..d542b3c 100644 --- a/include/xgpu/vulkan.h +++ b/include/xgpu/vulkan.h @@ -1,7 +1,9 @@ #ifndef RHI_VULKAN_H #define RHI_VULKAN_H +#include "xgpu/vk/core.h" #include "xgpu/vk/instance.h" #include "xgpu/vk/device.h" +#include "xgpu/vk/buffer.h" #endif // RHI_VULKAN_H diff --git a/src/instance.cc b/src/instance.cc index e507dce..8f3d6e9 100644 --- a/src/instance.cc +++ b/src/instance.cc @@ -27,7 +27,8 @@ namespace xgpu return unexpected(expected_instance.unwrap_error()); } - instance._handle = std::make_unique(expected_instance.unwrap()); + instance = expected_instance.unwrap(); + return ok(instance); } } diff --git a/src/vk/device.cc b/src/vk/device.cc index cb658c8..09b2e65 100644 --- a/src/vk/device.cc +++ b/src/vk/device.cc @@ -227,7 +227,8 @@ namespace xgpu::vk } auto Device::create_default(const DefaultDeviceContext& ctx) noexcept -> expected { - auto& vk_handle = *dynamic_cast(ctx.instance.handle()); + auto& vk_handle = *ctx.instance.get_as(); + // auto& vk_handle = *dynamic_cast(ctx.instance.handle()); auto available_devices = getPhysicalDevices(vk_handle.native_handle()); // This is largely based on https://vulkan-tutorial.com/Drawing_a_triangle/Setup/Physical_devices_and_queue_families