initial commit

This commit is contained in:
2024-04-06 14:11:26 +02:00
commit 1d44ecc0ee
85 changed files with 11573 additions and 0 deletions

29
include/iwa/addon.hpp Normal file
View File

@@ -0,0 +1,29 @@
#pragma once
#if !defined(IWA_ADDON_HPP_INCLUDED)
#define IWA_ADDON_HPP_INCLUDED
#include <functional>
#include <span>
#include "iwa/instance.hpp"
namespace iwa
{
struct AddonInitArgs
{
ObjectPtr<Instance> instance;
InstanceCreationArgs& instanceCreationArgs;
};
class Addon
{
public:
Addon();
virtual ~Addon() = default;
virtual void init(const AddonInitArgs& args) = 0;
virtual void cleanup() = 0;
};
[[nodiscard]] std::span<Addon* const> getAddons() noexcept;
} // namespace iwa
#endif // !defined(IWA_ADDON_HPP_INCLUDED)

View File

@@ -0,0 +1,62 @@
#pragma once
#if !defined(IWA_ADDONS_IMGUI_ADDON_HPP_INCLUDED)
#define IWA_ADDONS_IMGUI_ADDON_HPP_INCLUDED
#include <memory>
#include <type_traits>
#include <vector>
#include "iwa/addon.hpp"
#include "iwa/descriptor_set.hpp"
#include "iwa/swapchain.hpp"
#include "iwa/window.hpp"
#include "iwa/addons/imgui/widget.hpp"
namespace iwa
{
struct ImguiCreateResourcesArgs
{
Swapchain& swapchain;
vk::Format format;
};
class ImGuiAddon : public Addon
{
private:
ObjectPtr<DescriptorPool> mDescriptorPool;
std::vector<std::unique_ptr<ImGuiWidget>> mWidgets;
public:
void init(const AddonInitArgs& args) override;
void cleanup() override;
mijin::Task<> c_createResources(const ImguiCreateResourcesArgs& args);
void renderFrame(vk::CommandBuffer cmdBuffer);
template<typename TWidget> requires (std::is_base_of_v<ImGuiWidget, TWidget>)
TWidget* addWidget(std::unique_ptr<TWidget>&& widget)
{
TWidget* raw = widget.get();
mWidgets.push_back(std::move(widget));
return raw;
}
template<typename TWidget, typename... TArgs> requires (std::is_base_of_v<ImGuiWidget, TWidget>)
TWidget* emplaceWidget(TArgs&&... args)
{
return addWidget(std::make_unique<TWidget>(std::forward<TArgs>(args)...));
}
void removeWidget(ImGuiWidget* widget);
private:
void beginFrame() noexcept;
void handleKeyChanged(const KeyEvent& event);
void handleMouseButtonChanged(const MouseButtonEvent& event);
void handleMouseMoved(const MouseMoveEvent& event);
void handleMouseScrolled(const MouseWheelEvent& event);
void handleTextEntered(const TextInputEvent& event);
public:
static ImGuiAddon& get() noexcept;
};
} // namespace
#endif // !defined(IWA_ADDONS_IMGUI_ADDON_HPP_INCLUDED)

View File

@@ -0,0 +1,22 @@
#pragma once
#if !defined(IWA_ADDONS_IMGUI_FPS_WIDGET_HPP_INCLUDED)
#define IWA_ADDONS_IMGUI_FPS_WIDGET_HPP_INCLUDED
#include <chrono>
#include "iwa/addons/imgui/widget.hpp"
#include "iwa/util/fps_calculator.hpp"
namespace iwa
{
class ImGuiFpsWidget : public ImGuiWidget
{
private:
FpsCalculator<100> mFpsCalculator;
public:
void draw() override;
};
} // namespace iwa
#endif // !defined(IWA_ADDONS_IMGUI_FPS_WIDGET_HPP_INCLUDED)

View File

@@ -0,0 +1,34 @@
#pragma once
#if !defined(IWA_ADDONS_IMGUI_MISC_WIDGETS_HPP_INCLUDED)
#define IWA_ADDONS_IMGUI_MISC_WIDGETS_HPP_INCLUDED
#include <imgui.h>
#include <magic_enum.hpp>
namespace iwa
{
template<typename TEnum>
bool MagicCombo(const char* label, TEnum& currentValue)
{
const std::string currentValueName(magic_enum::enum_name(currentValue));
bool result = false;
if (ImGui::BeginCombo(label, currentValueName.c_str()))
{
for (TEnum value : magic_enum::enum_values<TEnum>())
{
const std::string valueName(magic_enum::enum_name(value));
if (ImGui::Selectable(valueName.c_str())) {
currentValue = value;
result = true;
}
}
ImGui::EndCombo();
}
return result;
}
} // namespace iwa
#endif // !defined(IWA_ADDONS_IMGUI_MISC_WIDGETS_HPP_INCLUDED)

View File

@@ -0,0 +1,20 @@
#pragma once
#if !defined(IWA_ADDONS_IMGUI_WIDGET_HPP_INCLUDED)
#define IWA_ADDONS_IMGUI_WIDGET_HPP_INCLUDED
#include "iwa/object.hpp"
namespace iwa
{
class ImGuiWidget
{
public:
virtual ~ImGuiWidget() = default;
virtual void draw() = 0;
};
} // namespace iwa
#endif // !defined(IWA_ADDONS_IMGUI_WIDGET_HPP_INCLUDED)

View File

@@ -0,0 +1,45 @@
#pragma once
#if !defined(IWA_APP_VULLKAN_APPLICATION_HPP_INCLUDED)
#define IWA_APP_VULLKAN_APPLICATION_HPP_INCLUDED
#include <mijin/util/bitflags.hpp>
#include "iwa/device.hpp"
#include "iwa/instance.hpp"
#include "iwa/swapchain.hpp"
namespace iwa
{
struct ApplicationCreationFlags
{
std::uint8_t skipDefaultArgs : 1 = 0;
};
struct ApplicationCreationArgs
{
ApplicationCreationFlags flags;
InstanceCreationArgs instanceArgs;
DeviceCreationArgs deviceArgs;
WindowCreationArgs mainWindowArgs;
SwapchainCreationArgs mainWindowSwapchainArgs;
fs::path assetPath = fs::current_path() / "assets";
};
class VulkanApplication : public Object<VulkanApplication>
{
protected:
ObjectPtr<Instance> mInstance;
ObjectPtr<Device> mDevice;
ObjectPtr<Window> mMainWindow;
ObjectPtr<Swapchain> mMainWindowSwapchain;
protected:
explicit VulkanApplication(const ApplicationCreationArgs& args, ObjectPtr<> owner = nullptr);
public:
virtual mijin::Task<> c_init() = 0;
[[nodiscard]] int execute(int argc, char** argv); // NOLINT
};
} // namespace iwa
#endif // !defined(IWA_APP_VULLKAN_APPLICATION_HPP_INCLUDED)

152
include/iwa/buffer.hpp Normal file
View File

@@ -0,0 +1,152 @@
#pragma once
#if !defined(IWA_BUFFER_HPP_INCLUDED)
#define IWA_BUFFER_HPP_INCLUDED
#include <ranges>
#include <mijin/async/coroutine.hpp>
#include <mijin/container/stride_span.hpp>
#include <mijin/container/typeless_buffer.hpp>
#include <mijin/util/flag.hpp>
#include "iwa/device_memory.hpp"
#include "iwa/util/vkutil.hpp"
namespace iwa
{
MIJIN_DEFINE_FLAG(HostVisible);
MIJIN_DEFINE_FLAG(HostCoherent);
struct BufferCreationArgs
{
vk::BufferCreateFlags flags;
vk::DeviceSize size;
vk::BufferUsageFlags usage;
vk::SharingMode sharingMode = vk::SharingMode::eExclusive;
std::vector<std::uint32_t> queueFamilyIndices;
};
class Buffer : public Object<Buffer, BaseObject, class Device>, public MixinVulkanObject<vk::Buffer>
{
public:
ObjectPtr<DeviceMemory> mMemory;
vk::DeviceSize mBytesSize;
public:
Buffer(ObjectPtr<class Device> owner, const BufferCreationArgs& args);
~Buffer() noexcept override;
[[nodiscard]] const ObjectPtr<DeviceMemory>& getMemory() const noexcept { return mMemory; }
void allocateMemory(HostVisible hostVisible = HostVisible::NO, HostCoherent hostCoherent = HostCoherent::YES); // the latter is ignored if the first one is NO
void bindMemory(ObjectPtr<DeviceMemory> memory, vk::DeviceSize offset = 0);
mijin::Task<> c_fill(std::uint32_t data, std::size_t bytes = VK_WHOLE_SIZE, std::size_t byteOffset = 0);
mijin::Task<> c_copyFrom(vk::Buffer srcBuffer, vk::BufferCopy region);
mijin::Task<> c_upload(const void* data, std::size_t bytes, std::size_t byteOffset = 0);
mijin::Task<> c_upload(const mijin::TypelessBuffer& data, std::size_t byteOffset = 0);
template<std::ranges::contiguous_range TRange>
mijin::Task<> c_upload(const TRange& range, std::size_t byteOffset = 0)
{
return c_upload(std::ranges::cdata(range), std::ranges::size(range) * sizeof(std::ranges::range_value_t<TRange>), byteOffset);
}
};
template<typename T>
struct TypedBufferCreationArgs
{
vk::BufferCreateFlags flags;
vk::BufferUsageFlags usage;
unsigned arraySize = 1;
vk::DeviceSize stride = sizeof(T);
vk::SharingMode sharingMode = vk::SharingMode::eExclusive;
std::vector<std::uint32_t> queueFamilyIndices;
};
template<typename Type>
class TypedBuffer : public Object<TypedBuffer<Type>, Buffer>
{
public:
using super_t = typename Object<TypedBuffer<Type>, Buffer>::super_t;
protected:
unsigned mArraySize = 0;
vk::DeviceSize mStride = 0;
public:
TypedBuffer(ObjectPtr<class Device> owner, const TypedBufferCreationArgs<Type>& args)
: super_t(std::move(owner), BufferCreationArgs{
.flags = args.flags,
.size = sizeof(Type) + (args.arraySize - 1) * args.stride,
.usage = args.usage,
.sharingMode = args.sharingMode,
.queueFamilyIndices = args.queueFamilyIndices
}), mArraySize(args.arraySize), mStride(args.stride) {}
[[nodiscard]] unsigned getArraySize() const noexcept { return mArraySize; }
[[nodiscard]] vk::DeviceSize getStride() const noexcept { return mStride; }
[[nodiscard]] mijin::StrideSpan<Type> mapToSpan() const;
};
template<typename T>
struct TypedUniformBufferCreationArgs
{
vk::BufferCreateFlags flags;
vk::BufferUsageFlags usage;
unsigned arraySize = 1;
vk::SharingMode sharingMode = vk::SharingMode::eExclusive;
std::vector<std::uint32_t> queueFamilyIndices;
};
template<typename Type>
class TypedUniformBuffer : public Object<TypedUniformBuffer<Type>, TypedBuffer<Type>>
{
public:
using super_t = typename Object<TypedUniformBuffer<Type>, TypedBuffer<Type>>::super_t;
public:
TypedUniformBuffer(ObjectPtr<class Device> owner, const TypedUniformBufferCreationArgs<Type>& args)
: super_t(owner, TypedBufferCreationArgs<Type>{
.flags = args.flags,
.usage = args.usage | vk::BufferUsageFlagBits::eUniformBuffer,
.arraySize = args.arraySize,
.stride = calcVkUniformStride<Type>(*owner),
.sharingMode = args.sharingMode,
.queueFamilyIndices = args.queueFamilyIndices
}) {}
};
template<typename Type>
using TypedStorageBufferCreationArgs = TypedUniformBufferCreationArgs<Type>;
template<typename Type>
class TypedStorageBuffer : public Object<TypedStorageBuffer<Type>, TypedBuffer<Type>>
{
public:
using super_t = typename Object<TypedStorageBuffer<Type>, TypedBuffer<Type>>::super_t;
public:
TypedStorageBuffer(ObjectPtr<class Device> owner, const TypedUniformBufferCreationArgs<Type>& args)
: super_t(owner, TypedBufferCreationArgs<Type>{
.flags = args.flags,
.usage = args.usage | vk::BufferUsageFlagBits::eStorageBuffer,
.arraySize = args.arraySize,
.stride = calcVkStorageBufferStride<Type>(*owner),
.sharingMode = args.sharingMode,
.queueFamilyIndices = args.queueFamilyIndices
}) {}
};
template<typename Type>
mijin::StrideSpan<Type> TypedBuffer<Type>::mapToSpan() const
{
std::byte* rawMemory = static_cast<std::byte*>(this->getOwner()->getVkHandle().mapMemory(
/* memory = */ *this->getMemory(),
/* offset = */ 0,
/* size = */ VK_WHOLE_SIZE
));
return mijin::StrideSpan<Type>(
std::bit_cast<Type*>(rawMemory),
std::bit_cast<Type*>(rawMemory + mArraySize * mStride),
mStride
);
}
} // namespace iwa
#endif // !defined(IWA_BUFFER_HPP_INCLUDED)

41
include/iwa/command.hpp Normal file
View File

@@ -0,0 +1,41 @@
#pragma once
#if !defined(IWA_COMMAND_HPP_INCLUDED)
#define IWA_COMMAND_HPP_INCLUDED
#include "iwa/object.hpp"
#include "iwa/vkwrapper.hpp"
namespace iwa
{
struct CommandPoolCreationArgs
{
vk::CommandPoolCreateFlags flags;
std::uint32_t queueFamilyIndex;
};
struct CommandBufferAllocateArgs
{
vk::CommandBufferLevel level = vk::CommandBufferLevel::ePrimary;
};
class CommandPool : public Object<CommandPool, BaseObject, class Device>, public MixinVulkanObject<vk::CommandPool>
{
public:
CommandPool(ObjectPtr<class Device> owner, CommandPoolCreationArgs args);
~CommandPool() noexcept override;
[[nodiscard]] ObjectPtr<class CommandBuffer> allocateCommandBuffer(const CommandBufferAllocateArgs& args = {});
// [[nodiscard]] std::vector<vk::CommandBuffer> allocateCommandBuffers(std::size_t count, const CommandBufferAllocateArgs& args = {}) const noexcept;
};
class CommandBuffer : public Object<CommandBuffer, BaseObject, CommandPool>, public MixinVulkanObject<vk::CommandBuffer>
{
public:
CommandBuffer(ObjectPtr<CommandPool> owner, vk::CommandBuffer handle);
~CommandBuffer() noexcept override;
};
} // namespace iwa
#endif // !defined(IWA_COMMAND_HPP_INCLUDED)

View File

@@ -0,0 +1,61 @@
#pragma once
#if !defined(IWA_DESCRIPTOR_SET_HPP_INCLUDED)
#define IWA_DESCRIPTOR_SET_HPP_INCLUDED
#include <vector>
#include "iwa/object.hpp"
#include "iwa/vkwrapper.hpp"
namespace iwa
{
struct DescriptorSetLayoutCreationArgs
{
std::vector<vk::DescriptorSetLayoutBinding> bindings;
std::vector<vk::DescriptorBindingFlags> bindingFlags;
vk::DescriptorSetLayoutCreateFlags flags = {};
};
class DescriptorSetLayout : public Object<DescriptorSetLayout, BaseObject, class Device>, public MixinVulkanObject<vk::DescriptorSetLayout>
{
public:
DescriptorSetLayout(ObjectPtr<class Device> owner, const DescriptorSetLayoutCreationArgs& args);
~DescriptorSetLayout() noexcept override;
};
struct DescriptorPoolCreationArgs
{
vk::DescriptorPoolCreateFlags flags = {};
unsigned maxSets = 0;
std::vector<vk::DescriptorPoolSize> poolSizes;
};
struct DescriptorSetAllocateArgs
{
ObjectPtr<DescriptorSetLayout> layout;
std::uint32_t variableDescriptorCount = 0;
};
class DescriptorPool : public Object<DescriptorPool, BaseObject, class Device>, public MixinVulkanObject<vk::DescriptorPool>
{
private:
bool mCanFree = false;
public:
DescriptorPool(ObjectPtr<class Device> owner, const DescriptorPoolCreationArgs& args);
~DescriptorPool() noexcept override;
[[nodiscard]] ObjectPtr<class DescriptorSet> allocateDescriptorSet(const DescriptorSetAllocateArgs& args);
[[nodiscard]] bool getCanFree() const noexcept { return mCanFree; }
};
class DescriptorSet : public Object<DescriptorSet, BaseObject, DescriptorPool>, public MixinVulkanObject<vk::DescriptorSet>
{
public:
DescriptorSet(ObjectPtr<DescriptorPool> owner, vk::DescriptorSet handle);
~DescriptorSet() noexcept override;
};
} // namespace iwa
#endif // !defined(IWA_DESCRIPTOR_SET_HPP_INCLUDED)

164
include/iwa/device.hpp Normal file
View File

@@ -0,0 +1,164 @@
#pragma once
#ifndef IWA_VULKAN_DEVICE_HPP_INCLUDED
#define IWA_VULKAN_DEVICE_HPP_INCLUDED
#include <limits>
#include <shared_mutex>
#include <mijin/util/bitflags.hpp>
#include <mijin/util/flag.hpp>
#include "iwa/command.hpp"
#include "iwa/device_memory.hpp"
#include "iwa/fence.hpp"
#include "iwa/vkwrapper.hpp"
#include "iwa/util/task_runner.hpp"
#define IWA_DELETE_DEVICE_OBJECT(device, var, deleter) \
if (var) \
{ \
(device)->queueDelete([handle=(var), dev=(device)->getVkHandle()]() \
{ \
dev.deleter(handle); \
}); \
}
namespace iwa
{
struct ExtensionInfo // TODO: where to put this?
{
const char* name;
union
{
// same thing, one for initialisation, one for runtime
bool required;
bool enabled;
};
};
using LayerInfo = ExtensionInfo;
struct RendererFeatures : mijin::BitFlags<RendererFeatures>
{
bool rayTracing : 1 = false;
bool meshShaders : 1 = false;
};
struct PhysicalDeviceInfo
{
vk::PhysicalDevice device;
vk::PhysicalDeviceProperties properties;
vk::SurfaceCapabilitiesKHR surfaceCapabilities;
vk::PhysicalDeviceFeatures features;
vk::PhysicalDeviceVulkan11Features vulkan11Features;
vk::PhysicalDeviceVulkan12Features vulkan12Features;
vk::PhysicalDeviceVulkan13Features vulkan13Features;
vk::PhysicalDeviceAccelerationStructureFeaturesKHR accelerationStructureFeatures;
vk::PhysicalDeviceRayTracingPipelineFeaturesKHR rayTracingPipelineFeatures;
vk::PhysicalDeviceMeshShaderFeaturesEXT meshShaderFeatures;
vk::PhysicalDeviceMemoryProperties memoryProperties;
vk::PhysicalDeviceRayTracingPipelinePropertiesKHR rayTracingProperties;
std::vector<vk::ExtensionProperties> extensions;
std::uint32_t graphicsQueueFamily = std::numeric_limits<std::uint32_t>::max();
std::uint32_t computeQueueFamily = std::numeric_limits<std::uint32_t>::max();
RendererFeatures availableFeatures = {};
};
struct PhysicalDeviceCriteria
{
template<typename T>
using feature_flag_t = vk::Bool32 T::*;
std::vector<feature_flag_t<vk::PhysicalDeviceFeatures>> requiredFeatures;
std::vector<feature_flag_t<vk::PhysicalDeviceVulkan11Features>> requiredVulkan11Features;
std::vector<feature_flag_t<vk::PhysicalDeviceVulkan12Features>> requiredVulkan12Features;
std::vector<feature_flag_t<vk::PhysicalDeviceVulkan13Features>> requiredVulkan13Features;
std::vector<feature_flag_t<vk::PhysicalDeviceAccelerationStructureFeaturesKHR>> requiredAccelerationStructureFeatures;
std::vector<feature_flag_t<vk::PhysicalDeviceDescriptorIndexingFeatures>> requiredDescriptorIndexingFeatures;
std::vector<feature_flag_t<vk::PhysicalDeviceRayTracingPipelineFeaturesKHR>> requiredRayTracingPipelineFeatures;
std::vector<feature_flag_t<vk::PhysicalDeviceMeshShaderFeaturesEXT>> requredMeshShaderFeatures;
};
struct DeviceCreationFlags : mijin::BitFlags<DeviceCreationFlags>
{
/** Optimize Vulkan API calls for this device. Set to NO if you plan to create multiple devices. */
std::uint8_t singleDevice : 1 = 1;
std::uint8_t noDefaultExtensions : 1 = 0;
};
struct DeviceCreationArgs
{
PhysicalDeviceCriteria physicalDeviceCriteria;
std::vector<ExtensionInfo> extensions;
DeviceCreationFlags flags;
};
class ScratchCommandPool
{
public:
struct Buffer
{
ObjectPtr<CommandBuffer> cmdBuffer;
mijin::FuturePtr<void> doneFuture;
};
private:
ObjectPtr<CommandPool> mCommandPool;
std::vector<Buffer> mBuffers;
public:
explicit ScratchCommandPool(Device& device);
ScratchCommandPool(const ScratchCommandPool&) = default;
ScratchCommandPool(ScratchCommandPool&&) = default;
ScratchCommandPool& operator=(const ScratchCommandPool&) = default;
ScratchCommandPool& operator=(ScratchCommandPool&&) = default;
[[nodiscard]] ObjectPtr<CommandBuffer> allocateCommandBuffer();
[[nodiscard]] mijin::FuturePtr<void> getFuture(const ObjectPtr<CommandBuffer>& cmdBuffer) noexcept;
};
class Device : public Object<Device, BaseObject, class Instance>, public MixinVulkanObject<vk::Device>
{
private:
struct PendingCommandBuffer
{
ObjectPtr<CommandBuffer> cmdBuffer;
ObjectPtr<Fence> doneFence;
mijin::FuturePtr<void> future;
};
struct PendingSubmit
{
vk::Queue queue;
ObjectPtr<CommandBuffer> cmdBuffer;
mijin::FuturePtr<void> future;
};
vk::Queue mGraphicsQueue;
vk::Queue mComputeQueue;
std::vector<ExtensionInfo> mExtensions;
const PhysicalDeviceInfo* mDeviceInfo;
std::unordered_map<std::thread::id, ScratchCommandPool> mScratchCommandPools;
std::shared_mutex mScratchCommandPoolsMutex;
std::vector<PendingCommandBuffer> mPendingScratchCmdBuffers;
std::mutex mPendingScratchCmdBuffersMutex;
mijin::TaskHandle mUpdateLoopHandle;
mijin::MessageQueue<PendingSubmit> pendingSubmits;
public:
Device(ObjectPtr<class Instance> owner, DeviceCreationArgs args);
~Device() noexcept override;
[[nodiscard]] vk::Queue getGraphicsQueue() const noexcept { return mGraphicsQueue; }
[[nodiscard]] vk::Queue getComputeQueue() const noexcept { return mComputeQueue; }
[[nodiscard]] vk::PhysicalDevice getVkPhysicalDevice() const noexcept { return mDeviceInfo->device; }
[[nodiscard]] const PhysicalDeviceInfo& getDeviceInfo() const noexcept { return *mDeviceInfo; }
[[nodiscard]] ObjectPtr<DeviceMemory> allocateDeviceMemory(const DeviceMemoryAllocationArgs& args);
[[nodiscard]] ObjectPtr<CommandBuffer> beginScratchCommandBuffer();
mijin::FuturePtr<void> endScratchCommandBuffer(ObjectPtr<CommandBuffer> cmdBuffer);
void queueDelete(std::function<void()> deleter) noexcept;
private:
mijin::Task<> c_updateLoop() noexcept;
};
}
#endif // IWA_VULKAN_DEVICE_HPP_INCLUDED

