initial commit
This commit is contained in:
29
include/iwa/addon.hpp
Normal file
29
include/iwa/addon.hpp
Normal 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)
|
||||
62
include/iwa/addons/imgui/addon.hpp
Normal file
62
include/iwa/addons/imgui/addon.hpp
Normal 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)
|
||||
22
include/iwa/addons/imgui/fps_widget.hpp
Normal file
22
include/iwa/addons/imgui/fps_widget.hpp
Normal 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)
|
||||
34
include/iwa/addons/imgui/misc_widgets.hpp
Normal file
34
include/iwa/addons/imgui/misc_widgets.hpp
Normal 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)
|
||||
20
include/iwa/addons/imgui/widget.hpp
Normal file
20
include/iwa/addons/imgui/widget.hpp
Normal 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)
|
||||
45
include/iwa/app/vulkan_application.hpp
Normal file
45
include/iwa/app/vulkan_application.hpp
Normal 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
152
include/iwa/buffer.hpp
Normal 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
41
include/iwa/command.hpp
Normal 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)
|
||||
61
include/iwa/descriptor_set.hpp
Normal file
61
include/iwa/descriptor_set.hpp
Normal 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
164
include/iwa/device.hpp
Normal 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
|
||||
29
include/iwa/device_memory.hpp
Normal file
29
include/iwa/device_memory.hpp
Normal 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
26
include/iwa/event.hpp
Normal 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
30
include/iwa/fence.hpp
Normal 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
270
include/iwa/image.hpp
Normal 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
483
include/iwa/input.hpp
Normal 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
157
include/iwa/instance.hpp
Normal 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
123
include/iwa/io/bitmap.hpp
Normal 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
55
include/iwa/io/font.hpp
Normal 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
11
include/iwa/io/mesh.hpp
Normal 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
57
include/iwa/log.hpp
Normal 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
566
include/iwa/object.hpp
Normal 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
136
include/iwa/pipeline.hpp
Normal 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)
|
||||
59
include/iwa/render_pass.hpp
Normal file
59
include/iwa/render_pass.hpp
Normal 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)
|
||||
73
include/iwa/resource/bitmap.hpp
Normal file
73
include/iwa/resource/bitmap.hpp
Normal 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)
|
||||
55
include/iwa/resource/font.hpp
Normal file
55
include/iwa/resource/font.hpp
Normal 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
25
include/iwa/semaphore.hpp
Normal 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)
|
||||
27
include/iwa/shader_module.hpp
Normal file
27
include/iwa/shader_module.hpp
Normal 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
68
include/iwa/swapchain.hpp
Normal 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
72
include/iwa/texture.hpp
Normal 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)
|
||||
47
include/iwa/util/color.hpp
Normal file
47
include/iwa/util/color.hpp
Normal 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)
|
||||
116
include/iwa/util/dir_stack_file_includer.hpp
Normal file
116
include/iwa/util/dir_stack_file_includer.hpp
Normal 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)
|
||||
49
include/iwa/util/fps_calculator.hpp
Normal file
49
include/iwa/util/fps_calculator.hpp
Normal 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)
|
||||
152
include/iwa/util/glsl_compiler.hpp
Normal file
152
include/iwa/util/glsl_compiler.hpp
Normal 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)
|
||||
27
include/iwa/util/growing_descriptor_pool.hpp
Normal file
27
include/iwa/util/growing_descriptor_pool.hpp
Normal 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)
|
||||
106
include/iwa/util/image_reference.hpp
Normal file
106
include/iwa/util/image_reference.hpp
Normal 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)
|
||||
58
include/iwa/util/next_chain.hpp
Normal file
58
include/iwa/util/next_chain.hpp
Normal 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)
|
||||
17
include/iwa/util/reflect_glsl.hpp
Normal file
17
include/iwa/util/reflect_glsl.hpp
Normal 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)
|
||||
67
include/iwa/util/render_loop.hpp
Normal file
67
include/iwa/util/render_loop.hpp
Normal 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)
|
||||
683
include/iwa/util/shader_meta.hpp
Normal file
683
include/iwa/util/shader_meta.hpp
Normal 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)
|
||||
60
include/iwa/util/task_runner.hpp
Normal file
60
include/iwa/util/task_runner.hpp
Normal 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)
|
||||
104
include/iwa/util/texture_atlas.hpp
Normal file
104
include/iwa/util/texture_atlas.hpp
Normal 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)
|
||||
13
include/iwa/util/vertex_attribute_semantic.gen.hpp.jinja
Normal file
13
include/iwa/util/vertex_attribute_semantic.gen.hpp.jinja
Normal 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)
|
||||
42
include/iwa/util/vertex_layout.hpp
Normal file
42
include/iwa/util/vertex_layout.hpp
Normal 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
210
include/iwa/util/vkutil.hpp
Normal 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
28
include/iwa/vkwrapper.hpp
Normal 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
86
include/iwa/window.hpp
Normal 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
|
||||
Reference in New Issue
Block a user