View File

@@ -0,0 +1,29 @@
#pragma once
#if !defined(IWA_DEVICE_MEMORY_HPP_INCLUDED)
#define IWA_DEVICE_MEMORY_HPP_INCLUDED
#include <optional>
#include "iwa/object.hpp"
#include "iwa/vkwrapper.hpp"
namespace iwa
{
struct DeviceMemoryAllocationArgs
{
vk::DeviceSize allocationSize;
std::uint32_t memoryTypeIndex;
};
class DeviceMemory : public Object<DeviceMemory, BaseObject, class Device>, public MixinVulkanObject<vk::DeviceMemory>
{
public:
DeviceMemory(ObjectPtr<class Device> owner, const DeviceMemoryAllocationArgs& args);
~DeviceMemory() noexcept override;
};
[[nodiscard]] std::optional<std::uint32_t> findMemoryType(class Device& device, const vk::MemoryRequirements& requirements, vk::MemoryPropertyFlags properties);
} // namespace iwa
#endif // !defined(IWA_DEVICE_MEMORY_HPP_INCLUDED)

26
include/iwa/event.hpp Normal file
View File

@@ -0,0 +1,26 @@
#if !defined(IWA_EVENT_HPP_INCLUDED)
#define IWA_EVENT_HPP_INCLUDED
#include <mijin/async/coroutine.hpp>
#include "iwa/object.hpp"
#include "iwa/vkwrapper.hpp"
namespace iwa
{
struct EventCreationArgs
{
vk::EventCreateFlags flags;
};
class Event : public Object<Event, BaseObject, class Device>, public MixinVulkanObject<vk::Event>
{
public:
explicit Event(ObjectPtr<class Device> owner, const EventCreationArgs& args = {});
~Event() noexcept override;
mijin::Task<> c_wait();
};
}
#endif // !defined(IWA_EVENT_HPP_INCLUDED)

30
include/iwa/fence.hpp Normal file
View File

@@ -0,0 +1,30 @@
#pragma once
#if !defined(IWA_FENCE_HPP_INCLUDED)
#define IWA_FENCE_HPP_INCLUDED
#include <mijin/async/coroutine.hpp>
#include "iwa/object.hpp"
#include "iwa/vkwrapper.hpp"
namespace iwa
{
struct FenceCreationArgs
{
vk::FenceCreateFlags flags;
};
class Fence : public Object<Fence, BaseObject, class Device>, public MixinVulkanObject<vk::Fence>
{
public:
explicit Fence(ObjectPtr<class Device> owner, const FenceCreationArgs& args = {});
~Fence() noexcept override;
[[nodiscard]] bool isDone() const;
[[nodiscard]] mijin::Task<> c_wait() const;
void reset() const;
};
} // namespace iwa
#endif // !defined(IWA_FENCE_HPP_INCLUDED)

270
include/iwa/image.hpp Normal file
View File

@@ -0,0 +1,270 @@
#pragma once
#ifndef IWA_IMAGE_HPP_INCLUDED
#define IWA_IMAGE_HPP_INCLUDED
#include <mijin/async/coroutine.hpp>
#include <mijin/util/bitflags.hpp>
#include <mijin/util/flag.hpp>
#include "iwa/device_memory.hpp"
#include "iwa/object.hpp"
#include "iwa/vkwrapper.hpp"
#include "iwa/util/vkutil.hpp"
namespace iwa
{
inline constexpr std::uint32_t MAX_MIP_LEVELS = std::numeric_limits<std::uint32_t>::max();
static constexpr vk::ComponentMapping DEFAULT_COMPONENT_MAPPING = {
.r = vk::ComponentSwizzle::eIdentity,
.g = vk::ComponentSwizzle::eIdentity,
.b = vk::ComponentSwizzle::eIdentity,
.a = vk::ComponentSwizzle::eIdentity
};
static constexpr vk::ImageSubresourceRange DEFAULT_SUBRESOURCE_RANGE = {
.aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = VK_REMAINING_MIP_LEVELS,
.baseArrayLayer = 0,
.layerCount = VK_REMAINING_ARRAY_LAYERS
};
static constexpr vk::ImageSubresourceRange DEFAULT_DEPTH_SUBRESOURCE_RANGE = {
.aspectMask = vk::ImageAspectFlagBits::eDepth,
.baseMipLevel = 0,
.levelCount = VK_REMAINING_MIP_LEVELS,
.baseArrayLayer = 0,
.layerCount = VK_REMAINING_ARRAY_LAYERS
};
static constexpr vk::ImageSubresourceLayers DEFAULT_SUBRESOURCE_LAYERS = {
.aspectMask = vk::ImageAspectFlagBits::eColor,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1
};
struct ImageTransition
{
vk::PipelineStageFlags stages;
vk::ImageLayout layout;
vk::AccessFlags access;
vk::ImageSubresourceRange subResourceRange = {
.aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = VK_REMAINING_MIP_LEVELS,
.baseArrayLayer = 0,
.layerCount = VK_REMAINING_ARRAY_LAYERS
};
};
static constexpr ImageTransition IMAGE_TRANSITION_FRAGMENT_READ = {
.stages = vk::PipelineStageFlagBits::eFragmentShader,
.layout = vk::ImageLayout::eShaderReadOnlyOptimal,
.access = vk::AccessFlagBits::eShaderRead
};
static constexpr ImageTransition IMAGE_TRANSITION_COMPUTE_WRITE = {
.stages = vk::PipelineStageFlagBits::eComputeShader,
.layout = vk::ImageLayout::eGeneral,
.access = vk::AccessFlagBits::eShaderWrite
};
static constexpr ImageTransition IMAGE_TRANSITION_COMPUTE_READ = {
.stages = vk::PipelineStageFlagBits::eComputeShader,
.layout = vk::ImageLayout::eGeneral,
.access = vk::AccessFlagBits::eShaderRead
};
static constexpr ImageTransition IMAGE_TRANSITION_COLOR_ATTACHMENT = {
.stages = vk::PipelineStageFlagBits::eColorAttachmentOutput,
.layout = vk::ImageLayout::eColorAttachmentOptimal,
.access = vk::AccessFlagBits::eColorAttachmentWrite | vk::AccessFlagBits::eColorAttachmentRead
};
static constexpr ImageTransition IMAGE_TRANSITION_TRANSFER_READ = {
.stages = vk::PipelineStageFlagBits::eTransfer,
.layout = vk::ImageLayout::eTransferSrcOptimal,
.access = vk::AccessFlagBits::eTransferRead
};
static constexpr ImageTransition IMAGE_TRANSITION_TRANSFER_WRITE = {
.stages = vk::PipelineStageFlagBits::eTransfer,
.layout = vk::ImageLayout::eTransferDstOptimal,
.access = vk::AccessFlagBits::eTransferWrite
};
struct ImageCreationArgs
{
vk::ImageCreateFlags flags = {};
vk::ImageType imageType = vk::ImageType::e2D;
vk::Format format = vk::Format::eR8G8B8A8Unorm;
vk::Extent3D extent = {};
uint32_t mipLevels = 1;
uint32_t arrayLayers = 1;
vk::SampleCountFlagBits samples = vk::SampleCountFlagBits::e1;
vk::ImageTiling tiling = vk::ImageTiling::eOptimal;
vk::ImageUsageFlags usage = {};
vk::SharingMode sharingMode = vk::SharingMode::eExclusive;
std::vector<std::uint32_t> queueFamilyIndices;
vk::ImageLayout initialLayout = vk::ImageLayout::eUndefined;
};
struct ImageWrapArgs
{
vk::Image handle;
vk::ImageType type;
vk::Format format;
vk::ImageUsageFlags usage;
vk::Extent3D size;
unsigned mipLevels = 1;
};
struct ImageFromBitmapArgs
{
const class Bitmap* bitmap;
vk::ImageCreateFlags flags = {};
vk::ImageTiling tiling = vk::ImageTiling::eOptimal;
vk::ImageUsageFlags usage = {};
vk::SharingMode sharingMode = vk::SharingMode::eExclusive;
std::vector<std::uint32_t> queueFamilyIndices;
vk::ImageLayout initialLayout = vk::ImageLayout::eUndefined;
};
struct ImageViewCreationArgs
{
vk::ImageViewCreateFlags flags;
vk::ImageViewType viewType = vk::ImageViewType::e2D;
vk::Format format = vk::Format::eUndefined;
vk::ComponentMapping components = DEFAULT_COMPONENT_MAPPING;
vk::ImageSubresourceRange subresourceRange = DEFAULT_SUBRESOURCE_RANGE;
};
MIJIN_DEFINE_FLAG(ResetLayout);
class Image : public Object<Image, BaseObject, class Device>, public MixinVulkanObject<vk::Image>
{
private:
vk::ImageCreateFlags mFlags;
vk::ImageType mType;
vk::Format mFormat;
vk::ImageTiling mTiling;
vk::ImageUsageFlags mUsage;
vk::Extent3D mSize;
unsigned mArrayLayers;
unsigned mMipLevels;
bool mWrapped = false;
ObjectPtr<DeviceMemory> mMemory;
vk::ImageLayout currentLayout = vk::ImageLayout::eUndefined;
vk::PipelineStageFlags lastUsageStages = vk::PipelineStageFlagBits::eTopOfPipe;
vk::AccessFlags lastAccess = {};
public:
Image(ObjectPtr<class Device> owner, ImageCreationArgs args);
Image(ObjectPtr<class Device> owner, ImageWrapArgs args);
~Image() noexcept override;
[[nodiscard]] vk::ImageType getType() const noexcept { return mType; }
[[nodiscard]] vk::Format getFormat() const noexcept { return mFormat; }
[[nodiscard]] vk::ImageUsageFlags getUsage() const noexcept { return mUsage; }
[[nodiscard]] const vk::Extent3D& getSize() const noexcept { return mSize; }
[[nodiscard]] unsigned getArrayLayers() const noexcept { return mArrayLayers; }
[[nodiscard]] unsigned getMipLevels() const noexcept { return mMipLevels; }
void allocateMemory();
void bindMemory(ObjectPtr<DeviceMemory> memory, vk::DeviceSize offset = 0);
void resetUsage(ResetLayout resetLayout = ResetLayout::NO) noexcept;
void applyTransition(vk::CommandBuffer cmdBuffer, const ImageTransition& transition);
[[nodiscard]] ObjectPtr<class ImageView> createImageView(const ImageViewCreationArgs& args = {});
mijin::Task<> c_doTransition(const ImageTransition& transition);
mijin::Task<> c_upload(const void* data, std::size_t bytes, vk::Extent3D bufferImageSize, vk::Offset3D imageOffset, unsigned baseLayer = 0, unsigned layerCount = 1);
mijin::Task<> c_upload(const class Bitmap& bitmap, vk::Offset3D imageOffset = {}, unsigned baseLayer = 0, unsigned layerCount = 1);
mijin::Task<> c_blitFrom(Image& srcImage, std::vector<vk::ImageBlit> regions, vk::Filter filter = vk::Filter::eNearest);
mijin::Task<> c_blitFrom(const class Bitmap& bitmap, std::vector<vk::ImageBlit> regions, vk::Filter filter = vk::Filter::eNearest);
mijin::Task<> c_copyFrom(Image& srcImage, std::vector<vk::ImageCopy> regions);
private:
[[nodiscard]] std::uint32_t clampMipLevels(std::uint32_t levels) const;
void generateMipMaps(vk::CommandBuffer cmdBuffer);
public:
static mijin::Task<ObjectPtr<Image>> c_create(ObjectPtr<class Device> owner, ImageFromBitmapArgs args);
};
class ImageView : public Object<ImageView, BaseObject, Image>, public MixinVulkanObject<vk::ImageView>
{
public:
ImageView(ObjectPtr<Image> owner, const ImageViewCreationArgs& args = {});
~ImageView() noexcept override;
};
struct SamplerCreationOptions : mijin::BitFlags<SamplerCreationOptions>
{
std::uint8_t anisotropyEnable : 1 = 0;
std::uint8_t compareEnable : 1 = 0;
std::uint8_t unnormalizedCoordinates : 1 = 0;
};
struct SamplerCreationArgs
{
vk::SamplerCreateFlags flags = {};
SamplerCreationOptions options;
vk::Filter magFilter = vk::Filter::eLinear;
vk::Filter minFilter = vk::Filter::eNearest;
vk::SamplerMipmapMode mipmapMode = vk::SamplerMipmapMode::eLinear;
vk::SamplerAddressMode addressModeU = vk::SamplerAddressMode::eRepeat;
vk::SamplerAddressMode addressModeV = vk::SamplerAddressMode::eRepeat;
vk::SamplerAddressMode addressModeW = vk::SamplerAddressMode::eRepeat;
float mipLodBias = 0.f;
float maxAnisotropy = 1.f;
vk::CompareOp compareOp = vk::CompareOp::eAlways;
float minLod = 0.f;
float maxLod = VK_LOD_CLAMP_NONE;
vk::BorderColor borderColor = vk::BorderColor::eFloatTransparentBlack;
};
class Sampler : public Object<Sampler, BaseObject, class Device>, public MixinVulkanObject<vk::Sampler>
{
public:
Sampler(ObjectPtr<class Device> owner, const SamplerCreationArgs& args = {});
~Sampler() noexcept override;
};
inline vk::ImageSubresourceRange defaultDepthSubresourceRange(vk::Format format, bool withStencil = true)
{
return {
.aspectMask = vk::ImageAspectFlagBits::eDepth | (withStencil && isStencilFormat(format) ? vk::ImageAspectFlagBits::eStencil : vk::ImageAspectFlagBits()),
.baseMipLevel = 0,
.levelCount = 1, // not really any miplevels for depth textures
.baseArrayLayer = 0,
.layerCount = VK_REMAINING_ARRAY_LAYERS
};
}
inline vk::ImageSubresourceLayers defaultDepthSubresourceLayers(vk::Format format)
{
return {
.aspectMask = vk::ImageAspectFlagBits::eDepth | (isStencilFormat(format) ? vk::ImageAspectFlagBits::eStencil : vk::ImageAspectFlagBits()),
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = VK_REMAINING_ARRAY_LAYERS
};
}
inline ImageTransition imageTransitionDepthAttachment(vk::Format depthFormat)
{
return {
.stages = vk::PipelineStageFlagBits::eEarlyFragmentTests | vk::PipelineStageFlagBits::eLateFragmentTests,
.layout = isStencilFormat(depthFormat) ? vk::ImageLayout::eDepthAttachmentStencilReadOnlyOptimal : vk::ImageLayout::eDepthAttachmentOptimal,
.access = vk::AccessFlagBits::eDepthStencilAttachmentRead | vk::AccessFlagBits::eDepthStencilAttachmentWrite,
.subResourceRange = defaultDepthSubresourceRange(depthFormat)
};
}
} // namespace iwa
#endif // !included(IWA_IMAGE_HPP_INCLUDED)

483
include/iwa/input.hpp Normal file
View File

@@ -0,0 +1,483 @@
#pragma once
#if !defined(IWA_INPUT_HPP_INCLUDED)
#define IWA_INPUT_HPP_INCLUDED
#include <cstdint>
#include <string>
#include <SDL.h>
namespace iwa
{
// <editor-fold desc="KeyCode and ScanCode enums">
enum class KeyCode
{
UNKNOWN = -1,
RETURN = SDLK_RETURN,
ESCAPE = SDLK_ESCAPE,
BACKSPACE = SDLK_BACKSPACE,
TAB = SDLK_TAB,
SPACE = SDLK_SPACE,
EXCLAIM = SDLK_EXCLAIM,
QUOTEDBL = SDLK_QUOTEDBL,
HASH = SDLK_HASH,
PERCENT = SDLK_PERCENT,
DOLLAR = SDLK_DOLLAR,
AMPERSAND = SDLK_AMPERSAND,
QUOTE = SDLK_QUOTE,
LEFTPAREN = SDLK_LEFTPAREN,
RIGHTPAREN = SDLK_RIGHTPAREN,
ASTERISK = SDLK_ASTERISK,
PLUS = SDLK_PLUS,
COMMA = SDLK_COMMA,
MINUS = SDLK_MINUS,
PERIOD = SDLK_PERIOD,
SLASH = SDLK_SLASH,
_0 = SDLK_0,
_1 = SDLK_1,
_2 = SDLK_2,
_3 = SDLK_3,
_4 = SDLK_4,
_5 = SDLK_5,
_6 = SDLK_6,
_7 = SDLK_7,
_8 = SDLK_8,
_9 = SDLK_9,
COLON = SDLK_COLON,
SEMICOLON = SDLK_SEMICOLON,
LESS = SDLK_LESS,
EQUALS = SDLK_EQUALS,
GREATER = SDLK_GREATER,
QUESTION = SDLK_QUESTION,
AT = SDLK_AT,
/*
Skip uppercase letters
*/
LEFTBRACKET = SDLK_LEFTBRACKET,
BACKSLASH = SDLK_BACKSLASH,
RIGHTBRACKET = SDLK_RIGHTBRACKET,
CARET = SDLK_CARET,
UNDERSCORE = SDLK_UNDERSCORE,
BACKQUOTE = SDLK_BACKQUOTE,
A = SDLK_a,
B = SDLK_b,
C = SDLK_c,
D = SDLK_d,
E = SDLK_e,
F = SDLK_f,
G = SDLK_g,
H = SDLK_h,
I = SDLK_i,
J = SDLK_j,
K = SDLK_k,
L = SDLK_l,
M = SDLK_m,
N = SDLK_n,
O = SDLK_o,
P = SDLK_p,
Q = SDLK_q,
R = SDLK_r,
S = SDLK_s,
T = SDLK_t,
U = SDLK_u,
V = SDLK_v,
W = SDLK_w,
X = SDLK_x,
Y = SDLK_y,
Z = SDLK_z,
CAPSLOCK = SDLK_CAPSLOCK,
F1 = SDLK_F1,
F2 = SDLK_F2,
F3 = SDLK_F3,
F4 = SDLK_F4,
F5 = SDLK_F5,
F6 = SDLK_F6,
F7 = SDLK_F7,
F8 = SDLK_F8,
F9 = SDLK_F9,
F10 = SDLK_F10,
F11 = SDLK_F11,
F12 = SDLK_F12,
PRINTSCREEN = SDLK_PRINTSCREEN,
SCROLLLOCK = SDLK_SCROLLLOCK,
PAUSE = SDLK_PAUSE,
INSERT = SDLK_INSERT,
HOME = SDLK_HOME,
PAGEUP = SDLK_PAGEUP,
DELETE = SDLK_DELETE,
END = SDLK_END,
PAGEDOWN = SDLK_PAGEDOWN,
RIGHT = SDLK_RIGHT,
LEFT = SDLK_LEFT,
DOWN = SDLK_DOWN,
UP = SDLK_UP,
NUMLOCKCLEAR = SDLK_NUMLOCKCLEAR,
KP_DIVIDE = SDLK_KP_DIVIDE,
KP_MULTIPLY = SDLK_KP_MULTIPLY,
KP_MINUS = SDLK_KP_MINUS,
KP_PLUS = SDLK_KP_PLUS,
KP_ENTER = SDLK_KP_ENTER,
KP_1 = SDLK_KP_1,
KP_2 = SDLK_KP_2,
KP_3 = SDLK_KP_3,
KP_4 = SDLK_KP_4,
KP_5 = SDLK_KP_5,
KP_6 = SDLK_KP_6,
KP_7 = SDLK_KP_7,
KP_8 = SDLK_KP_8,
KP_9 = SDLK_KP_9,
KP_0 = SDLK_KP_0,
KP_PERIOD = SDLK_KP_PERIOD,
APPLICATION = SDLK_APPLICATION,
POWER = SDLK_POWER,
KP_EQUALS = SDLK_KP_EQUALS,
F13 = SDLK_F13,
F14 = SDLK_F14,
F15 = SDLK_F15,
F16 = SDLK_F16,
F17 = SDLK_F17,
F18 = SDLK_F18,
F19 = SDLK_F19,
F20 = SDLK_F20,
F21 = SDLK_F21,
F22 = SDLK_F22,
F23 = SDLK_F23,
F24 = SDLK_F24,
EXECUTE = SDLK_EXECUTE,
HELP = SDLK_HELP,
MENU = SDLK_MENU,
SELECT = SDLK_SELECT,
STOP = SDLK_STOP,
AGAIN = SDLK_AGAIN,
UNDO = SDLK_UNDO,
CUT = SDLK_CUT,
COPY = SDLK_COPY,
PASTE = SDLK_PASTE,
FIND = SDLK_FIND,
MUTE = SDLK_MUTE,
VOLUMEUP = SDLK_VOLUMEUP,
VOLUMEDOWN = SDLK_VOLUMEDOWN,
KP_COMMA = SDLK_KP_COMMA,
KP_EQUALSAS400 = SDLK_KP_EQUALSAS400,
ALTERASE = SDLK_ALTERASE,
SYSREQ = SDLK_SYSREQ,
CANCEL = SDLK_CANCEL,
CLEAR = SDLK_CLEAR,
PRIOR = SDLK_PRIOR,
RETURN2 = SDLK_RETURN2,
SEPARATOR = SDLK_SEPARATOR,
OUT = SDLK_OUT,
OPER = SDLK_OPER,
CLEARAGAIN = SDLK_CLEARAGAIN,
CRSEL = SDLK_CRSEL,
EXSEL = SDLK_EXSEL,
KP_00 = SDLK_KP_00,
KP_000 = SDLK_KP_000,
THOUSANDSSEPARATOR = SDLK_THOUSANDSSEPARATOR,
DECIMALSEPARATOR = SDLK_DECIMALSEPARATOR,
CURRENCYUNIT = SDLK_CURRENCYUNIT,
CURRENCYSUBUNIT = SDLK_CURRENCYSUBUNIT,
KP_LEFTPAREN = SDLK_KP_LEFTPAREN,
KP_RIGHTPAREN = SDLK_KP_RIGHTPAREN,
KP_LEFTBRACE = SDLK_KP_LEFTBRACE,
KP_RIGHTBRACE = SDLK_KP_RIGHTBRACE,
KP_TAB = SDLK_KP_TAB,
KP_BACKSPACE = SDLK_KP_BACKSPACE,
KP_A = SDLK_KP_A,
KP_B = SDLK_KP_B,
KP_C = SDLK_KP_C,
KP_D = SDLK_KP_D,
KP_E = SDLK_KP_E,
KP_F = SDLK_KP_F,
KP_XOR = SDLK_KP_XOR,
KP_POWER = SDLK_KP_POWER,
KP_PERCENT = SDLK_KP_PERCENT,
KP_LESS = SDLK_KP_LESS,
KP_GREATER = SDLK_KP_GREATER,
KP_AMPERSAND = SDLK_KP_AMPERSAND,
KP_DBLAMPERSAND = SDLK_KP_DBLAMPERSAND,
KP_VERTICALBAR = SDLK_KP_VERTICALBAR,
KP_DBLVERTICALBAR = SDLK_KP_DBLVERTICALBAR,
KP_COLON = SDLK_KP_COLON,
KP_HASH = SDLK_KP_HASH,
KP_SPACE = SDLK_KP_SPACE,
KP_AT = SDLK_KP_AT,
KP_EXCLAM = SDLK_KP_EXCLAM,
KP_MEMSTORE = SDLK_KP_MEMSTORE,
KP_MEMRECALL = SDLK_KP_MEMRECALL,
KP_MEMCLEAR = SDLK_KP_MEMCLEAR,
KP_MEMADD = SDLK_KP_MEMADD,
KP_MEMSUBTRACT = SDLK_KP_MEMSUBTRACT,
KP_MEMMULTIPLY = SDLK_KP_MEMMULTIPLY,
KP_MEMDIVIDE = SDLK_KP_MEMDIVIDE,
KP_PLUSMINUS = SDLK_KP_PLUSMINUS,
KP_CLEAR = SDLK_KP_CLEAR,
KP_CLEARENTRY = SDLK_KP_CLEARENTRY,
KP_BINARY = SDLK_KP_BINARY,
KP_OCTAL = SDLK_KP_OCTAL,
KP_DECIMAL = SDLK_KP_DECIMAL,
KP_HEXADECIMAL = SDLK_KP_HEXADECIMAL,
LCTRL = SDLK_LCTRL,
LSHIFT = SDLK_LSHIFT,
LALT = SDLK_LALT,
LGUI = SDLK_LGUI,
RCTRL = SDLK_RCTRL,
RSHIFT = SDLK_RSHIFT,
RALT = SDLK_RALT,
RGUI = SDLK_RGUI,
MODE = SDLK_MODE,
AUDIONEXT = SDLK_AUDIONEXT,
AUDIOPREV = SDLK_AUDIOPREV,
AUDIOSTOP = SDLK_AUDIOSTOP,
AUDIOPLAY = SDLK_AUDIOPLAY,
AUDIOMUTE = SDLK_AUDIOMUTE,
MEDIASELECT = SDLK_MEDIASELECT,
WWW = SDLK_WWW,
MAIL = SDLK_MAIL,
CALCULATOR = SDLK_CALCULATOR,
COMPUTER = SDLK_COMPUTER,
AC_SEARCH = SDLK_AC_SEARCH,
AC_HOME = SDLK_AC_HOME,
AC_BACK = SDLK_AC_BACK,
AC_FORWARD = SDLK_AC_FORWARD,
AC_STOP = SDLK_AC_STOP,
AC_REFRESH = SDLK_AC_REFRESH,
AC_BOOKMARKS = SDLK_AC_BOOKMARKS,
BRIGHTNESSDOWN = SDLK_BRIGHTNESSDOWN,
BRIGHTNESSUP = SDLK_BRIGHTNESSUP,
DISPLAYSWITCH = SDLK_DISPLAYSWITCH,
KBDILLUMTOGGLE = SDLK_KBDILLUMTOGGLE,
KBDILLUMDOWN = SDLK_KBDILLUMDOWN,
KBDILLUMUP = SDLK_KBDILLUMUP,
EJECT = SDLK_EJECT,
SLEEP = SDLK_SLEEP,
APP1 = SDLK_APP1,
APP2 = SDLK_APP2,
AUDIOREWIND = SDLK_AUDIOREWIND,
AUDIOFASTFORWARD = SDLK_AUDIOFASTFORWARD,
SOFTLEFT = SDLK_SOFTLEFT,
SOFTRIGHT = SDLK_SOFTRIGHT,
CALL = SDLK_CALL,
ENDCALL = SDLK_ENDCALL
};
enum class ScanCode
{
UNKNOWN = -1,
/* Printable keys */
SPACE = SDL_SCANCODE_SPACE,
APOSTROPHE = SDL_SCANCODE_APOSTROPHE,
COMMA = SDL_SCANCODE_COMMA,
MINUS = SDL_SCANCODE_MINUS,
PERIOD = SDL_SCANCODE_PERIOD,
SLASH = SDL_SCANCODE_SLASH,
_0 = SDL_SCANCODE_0,
_1 = SDL_SCANCODE_1,
_2 = SDL_SCANCODE_2,
_3 = SDL_SCANCODE_3,
_4 = SDL_SCANCODE_4,
_5 = SDL_SCANCODE_5,
_6 = SDL_SCANCODE_6,
_7 = SDL_SCANCODE_7,
_8 = SDL_SCANCODE_8,
_9 = SDL_SCANCODE_9,
SEMICOLON = SDL_SCANCODE_SEMICOLON,
EQUALS = SDL_SCANCODE_EQUALS,
A = SDL_SCANCODE_A,
B = SDL_SCANCODE_B,
C = SDL_SCANCODE_C,
D = SDL_SCANCODE_D,
E = SDL_SCANCODE_E,
F = SDL_SCANCODE_F,
G = SDL_SCANCODE_G,
H = SDL_SCANCODE_H,
I = SDL_SCANCODE_I,
J = SDL_SCANCODE_J,
K = SDL_SCANCODE_K,
L = SDL_SCANCODE_L,
M = SDL_SCANCODE_M,
N = SDL_SCANCODE_N,
O = SDL_SCANCODE_O,
P = SDL_SCANCODE_P,
Q = SDL_SCANCODE_Q,
R = SDL_SCANCODE_R,
S = SDL_SCANCODE_S,
T = SDL_SCANCODE_T,
U = SDL_SCANCODE_U,
V = SDL_SCANCODE_V,
W = SDL_SCANCODE_W,
X = SDL_SCANCODE_X,
Y = SDL_SCANCODE_Y,
Z = SDL_SCANCODE_Z,
LEFTBRACKET = SDL_SCANCODE_LEFTBRACKET,
BACKSLASH = SDL_SCANCODE_BACKSLASH,
RIGHTBRACKET = SDL_SCANCODE_RIGHTBRACKET,
GRAVE = SDL_SCANCODE_GRAVE,
// WORLD_1 = SDL_SCANCODE_WORLD_1,
// WORLD_2 = SDL_SCANCODE_WORLD_2,
/* Function keys */
ESCAPE = SDL_SCANCODE_ESCAPE,
ENTER = SDL_SCANCODE_RETURN,
TAB = SDL_SCANCODE_TAB,
BACKSPACE = SDL_SCANCODE_BACKSPACE,
INSERT = SDL_SCANCODE_INSERT,
DELETE = SDL_SCANCODE_DELETE,
RIGHT = SDL_SCANCODE_RIGHT,
LEFT = SDL_SCANCODE_LEFT,
DOWN = SDL_SCANCODE_DOWN,
UP = SDL_SCANCODE_UP,
PAGEUP = SDL_SCANCODE_PAGEUP,
PAGEDOWN = SDL_SCANCODE_PAGEDOWN,
HOME = SDL_SCANCODE_HOME,
END = SDL_SCANCODE_END,
CAPSLOCK = SDL_SCANCODE_CAPSLOCK,
SCROLLLOCK = SDL_SCANCODE_SCROLLLOCK,
NUMLOCK = SDL_SCANCODE_NUMLOCKCLEAR,
PRINTSCREEN = SDL_SCANCODE_PRINTSCREEN,
PAUSE = SDL_SCANCODE_PAUSE,
F1 = SDL_SCANCODE_F1,
F2 = SDL_SCANCODE_F2,
F3 = SDL_SCANCODE_F3,
F4 = SDL_SCANCODE_F4,
F5 = SDL_SCANCODE_F5,
F6 = SDL_SCANCODE_F6,
F7 = SDL_SCANCODE_F7,
F8 = SDL_SCANCODE_F8,
F9 = SDL_SCANCODE_F9,
F10 = SDL_SCANCODE_F10,
F11 = SDL_SCANCODE_F11,
F12 = SDL_SCANCODE_F12,
F13 = SDL_SCANCODE_F13,
F14 = SDL_SCANCODE_F14,
F15 = SDL_SCANCODE_F15,
F16 = SDL_SCANCODE_F16,
F17 = SDL_SCANCODE_F17,
F18 = SDL_SCANCODE_F18,
F19 = SDL_SCANCODE_F19,
F20 = SDL_SCANCODE_F20,
F21 = SDL_SCANCODE_F21,
F22 = SDL_SCANCODE_F22,
F23 = SDL_SCANCODE_F23,
F24 = SDL_SCANCODE_F24,
KP_0 = SDL_SCANCODE_KP_0,
KP_1 = SDL_SCANCODE_KP_1,
KP_2 = SDL_SCANCODE_KP_2,
KP_3 = SDL_SCANCODE_KP_3,
KP_4 = SDL_SCANCODE_KP_4,
KP_5 = SDL_SCANCODE_KP_5,
KP_6 = SDL_SCANCODE_KP_6,
KP_7 = SDL_SCANCODE_KP_7,
KP_8 = SDL_SCANCODE_KP_8,
KP_9 = SDL_SCANCODE_KP_9,
KP_DECIMAL = SDL_SCANCODE_KP_DECIMAL,
KP_DIVIDE = SDL_SCANCODE_KP_DIVIDE,
KP_MULTIPLY = SDL_SCANCODE_KP_MULTIPLY,
KP_MINUS = SDL_SCANCODE_KP_MINUS,
KP_PLUS = SDL_SCANCODE_KP_PLUS,
KP_ENTER = SDL_SCANCODE_KP_ENTER,
KP_EQUALS = SDL_SCANCODE_KP_EQUALS,
LSHIFT = SDL_SCANCODE_LSHIFT,
LCONTROL = SDL_SCANCODE_LCTRL,
LALT = SDL_SCANCODE_LALT,
LGUI = SDL_SCANCODE_LGUI,
RSHIFT = SDL_SCANCODE_RSHIFT,
RCONTROL = SDL_SCANCODE_RCTRL,
RALT = SDL_SCANCODE_RALT,
RGUI = SDL_SCANCODE_RGUI,
MENU = SDL_SCANCODE_MENU,
};
// </editor-fold>
struct KeyModifiers
{
bool leftShift : 1;
bool rightShift : 1;
bool leftCtrl : 1;
bool rightCtrl : 1;
bool leftAlt : 1;
bool rightAlt : 1;
bool leftMeta : 1;
bool rightMeta : 1;
};
struct InputEvent
{
};
struct KeyEvent : InputEvent
{
KeyCode keyCode;
ScanCode scanCode;
KeyModifiers modifiers;
bool down : 1;
bool repeat : 1;
};
enum class MouseButton
{
LEFT = SDL_BUTTON_LEFT,
MIDDLE = SDL_BUTTON_MIDDLE,
RIGHT = SDL_BUTTON_RIGHT,
EXTRA_1 = SDL_BUTTON_X1,
EXTRA_2 = SDL_BUTTON_X2
};
struct MouseMoveEvent
{
int relativeX;
int relativeY;
int absoluteX;
int absoluteY;
bool warped;
};
struct MouseWheelEvent
{
int relativeX;
int relativeY;
};
struct MouseButtonEvent
{
MouseButton button;
std::uint8_t clicks : 7;
bool down : 1;
};
struct TextInputEvent
{
std::string text;
};
struct KeyState
{
bool pressed;
};
[[nodiscard]] KeyState getKeyState(ScanCode scanCode) noexcept;
void captureMouse() noexcept;
void uncaptureMouse() noexcept;
[[nodiscard]] std::pair<int, int> getMouseScreenPosition() noexcept;
} // namespace iwa
#endif // !defined(IWA_INPUT_HPP_INCLUDED)

157
include/iwa/instance.hpp Normal file
View File

@@ -0,0 +1,157 @@
#pragma once
#ifndef IWA_VULKAN_INSTANCE_HPP_INCLUDED
#define IWA_VULKAN_INSTANCE_HPP_INCLUDED
#include <thread>
#include <typeindex>
#include <unordered_map>
#include <mijin/async/coroutine.hpp>
#include <mijin/util/bitflags.hpp>
#include <mijin/virtual_filesystem/stacked.hpp>
#include "iwa/object.hpp"
#include "iwa/device.hpp"
#include "iwa/window.hpp"
#include "iwa/vkwrapper.hpp"
#define IWA_DELETE_INSTANCE_OBJECT(instance, var, deleter) \
(instance)->queueDelete([handle=(var), inst=(instance)]() \
{ \
inst->getVkInstance().deleter(handle); \
})
#define IWA_CORO_ENSURE_MAIN_THREAD(instance) \
if (!(instance).isOnMainThread()) { \
co_await mijin::switchContext((instance).getMainTaskLoop()); \
}
namespace iwa
{
struct InstanceCreationFlags : mijin::BitFlags<InstanceCreationFlags>
{
std::uint8_t noDefaultExtensions : 1 = 0;
std::uint8_t noDefaultLayers : 1 = 0;
};
struct InstanceCreationArgs
{
vk::ApplicationInfo applicationInfo = {
.pApplicationName = "Iwa VulkanApplication",
.applicationVersion = 0,
.pEngineName = "Iwa Engine",
.engineVersion = 0,
.apiVersion = VK_MAKE_API_VERSION(0, 1, 3, 0)
};
std::vector<ExtensionInfo> extensions;
std::vector<LayerInfo> layers;
InstanceCreationFlags flags;
};
class InstanceExtension : public AbstractObject<InstanceExtension>
{
protected:
InstanceExtension() noexcept : super_t(nullptr) {}
};
class Instance : public Object<Instance>, public MixinVulkanObject<vk::Instance>
{
public:
using deleter_t = std::function<void()>;
private:
struct DeleteQueueEntry
{
deleter_t deleter;
int remainingFrames;
};
mijin::SimpleTaskLoop mMainTaskLoop;
mijin::MultiThreadedTaskLoop mWorkerTaskLoop;
mijin::StackedFileSystemAdapter mPrimaryFSAdapter;
std::vector<ExtensionInfo> mExtensions;
std::vector<LayerInfo> mLayers;
std::vector<PhysicalDeviceInfo> mPhysicalDevices;
std::vector<DeleteQueueEntry> mDeleteQueue;
std::unordered_map<std::type_index, ObjectPtr<InstanceExtension>> mInstanceExtensions;
vk::DebugUtilsMessengerEXT mDebugMessenger = VK_NULL_HANDLE;
bool mQuitRequested = false;
std::thread::id mMainThread;
public:
explicit Instance(InstanceCreationArgs args = {});
~Instance() noexcept override;
[[nodiscard]] const std::vector<PhysicalDeviceInfo>& getPhysicalDevices() const noexcept { return mPhysicalDevices; }
[[nodiscard]] inline bool isQuitRequested() const noexcept { return mQuitRequested; }
[[nodiscard]] inline mijin::SimpleTaskLoop& getMainTaskLoop() noexcept { return mMainTaskLoop; }
[[nodiscard]] inline mijin::MultiThreadedTaskLoop& getWorkerTaskLoop() noexcept { return mWorkerTaskLoop; }
[[nodiscard]] inline mijin::StackedFileSystemAdapter& getPrimaryFSAdapter() noexcept { return mPrimaryFSAdapter; }
[[nodiscard]] bool isExtensionEnabled(const char* name) const noexcept;
[[nodiscard]] bool isLayerEnabled(const char* name) const noexcept;
ObjectPtr<Window> createWindow(const WindowCreationArgs& args = {});
ObjectPtr<Device> createDevice(DeviceCreationArgs args = {});
void queueDelete(deleter_t deleter) noexcept;
void tickDeleteQueue();
void setMainThread();
void requestQuit() noexcept;
[[nodiscard]] bool isOnMainThread() const noexcept { return mMainThread == std::this_thread::get_id(); }
template<typename TFunction>
mijin::FuturePtr<std::invoke_result_t<TFunction>> runOnMainThread(TFunction&& function);
template<typename TExtension> requires (std::is_base_of_v<InstanceExtension, TExtension>)
[[nodiscard]] TExtension& getInstanceExtension()
{
ObjectPtr<InstanceExtension>& extensionPtr = mInstanceExtensions[typeid(TExtension)];
if (extensionPtr == nullptr)
{
extensionPtr = TExtension::create();
}
return static_cast<TExtension&>(*extensionPtr);
}
private:
void runDeleters(bool runAll = false);
public:
mijin::Signal<> quitRequested;
mijin::Signal<Device&> deviceCreated;
mijin::Signal<Window&> windowCreated;
};
template<typename TFunction>
mijin::FuturePtr<std::invoke_result_t<TFunction>> Instance::runOnMainThread(TFunction&& function)
{
using result_t = std::invoke_result_t<TFunction>;
if (std::this_thread::get_id() == mMainThread)
{
mijin::FuturePtr<result_t> future = std::make_shared<mijin::Future<result_t>>();
if constexpr (std::is_same_v<result_t, void>)
{
function();
future->set();
}
else
{
future->set(function());
}
return future;
}
return getMainTaskLoop().addTask([](std::function<result_t()> function) -> mijin::Task<result_t>
{
if constexpr (std::is_same_v<result_t, void>)
{
function();
co_return;
}
else
{
co_return function();
}
}(std::forward<TFunction>(function)));
}
}
#endif // IWA_VULKAN_INSTANCE_HPP_INCLUDED

123
include/iwa/io/bitmap.hpp Normal file
View File

@@ -0,0 +1,123 @@
#pragma once
#if !defined(IWA_IO_BITMAP_HPP)
#define IWA_IO_BITMAP_HPP
#include <cstdint>
#include <filesystem>
#include <span>
#include <string>
#include <mijin/io/stream.hpp>
#include <mijin/util/hash.hpp>
#include <mijin/virtual_filesystem/filesystem.hpp>
#include "iwa/resource/bitmap.hpp"
namespace iwa
{
namespace fs = std::filesystem;
enum class ImageFormatType
{
UNORM = 0,
UINT = 1,
SRGB = 2
};
enum class BitmapCodec
{
NONE = 0,
PNG = 1,
JPEG = 2
};
struct BitmapLoadOptions
{
mijin::Stream* stream;
BitmapCodec codec = BitmapCodec::NONE;
ImageFormatType formatType = ImageFormatType::UNORM;
};
struct BitmapLoadFileOptions
{
mijin::PathReference path;
BitmapCodec codec = BitmapCodec::NONE;
ImageFormatType formatType = ImageFormatType::UNORM;
auto operator<=>(const BitmapLoadFileOptions&) const noexcept = default;
};
struct BitmapLoadMemoryOptions
{
std::span<std::uint8_t> data;
BitmapCodec codec = BitmapCodec::NONE;
ImageFormatType formatType = ImageFormatType::UNORM;
};
struct BitmapSaveOptions
{
std::string fileName = {};
BitmapCodec codec = BitmapCodec::NONE;
int jpegQuality = 90;
auto operator<=>(const BitmapSaveOptions&) const noexcept = default;
};
//
// public functions
//
[[nodiscard]] ObjectPtr<Bitmap> loadBitmap(const BitmapLoadOptions& options);
[[nodiscard]] inline ObjectPtr<Bitmap> loadBitmapFromFile(const BitmapLoadFileOptions& options)
{
std::unique_ptr<mijin::Stream> stream;
const mijin::StreamError error = options.path.open(mijin::FileOpenMode::READ, stream);
if (error != mijin::StreamError::SUCCESS) {
throw std::runtime_error("Error opening bitmap for reading.");
}
return loadBitmap({
.stream = stream.get(),
.codec = options.codec,
.formatType = options.formatType
});
}
[[nodiscard]] inline ObjectPtr<Bitmap> loadBitmapFromMemory(const BitmapLoadMemoryOptions& options)
{
mijin::MemoryStream stream;
stream.openRO(options.data);
return loadBitmap({
.stream = &stream,
.codec = options.codec,
.formatType = options.formatType
});
}
void saveBitmap(const Bitmap& bitmap, const BitmapSaveOptions& options);
constexpr BitmapCodec bitmapCodecFromMimeType(std::string_view mimeType)
{
if (mimeType == "image/png") {
return BitmapCodec::PNG;
}
if (mimeType == "image/jpeg") {
return BitmapCodec::JPEG;
}
return BitmapCodec::NONE;
}
} // namespace iwa
template<>
struct std::hash<iwa::BitmapLoadFileOptions>
{
std::size_t operator()(const iwa::BitmapLoadFileOptions& options) const noexcept
{
std::size_t hash = 0;
mijin::hashCombine(hash, options.path);
mijin::hashCombine(hash, options.codec);
mijin::hashCombine(hash, options.formatType);
return hash;
}
};
#endif // !defined(IWA_IO_BITMAP_HPP)

55
include/iwa/io/font.hpp Normal file
View File

@@ -0,0 +1,55 @@
#pragma once
#if !defined(IWA_IO_FONT_HPP_INCLUDED)
#define IWA_IO_FONT_HPP_INCLUDED
#include <mijin/io/stream.hpp>
#include <mijin/virtual_filesystem/filesystem.hpp>
#include "iwa/resource/font.hpp"
namespace iwa
{
struct FontLoadOptions
{
mijin::Stream* stream;
float size = 20.f;
};
struct FontLoadFileOptions
{
mijin::PathReference path;
float size = 20.f;
auto operator<=>(const FontLoadFileOptions& other) const noexcept = default;
};
[[nodiscard]] ObjectPtr<Font> loadFont(const FontLoadOptions& options);
[[nodiscard]] inline ObjectPtr<Font> loadFontFromFile(const FontLoadFileOptions& options)
{
std::unique_ptr<mijin::Stream> stream;
const mijin::StreamError error = options.path.open(mijin::FileOpenMode::READ, stream);
if (error != mijin::StreamError::SUCCESS) {
throw std::runtime_error("Error opening font file.");
}
return loadFont({
.stream = stream.get(),
.size = options.size
});
}
} // namespace iwa
template<>
struct std::hash<iwa::FontLoadFileOptions>
{
std::size_t operator()(const iwa::FontLoadFileOptions& options) const noexcept
{
std::size_t hash = 0;
mijin::hashCombine(hash, options.path);
mijin::hashCombine(hash, options.size);
return hash;
}
};
#endif // !defined(IWA_IO_FONT_HPP_INCLUDED)

11
include/iwa/io/mesh.hpp Normal file
View File

@@ -0,0 +1,11 @@
#pragma once
#if !defined(IWA_IO_MESH_HPP_INCLUDED)
#define IWA_IO_MESH_HPP_INCLUDED
namespace iwa
{
} // namespace iwa
#endif // !defined(IWA_IO_MESH_HPP_INCLUDED)

57
include/iwa/log.hpp Normal file
View File

@@ -0,0 +1,57 @@
#pragma once
#ifndef IWA_LOG_HPP_INCLUDED
#define IWA_LOG_HPP_INCLUDED
#include <cstdio>
#include <fmt/format.h>
#include <fmt/ranges.h>
#include <mijin/debug/stacktrace.hpp>
#include <mijin/util/iterators.hpp>
namespace iwa
{
inline void logMsg(const std::string& msg) noexcept
{
std::puts(msg.c_str());
(void) std::fflush(stdout);
}
template<typename... TArgs>
inline void logMsg(std::string_view msg, TArgs&&... args) noexcept
{
fmt::print(stdout, fmt::runtime(msg), std::forward<TArgs>(args)...);
(void) std::fputc('\n', stdout);
(void) std::fflush(stdout);
}
inline void vlogMsg(std::string_view msg, fmt::format_args args)
{
fmt::vprint(stdout, msg, args);
(void) std::fputc('\n', stdout);
(void) std::fflush(stdout);
}
template<typename... TArgs>
[[noreturn]]
inline void logAndDie(std::string_view msg, TArgs&&... args) noexcept
{
fmt::print(stderr, fmt::runtime(msg), std::forward<TArgs>(args)...);
(void) std::fputc('\n', stderr);
(void) std::fflush(stderr);
std::abort();
}
template<typename... TArgs>
inline void logVerbose([[maybe_unused]] std::string_view msg, [[maybe_unused]] TArgs&&... args) noexcept
{
#if defined(KAZAN_VERBOSE_LOGGING)
std::printf("[VERBOSE] ");
logMsg(msg, std::forward<TArgs>(args)...);
#endif
}
}
#endif // IWA_LOG_HPP_INCLUDED

566
include/iwa/object.hpp Normal file
View File

@@ -0,0 +1,566 @@
#pragma once
#ifndef IWA_OBJECT_HPP_INCLUDED
#define IWA_OBJECT_HPP_INCLUDED
#include <atomic>
#include <cstdint>
#include <functional>
#include <list>
#include <optional>
#include <type_traits>
#include <utility>
#include <vector>
#include <mijin/debug/assert.hpp>
#if !defined(IWA_OBJECTPTR_TRACKING)
# if !defined(KAZAN_RELEASE)
# define IWA_OBJECTPTR_TRACKING 1
# else
# define IWA_OBJECTPTR_TRACKING 0
# endif
#endif
#define IWA_NEW_OBJECT(type, ...) (new type(__VA_ARGS__))->getPointer(/* skipCheck = */ true)
#if IWA_OBJECTPTR_TRACKING > 1
#include <mijin/debug/stacktrace.hpp>
#endif
namespace iwa
{
using object_id_t = std::uint64_t;
using object_destruction_handler_t = std::function<void()>;
class BaseObject;
template<typename TObject>
class WeakObjectPtr;
#if IWA_OBJECTPTR_TRACKING
namespace impl
{
struct ObjectPtrAllocation
{
class BaseObject* object;
#if IWA_OBJECTPTR_TRACKING > 1
mijin::Result<mijin::Stacktrace> stacktrace;
#endif
};
using objectptr_allocation_handle_t = std::optional<std::list<ObjectPtrAllocation>::iterator>;
#if IWA_OBJECTPTR_TRACKING > 1
objectptr_allocation_handle_t trackObjectPtr(class BaseObject* object, mijin::Result<mijin::Stacktrace>&& stacktrace) noexcept;
#else
objectptr_allocation_handle_t trackObjectPtr(class BaseObject* object) noexcept;
#endif
void untrackObjectPtr(objectptr_allocation_handle_t handle) noexcept;
}
#if IWA_OBJECTPTR_TRACKING > 1
#define IWA_DECLARE_OBJECTPTR_TRACKING() impl::objectptr_allocation_handle_t mAllocationHandle;
#define IWA_TRACK_OBJECTPTR() mAllocationHandle = impl::trackObjectPtr(mObject, mijin::captureStacktrace(1));
#define IWA_TRACK_OBJECTPTR_NEW() ptr.mAllocationHandle = impl::trackObjectPtr(ptr.mObject, mijin::captureStacktrace(1));
#define IWA_UNTRACK_OBJECTPTR() \
if (mAllocationHandle != impl::objectptr_allocation_handle_t()) \
{ \
impl::untrackObjectPtr(mAllocationHandle); \
mAllocationHandle = impl::objectptr_allocation_handle_t(); \
}
#define IWA_MOVE_CONSTRUCT_OBJECTPTR_HANDLE() , mAllocationHandle(std::exchange(otherPtr.mAllocationHandle, impl::objectptr_allocation_handle_t()))
#define IWA_MOVE_ASSIGN_OBJECTPTR_HANDLE() mAllocationHandle = std::exchange(otherPtr.mAllocationHandle, impl::objectptr_allocation_handle_t());
#define IWA_UPDATE_OBJECTPTR_HANDLE() \
if (mAllocationHandle != impl::objectptr_allocation_handle_t()) \
{ \
(*mAllocationHandle)->stacktrace = mijin::captureStacktrace(1); \
}
#else
#define IWA_DECLARE_OBJECTPTR_TRACKING() impl::objectptr_allocation_handle_t mAllocationHandle;
#define IWA_TRACK_OBJECTPTR() mAllocationHandle = impl::trackObjectPtr(mObject);
#define IWA_TRACK_OBJECTPTR_NEW() ptr.mAllocationHandle = impl::trackObjectPtr(ptr.mObject);
#define IWA_UNTRACK_OBJECTPTR() \
if (mAllocationHandle != impl::objectptr_allocation_handle_t()) \
{ \
impl::untrackObjectPtr(mAllocationHandle); \
mAllocationHandle = impl::objectptr_allocation_handle_t(); \
}
#define IWA_MOVE_CONSTRUCT_OBJECTPTR_HANDLE() , mAllocationHandle(std::exchange(otherPtr.mAllocationHandle, impl::objectptr_allocation_handle_t()))
#define IWA_MOVE_ASSIGN_OBJECTPTR_HANDLE() mAllocationHandle = std::exchange(otherPtr.mAllocationHandle, impl::objectptr_allocation_handle_t());
#define IWA_UPDATE_OBJECTPTR_HANDLE()
#endif
#else // IWA_OBJECTPTR_TRACKING
#define IWA_DECLARE_OBJECTPTR_TRACKING()
#define IWA_TRACK_OBJECTPTR()
#define IWA_TRACK_OBJECTPTR_NEW()
#define IWA_UNTRACK_OBJECTPTR()
#define IWA_MOVE_CONSTRUCT_OBJECTPTR_HANDLE()
#define IWA_MOVE_ASSIGN_OBJECTPTR_HANDLE()
#define IWA_UPDATE_OBJECTPTR_HANDLE()
#endif // else IWA_OBJECTPTR_TRACKING
template<typename TObject = class BaseObject>
class ObjectPtr
{
private:
TObject* mObject = nullptr;
IWA_DECLARE_OBJECTPTR_TRACKING()
private:
// ObjectPtr(TObject* object) noexcept : mObject(object) {}
public:
/** default construction */
ObjectPtr() = default;
/** nullptr initialization */
ObjectPtr(std::nullptr_t) noexcept : mObject(nullptr) {}
/** copy */
ObjectPtr(const ObjectPtr& otherPtr) : mObject(otherPtr.mObject)
{
if (mObject != nullptr)
{
mObject->increaseReferenceCount();
IWA_TRACK_OBJECTPTR()
}
}
/** move */
ObjectPtr(ObjectPtr&& otherPtr) noexcept : mObject(std::exchange(otherPtr.mObject, nullptr))
IWA_MOVE_CONSTRUCT_OBJECTPTR_HANDLE()
{
IWA_UPDATE_OBJECTPTR_HANDLE()
}
/** implicit conversion */
template<typename TOtherObject> requires(std::is_base_of_v<TObject, TOtherObject>)
ObjectPtr(const ObjectPtr<TOtherObject>& otherPtr) noexcept : mObject(otherPtr.mObject)
{
if (mObject != nullptr)
{
mObject->increaseReferenceCount();
IWA_TRACK_OBJECTPTR()
}
}
/** implicit conversion */
template<typename TOtherObject> requires(std::is_base_of_v<TObject, TOtherObject>)
ObjectPtr(ObjectPtr<TOtherObject>&& otherPtr) noexcept : mObject(std::exchange(otherPtr.mObject, nullptr))
IWA_MOVE_CONSTRUCT_OBJECTPTR_HANDLE()
{
IWA_UPDATE_OBJECTPTR_HANDLE()
}
/** explicit conversion */
template<typename TOtherObject> requires(!std::is_same_v<TObject, TOtherObject> && std::is_base_of_v<TOtherObject, TObject>)
explicit ObjectPtr(const ObjectPtr<TOtherObject>& otherPtr) noexcept : mObject(static_cast<TObject*>(otherPtr.mObject))
{
if (mObject != nullptr)
{
mObject->increaseReferenceCount();
IWA_TRACK_OBJECTPTR()
}
}
/** explicit conversion */
template<typename TOtherObject> requires(!std::is_same_v<TObject, TOtherObject> && std::is_base_of_v<TOtherObject, TObject>)
explicit ObjectPtr(ObjectPtr<TOtherObject>&& otherPtr) noexcept : mObject(static_cast<TObject*>(std::exchange(otherPtr.mObject, nullptr)))
IWA_MOVE_CONSTRUCT_OBJECTPTR_HANDLE()
{
IWA_UPDATE_OBJECTPTR_HANDLE()
}
~ObjectPtr() noexcept
{
if (mObject != nullptr)
{
mObject->decreaseReferenceCount();
}
IWA_UNTRACK_OBJECTPTR()
}
ObjectPtr& operator=(const ObjectPtr& otherPtr) noexcept
{
if (this != &otherPtr)
{
if (mObject != nullptr)
{
mObject->decreaseReferenceCount();
}
IWA_UNTRACK_OBJECTPTR()
mObject = otherPtr.mObject;
if (mObject != nullptr)
{
mObject->increaseReferenceCount();
IWA_TRACK_OBJECTPTR()
}
}
return *this;
}
ObjectPtr& operator=(ObjectPtr&& otherPtr) noexcept
{
if (this != &otherPtr)
{
if (mObject != nullptr)
{
mObject->decreaseReferenceCount();
}
IWA_UNTRACK_OBJECTPTR()
mObject = std::exchange(otherPtr.mObject, nullptr);
IWA_MOVE_ASSIGN_OBJECTPTR_HANDLE()
IWA_UPDATE_OBJECTPTR_HANDLE()
}
return *this;
}
template<typename TOtherObject> requires(std::is_base_of_v<TObject, TOtherObject>)
ObjectPtr& operator=(const ObjectPtr<TOtherObject>& otherPtr) noexcept
{
if (static_cast<const void*>(this) != static_cast<const void*>(&otherPtr))
{
if (mObject != nullptr)
{
mObject->decreaseReferenceCount();
}
IWA_UNTRACK_OBJECTPTR()
mObject = otherPtr.mObject;
if (mObject != nullptr)
{
mObject->increaseReferenceCount();
IWA_TRACK_OBJECTPTR()
}
}
return *this;
}
template<typename TOtherObject> requires(std::is_base_of_v<TObject, TOtherObject>)
ObjectPtr& operator=(ObjectPtr<TOtherObject>&& otherPtr) noexcept
{
if (static_cast<void*>(this) != static_cast<void*>(&otherPtr))
{
if (mObject != nullptr)
{
mObject->decreaseReferenceCount();
}
mObject = std::exchange(otherPtr.mObject, nullptr);
IWA_MOVE_ASSIGN_OBJECTPTR_HANDLE()
IWA_UPDATE_OBJECTPTR_HANDLE()
}
return *this;
}
ObjectPtr& operator=(std::nullptr_t) noexcept
{
if (mObject != nullptr)
{
mObject->decreaseReferenceCount();
}
IWA_UNTRACK_OBJECTPTR()
mObject = nullptr;
return *this;
}
[[nodiscard]] bool operator!() const noexcept { return mObject == nullptr; }
[[nodiscard]] TObject& operator*() const noexcept { return *mObject; }
[[nodiscard]] TObject* operator->() const noexcept { return mObject; }
[[nodiscard]] auto operator<=>(const ObjectPtr&) const noexcept = default;
[[nodiscard]] operator bool() const noexcept { return mObject != nullptr; }
[[nodiscard]] bool operator==(std::nullptr_t) const noexcept { return mObject == nullptr; }
[[nodiscard]] bool operator!=(std::nullptr_t) const noexcept { return mObject != nullptr; }
#define MAKE_OPERATOR(op) \
template<typename TOtherObject> requires (std::is_base_of_v<TObject, TOtherObject> || std::is_base_of_v<TOtherObject, TObject>) \
auto operator op (const ObjectPtr<TOtherObject>& otherPtr) const noexcept \
{ \
return mObject op otherPtr.mObject; \
}
MAKE_OPERATOR(<=>)
MAKE_OPERATOR(==)
MAKE_OPERATOR(!=)
MAKE_OPERATOR(<)
MAKE_OPERATOR(<=)
MAKE_OPERATOR(>)
MAKE_OPERATOR(>=)
#undef MAKE_OPERATOR
[[nodiscard]] TObject* getRaw() const noexcept { return mObject; }
[[nodiscard]] WeakObjectPtr<TObject> makeWeak() const noexcept;
template<typename TOtherObject> requires(std::is_base_of_v<TObject, TOtherObject>)
[[nodiscard]] ObjectPtr<TOtherObject> dynamicCast() const noexcept;
friend class BaseObject;
template<typename TConcrete, typename TBase, typename TOwner>
friend class Object;
template<typename TOthterObject>
friend class ObjectPtr;
};
namespace impl
{
[[nodiscard]] object_id_t nextObjectId() noexcept;
void registerObject(BaseObject* object) noexcept;
void unregisterObject(BaseObject* object) noexcept;
[[nodiscard]] ObjectPtr<BaseObject> getRegisteredObject(object_id_t objectId) noexcept;
}
class BaseObject
{
public:
using owner_t = BaseObject;
private:
object_id_t mId = 0;
std::atomic_uint32_t mReferenceCount = 1;
protected:
ObjectPtr<BaseObject> mOwner = nullptr;
#if !defined(KAZAN_RELEASE)
// std::vector<BaseObject*> mChildren;
#endif
protected:
explicit BaseObject(ObjectPtr<BaseObject> owner = nullptr) noexcept : mId(impl::nextObjectId()), mOwner(std::move(owner))
{
impl::registerObject(this);
}
public:
BaseObject(const BaseObject&) = delete;
BaseObject(BaseObject&&) = delete;
virtual ~BaseObject() noexcept
{
impl::unregisterObject(this);
}
BaseObject& operator=(const BaseObject&) = delete;
BaseObject& operator=(BaseObject&&) = delete;
[[nodiscard]] inline bool canCreatePointer() const noexcept { return mReferenceCount > 0; }
[[nodiscard]] inline object_id_t getId() const noexcept { return mId; }
[[nodiscard]] inline ObjectPtr<BaseObject> getPointer(bool skipCheck = false) noexcept;
[[nodiscard]] inline WeakObjectPtr<BaseObject> getWeakPointer() noexcept;
protected:
inline void increaseReferenceCount() noexcept
{
++mReferenceCount;
}
inline void decreaseReferenceCount() noexcept
{
if (--mReferenceCount == 0)
{
delete this;
}
}
[[nodiscard]] inline std::uint32_t getReferenceCount() const noexcept { return mReferenceCount; }
template<typename TObject>
friend class ObjectPtr;
};
template<typename TConcrete, typename TBase = BaseObject, typename TOwner = TBase::owner_t>
class Object : public TBase
{
public:
using super_t = Object<TConcrete, TBase, TOwner>;
using owner_t = TOwner;
protected:
template<typename... TArgs>
explicit Object(TArgs&&... args) : TBase(std::forward<TArgs>(args)...) {}
public:
[[nodiscard]] TOwner* getOwner() const noexcept { return static_cast<TOwner*>(TBase::mOwner.getRaw()); }
[[nodiscard]] ObjectPtr<TConcrete> getPointer(bool skipCheck = false) noexcept { return static_cast<ObjectPtr<TConcrete>>(BaseObject::getPointer(skipCheck)); }
[[nodiscard]] WeakObjectPtr<TConcrete> getWeakPointer() noexcept { return static_cast<WeakObjectPtr<TConcrete>>(BaseObject::getWeakPointer()); }
inline ObjectPtr<TConcrete> makeUnique()
{
if (TBase::getReferenceCount() == 1)
{
return getPointer();
}
else
{
return static_cast<TConcrete*>(this)->clone();
}
}
template<typename TObject, typename... TArgs>
ObjectPtr<TObject> createChild(TArgs&&... args)
{
ObjectPtr<TObject> child = TObject::create(static_cast<TConcrete*>(this)->getPointer(), std::forward<TArgs>(args)...);
#if !defined(KAZAN_RELEASE)
// mChildren.push_back(&*child);
#endif
return child;
}
public:
template<typename... TArgs>
static ObjectPtr<TConcrete> create(TArgs&&... args) noexcept;
};
template<typename TConcrete, typename TBase = BaseObject, typename TOwner = TBase::owner_t>
class AbstractObject : public TBase
{
public:
using super_t = AbstractObject<TConcrete, TBase, TOwner>;
using owner_t = TOwner;
protected:
template<typename... TArgs>
explicit AbstractObject(TArgs&&... args) noexcept : TBase(std::forward<TArgs>(args)...) {}
public:
[[nodiscard]] TOwner* getOwner() const noexcept { return static_cast<TOwner*>(TBase::mOwner.getRaw()); }
[[nodiscard]] ObjectPtr<TConcrete> getPointer(bool skipCheck = false) noexcept { return static_cast<ObjectPtr<TConcrete>>(BaseObject::getPointer(skipCheck)); }
[[nodiscard]] WeakObjectPtr<TConcrete> getWeakPointer() noexcept { return static_cast<WeakObjectPtr<TConcrete>>(BaseObject::getWeakPointer()); }
};
template<typename TObject>
class WeakObjectPtr;
template<typename TObject>
class WeakObjectPtr
{
private:
object_id_t mId = 0;
public:
/** default construction */
WeakObjectPtr() = default;
/** copy */
WeakObjectPtr(const WeakObjectPtr&) = default;
/** implicit conversion */
template<typename TOtherObject> requires(std::is_base_of_v<TObject, TOtherObject>)
WeakObjectPtr(const WeakObjectPtr<TOtherObject>& otherPtr) noexcept : mId(otherPtr.mId) {}
/** explicit conversion */
template<typename TOtherObject> requires(!std::is_same_v<TObject, TOtherObject> && std::is_base_of_v<TOtherObject, TObject>)
explicit WeakObjectPtr(const WeakObjectPtr<TOtherObject>& otherPtr) noexcept : mId(otherPtr.mId) {}
/** construction from a regular ObjectPtr */
template<typename TOtherObject> requires(std::is_base_of_v<TObject, TOtherObject>)
WeakObjectPtr(const ObjectPtr<TOtherObject>& otherPtr) noexcept : mId(otherPtr ? otherPtr->getId() : 0) {}
/** construction directly from the object */
template<typename TOtherObject> requires(std::is_base_of_v<TObject, TOtherObject>)
WeakObjectPtr(const TOtherObject& object) noexcept : mId(object.getId()) {}
WeakObjectPtr& operator=(const WeakObjectPtr&) noexcept = default;
template<typename TOtherObject> requires(std::is_base_of_v<TObject, TOtherObject>)
WeakObjectPtr& operator=(const WeakObjectPtr<TOtherObject>& otherPtr) noexcept
{
mId = otherPtr.mId;
return *this;
}
WeakObjectPtr& operator=(std::nullptr_t) noexcept
{
mId = 0;
return *this;
}
[[nodiscard]] bool operator!() const noexcept { return mId == 0; }
[[nodiscard]] auto operator<=>(const WeakObjectPtr&) const noexcept = default;
[[nodiscard]] bool operator==(std::nullptr_t) const noexcept { return mId == 0; }
[[nodiscard]] bool operator!=(std::nullptr_t) const noexcept { return mId != 0; }
[[nodiscard]] operator bool() const noexcept { return mId != 0; }
#define MAKE_OPERATOR(op) \
template<typename TOtherObject> requires (std::is_base_of_v<TObject, TOtherObject> || std::is_base_of_v<TOtherObject, TObject>) \
auto operator op (const ObjectPtr<TOtherObject>& otherPtr) const noexcept \
{ \
return mId op otherPtr.mId; \
}
MAKE_OPERATOR(<=>)
MAKE_OPERATOR(==)
MAKE_OPERATOR(!=)
MAKE_OPERATOR(<)
MAKE_OPERATOR(<=)
MAKE_OPERATOR(>)
MAKE_OPERATOR(>=)
#undef MAKE_OPERATOR
[[nodiscard]] object_id_t getId() const noexcept { return mId; }
[[nodiscard]] ObjectPtr<TObject> pin() const noexcept
{
return static_cast<ObjectPtr<TObject>>(impl::getRegisteredObject(mId));
}
template<typename TOtherObject>
friend class WeakObjectPtr;
};
ObjectPtr<BaseObject> BaseObject::getPointer([[maybe_unused]] bool skipCheck) noexcept
{
// MIJIN_ASSERT(skipCheck || canCreatePointer(), "Cannot create an object pointer for an object that has not been created using ::create()!");
if (!skipCheck)
{
increaseReferenceCount();
}
ObjectPtr<BaseObject> ptr;
ptr.mObject = this;
IWA_TRACK_OBJECTPTR_NEW();
return ptr;
}
WeakObjectPtr<BaseObject> BaseObject::getWeakPointer() noexcept
{
return WeakObjectPtr<BaseObject>(*this);
}
template<typename TObject>
WeakObjectPtr<TObject> ObjectPtr<TObject>::makeWeak() const noexcept
{
return WeakObjectPtr<TObject>(*this);
}
template<typename TObject>
template<typename TOtherObject> requires(std::is_base_of_v<TObject, TOtherObject>)
ObjectPtr<TOtherObject> ObjectPtr<TObject>::dynamicCast() const noexcept
{
if (dynamic_cast<const TOtherObject*>(mObject)) {
return ObjectPtr<TOtherObject>(*this);
}
return nullptr;
}
template<typename TConcrete, typename TBase, typename TOwner>
template<typename... TArgs>
ObjectPtr<TConcrete> Object<TConcrete, TBase, TOwner>::create(TArgs&&... args) noexcept
{
return (new TConcrete(std::forward<TArgs>(args)...))->getPointer(/* skipCheck = */ true);
}
void registerObjectDestructionHandler(const BaseObject& object, object_destruction_handler_t handler) noexcept;
} // namespace iwa
template<typename TObject>
struct std::hash<iwa::ObjectPtr<TObject>> // NOLINT false positive
{
std::size_t operator()(const iwa::ObjectPtr<TObject>& ptr) const noexcept
{
return std::hash<void*>()(ptr.getRaw());
}
};
template<typename TObject>
struct std::hash<iwa::WeakObjectPtr<TObject>> // NOLINT false positive
{
std::size_t operator()(const iwa::WeakObjectPtr<TObject>& ptr) const noexcept
{
return std::hash<iwa::object_id_t>()(ptr.getId());
}
};
#undef IWA_DECLARE_OBJECTPTR_TRACKING
#undef IWA_TRACK_OBJECTPTR
#undef IWA_TRACK_OBJECTPTR_NEW
#undef IWA_UNTRACK_OBJECTPTR
#undef IWA_MOVE_CONSTRUCT_OBJECTPTR_HANDLE
#undef IWA_MOVE_ASSIGN_OBJECTPTR_HANDLE
#undef IWA_UPDATE_OBJECTPTR_HANDLE
#endif // IWA_OBJECT_HPP_INCLUDED

136
include/iwa/pipeline.hpp Normal file
View File

@@ -0,0 +1,136 @@
#pragma once
#if !defined(IWA_PIPELINE_HPP_INCLUDED)
#define IWA_PIPELINE_HPP_INCLUDED
#include <optional>
#include "iwa/descriptor_set.hpp"
#include "iwa/object.hpp"
#include "iwa/render_pass.hpp"
#include "iwa/shader_module.hpp"
#include "iwa/vkwrapper.hpp"
namespace iwa
{
inline constexpr vk::PipelineColorBlendAttachmentState DEFAULT_BLEND_ATTACHMENT =
{
.blendEnable = VK_TRUE,
.srcColorBlendFactor = vk::BlendFactor::eSrcAlpha,
.dstColorBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha,
.colorBlendOp = vk::BlendOp::eAdd,
.srcAlphaBlendFactor = vk::BlendFactor::eSrcAlpha,
.dstAlphaBlendFactor = vk::BlendFactor::eDstAlpha,
.alphaBlendOp = vk::BlendOp::eAdd,
.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG
| vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA
};
inline constexpr vk::PipelineColorBlendAttachmentState DISABLED_BLEND_ATTACHMENT =
{
.blendEnable = VK_FALSE,
.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG
| vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA
};
struct PipelineLayoutCreationArgs
{
std::vector<ObjectPtr<DescriptorSetLayout>> setLayouts;
std::vector<vk::PushConstantRange> pushConstantRanges;
vk::PipelineLayoutCreateFlags flags = {};
};
class PipelineLayout : public Object<PipelineLayout, BaseObject, class Device>, public MixinVulkanObject<vk::PipelineLayout>
{
public:
PipelineLayout(ObjectPtr<class Device> owner, const PipelineLayoutCreationArgs& args);
~PipelineLayout() noexcept override;
};
class Pipeline : public AbstractObject<Pipeline, BaseObject, class Device>, public MixinVulkanObject<vk::Pipeline>
{
protected:
explicit Pipeline(ObjectPtr<class Device> owner) noexcept;
public:
~Pipeline() noexcept override;
};
struct PipelineStage
{
ObjectPtr<ShaderModule> shader;
vk::ShaderStageFlagBits stage;
std::string name = "main";
};
struct GraphicsPipelineRenderingInfo
{
std::uint32_t viewMask = 0;
std::vector<vk::Format> colorAttachmentFormats;
vk::Format depthFormat = vk::Format::eUndefined;
vk::Format stencilFormat = vk::Format::eUndefined;
};
struct VertexInput
{
std::vector<vk::VertexInputBindingDescription> bindings;
std::vector<vk::VertexInputAttributeDescription> attributes;
};
struct GraphicsPipelineCreationArgs
{
std::vector<PipelineStage> stages;
VertexInput vertexInput;
vk::PipelineInputAssemblyStateCreateInfo inputAssembly = { .topology = vk::PrimitiveTopology::eTriangleList };
vk::PipelineViewportStateCreateInfo viewport = { .viewportCount = 1, .scissorCount = 1 };
vk::PipelineRasterizationStateCreateInfo rasterization =
{ .rasterizerDiscardEnable = VK_FALSE, .polygonMode = vk::PolygonMode::eFill, .cullMode = vk::CullModeFlagBits::eBack,
.frontFace = vk::FrontFace::eCounterClockwise, .depthBiasEnable = VK_FALSE, .lineWidth = 1.f };
vk::PipelineMultisampleStateCreateInfo multisample =
{ .rasterizationSamples = vk::SampleCountFlagBits::e1, .sampleShadingEnable = VK_FALSE, .minSampleShading = 0.2f,
.pSampleMask = nullptr, .alphaToCoverageEnable = VK_FALSE, .alphaToOneEnable = VK_FALSE };
vk::PipelineDepthStencilStateCreateInfo depthStencil =
{ .depthTestEnable = false, .depthWriteEnable = false, .depthCompareOp = vk::CompareOp::eLess,
.depthBoundsTestEnable = VK_FALSE, .minDepthBounds = 0.0, .maxDepthBounds = 1.0 };
struct
{
std::optional<vk::LogicOp> logicOp;
std::vector<vk::PipelineColorBlendAttachmentState> attachements;
} colorBlend;
std::vector<vk::DynamicState> dynamicState = {vk::DynamicState::eViewport, vk::DynamicState::eScissor};
std::optional<GraphicsPipelineRenderingInfo> renderingInfo;
ObjectPtr<PipelineLayout> layout;
ObjectPtr<RenderPass> renderPass;
std::uint32_t subpass = 0;
};
class GraphicsPipeline : public Object<GraphicsPipeline, Pipeline>
{
public:
GraphicsPipeline(ObjectPtr<class Device> owner, const GraphicsPipelineCreationArgs& args) noexcept;
};
struct ComputePipelineCreationArgs
{
PipelineStage stage;
ObjectPtr<PipelineLayout> layout;
};
class ComputePipeline : public Object<ComputePipeline, Pipeline>
{
public:
ComputePipeline(ObjectPtr<class Device> owner, const ComputePipelineCreationArgs& args) noexcept;
};
struct RayTracingPipelineCreationArgs
{
};
class RayTracingPipeline : public Object<RayTracingPipeline, Pipeline>
{
public:
RayTracingPipeline(ObjectPtr<class Device> owner, const RayTracingPipelineCreationArgs& args) noexcept;
};
} // namespace iwa
#endif // !defined(IWA_PIPELINE_HPP_INCLUDED)

View File

@@ -0,0 +1,59 @@
#pragma once
#if !defined(IWA_RENDER_PASS_HPP_INCLUDED)
#define IWA_RENDER_PASS_HPP_INCLUDED
#include <optional>
#include "iwa/object.hpp"
#include "iwa/vkwrapper.hpp"
namespace iwa
{
struct SubpassDescription
{
vk::SubpassDescriptionFlags flags = {};
vk::PipelineBindPoint pipelineBindPoint;
std::vector<vk::AttachmentReference> inputAttachments;
std::vector<vk::AttachmentReference> colorAttachments;
std::vector<vk::AttachmentReference> resolveAttachments;
std::optional<vk::AttachmentReference> depthStencilAttachment;
std::vector<std::uint32_t> preserveAttachments;
};
struct RenderPassCreationArgs
{
vk::RenderPassCreateFlags flags = {};
std::vector<vk::AttachmentDescription> attachments;
std::vector<SubpassDescription> subpasses;
std::vector<vk::SubpassDependency> dependencies;
};
class RenderPass : public Object<RenderPass, BaseObject, class Device>, public MixinVulkanObject<vk::RenderPass>
{
public:
RenderPass(ObjectPtr<class Device> owner, const RenderPassCreationArgs& args);
~RenderPass() noexcept override;
};
struct FramebufferCreationArgs
{
vk::FramebufferCreateFlags flags = {};
ObjectPtr<RenderPass> renderPass;
std::vector<ObjectPtr<class ImageView>> attachments;
std::uint32_t width = 0;
std::uint32_t height = 0;
std::uint32_t layers = 1;
};
class Framebuffer : public Object<Framebuffer, BaseObject, class Device>, public MixinVulkanObject<vk::Framebuffer>
{
private:
std::vector<ObjectPtr<class ImageView>> mImageViews;
public:
Framebuffer(ObjectPtr<class Device> owner, const FramebufferCreationArgs& args);
~Framebuffer() noexcept override;
};
} // namespace iwa
#endif // !defined(IWA_RENDER_PASS_HPP_INCLUDED)

View File

@@ -0,0 +1,73 @@
#pragma once
#if !defined(IWA_RESOURCE_BITMAP_HPP_INCLUDED)
#define IWA_RESOURCE_BITMAP_HPP_INCLUDED
#include <memory>
#include <span>
#include <vector>
#include <glm/vec4.hpp>
#include <mijin/container/optional.hpp>
#include <mijin/container/typeless_buffer.hpp>
#include "../object.hpp"
#include "../vkwrapper.hpp"
namespace iwa
{
enum class ColorChannel
{
R = 0,
G = 1,
B = 2,
A = 3
};
struct ChannelMapping
{
ColorChannel from;
ColorChannel to;
};
struct BitmapCreationArgs
{
vk::Format format;
vk::Extent2D size;
mijin::Optional<mijin::TypelessBuffer> initialData;
};
class Bitmap : public Object<Bitmap>
{
private:
mijin::TypelessBuffer mData;
vk::Format mFormat;
vk::Extent2D mSize;
std::unique_ptr<class BitmapViewBase> mView;
public:
explicit Bitmap(BitmapCreationArgs args, ObjectPtr<BaseObject> owner = nullptr);
~Bitmap() override;
// properties
inline vk::Format getFormat() const { return mFormat; }
inline vk::Extent2D getSize() const { return mSize; }
inline std::span<std::uint8_t> getData() { return mData.makeSpan<std::uint8_t>(); }
inline std::span<const std::uint8_t> getData() const { return mData.makeSpan<const std::uint8_t>(); }
// access
// TODO: maybe add accessors for whole rows or the whole image?
[[nodiscard]] glm::vec4 getPixel(unsigned x, unsigned y) const;
[[nodiscard]] std::vector<glm::vec4> getPixels(unsigned x, unsigned y, unsigned width, unsigned height) const;
[[nodiscard]] inline std::vector<glm::vec4> getAllPixels() const {
return getPixels(0, 0, mSize.width, mSize.height);
}
// drawing
void fill(const glm::vec4& color);
void copyChannels(const Bitmap& other, const std::vector<ChannelMapping>& mappings);
void multiply(const glm::vec4& color);
private:
void createView();
};
} // namespace iwa
#endif // !defined(IWA_RESOURCE_BITMAP_HPP_INCLUDED)

View File

@@ -0,0 +1,55 @@
#pragma once
#if !defined(IWA_RESOURCE_FONT_HPP_INCLUDED)
#define IWA_RESOURCE_FONT_HPP_INCLUDED
#include <unordered_map>
#include <glm/vec2.hpp>
#include "iwa/object.hpp"
#include "iwa/resource/bitmap.hpp"
namespace iwa
{
struct GlyphInfo
{
glm::vec2 uvPos0;
glm::vec2 uvPos1;
float xOffsetBefore;
float xOffsetAfter;
float yOffsetBefore;
float yOffsetAfter;
float xAdvance;
};
struct FontMetrics
{
float ascent;
float descent;
float lineGap;
float sizeFactor;
};
struct FontCreationArgs
{
ObjectPtr<Bitmap> bitmap;
std::unordered_map<char32_t, GlyphInfo> glyphMap;
FontMetrics metrics;
};
class Font : public Object<Font>
{
private:
ObjectPtr<Bitmap> mBitmap;
std::unordered_map<char32_t, GlyphInfo> mGlyphMap;
FontMetrics mMetrics;
public:
explicit Font(FontCreationArgs args);
[[nodiscard]] const ObjectPtr<Bitmap>& getBitmap() const noexcept { return mBitmap; }
[[nodiscard]] const std::unordered_map<char32_t, GlyphInfo>& getGlyphMap() const noexcept { return mGlyphMap; }
[[nodiscard]] const FontMetrics& getMetrics() const noexcept { return mMetrics; }
};
} // namespace iwa
#endif // !defined(IWA_RESOURCE_FONT_HPP_INCLUDED)

25
include/iwa/semaphore.hpp Normal file
View File

@@ -0,0 +1,25 @@
#pragma once
#if !defined(IWA_SEMAPHORE_HPP_INCLUDED)
#define IWA_SEMAPHORE_HPP_INCLUDED
#include "iwa/object.hpp"
#include "iwa/vkwrapper.hpp"
namespace iwa
{
struct SemaphoreCreationArgs
{
vk::SemaphoreCreateFlags flags = {};
};
class Semaphore : public Object<Semaphore, BaseObject, class Device>, public MixinVulkanObject<vk::Semaphore>
{
public:
Semaphore(ObjectPtr<class Device> owner, const SemaphoreCreationArgs& args = {});
~Semaphore() noexcept override;
};
}
#endif // !defined(IWA_SEMAPHORE_HPP_INCLUDED)

View File

@@ -0,0 +1,27 @@
#pragma once
#if !defined(IWA_SHADER_MODULE_HPP_INCLUDED)
#define IWA_SHADER_MODULE_HPP_INCLUDED
#include <span>
#include <mijin/container/typeless_buffer.hpp>
#include "iwa/object.hpp"
#include "iwa/vkwrapper.hpp"
namespace iwa
{
struct ShaderModuleCreationArgs
{
std::span<const std::uint32_t> code;
};
class ShaderModule : public Object<ShaderModule, BaseObject, class Device>, public MixinVulkanObject<vk::ShaderModule>
{
public:
ShaderModule(ObjectPtr<class Device> owner, const ShaderModuleCreationArgs& args);
~ShaderModule() noexcept override;
};
} // namespace iwa
#endif // !defined(IWA_SHADER_MODULE_HPP_INCLUDED)

68
include/iwa/swapchain.hpp Normal file
View File

@@ -0,0 +1,68 @@
#pragma once
#ifndef IWA_SWAPCHAIN_HPP_INCLUDED
#define IWA_SWAPCHAIN_HPP_INCLUDED
#include <vector>
#include <mijin/async/signal.hpp>
#include "iwa/image.hpp"
#include "iwa/object.hpp"
#include "iwa/semaphore.hpp"
#include "iwa/vkwrapper.hpp"
#include "iwa/window.hpp"
namespace iwa
{
struct SwapchainCreationArgs
{
ObjectPtr<Window> window;
std::uint32_t parallelFrames = 3;
vk::ImageUsageFlags imageUsage = vk::ImageUsageFlagBits::eColorAttachment;
};
struct PresentArgs
{
vk::Queue queue;
std::vector<vk::Semaphore> waitSemaphores;
};
class Swapchain : public Object<Swapchain, BaseObject, class Device>, public MixinVulkanObject<vk::SwapchainKHR>
{
private:
static inline std::uint32_t INVALID_IMAGE_INDEX = std::numeric_limits<std::uint32_t>::max();
std::uint32_t mCurrentImageIdx = INVALID_IMAGE_INDEX;
unsigned mCurrentFrameIdx = 0;
ObjectPtr<Window> mWindow;
std::vector<ObjectPtr<Image>> mImages;
std::vector<ObjectPtr<Semaphore>> mImageAvailableSemaphores;
const vk::ImageUsageFlags mImageUsage;
vk::Format mFormat = vk::Format::eUndefined;
vk::Extent2D mExtent;
public:
Swapchain(ObjectPtr<class Device> owner, SwapchainCreationArgs args);
~Swapchain() noexcept override;
[[nodiscard]] const ObjectPtr<Window>& getWindow() const noexcept { return mWindow; }
[[nodiscard]] std::size_t getNumParallelFrames() const noexcept { return mImageAvailableSemaphores.size(); }
[[nodiscard]] const ObjectPtr<Semaphore>& getCurrentAvailableSemaphore() const noexcept { return mImageAvailableSemaphores[mCurrentFrameIdx]; }
[[nodiscard]] unsigned getCurrentFrameIdx() const noexcept { return mCurrentFrameIdx; }
[[nodiscard]] const std::vector<ObjectPtr<Image>>& getImages() const noexcept { return mImages; }
[[nodiscard]] std::uint32_t getCurrentImageIdx() const noexcept { return mCurrentImageIdx; }
[[nodiscard]] const ObjectPtr<Image>& getCurrentImage() const noexcept { return mImages[mCurrentImageIdx]; }
[[nodiscard]] vk::Format getFormat() const noexcept { return mFormat; }
[[nodiscard]] const vk::Extent2D& getExtent() const noexcept { return mExtent; }
mijin::Task<> c_present(const PresentArgs& args);
private:
void recreate();
void acquireImage();
mijin::Task<> c_acquireImage();
public: // signals
mijin::Signal<> recreated;
};
}
#endif // IWA_SWAPCHAIN_HPP_INCLUDED

72
include/iwa/texture.hpp Normal file
View File

@@ -0,0 +1,72 @@
#pragma once
#if !defined(IWA_TEXTURE_HPP_INCLUDED)
#define IWA_TEXTURE_HPP_INCLUDED
#include <glm/vec4.hpp>
#include <glm/gtx/hash.hpp>
#include <glm/gtx/spaceship.hpp>
#include <mijin/async/coroutine.hpp>
#include <mijin/util/hash.hpp>
#include "iwa/image.hpp"
namespace iwa
{
struct TextureCreationArgs
{
ObjectPtr<Image> image;
ImageViewCreationArgs imageViewArgs = {};
SamplerCreationArgs samplerArgs = {};
};
struct SingleColorTextureArgs
{
glm::vec4 color;
vk::Format format = vk::Format::eR8G8B8A8Unorm;
auto operator <=>(const SingleColorTextureArgs&) const noexcept = default;
};
struct TextureFromBitmapArgs
{
const Bitmap& bitmap;
ImageCreationArgs imageArgs = {};
ImageViewCreationArgs imageViewArgs = {};
SamplerCreationArgs samplerArgs = {};
};
class Texture : public Object<Texture>
{
private:
ObjectPtr<Image> mImage;
ObjectPtr<ImageView> mImageView;
ObjectPtr<Sampler> mSampler;
public:
explicit Texture(TextureCreationArgs args);
[[nodiscard]] const ObjectPtr<Image>& getImage() const noexcept { return mImage; }
[[nodiscard]] const ObjectPtr<ImageView>& getImageView() const noexcept { return mImageView; }
[[nodiscard]] const ObjectPtr<Sampler>& getSampler() const noexcept { return mSampler; }
static mijin::Task<ObjectPtr<Texture>> c_createSingleColor(const ObjectPtr<Device>& device, const SingleColorTextureArgs& args);
static mijin::Task<ObjectPtr<Texture>> c_createFromBitmap(const ObjectPtr<Device>& device, const TextureFromBitmapArgs& args);
};
} // namespace iwa
namespace std
{
template<>
struct hash<iwa::SingleColorTextureArgs>
{
std::size_t operator()(const iwa::SingleColorTextureArgs& args) const noexcept
{
std::size_t hash = 0;
mijin::hashCombine(hash, args.color);
mijin::hashCombine(hash, args.format);
return hash;
}
};
}
#endif // !defined(IWA_TEXTURE_HPP_INCLUDED)

View File

@@ -0,0 +1,47 @@
#pragma once
#if !defined(IWA_UTIL_COLOR_HPP_INCLUDED)
#define IWA_UTIL_COLOR_HPP_INCLUDED
#include <cmath>
#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
namespace iwa
{
[[nodiscard]] inline float linearToSrgb(float value) noexcept
{
if (value < 0.f) {
return 0.f;
}
if (value < 0.04045f) {
return value / 12.92f;
}
if (value < 1.f) {
return std::pow((value + 0.055f) / 1.055f, 2.4f);
}
return 1.f;
}
[[nodiscard]] inline glm::vec3 linearToSrgb(const glm::vec3& vector) noexcept
{
return glm::vec3(
linearToSrgb(vector[0]),
linearToSrgb(vector[1]),
linearToSrgb(vector[2])
);
}
[[nodiscard]] inline glm::vec4 linearToSrgb(const glm::vec4& vector) noexcept
{
return glm::vec4(
linearToSrgb(vector[0]),
linearToSrgb(vector[1]),
linearToSrgb(vector[2]),
vector.a
);
}
} // namespace iwa
#endif // !defined(IWA_UTIL_COLOR_HPP_INCLUDED)

View File

@@ -0,0 +1,116 @@
// copied from Glslang source and adjusted just a bit
#pragma once
#if !defined(KAZAN_DIR_STACK_FILE_INCLUDER_HPP_INCLUDED)
#define KAZAN_DIR_STACK_FILE_INCLUDER_HPP_INCLUDED
#include <algorithm>
#include <fstream>
#include <unordered_set>
#include <glslang/Public/ShaderLang.h>
namespace iwa::impl
{
class DirStackFileIncluder : public glslang::TShader::Includer
{
protected:
using tUserDataElement = char;
std::vector<std::string> directoryStack;
int externalLocalDirectoryCount = 0;
std::unordered_set<std::string> includedFiles;
public:
IncludeResult* includeLocal(const char* headerName, const char* includerName, size_t inclusionDepth) override;
IncludeResult* includeSystem(const char* headerName, const char* /*includerName*/, size_t /*inclusionDepth*/) override;
// Externally set directories. E.g., from a command-line -I<dir>.
// - Most-recently pushed are checked first.
// - All these are checked after the parse-time stack of local directories
// is checked.
// - This only applies to the "local" form of #include.
// - Makes its own copy of the path.
virtual void pushExternalLocalDirectory(const std::string& dir)
{
directoryStack.push_back(dir);
externalLocalDirectoryCount = (int)directoryStack.size();
}
void releaseInclude(IncludeResult* result) override
{
if (result != nullptr)
{
delete [] static_cast<tUserDataElement*>(result->userData);
delete result;
}
}
virtual std::unordered_set<std::string> getIncludedFiles() noexcept
{
return includedFiles;
}
protected:
// Search for a valid "local" path based on combining the stack of include
// directories and the nominal name of the header.
virtual IncludeResult* readLocalPath(const char* headerName, const char* includerName, int depth)
{
// Discard popped include directories, and
// initialize when at parse-time first level.
directoryStack.resize(depth + externalLocalDirectoryCount);
if (depth == 1)
directoryStack.back() = getDirectory(includerName);
// Find a directory that works, using a reverse search of the include stack.
for (auto it = directoryStack.rbegin(); it != directoryStack.rend(); ++it) {
std::string path = *it + '/' + headerName;
std::replace(path.begin(), path.end(), '\\', '/');
std::ifstream file(path, std::ios_base::binary | std::ios_base::ate);
if (file) {
directoryStack.push_back(getDirectory(path));
includedFiles.insert(path);
return newIncludeResult(path, file, (int)file.tellg());
}
}
return nullptr;
}
// Search for a valid <system> path.
// Not implemented yet; returning nullptr signals failure to find.
virtual IncludeResult* readSystemPath(const char* /*headerName*/) const
{
return nullptr;
}
// Do actual reading of the file, filling in a new include result.
virtual IncludeResult* newIncludeResult(const std::string& path, std::ifstream& file, int length) const
{
char* content = new tUserDataElement [length];
file.seekg(0, file.beg);
file.read(content, length);
return new IncludeResult(path, content, length, content);
}
// If no path markers, return current working directory.
// Otherwise, strip file name and return path leading up to it.
virtual std::string getDirectory(const std::string& path) const
{
const std::size_t last = path.find_last_of("/\\");
return last == std::string::npos ? "." : path.substr(0, last);
}
};
glslang::TShader::Includer::IncludeResult*
DirStackFileIncluder::includeLocal(const char* headerName, const char* includerName, size_t inclusionDepth)
{
return readLocalPath(headerName, includerName, (int)inclusionDepth);
}
glslang::TShader::Includer::IncludeResult*
DirStackFileIncluder::includeSystem(const char* headerName, const char*, size_t)
{
return readSystemPath(headerName);
}
} // namespace iwa::impl
#endif // !defined(KAZAN_DIR_STACK_FILE_INCLUDER_HPP_INCLUDED)

View File

@@ -0,0 +1,49 @@
#pragma once
#if !defined(IWA_UTIL_FPS_CALCULATOR_HPP_INCLUDED)
#define IWA_UTIL_FPS_CALCULATOR_HPP_INCLUDED
#include <array>
#include <chrono>
namespace iwa
{
template<unsigned NUM_VALUES = 20>
class FpsCalculator
{
private:
using clock_t = std::chrono::high_resolution_clock;
using doubledur_t = std::chrono::duration<double>;
clock_t::time_point mLastFrame = clock_t::now();
std::array<double, NUM_VALUES> mLatestValues = {};
unsigned mNextValueIndex = 0;
public:
inline void tickFrame() noexcept;
[[nodiscard]] inline double getFps() const noexcept;
};
template<unsigned NUM_VALUES>
void FpsCalculator<NUM_VALUES>::tickFrame() noexcept
{
const clock_t::time_point currentFrame = clock_t::now();
const clock_t::duration difference = currentFrame - mLastFrame;
mLastFrame = currentFrame;
mLatestValues[mNextValueIndex] = 1.0 / std::chrono::duration_cast<doubledur_t>(difference).count();
mNextValueIndex = (mNextValueIndex + 1) % NUM_VALUES;
}
template<unsigned NUM_VALUES>
double FpsCalculator<NUM_VALUES>::getFps() const noexcept
{
double sum = 0;
for (const double fps : mLatestValues)
{
sum += fps;
}
return sum / NUM_VALUES;
}
} // namespace iwa
#endif // !defined(IWA_UTIL_FPS_CALCULATOR_HPP_INCLUDED)

View File

@@ -0,0 +1,152 @@
#pragma once
#if !defined(IWA_UTIL_GLSL_COMPILER_HPP_INCLUDED)
#define IWA_UTIL_GLSL_COMPILER_HPP_INCLUDED
#include <memory>
#include <string>
#include <mijin/container/optional.hpp>
#include <mijin/util/bitflags.hpp>
#include <mijin/virtual_filesystem/filesystem.hpp>
#include "iwa/instance.hpp"
#include "iwa/object.hpp"
#include "iwa/pipeline.hpp"
#include "iwa/shader_module.hpp"
#include "iwa/vkwrapper.hpp"
#include "iwa/util/shader_meta.hpp"
namespace glslang
{
class TShader;
class TProgram;
}
namespace YAML
{
class Node;
}
namespace iwa
{
struct ShaderSource
{
std::string code;
std::string fileName = {}; // also required for parsing (e.g. relative includes)
#if !defined(KAZAN_RELEASE)
std::string name = {}; // only for debug information!
#endif
[[nodiscard]] static ShaderSource fromStream(mijin::Stream& stream, std::string fileName = {});
[[nodiscard]] static ShaderSource fromFile(const mijin::PathReference& file);
[[nodiscard]] static ShaderSource fromYaml(const YAML::Node& node, const mijin::PathReference& yamlFile);
// static ShaderSource fromFile(std::string fileName, std::string name = "");
};
class GLSLCompilerSettings : public Object<GLSLCompilerSettings, InstanceExtension>
{
private:
std::string mCommonPreamble;
public:
[[nodiscard]] const std::string& getCommonPreamble() const noexcept { return mCommonPreamble; }
void setCommonPreamble(std::string preamble) noexcept { mCommonPreamble = std::move(preamble); }
};
struct GLSLShaderCreationArgs
{
std::vector<ShaderSource> sources;
std::vector<std::string> defines;
vk::ShaderStageFlagBits type;
};
struct GLSLShaderLinkFlags : mijin::BitFlags<GLSLShaderLinkFlags>
{
#if !defined(KAZAN_RELEASE)
std::uint8_t withDebugInfo : 1 = 1;
#else
std::uint8_t withDebugInfo : 1 = 0;
#endif
};
class GLSLShader : public Object<GLSLShader, BaseObject, class Instance>
{
private:
std::unique_ptr<glslang::TShader> mHandle;
vk::ShaderStageFlagBits mType;
std::vector<ShaderSource> mSources;
std::vector<std::string> mDefines;
public:
GLSLShader(ObjectPtr<class Instance> owner, GLSLShaderCreationArgs args) noexcept;
~GLSLShader() noexcept override;
[[nodiscard]] std::unique_ptr<glslang::TShader> releaseHandle();
[[nodiscard]] vk::ShaderStageFlagBits getType() const noexcept { return mType; }
[[nodiscard]] ShaderMeta getPartialMeta();
private:
void compile();
};
struct GLSLSemanticMapping
{
unsigned semantic;
unsigned semanticIdx = 0;
int newSet = -1;
int newBinding = -1;
};
struct GLSLShaderProgramCreationArgs
{
std::vector<ObjectPtr<GLSLShader>> shaders;
GLSLShaderLinkFlags linkFlags;
std::vector<GLSLSemanticMapping> semanticMappings;
};
struct PrepareGraphicsPipelineArgs
{
const struct VertexLayout& vertexLayout;
GeneratePipelineLayoutArgs layoutArgs;
PipelineLayoutMeta pipelineLayoutMeta;
PipelineAndDescriptorSetLayouts layouts;
};
struct PrepareComputePipelineArgs
{
GeneratePipelineLayoutArgs layoutArgs;
PipelineLayoutMeta pipelineLayoutMeta;
PipelineAndDescriptorSetLayouts layouts;
};
class GLSLShaderProgram : public Object<GLSLShaderProgram, BaseObject, class Device>
{
private:
std::vector<std::unique_ptr<glslang::TShader>> mShaderHandles; // must keep the TShaders alive
std::unique_ptr<glslang::TProgram> mHandle;
ShaderMeta mMeta;
GLSLShaderLinkFlags mLinkFlags;
public:
GLSLShaderProgram(ObjectPtr<class Device> owner, GLSLShaderProgramCreationArgs args);
[[nodiscard]] glslang::TProgram* getHandle() const noexcept { return mHandle.get(); }
[[nodiscard]] const ShaderMeta& getMeta() const noexcept { return mMeta; }
[[nodiscard]] std::vector<std::uint32_t> generateSpirv(vk::ShaderStageFlagBits stage) const;
[[nodiscard]] std::vector<PipelineStage> generatePipelineStages() const;
[[nodiscard]] GraphicsPipelineCreationArgs prepareGraphicsPipeline(PrepareGraphicsPipelineArgs& args) const;
[[nodiscard]] ComputePipelineCreationArgs prepareComputePipeline(PrepareComputePipelineArgs& args) const;
};
// struct PrepareGLSLGraphicsPipelineArgs
// {
// std::vector<ObjectPtr<GLSLShader>> shaders;
// };
//
// struct PrepareGLSLGraphicsPipelineResult
// {
// std::vector<PipelineStage> stages;
// ShaderMeta
// };
//
// [[nodiscard]] PrepareGLSLGraphicsPipelineResult prepareGLSLGraphicsPipeline(const PrepareGLSLGraphicsPipelineArgs& args);
} // namespace iwa
#endif // !defined(IWA_UTIL_GLSL_COMPILER_HPP_INCLUDED)

View File

@@ -0,0 +1,27 @@
#pragma once
#if !defined(IWA_UTIL_GROWING_DESCRIPTOR_POOL_HPP_INCLUDED)
#define IWA_UTIL_GROWING_DESCRIPTOR_POOL_HPP_INCLUDED
#include "iwa/descriptor_set.hpp"
namespace iwa
{
using GrowingDescriptorPoolCreationArgs = DescriptorPoolCreationArgs;
class GrowingDescriptorPool : public Object<GrowingDescriptorPool, BaseObject, class Device>
{
private:
std::vector<ObjectPtr<DescriptorPool>> mPools;
DescriptorPoolCreationArgs mCreationArgs;
public:
GrowingDescriptorPool(ObjectPtr<class Device> owner, GrowingDescriptorPoolCreationArgs args);
[[nodiscard]] ObjectPtr<class DescriptorSet> allocateDescriptorSet(const DescriptorSetAllocateArgs& args);
[[nodiscard]] bool getCanFree() const noexcept { return (mCreationArgs.flags & vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet) != vk::DescriptorPoolCreateFlags(); }
};
} // namespace iwa
#endif // !defined(IWA_UTIL_GROWING_DESCRIPTOR_POOL_HPP_INCLUDED)

View File

@@ -0,0 +1,106 @@
#pragma once
#if !defined(IWA_UTIL_RENDER_TARGET_HPP_INCLUDED)
#define IWA_UTIL_RENDER_TARGET_HPP_INCLUDED
#include <mijin/container/optional.hpp>
#include "iwa/fence.hpp"
#include "iwa/object.hpp"
#include "iwa/semaphore.hpp"
#include "iwa/swapchain.hpp"
namespace iwa
{
struct ImageReferenceFrame
{
Image* image;
ImageView* imageView;
vk::Offset2D offset = {0, 0};
};
struct ImageReferenceFinalizeArgs
{
const class CommandBuffer& cmdBuffer;
std::vector<vk::Semaphore>& waitSemaphores;
std::vector<vk::Semaphore>& signalSemaphores;
};
class ImageReference : public AbstractObject<ImageReference, BaseObject, class Device>
{
protected:
explicit ImageReference(ObjectPtr<class Device> owner);
public:
[[nodiscard]] virtual vk::Format getFormat() = 0;
[[nodiscard]] virtual vk::Extent2D getExtent() = 0;
virtual ImageReferenceFrame getCurrentFrame() = 0;
virtual void finalize(ImageReferenceFinalizeArgs& args);
virtual mijin::Task<> c_present();
};
struct SwapchainImageReferenceCreationArgs
{
ObjectPtr<Swapchain> swapchain;
};
class SwapchainImageReference : public Object<SwapchainImageReference, ImageReference>
{
private:
std::vector<ObjectPtr<Semaphore>> mPresentReadySemaphores;
std::vector<ObjectPtr<ImageView>> mImageViews;
ObjectPtr<Swapchain> mSwapchain;
public:
SwapchainImageReference(ObjectPtr<class Device> owner, SwapchainImageReferenceCreationArgs args);
[[nodiscard]] vk::Format getFormat() override;
[[nodiscard]] vk::Extent2D getExtent() override;
ImageReferenceFrame getCurrentFrame() override;
void finalize(ImageReferenceFinalizeArgs& args) override;
mijin::Task<> c_present() override;
private:
void createImageViews();
};
struct DirectImageReferenceCreationArgs
{
ObjectPtr<Image> image;
ObjectPtr<ImageView> imageView;
};
class DirectImageReference : public Object<DirectImageReference, ImageReference>
{
protected:
ObjectPtr<Image> mImage;
ObjectPtr<ImageView> mImageView;
public:
DirectImageReference(ObjectPtr<class Device> owner, DirectImageReferenceCreationArgs args);
[[nodiscard]] vk::Format getFormat() override;
[[nodiscard]] vk::Extent2D getExtent() override;
ImageReferenceFrame getCurrentFrame() override;
};
struct AutoResizeImageReferenceCreationArgs
{
ObjectPtr<ImageReference> referenceImageRef;
ImageCreationArgs imageCreationArgs = {};
ImageViewCreationArgs imageViewCreationArgs = {};
};
class AutoResizeImageReference : public Object<AutoResizeImageReference, DirectImageReference>
{
private:
ObjectPtr<ImageReference> mReferenceImageRef;
ImageCreationArgs mImageCreationArgs;
ImageViewCreationArgs mImageViewCreationArgs;
public:
AutoResizeImageReference(ObjectPtr<class Device> owner, AutoResizeImageReferenceCreationArgs args);
[[nodiscard]] vk::Extent2D getExtent() override;
ImageReferenceFrame getCurrentFrame() override;
private:
void createImage();
};
} // namespace iwa
#endif // !defined(IWA_UTIL_RENDER_TARGET_HPP_INCLUDED)

View File

@@ -0,0 +1,58 @@
#pragma once
#if !defined(KAZAN_NEXT_CHAIN_HPP_INCLUDED)
#define KAZAN_NEXT_CHAIN_HPP_INCLUDED
#include <cstddef>
#include <cstring>
#include <vector>
#include <mijin/util/align.hpp>
#include "iwa/vkwrapper.hpp"
namespace iwa
{
class NextChain
{
private:
std::vector<std::byte> data;
std::vector<std::size_t> offsets;
public:
template<typename TStruct>
void append(const TStruct& element) noexcept
{
const std::size_t oldSize = mijin::alignUp(data.size(), alignof(TStruct));
data.resize(oldSize + sizeof(TStruct));
std::memcpy(&data[oldSize], &element, sizeof(TStruct));
offsets.push_back(oldSize);
}
void* finalize() noexcept
{
auto itCur = offsets.begin();
if (itCur == offsets.end())
{
return nullptr;
}
auto itNext = std::next(itCur);
while (true)
{
vk::BaseOutStructure& curStruct = *std::bit_cast<vk::BaseOutStructure*>(&data[*itCur]);
if (itNext == offsets.end())
{
curStruct.pNext = nullptr;
break;
}
curStruct.pNext = std::bit_cast<vk::BaseOutStructure*>(&data[*itNext]);
itCur = itNext;
++itNext;
}
// first is always at offset 0
return data.data();
}
};
} // namespace iwa
#endif // !defined(KAZAN_NEXT_CHAIN_HPP_INCLUDED)

View File

@@ -0,0 +1,17 @@
#pragma once
#if !defined(IWA_UTIL_REFLECT_GLSL_HPP_INCLUDED)
#define IWA_UTIL_REFLECT_GLSL_HPP_INCLUDED
#include <glslang/Public/ShaderLang.h>
#include "iwa/util/shader_meta.hpp"
namespace iwa
{
[[nodiscard]] ShaderMeta reflectShader(glslang::TShader& shader);
[[nodiscard]] ShaderMeta reflectProgram(glslang::TProgram& program);
[[nodiscard]] ShaderMeta reflectIntermediate(glslang::TIntermediate& intermediate, vk::ShaderStageFlagBits stage);
} // namespace iwa
#endif // !defined(IWA_UTIL_REFLECT_GLSL_HPP_INCLUDED)

View File

@@ -0,0 +1,67 @@
#pragma once
#if !defined(IWA_UTIL_RENDER_LOOP_HPP_INCLUDED)
#define IWA_UTIL_RENDER_LOOP_HPP_INCLUDED
#include <unordered_set>
#include <mijin/util/bitflags.hpp>
#include "iwa/device.hpp"
#include "iwa/fence.hpp"
#include "iwa/object.hpp"
#include "iwa/swapchain.hpp"
#include "iwa/vkwrapper.hpp"
#include "iwa/util/image_reference.hpp"
#include "iwa/util/task_runner.hpp"
namespace iwa
{
struct RenderLoopCreationFlags : mijin::BitFlags<RenderLoopCreationFlags>
{
std::uint8_t advanceDeleteQueue : 1 = 1;
};
struct RenderLoopCreationArgs
{
ObjectPtr<CommandPool> commandPool;
RenderLoopCreationFlags flags;
unsigned parallelFrames = 3;
unsigned targetFps = 0;
};
struct RenderLoopRenderArgs
{
const CommandBuffer& cmdBuffer;
const unsigned frameIdx;
std::unordered_set<ObjectPtr<ImageReference>> usedImageReferences;
};
class RenderLoop : public AbstractObject<RenderLoop, BaseObject, class Device>, public MixinTaskRunner<RenderLoop>
{
private:
struct Alternating
{
ObjectPtr<CommandBuffer> commandBuffer;
ObjectPtr<Fence> renderDoneFence;
};
std::vector<Alternating> mAlternating;
unsigned mFrameIdx = 0;
protected:
const bool mAdvanceDeleteQueue;
protected:
explicit RenderLoop(ObjectPtr<class Device> owner, RenderLoopCreationArgs args);
public:
void start() noexcept;
virtual mijin::Task<> c_init();
virtual mijin::Task<> c_render(RenderLoopRenderArgs& args) = 0;
[[nodiscard]] std::size_t getNumParallelFrames() const noexcept { return mAlternating.size(); }
mijin::SimpleTaskLoop& getTaskLoop() const noexcept;
private:
mijin::Task<> c_renderLoop();
};
} // namespace iwa
#endif // !defined(IWA_UTIL_RENDER_LOOP_HPP_INCLUDED)

View File

@@ -0,0 +1,683 @@
#pragma once
#if !defined(IWA_UTIL_SHADER_META_HPP_INCLUDED)
#define IWA_UTIL_SHADER_META_HPP_INCLUDED
#include <fmt/format.h>
#include <magic_enum.hpp>
#include <mijin/detect.hpp>
#include <cstdint>
#include <unordered_map>
#include "iwa/descriptor_set.hpp"
#include "iwa/device.hpp"
#include "iwa/instance.hpp"
#include "iwa/log.hpp"
#include "iwa/pipeline.hpp"
#include "iwa/vkwrapper.hpp"
namespace iwa
{
inline constexpr unsigned UNSPECIFIED_INDEX = std::numeric_limits<unsigned>::max();
enum class ImageDim
{
ONE = VK_IMAGE_TYPE_1D,
TWO = VK_IMAGE_TYPE_2D,
THREE = VK_IMAGE_TYPE_3D,
CUBE
};
#if MIJIN_COMPILER == MIJIN_COMPILER_GCC || MIJIN_COMPILER == MIJIN_COMPILER_CLANG
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
#endif
union ShaderTypeBits
{
struct
{
bool compute : 1;
bool vertex : 1;
bool fragment : 1;
bool rayGeneration : 1;
bool rayClosestHit : 1;
bool rayAnyHit : 1;
bool rayMiss : 1;
bool rayIntersection : 1;
bool callable : 1;
bool task : 1;
bool mesh : 1;
bool tessellationControl : 1;
bool tesselationEvaluation : 1;
bool geometry : 1;
};
std::uint16_t bits = 0;
// static_assert(static_cast<int>(ShaderType::NUM_VALUES) < 8 * sizeof(bits));
[[nodiscard]] constexpr bool get(vk::ShaderStageFlagBits shaderType) const;
constexpr void set(vk::ShaderStageFlagBits shaderType, bool value);
constexpr ShaderTypeBits& operator |=(const ShaderTypeBits& other) {
bits |= other.bits;
return *this;
}
constexpr operator bool() const {
return bits != 0;
}
constexpr bool operator!() const {
return bits == 0;
}
[[nodiscard]] constexpr vk::ShaderStageFlags toVulkan() const
{
return vk::ShaderStageFlagBits{}
| (compute ? vk::ShaderStageFlagBits::eCompute : vk::ShaderStageFlagBits{})
| (vertex ? vk::ShaderStageFlagBits::eVertex : vk::ShaderStageFlagBits{})
| (fragment ? vk::ShaderStageFlagBits::eFragment : vk::ShaderStageFlagBits{})
| (rayGeneration ? vk::ShaderStageFlagBits::eRaygenKHR : vk::ShaderStageFlagBits{})
| (rayClosestHit ? vk::ShaderStageFlagBits::eClosestHitKHR : vk::ShaderStageFlagBits{})
| (rayAnyHit ? vk::ShaderStageFlagBits::eAnyHitKHR : vk::ShaderStageFlagBits{})
| (rayMiss ? vk::ShaderStageFlagBits::eMissKHR : vk::ShaderStageFlagBits{})
| (rayIntersection ? vk::ShaderStageFlagBits::eIntersectionKHR : vk::ShaderStageFlagBits{})
| (callable ? vk::ShaderStageFlagBits::eCallableKHR : vk::ShaderStageFlagBits{})
| (task ? vk::ShaderStageFlagBits::eTaskEXT : vk::ShaderStageFlagBits{})
| (mesh ? vk::ShaderStageFlagBits::eMeshEXT : vk::ShaderStageFlagBits{})
| (tessellationControl ? vk::ShaderStageFlagBits::eTessellationControl : vk::ShaderStageFlagBits{})
| (tesselationEvaluation ? vk::ShaderStageFlagBits::eTessellationEvaluation : vk::ShaderStageFlagBits{})
| (geometry ? vk::ShaderStageFlagBits::eGeometry : vk::ShaderStageFlagBits{});
}
[[nodiscard]] constexpr vk::ShaderStageFlagBits getFirst() const
{
if (compute) {
return vk::ShaderStageFlagBits::eCompute;
}
if (vertex) {
return vk::ShaderStageFlagBits::eVertex;
}
if (fragment) {
return vk::ShaderStageFlagBits::eFragment;
}
if (rayGeneration) {
return vk::ShaderStageFlagBits::eRaygenKHR;
}
if (rayClosestHit) {
return vk::ShaderStageFlagBits::eClosestHitKHR;
}
if (rayAnyHit) {
return vk::ShaderStageFlagBits::eAnyHitKHR;
}
if (rayMiss) {
return vk::ShaderStageFlagBits::eMissKHR;
}
if (rayIntersection) {
return vk::ShaderStageFlagBits::eIntersectionKHR;
}
if (callable) {
return vk::ShaderStageFlagBits::eCallableKHR;
}
if (task) {
return vk::ShaderStageFlagBits::eTaskEXT;
}
if (mesh) {
return vk::ShaderStageFlagBits::eMeshEXT;
}
if (tessellationControl) {
return vk::ShaderStageFlagBits::eTessellationControl;
}
if (tesselationEvaluation) {
return vk::ShaderStageFlagBits::eTessellationEvaluation;
}
if (geometry) {
return vk::ShaderStageFlagBits::eGeometry;
}
return vk::ShaderStageFlagBits();
}
[[nodiscard]] constexpr struct ShaderTypeBitsIterator begin() const noexcept;
[[nodiscard]] constexpr struct ShaderTypeBitsIterator end() const noexcept;
static constexpr ShaderTypeBits make(vk::ShaderStageFlagBits type) {
ShaderTypeBits bits;
bits.set(type, true);
return bits;
}
};
struct ShaderTypeBitsIterator
{
using value_type = vk::ShaderStageFlagBits;
ShaderTypeBits value;
unsigned pos = 16;
ShaderTypeBitsIterator() = default;
explicit constexpr ShaderTypeBitsIterator(ShaderTypeBits value_) noexcept : value(value_), pos(0)
{
for (; pos < 16; ++pos)
{
if (value.bits & (1 << pos))
{
break;
}
}
}
constexpr bool operator==(const ShaderTypeBitsIterator& other) const noexcept {
return pos == other.pos;
}
constexpr bool operator!=(const ShaderTypeBitsIterator& other) const noexcept {
return !(*this == other);
}
value_type operator*() const noexcept
{
MIJIN_ASSERT(pos < 16, "Attempt to dereference invalid iterator.");
return ShaderTypeBits{.bits = static_cast<std::uint16_t>(1 << pos)}.getFirst();
}
ShaderTypeBitsIterator& operator++() noexcept
{
++pos;
for (; pos < 16; ++pos)
{
if (value.bits & (1 << pos))
{
break;
}
}
return *this;
}
};
#if MIJIN_COMPILER == MIJIN_COMPILER_GCC || MIJIN_COMPILER == MIJIN_COMPILER_CLANG
#pragma GCC diagnostic pop
#endif
enum class ShaderVariableBaseType
{
NONE = 0,
SIMPLE = 1,
IMAGE = 2,
STRUCT = 3,
MATRIX = 4,
ACCELERATION_STRUCTURE = 5
};
struct ShaderVariableSimpleType
{
vk::Format format;
};
struct ShaderVariableImageType
{
ImageDim dimensions;
vk::Format format;
};
struct ShaderVariableStructMember;
struct ShaderVariableStructType
{
std::vector<ShaderVariableStructMember> members;
ShaderVariableStructType();
~ShaderVariableStructType();
};
enum class ShaderVariableMatrixType
{
UNDEFINED = 0,
MAT2 = 1,
MAT3 = 2,
MAT4 = 3
};
struct ShaderVariableType
{
ShaderVariableBaseType baseType = ShaderVariableBaseType::NONE;
ShaderVariableImageType image = {};
ShaderVariableSimpleType simple = {};
ShaderVariableStructType struct_ = {};
ShaderVariableMatrixType matrixType = ShaderVariableMatrixType::UNDEFINED;
unsigned arraySize = 1;
bool dynamicArraySize : 1 = false;
static ShaderVariableType fromVkFormat(vk::Format format)
{
return ShaderVariableType{
.baseType = ShaderVariableBaseType::SIMPLE,
.simple = {
.format = format
}
};
}
inline bool operator==(const ShaderVariableType& other) const;
inline bool operator!=(const ShaderVariableType& other) const {
return !(*this == other);
}
std::size_t calcHash(std::size_t appendTo = 0) const;
};
struct ShaderVariableStructMember
{
std::string name;
std::size_t offset;
ShaderVariableType type;
unsigned semantic = UNSPECIFIED_INDEX;
unsigned semanticIdx = 0;
// inline ~ShaderVariableStructMember();
};
struct ShaderVariableOffsetComparator
{
constexpr bool operator()(const ShaderVariableStructMember& first, const ShaderVariableStructMember& second) const
{
return first.offset < second.offset;
}
};
struct ShaderVariable
{
ShaderVariableType type;
vk::DescriptorType descriptorType;
unsigned binding = UNSPECIFIED_INDEX;
unsigned semantic = UNSPECIFIED_INDEX;
unsigned semanticIndex = 0;
std::string name;
void verifyCompatible(const ShaderVariable& other) const;
std::size_t calcHash(std::size_t appendTo = 0) const;
};
struct ShaderPushConstantBlock
{
ShaderVariableType type;
std::uint32_t offset = 0;
inline operator bool() const {
return type.baseType != ShaderVariableBaseType::NONE;
}
inline bool operator!() const {
return type.baseType == ShaderVariableBaseType::NONE;
}
};
struct ShaderVariableFindResult
{
unsigned setIndex = 0;
unsigned bindIndex = 0;
};
struct ShaderVariableSet
{
ShaderTypeBits usedInStages; // primarily for use in pipelines which may have multiple stages (in contrast to shaders and fragments)
unsigned setIndex = UNSPECIFIED_INDEX;
std::vector<ShaderVariable> variables;
[[nodiscard]] bool find(std::string_view varName, ShaderVariableFindResult& outResult) const noexcept;
[[nodiscard]] bool find(unsigned semantic, unsigned semanticIdx, ShaderVariableFindResult& outResult) const noexcept;
[[nodiscard]] bool find(unsigned semantic, ShaderVariableFindResult& outResult) const noexcept {
return find(semantic, 0, outResult);
}
[[nodiscard]] const ShaderVariable& getVariableAtBinding(unsigned bindingIdx) const;
[[nodiscard]] const ShaderVariable* getVariableAtBindingOpt(unsigned bindingIdx) const;
[[nodiscard]] const ShaderVariable* getVariableAtSemanticOpt(unsigned semantic, unsigned semanticIdx) const;
std::size_t calcHash(std::size_t appendTo = 0) const;
};
struct ShaderAttribute
{
vk::ShaderStageFlagBits stage;
ShaderVariableType type;
unsigned location = UNSPECIFIED_INDEX;
unsigned semantic = UNSPECIFIED_INDEX;
unsigned semanticIndex = 0;
std::string name;
};
struct ShaderSpecializationConstant
{
ShaderVariableType type;
unsigned id;
};
struct DescriptorSetMeta
{
vk::DescriptorSetLayoutCreateFlags flags = {};
std::vector<vk::DescriptorSetLayoutBinding> bindings;
std::vector<vk::DescriptorBindingFlags> bindingFlags;
std::vector<vk::DescriptorType> descriptorTypes;
[[nodiscard]] ObjectPtr<DescriptorSetLayout> createDescriptorSetLayout(Device& device) const;
};
struct PipelineAndDescriptorSetLayouts
{
std::vector<ObjectPtr<DescriptorSetLayout>> descriptorSetLayouts;
ObjectPtr<PipelineLayout> pipelineLayout;
[[nodiscard]] std::vector<ObjectPtr<DescriptorSet>> createDescriptorSets(DescriptorPool& pool) const;
[[nodiscard]] ObjectPtr<DescriptorSet> createDescriptorSet(DescriptorPool& pool, unsigned setIdx) const;
};
struct PipelineLayoutMeta
{
std::vector<DescriptorSetMeta> descriptorSets;
vk::PushConstantRange pushConstantRange;
[[nodiscard]] PipelineAndDescriptorSetLayouts createPipelineLayout(Device& device) const;
};
struct NamedVertexInput
{
struct Attribute
{
unsigned binding;
unsigned offset;
};
std::vector<vk::VertexInputBindingDescription> bindings;
std::unordered_map<std::string, Attribute> attributes;
};
struct GenerateDescriptorSetLayoutArgs
{
std::unordered_map<unsigned, std::uint32_t> descriptorCounts = {};
vk::DescriptorSetLayoutCreateFlags flags = {};
};
struct GeneratePipelineLayoutArgs
{
std::unordered_map<unsigned, GenerateDescriptorSetLayoutArgs> descriptorSets;
};
struct ShaderMeta
{
static constexpr const int STRUCT_VERSION = 1;
std::vector<ShaderVariableSet> interfaceVariableSets;
std::vector<ShaderAttribute> inputAttributes;
std::vector<ShaderAttribute> outputAttributes;
ShaderTypeBits stages;
ShaderTypeBits pushConstantStages;
ShaderPushConstantBlock pushConstantBlock;
unsigned localSizeX = 0;
unsigned localSizeY = 0;
unsigned localSizeZ = 0;
private:
mutable std::size_t hash = 0;
public:
[[nodiscard]] inline ShaderVariableSet& getOrCreateInterfaceVariableSet(unsigned setIdx);
void extend(ShaderMeta other);
[[nodiscard]] bool findInterfaceVariable(std::string_view varName, ShaderVariableFindResult& outResult) const noexcept;
[[nodiscard]] bool findInterfaceVariable(unsigned semantic, unsigned semanticIdx, ShaderVariableFindResult& outResult) const noexcept;
[[nodiscard]] bool findInterfaceVariable(unsigned semantic, ShaderVariableFindResult& outResult) const noexcept {
return findInterfaceVariable(semantic, 0, outResult);
}
[[nodiscard]] const ShaderVariableSet& getInterfaceVariableSet(unsigned setIdx) const;
[[nodiscard]] const ShaderVariableSet* getInterfaceVariableSetOpt(unsigned setIdx) const;
[[nodiscard]] const ShaderVariableType& getInterfaceVariableType(unsigned setIdx, unsigned bindingIdx) const;
[[nodiscard]] inline const ShaderVariableType& getInterfaceVariableType(const ShaderVariableFindResult& findResult) const {
return getInterfaceVariableType(findResult.setIndex, findResult.bindIndex);
}
[[nodiscard]] VertexInput generateVertexInput(const NamedVertexInput& namedInput) const noexcept;
[[nodiscard]] VertexInput generateVertexInputFromLayout(const struct VertexLayout& layout) const noexcept;
[[nodiscard]] DescriptorSetMeta generateDescriptorSetLayout(const ShaderVariableSet& set, const GenerateDescriptorSetLayoutArgs& args = {}) const;
[[nodiscard]] inline DescriptorSetMeta generateDescriptorSetLayout(unsigned setIdx, const GenerateDescriptorSetLayoutArgs& args = {}) const {
return generateDescriptorSetLayout(getInterfaceVariableSet(setIdx), args);
}
[[nodiscard]] PipelineLayoutMeta generatePipelineLayout(const GeneratePipelineLayoutArgs& args = {}) const;
[[nodiscard]] bool empty() const;
std::size_t getHash() const;
void extendPushConstant(ShaderPushConstantBlock pushConstantBlock, ShaderTypeBits stages);
void addInputAttribute(ShaderAttribute attribute);
void addOutputAttribute(ShaderAttribute attribute);
};
// ShaderVariableType::~ShaderVariableType() // NOLINT clang-tidy just doesn't understand my genius
// {
// // defined here since
// }
// ShaderVariableStructMember::~ShaderVariableStructMember() {}
[[nodiscard]] unsigned calcShaderTypeSize(const ShaderVariableType& type, bool ignoreArraySize = false) noexcept;
constexpr bool ShaderTypeBits::get(vk::ShaderStageFlagBits shaderType) const
{
switch(shaderType)
{
case vk::ShaderStageFlagBits::eCompute:
return compute;
case vk::ShaderStageFlagBits::eVertex:
return vertex;
case vk::ShaderStageFlagBits::eFragment:
return fragment;
case vk::ShaderStageFlagBits::eRaygenKHR:
return rayGeneration;
case vk::ShaderStageFlagBits::eClosestHitKHR:
return rayClosestHit;
case vk::ShaderStageFlagBits::eAnyHitKHR:
return rayAnyHit;
case vk::ShaderStageFlagBits::eMissKHR:
return rayMiss;
case vk::ShaderStageFlagBits::eIntersectionKHR:
return rayIntersection;
case vk::ShaderStageFlagBits::eCallableKHR:
return callable;
case vk::ShaderStageFlagBits::eTaskEXT:
return task;
case vk::ShaderStageFlagBits::eMeshEXT:
return mesh;
case vk::ShaderStageFlagBits::eTessellationControl:
return tessellationControl;
case vk::ShaderStageFlagBits::eTessellationEvaluation:
return tesselationEvaluation;
case vk::ShaderStageFlagBits::eGeometry:
return geometry;
case vk::ShaderStageFlagBits::eAllGraphics:
case vk::ShaderStageFlagBits::eAll:
case vk::ShaderStageFlagBits::eSubpassShadingHUAWEI:
case vk::ShaderStageFlagBits::eClusterCullingHUAWEI:
break; // let it fail
}
logAndDie("Invalid shader type in ShaderTypeBits::get()");
}
constexpr void ShaderTypeBits::set(vk::ShaderStageFlagBits shaderType, bool value)
{
switch(shaderType)
{
case vk::ShaderStageFlagBits::eCompute:
compute = value;
return;
case vk::ShaderStageFlagBits::eVertex:
vertex = value;
return;
case vk::ShaderStageFlagBits::eFragment:
fragment = value;
return;
case vk::ShaderStageFlagBits::eRaygenKHR:
rayGeneration = value;
return;
case vk::ShaderStageFlagBits::eClosestHitKHR:
rayClosestHit = value;
return;
case vk::ShaderStageFlagBits::eAnyHitKHR:
rayAnyHit = value;
return;
case vk::ShaderStageFlagBits::eMissKHR:
rayMiss = value;
return;
case vk::ShaderStageFlagBits::eIntersectionKHR:
rayIntersection = value;
return;
case vk::ShaderStageFlagBits::eCallableKHR:
callable = value;
return;
case vk::ShaderStageFlagBits::eTaskEXT:
task = value;
return;
case vk::ShaderStageFlagBits::eMeshEXT:
mesh = value;
return;
case vk::ShaderStageFlagBits::eTessellationControl:
tessellationControl = value;
return;
case vk::ShaderStageFlagBits::eTessellationEvaluation:
tesselationEvaluation = value;
return;
case vk::ShaderStageFlagBits::eGeometry:
geometry = value;
return;
case vk::ShaderStageFlagBits::eAllGraphics:
case vk::ShaderStageFlagBits::eAll:
case vk::ShaderStageFlagBits::eSubpassShadingHUAWEI:
case vk::ShaderStageFlagBits::eClusterCullingHUAWEI:
break; // let it fail
}
logAndDie("Invalid shader type in ShaderTypeBits::set()");
}
constexpr ShaderTypeBitsIterator ShaderTypeBits::begin() const noexcept
{
return ShaderTypeBitsIterator(*this);
}
constexpr ShaderTypeBitsIterator ShaderTypeBits::end() const noexcept
{
return ShaderTypeBitsIterator();
}
inline bool ShaderVariableType::operator==(const ShaderVariableType& other) const
{
if (baseType != other.baseType) {
return false;
}
if (dynamicArraySize != other.dynamicArraySize) {
return false;
}
if (!dynamicArraySize && (arraySize != other.arraySize)) {
return false;
}
switch (baseType)
{
case ShaderVariableBaseType::NONE:
return true;
case ShaderVariableBaseType::SIMPLE:
return simple.format == other.simple.format;
case ShaderVariableBaseType::IMAGE:
return image.format == other.image.format
&& image.dimensions == other.image.dimensions;
case ShaderVariableBaseType::MATRIX:
return matrixType == other.matrixType;
case ShaderVariableBaseType::ACCELERATION_STRUCTURE:
return true;
case ShaderVariableBaseType::STRUCT:
if (struct_.members.size() != other.struct_.members.size()) {
return false;
}
assert(std::is_sorted(struct_.members.begin(), struct_.members.end(), ShaderVariableOffsetComparator()));
assert(std::is_sorted(other.struct_.members.begin(), other.struct_.members.end(), ShaderVariableOffsetComparator()));
for (std::size_t idx = 0; idx < struct_.members.size(); ++idx)
{
if (struct_.members[idx].offset != other.struct_.members[idx].offset) {
return false;
}
if (struct_.members[idx].type != other.struct_.members[idx].type) {
return false;
}
// name doesn't really matter, does it?
}
return true;
}
logAndDie("Unhandled base type in ShaderVariableType::operator==()!");
}
inline ShaderVariableSet& getOrCreateSet(std::vector<ShaderVariableSet>& sets, unsigned setIdx)
{
for (ShaderVariableSet& set : sets)
{
if (set.setIndex == setIdx) {
return set;
}
}
ShaderVariableSet& newSet = sets.emplace_back();
newSet.setIndex = setIdx;
return newSet;
}
inline ShaderVariableSet& ShaderMeta::getOrCreateInterfaceVariableSet(unsigned setIdx)
{
return getOrCreateSet(interfaceVariableSets, setIdx);
}
} // namespace iwa
template<>
struct fmt::formatter<iwa::ShaderVariableType>
{
constexpr auto parse(format_parse_context& ctx) -> decltype(ctx.begin())
{
auto it = ctx.begin();
auto end = ctx.end();
if (it != end && *it != '}') throw format_error("invalid format");
return it;
}
template<typename TContext>
auto format(const iwa::ShaderVariableType& varType, TContext& ctx) const -> decltype(ctx.out())
{
auto it = ctx.out();
it = fmt::format_to(it, "[");
switch (varType.baseType)
{
case iwa::ShaderVariableBaseType::NONE:
it = fmt::format_to(it, "<none>]");
return it;
case iwa::ShaderVariableBaseType::SIMPLE:
it = fmt::format_to(it, "{}", "<TODO!>"); // magic_enum::enum_name(varType.simple.format));
break;
case iwa::ShaderVariableBaseType::IMAGE:
it = fmt::format_to(it, "<img {} {}>", "<TODO!>", // magic_enum::enum_name(varType.image.dimensions),
"<TODO!>"); // magic_enum::enum_name(varType.image.format));
break;
case iwa::ShaderVariableBaseType::MATRIX:
it = fmt::format_to(it, "{}", magic_enum::enum_name(varType.matrixType));
break;
case iwa::ShaderVariableBaseType::ACCELERATION_STRUCTURE:
it = fmt::format_to(it, "acceleration structure");
break;
case iwa::ShaderVariableBaseType::STRUCT:
it = fmt::format_to(it, "<struct of");
for (const iwa::ShaderVariableStructMember& member: varType.struct_.members)
{
it = fmt::format_to(it, " {}(@{}) {}", member.name, member.offset, member.type);
}
it = fmt::format_to(it, ">");
break;
}
if (varType.dynamicArraySize)
{
it = fmt::format_to(it, "[]");
} else if (varType.arraySize > 0)
{
it = fmt::format_to(it, "[{}]", varType.arraySize);
}
it = fmt::format_to(it, "]");
return it;
}
};
#endif // !defined(IWA_UTIL_SHADER_META_HPP_INCLUDED)

View File

@@ -0,0 +1,60 @@
#pragma once
#if !defined(IWA_UTIL_TASK_RUNNER_HPP_INCLUDED)
#define IWA_UTIL_TASK_RUNNER_HPP_INCLUDED
#include <mijin/async/coroutine.hpp>
namespace iwa
{
template<typename TConcrete>
class MixinTaskRunner
{
private:
std::vector<mijin::TaskHandle> mTasks;
public:
MixinTaskRunner() noexcept;
~MixinTaskRunner() noexcept
{
for (const mijin::TaskHandle& handle : mTasks)
{
handle.cancel();
}
}
public:
template<typename TResult>
mijin::FuturePtr<TResult> addTask(mijin::TaskBase<TResult>&& task, mijin::TaskHandle* outHandle = nullptr) noexcept
{
mijin::TaskLoop& loop = static_cast<TConcrete*>(this)->getTaskLoop();
mijin::TaskHandle handle;
mijin::FuturePtr<TResult> result = loop.addTask(std::move(task), &handle);
if (outHandle != nullptr)
{
*outHandle = handle;
}
mTasks.push_back(std::move(handle));
return result;
}
};
template<typename TConcrete>
MixinTaskRunner<TConcrete>::MixinTaskRunner() noexcept
{
// addTask([&]() -> mijin::Task<>
// {
// while (true)
// {
// co_await mijin::c_suspend();
// auto newEnd = std::remove_if(mTasks.begin(), mTasks.end(), [](const mijin::TaskHandle& handle)
// {
// return !handle.isValid();
// });
// mTasks.erase(newEnd, mTasks.end());
// }
// }());
}
} // namespace iwa
#endif // !defined(IWA_UTIL_TASK_RUNNER_HPP_INCLUDED)

View File

@@ -0,0 +1,104 @@
#pragma once
#if !defined(IWA_UTIL_TEXTURE_ATLAS_HPP_INCLUDED)
#define IWA_UTIL_TEXTURE_ATLAS_HPP_INCLUDED
#include <glm/vec2.hpp>
#include <mijin/async/coroutine.hpp>
#include <mijin/async/signal.hpp>
#include <mijin/async/task_mutex.hpp>
#include <vector>
#include "iwa/image.hpp"
#include "iwa/object.hpp"
#include "iwa/vkwrapper.hpp"
namespace iwa
{
struct TextureAtlasCreationArgs
{
vk::Extent2D layerSize;
};
struct TextureSlotCreationArgs
{
vk::Rect2D usedSpace;
unsigned layer;
glm::vec2 uvOffset;
glm::vec2 uvScale;
};
struct AtlasedImageCreationArgs
{
vk::Extent2D size;
unsigned initialLayers = 1;
vk::Format format;
unsigned mipLevels = 1;
vk::ImageUsageFlags usage = vk::ImageUsageFlagBits::eSampled;
};
struct TextureAtlasLayer
{
std::vector<vk::Rect2D> freeSpaces;
};
class TextureSlot : public Object<TextureSlot, BaseObject, class TextureAtlas>
{
private:
vk::Rect2D mUsedSpace;
unsigned mLayer;
glm::vec2 mUvOffset;
glm::vec2 mUvScale;
public:
TextureSlot(ObjectPtr<class TextureAtlas> owner, const TextureSlotCreationArgs& args);
[[nodiscard]] const vk::Rect2D& getUsedSpace() const noexcept { return mUsedSpace; }
[[nodiscard]] unsigned getLayer() const noexcept { return mLayer; }
[[nodiscard]] const glm::vec2& getUvOffset() const noexcept { return mUvOffset; }
[[nodiscard]] const glm::vec2& getUvScale() const noexcept { return mUvScale; }
};
class TextureAtlas : public Object<TextureAtlas>
{
private:
std::vector<TextureAtlasLayer> mLayers;
vk::Extent2D mLayerSize;
public:
TextureAtlas(ObjectPtr<> owner, const TextureAtlasCreationArgs& args);
explicit TextureAtlas(const TextureAtlasCreationArgs& args) : TextureAtlas(nullptr, args) {}
[[nodiscard]] const vk::Extent2D& getLayerSize() const noexcept { return mLayerSize; }
[[nodiscard]] ObjectPtr<TextureSlot> allocateSlot(vk::Extent2D slotSize);
};
class AtlasedImage : public Object<AtlasedImage, BaseObject, Device>
{
private:
ObjectPtr<TextureAtlas> mAtlas;
ObjectPtr<Image> mImage;
ObjectPtr<ImageView> mImageView;
mutable mijin::TaskMutex mImageMutex;
vk::Format mFormat;
unsigned mMipLevels;
vk::ImageUsageFlags mUsage;
public:
AtlasedImage(ObjectPtr<Device> owner, const AtlasedImageCreationArgs& args);
[[nodiscard]] const ObjectPtr<Image>& getImage() const noexcept { return mImage; }
[[nodiscard]] const ObjectPtr<ImageView>& getImageView() const noexcept { return mImageView; }
mijin::Task<ObjectPtr<TextureSlot>> c_allocateSlot(vk::Extent2D slotSize);
mijin::Task<> c_upload(const TextureSlot& slot, const class Bitmap& bitmap) const noexcept;
mijin::Task<> c_upload(const TextureSlot& slot, const void* data, std::size_t bytes, const vk::Extent2D& bufferImageSize) const noexcept;
mijin::Task<> c_blit(const TextureSlot& slot, Image& srcImage) const noexcept;
mijin::Task<> c_blit(const TextureSlot& slot, const class Bitmap& bitmap) const noexcept;
mijin::Task<> c_copy(const TextureSlot& slot, Image& srcImage) const noexcept;
private:
ObjectPtr<Image> allocateImage(unsigned layers);
public:
mijin::Signal<> imageRecreated;
};
} // namespace iwa
#endif // !defined(IWA_UTIL_TEXTURE_ATLAS_HPP_INCLUDED)

View File

@@ -0,0 +1,13 @@
{% import 'enums.jinja' as enums -%}
// auto-generated using Jinja, do not edit!
#pragma once
#if !defined(IWA_UTIL_VERTEX_ATTRIBUTE_SEMANTIC_GEN_HPP_INCLUDED)
#define IWA_UTIL_VERTEX_ATTRIBUTE_SEMANTIC_GEN_HPP_INCLUDED
namespace iwa
{
{{ enums.cpp_enum('vertex_input_semantic') }}
}
#endif // defined(IWA_UTIL_VERTEX_ATTRIBUTE_SEMANTIC_GEN_HPP_INCLUDED)

View File

@@ -0,0 +1,42 @@
#pragma once
#if !defined(IWA_UTIL_VERTEX_LAYOUT_HPP_INCLUDED)
#define IWA_UTIL_VERTEX_LAYOUT_HPP_INCLUDED
#include <mijin/container/optional.hpp>
#include "iwa/vkwrapper.hpp"
#include "iwa/util/vertex_attribute_semantic.gen.hpp"
#include "iwa/util/vkutil.hpp"
#define IWA_VERTEX_ATTRIB_IDX(struct_, member_, semantic_, idx_) \
iwa::VertexAttribute \
{ \
.semantic = iwa::VertexAttributeSemantic::semantic_, \
.semanticIdx = idx_, \
.offset = offsetof(struct_, member_), \
.format = iwa::vkMemberFormat(&struct_::member_) \
}
#define IWA_VERTEX_ATTRIB(struct_, member_, semantic_) IWA_VERTEX_ATTRIB_IDX(struct_, member_, semantic_, 0)
namespace iwa
{
struct VertexAttribute
{
VertexAttributeSemantic semantic = VertexAttributeSemantic::CUSTOM;
unsigned semanticIdx = 0;
unsigned offset = 0;
vk::Format format = vk::Format::eUndefined;
};
struct VertexLayout
{
std::vector<VertexAttribute> attributes;
unsigned stride = 0;
[[nodiscard]] mijin::Optional<VertexAttribute&> findAttribute(VertexAttributeSemantic semantic, unsigned semanticIdx = 0) noexcept;
[[nodiscard]] mijin::Optional<const VertexAttribute&> findAttribute(VertexAttributeSemantic semantic, unsigned semanticIdx = 0) const noexcept;
};
} // namespace iwa
#endif // !defined(_VERTEX_LAYOUT_HPP_INCLUDED)

210
include/iwa/util/vkutil.hpp Normal file
View File

@@ -0,0 +1,210 @@
#pragma once
#if !defined(IWA_UTIL_VKUTIL_HPP_INCLUDED)
#define IWA_UTIL_VKUTIL_HPP_INCLUDED
#include <cstdint>
#include <glm/vec2.hpp>
#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <magic_enum.hpp>
#include <mijin/util/align.hpp>
#include <mijin/util/traits.hpp>
#include "iwa/vkwrapper.hpp"
namespace iwa
{
inline constexpr std::array DEPTH_FORMATS = {
vk::Format::eD24UnormS8Uint,
vk::Format::eD32Sfloat,
vk::Format::eD32SfloatS8Uint,
vk::Format::eD16Unorm,
vk::Format::eD16UnormS8Uint,
vk::Format::eX8D24UnormPack32
};
inline constexpr std::array STENCIL_FORMATS = {
vk::Format::eS8Uint,
vk::Format::eD24UnormS8Uint,
vk::Format::eD32SfloatS8Uint,
vk::Format::eD16UnormS8Uint
};
namespace impl
{
template<typename T>
constexpr vk::Format getVkFormat() noexcept
{
if constexpr (std::is_same_v<T, std::uint8_t>) {
return vk::Format::eR8Uint;
}
else if constexpr (std::is_same_v<T, std::int8_t>) {
return vk::Format::eR8Sint;
}
else if constexpr (std::is_same_v<T, std::uint16_t>) {
return vk::Format::eR16Uint;
}
else if constexpr (std::is_same_v<T, std::int16_t>) {
return vk::Format::eR16Sint;
}
else if constexpr (std::is_same_v<T, std::uint32_t>) {
return vk::Format::eR32Uint;
}
else if constexpr (std::is_same_v<T, std::int32_t>) {
return vk::Format::eR32Sint;
}
else if constexpr (std::is_same_v<T, std::uint64_t>) {
return vk::Format::eR64Uint;
}
else if constexpr (std::is_same_v<T, std::int64_t>) {
return vk::Format::eR64Sint;
}
else if constexpr (std::is_same_v<T, float>) {
return vk::Format::eR32Sfloat;
}
else if constexpr (std::is_same_v<T, double>) {
return vk::Format::eR64Sfloat;
}
else if constexpr (std::is_same_v<T, glm::vec2>) {
return vk::Format::eR32G32Sfloat;
}
else if constexpr (std::is_same_v<T, glm::vec3>) {
return vk::Format::eR32G32B32Sfloat;
}
else if constexpr (std::is_same_v<T, glm::vec4>) {
return vk::Format::eR32G32B32A32Sfloat;
}
else if constexpr (std::is_same_v<T, glm::ivec2>) {
return vk::Format::eR32G32Sint;
}
else if constexpr (std::is_same_v<T, glm::ivec3>) {
return vk::Format::eR32G32B32Sint;
}
else if constexpr (std::is_same_v<T, glm::ivec4>) {
return vk::Format::eR32G32B32A32Sint;
}
else {
static_assert(mijin::always_false_v<T>, "No Vulkan format for that type.");
}
}
template<typename T>
constexpr vk::IndexType getVkIndexType() noexcept
{
if constexpr (std::is_same_v<T, std::uint8_t>) {
return vk::IndexType::eUint8EXT;
}
else if constexpr (std::is_same_v<T, std::uint16_t>) {
return vk::IndexType::eUint16;
}
else if constexpr (std::is_same_v<T, std::uint32_t>) {
return vk::IndexType::eUint32;
}
else {
static_assert(mijin::always_false_v<T>, "No Vulkan index type for that type.");
}
}
} // namespace impl
template<typename T>
static constexpr vk::Format vk_format_v = impl::getVkFormat<T>();
template<typename T>
static constexpr vk::IndexType vk_index_type_v = impl::getVkIndexType<T>();
template<typename T, typename U>
static constexpr vk::Format vkMemberFormat(U T::*) noexcept { return vk_format_v<U>; }
[[nodiscard]] unsigned vkFormatSize(vk::Format format) noexcept;
[[nodiscard]] unsigned vkIndexTypeSize(vk::IndexType indexType) noexcept;
[[nodiscard]] bool isDepthFormat(vk::Format format) noexcept;
[[nodiscard]] bool isStencilFormat(vk::Format format) noexcept; // NOLINT(readability-redundant-declaration) forward declared in image.hpp, but it wouldn't make sense to remove it from here
template<typename TEnum>
std::optional<TEnum> vkEnumFromStringOpt(std::string_view string) noexcept
{
std::string enumName;
enumName.reserve(string.size() + 1);
bool first = true;
for (const char chr : string)
{
if (first)
{
enumName.push_back(std::toupper(chr));
first = false;
}
else {
enumName.push_back(chr);
}
}
return vk::from_string<TEnum>(enumName);
}
template<typename TEnum>
TEnum vkEnumFromString(std::string_view string, const char* error = "Invalid enum value.")
{
const std::optional<TEnum> value = vkEnumFromStringOpt<TEnum>(string);
if (!value.has_value()) {
throw std::runtime_error(error);
}
return *value;
}
template<typename T>
[[nodiscard]] const T* findInNextChain(const void* pNext, vk::StructureType sType = T().sType) noexcept
{
while (pNext)
{
const vk::BaseInStructure* inStruct = static_cast<const vk::BaseInStructure*>(pNext);
if (inStruct->sType == sType)
{
return static_cast<const T*>(pNext);
}
pNext = inStruct->pNext;
}
return nullptr;
}
// TODO
// std::size_t calcVkStructHash(const void* structure, std::size_t appendTo = 0);
[[nodiscard]] vk::SampleCountFlagBits samplesToVk(unsigned samples) noexcept;
[[nodiscard]] vk::Format detectDepthBufferFormat(class Device& device, unsigned samples = 1) noexcept;
[[nodiscard]] std::vector<unsigned> detectSupportedSampleCounts(class Device& device) noexcept;
template<typename T>
vk::DeviceSize calcVkUniformStride(class Device& device)
{
vk::DeviceSize stride = mijin::alignUp(sizeof(T), alignof(T));
return mijin::alignUp(stride, mijin::delayEvaluation<T>(device).getDeviceInfo().properties.limits.minUniformBufferOffsetAlignment);
}
template<typename T>
vk::DeviceSize calcVkStorageBufferStride(class Device& device)
{
vk::DeviceSize stride = mijin::alignUp(sizeof(T), alignof(T));
return mijin::alignUp(stride, mijin::delayEvaluation<T>(device).getDeviceInfo().properties.limits.minStorageBufferOffsetAlignment);
}
[[nodiscard]] inline bool vkIsSrgbFormat(vk::Format format) noexcept
{
switch (format)
{
case vk::Format::eR8Srgb:
case vk::Format::eR8G8Srgb:
case vk::Format::eR8G8B8Srgb:
case vk::Format::eR8G8B8A8Srgb:
case vk::Format::eB8G8R8Srgb:
case vk::Format::eB8G8R8A8Srgb:
case vk::Format::eA8B8G8R8SrgbPack32:
// TODO: all the weird compressed formats, I don't need them yet
return true;
default:
return false;
}
}
} // namespace iwa
#endif // !defined(IWA_UTIL_VKUTIL_HPP_INCLUDED)

28
include/iwa/vkwrapper.hpp Normal file
View File

@@ -0,0 +1,28 @@
#pragma once
#ifndef IWA_VULKAN_VKWRAPPER_HPP_INCLUDED
#define IWA_VULKAN_VKWRAPPER_HPP_INCLUDED
// disable what we don't need
#define VULKAN_HPP_NO_STRUCT_CONSTRUCTORS
#define VULKAN_HPP_NO_SMART_HANDLE
#define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1
#include "vulkan/vulkan.hpp"
namespace iwa
{
template<typename THandle>
class MixinVulkanObject
{
protected:
THandle mHandle;
protected:
explicit MixinVulkanObject(THandle handle = VK_NULL_HANDLE) noexcept : mHandle(handle) {}
public:
[[nodiscard]] const THandle& getVkHandle() const noexcept { return mHandle; }
operator THandle() const noexcept { return mHandle; }
};
}
#endif // IWA_VULKAN_VKWRAPPER_HPP_INCLUDED

86
include/iwa/window.hpp Normal file
View File

@@ -0,0 +1,86 @@
#pragma once
#if !defined(IWA_WINDOW_HPP_INCLUDED)
#define IWA_WINDOW_HPP_INCLUDED
#include <string>
#include <mijin/async/signal.hpp>
#include <mijin/container/optional.hpp>
#include <mijin/util/bitflags.hpp>
#include <SDL.h>
#include "iwa/input.hpp"
#include "iwa/object.hpp"
#include "iwa/vkwrapper.hpp"
namespace iwa
{
enum class MouseMode
{
NORMAL = 0,
CAPTURED = 1
};
struct WindowCreationFlags : mijin::BitFlags<WindowCreationFlags>
{
bool hidden : 1 = false;
bool resizable : 1 = true;
bool borderless : 1 = false;
bool alwayOnTop : 1 = false;
bool skipTaskbar : 1 = false;
};
struct WindowBorder
{
int left;
int right;
int top;
int bottom;
};
struct WindowCreationArgs
{
std::string title = "Iwa Window";
WindowCreationFlags flags;
int width = 1280;
int height = 720;
};
class Window : public Object<Window, BaseObject, class Instance>
{
private:
SDL_Window* mHandle = nullptr;
vk::SurfaceKHR mSurface = VK_NULL_HANDLE;
public:
Window(ObjectPtr<class Instance> owner, const WindowCreationArgs& args);
~Window() noexcept override;
[[nodiscard]] bool isVisible() const noexcept;
void setVisible(bool visible) noexcept;
[[nodiscard]] std::pair<int, int> getSize() const noexcept;
void setSize(int width, int height) noexcept;
[[nodiscard]] std::pair<int, int> getPosition() const noexcept;
void setPosition(int xPos, int yPos) noexcept;
[[nodiscard]] WindowBorder getWindowBorder() const noexcept;
[[nodiscard]] bool isFocused() const noexcept;
void focus() noexcept;
void setMouseMode(MouseMode mouseMode) noexcept;
void setModalFor(mijin::Optional<const Window&> parent) noexcept;
[[nodiscard]] SDL_Window* getSDLWindow() const noexcept { return mHandle; }
[[nodiscard]] vk::SurfaceKHR getVkSurface() const noexcept { return mSurface; }
mijin::Signal<const KeyEvent&> keyChanged;
mijin::Signal<const MouseButtonEvent&> mouseButtonChanged;
mijin::Signal<const MouseMoveEvent&> mouseMoved;
mijin::Signal<const MouseWheelEvent&> mouseScrolled;
mijin::Signal<const TextInputEvent&> textEntered;
mijin::Signal<> focusGained;
mijin::Signal<> focusLost;
mijin::Signal<> mouseEntered;
mijin::Signal<> mouseLeft;
mijin::Signal<> closeRequested;
};
}
#endif //IWA_WINDOW_HPP_INCLUDED