initial commit

This commit is contained in:
2025-05-01 23:03:47 +02:00
commit a23fbff52a
200 changed files with 434542 additions and 0 deletions

89
ScopEngine/Runtime/Includes/Renderer/Buffer.h git.filemode.normal_file
View File

@@ -0,0 +1,89 @@
#ifndef __SCOP_GPU_BUFFER__
#define __SCOP_GPU_BUFFER__
#include <kvf.h>
#include <Renderer/Enums.h>
#include <Core/Logs.h>
#include <Renderer/RenderCore.h>
#include <Utils/Buffer.h>
#include <Renderer/Memory/Block.h>
namespace Scop
{
class GPUBuffer
{
public:
GPUBuffer() = default;
void Init(BufferType type, VkDeviceSize size, VkBufferUsageFlags usage, CPUBuffer data);
void Destroy() noexcept;
bool CopyFrom(const GPUBuffer& buffer) noexcept;
void Swap(GPUBuffer& buffer) noexcept;
[[nodiscard]] inline void* GetMap() const noexcept { return m_memory.map; }
[[nodiscard]] inline VkBuffer operator()() const noexcept { return m_buffer; }
[[nodiscard]] inline VkBuffer Get() const noexcept { return m_buffer; }
[[nodiscard]] inline VkDeviceMemory GetMemory() const noexcept { return m_memory.memory; }
[[nodiscard]] inline VkDeviceSize GetSize() const noexcept { return m_memory.size; }
[[nodiscard]] inline VkDeviceSize GetOffset() const noexcept { return 0; }
[[nodiscard]] inline static std::size_t GetBufferCount() noexcept { return s_buffer_count; }
[[nodiscard]] inline bool IsInit() const noexcept { return m_buffer != VK_NULL_HANDLE; }
~GPUBuffer() = default;
protected:
void PushToGPU() noexcept;
protected:
VkBuffer m_buffer = VK_NULL_HANDLE;
MemoryBlock m_memory = NULL_MEMORY_BLOCK;
private:
void CreateBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, bool use_raw_size);
private:
inline static std::size_t s_buffer_count = 0;
VkBufferUsageFlags m_usage = 0;
VkMemoryPropertyFlags m_flags = 0;
};
class VertexBuffer : public GPUBuffer
{
public:
inline void Init(std::uint32_t size, VkBufferUsageFlags additional_flags = 0) { GPUBuffer::Init(BufferType::LowDynamic, size, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | additional_flags, {}); }
void SetData(CPUBuffer data);
inline void Bind(VkCommandBuffer cmd) const noexcept { VkDeviceSize offset = 0; RenderCore::Get().vkCmdBindVertexBuffers(cmd, 0, 1, &m_buffer, &offset); }
};
class IndexBuffer : public GPUBuffer
{
public:
inline void Init(std::uint32_t size, VkBufferUsageFlags additional_flags = 0) { GPUBuffer::Init(BufferType::LowDynamic, size, VK_BUFFER_USAGE_INDEX_BUFFER_BIT | additional_flags, {}); }
void SetData(CPUBuffer data);
inline void Bind(VkCommandBuffer cmd) const noexcept { RenderCore::Get().vkCmdBindIndexBuffer(cmd, m_buffer, 0, VK_INDEX_TYPE_UINT32); }
};
class UniformBuffer
{
public:
void Init(std::uint32_t size);
void SetData(CPUBuffer data, std::size_t frame_index);
void Destroy() noexcept;
inline VkDeviceSize GetSize(int i) const noexcept { return m_buffers[i].GetSize(); }
inline VkDeviceSize GetOffset(int i) const noexcept { return m_buffers[i].GetOffset(); }
inline VkBuffer GetVk(int i) const noexcept { return m_buffers[i].Get(); }
inline GPUBuffer& Get(int i) noexcept { return m_buffers[i]; }
private:
std::array<GPUBuffer, MAX_FRAMES_IN_FLIGHT> m_buffers;
std::array<void*, MAX_FRAMES_IN_FLIGHT> m_maps;
};
}
#endif

View File

@@ -0,0 +1,51 @@
#ifndef __SCOP_DESCRIPTOR_SET__
#define __SCOP_DESCRIPTOR_SET__
#include <vector>
#include <cstdint>
#include <kvf.h>
#include <Renderer/RenderCore.h>
#include <Utils/NonOwningPtr.h>
#include <Renderer/Pipelines/Shader.h>
namespace Scop
{
struct Descriptor
{
NonOwningPtr<class GPUBuffer> storage_buffer_ptr;
NonOwningPtr<class GPUBuffer> uniform_buffer_ptr;
NonOwningPtr<class Image> image_ptr;
VkDescriptorType type;
ShaderType shader_type;
std::uint32_t binding;
};
class DescriptorSet
{
public:
DescriptorSet() { m_set.fill(VK_NULL_HANDLE); }
DescriptorSet(const ShaderSetLayout& layout, VkDescriptorSetLayout vklayout, ShaderType shader_type);
void SetImage(std::size_t i, std::uint32_t binding, class Image& image);
void SetStorageBuffer(std::size_t i, std::uint32_t binding, class GPUBuffer& buffer);
void SetUniformBuffer(std::size_t i, std::uint32_t binding, class GPUBuffer& buffer);
void Update(std::size_t i, VkCommandBuffer cmd = VK_NULL_HANDLE) noexcept;
[[nodiscard]] inline VkDescriptorSet GetSet(std::size_t i) const noexcept { return m_set[i]; }
[[nodiscard]] inline DescriptorSet Duplicate() const { return DescriptorSet{ m_set_layout, m_descriptors }; }
[[nodiscard]] inline bool IsInit() const noexcept { return m_set[0] != VK_NULL_HANDLE; }
~DescriptorSet() = default;
private:
DescriptorSet(VkDescriptorSetLayout layout, const std::vector<Descriptor>& descriptors);
private:
std::vector<Descriptor> m_descriptors;
std::array<VkDescriptorSet, MAX_FRAMES_IN_FLIGHT> m_set;
VkDescriptorSetLayout m_set_layout;
};
}
#endif

31
ScopEngine/Runtime/Includes/Renderer/Enums.h git.filemode.normal_file
View File

@@ -0,0 +1,31 @@
#ifndef __SCOP_RENDERER_ENUMS__
#define __SCOP_RENDERER_ENUMS__
#include <cstddef>
namespace Scop
{
enum class BufferType
{
Constant = 0,
Staging,
HighDynamic, // typically stored in RAM
LowDynamic, // typically stored in VRAM
EndEnum
};
constexpr std::size_t BufferTypeCount = static_cast<std::size_t>(BufferType::EndEnum);
enum class ImageType
{
Color = 0,
Cube,
Depth,
// Maybe add depth array
EndEnum
};
constexpr std::size_t ImageTypeCount = static_cast<std::size_t>(ImageType::EndEnum);
}
#endif

138
ScopEngine/Runtime/Includes/Renderer/Image.h git.filemode.normal_file
View File

@@ -0,0 +1,138 @@
#ifndef __SCOP_IMAGE__
#define __SCOP_IMAGE__
#include <cstdint>
#include <vector>
#include <kvf.h>
#include <Maths/Vec4.h>
#include <Renderer/RenderCore.h>
#include <Renderer/Buffer.h>
#include <Utils/Buffer.h>
#include <Renderer/Enums.h>
#include <Renderer/Memory/Block.h>
namespace Scop
{
class Image
{
public:
Image() = default;
inline void Init(VkImage image, VkFormat format, std::uint32_t width, std::uint32_t height, VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED) noexcept
{
m_image = image;
m_format = format;
m_width = width;
m_height = height;
m_layout = layout;
}
void Init(ImageType type, std::uint32_t width, std::uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, bool is_multisampled = false);
void CreateImageView(VkImageViewType type, VkImageAspectFlags aspectFlags, int layer_count = 1) noexcept;
void CreateSampler() noexcept;
void TransitionLayout(VkImageLayout new_layout, VkCommandBuffer cmd = VK_NULL_HANDLE);
void Clear(VkCommandBuffer cmd, Vec4f color);
void DestroySampler() noexcept;
void DestroyImageView() noexcept;
virtual void Destroy() noexcept;
[[nodiscard]] inline VkImage Get() const noexcept { return m_image; }
[[nodiscard]] inline VkImage operator()() const noexcept { return m_image; }
[[nodiscard]] inline VkDeviceMemory GetDeviceMemory() const noexcept { return m_memory.memory; }
[[nodiscard]] inline VkImageView GetImageView() const noexcept { return m_image_view; }
[[nodiscard]] inline VkFormat GetFormat() const noexcept { return m_format; }
[[nodiscard]] inline VkImageTiling GetTiling() const noexcept { return m_tiling; }
[[nodiscard]] inline VkImageLayout GetLayout() const noexcept { return m_layout; }
[[nodiscard]] inline VkSampler GetSampler() const noexcept { return m_sampler; }
[[nodiscard]] inline std::uint32_t GetWidth() const noexcept { return m_width; }
[[nodiscard]] inline std::uint32_t GetHeight() const noexcept { return m_height; }
[[nodiscard]] inline bool IsInit() const noexcept { return m_image != VK_NULL_HANDLE; }
[[nodiscard]] inline ImageType GetType() const noexcept { return m_type; }
[[nodiscard]] inline static std::size_t GetImageCount() noexcept { return s_image_count; }
virtual ~Image() = default;
private:
inline static std::size_t s_image_count = 0;
MemoryBlock m_memory = NULL_MEMORY_BLOCK;
VkImage m_image = VK_NULL_HANDLE;
VkImageView m_image_view = VK_NULL_HANDLE;
VkSampler m_sampler = VK_NULL_HANDLE;
VkFormat m_format;
VkImageTiling m_tiling;
VkImageLayout m_layout = VK_IMAGE_LAYOUT_UNDEFINED;
ImageType m_type;
std::uint32_t m_width = 0;
std::uint32_t m_height = 0;
bool m_is_multisampled = false;
};
class DepthImage : public Image
{
public:
DepthImage() = default;
inline void Init(std::uint32_t width, std::uint32_t height, bool is_multisampled = false)
{
std::vector<VkFormat> candidates = { VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT };
VkFormat format = kvfFindSupportFormatInCandidates(RenderCore::Get().GetDevice(), candidates.data(), candidates.size(), VK_IMAGE_TILING_OPTIMAL, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT);
Image::Init(ImageType::Depth, width, height, format, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, is_multisampled);
Image::CreateImageView(VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_ASPECT_DEPTH_BIT);
Image::TransitionLayout(VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
}
~DepthImage() = default;
};
class Texture : public Image
{
public:
Texture() = default;
Texture(CPUBuffer pixels, std::uint32_t width, std::uint32_t height, VkFormat format = VK_FORMAT_R8G8B8A8_SRGB, bool is_multisampled = false)
{
Init(std::move(pixels), width, height, format, is_multisampled);
}
inline void Init(CPUBuffer pixels, std::uint32_t width, std::uint32_t height, VkFormat format = VK_FORMAT_R8G8B8A8_SRGB, bool is_multisampled = false)
{
Image::Init(ImageType::Color, width, height, format, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, is_multisampled);
Image::CreateImageView(VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_ASPECT_COLOR_BIT);
Image::CreateSampler();
if(pixels)
{
TransitionLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
GPUBuffer staging_buffer;
std::size_t size = width * height * kvfFormatSize(format);
staging_buffer.Init(BufferType::Staging, size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, pixels);
VkCommandBuffer cmd = kvfCreateCommandBuffer(RenderCore::Get().GetDevice());
kvfBeginCommandBuffer(cmd, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);
kvfCopyBufferToImage(cmd, Image::Get(), staging_buffer.Get(), staging_buffer.GetOffset(), VK_IMAGE_ASPECT_COLOR_BIT, { width, height, 1 });
RenderCore::Get().vkEndCommandBuffer(cmd);
VkFence fence = kvfCreateFence(RenderCore::Get().GetDevice());
kvfSubmitSingleTimeCommandBuffer(RenderCore::Get().GetDevice(), cmd, KVF_GRAPHICS_QUEUE, fence);
kvfDestroyFence(RenderCore::Get().GetDevice(), fence);
staging_buffer.Destroy();
}
if(!pixels)
TransitionLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
else
TransitionLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
}
~Texture() override { Destroy(); }
};
class CubeTexture : public Image
{
public:
CubeTexture() = default;
CubeTexture(CPUBuffer pixels, std::uint32_t width, std::uint32_t height, VkFormat format = VK_FORMAT_R8G8B8A8_SRGB)
{
Init(std::move(pixels), width, height, format);
}
void Init(CPUBuffer pixels, std::uint32_t width, std::uint32_t height, VkFormat format = VK_FORMAT_R8G8B8A8_SRGB);
~CubeTexture() override { Destroy(); }
};
}
#endif

View File

@@ -0,0 +1,49 @@
#ifndef __SCOP_VULKAN_MEMORY_BLOCK__
#define __SCOP_VULKAN_MEMORY_BLOCK__
#include <kvf.h>
#include <algorithm>
namespace Scop
{
class MemoryBlock
{
friend class MemoryChunk;
public:
MemoryBlock() = default;
[[nodiscard]] inline bool operator==(const MemoryBlock& rhs) const noexcept
{
return memory == rhs.memory &&
offset == rhs.offset &&
size == rhs.size &&
free == rhs.free &&
map == rhs.map;
}
inline void Swap(MemoryBlock& rhs) noexcept
{
std::swap(memory, rhs.memory);
std::swap(offset, rhs.offset);
std::swap(size, rhs.size);
std::swap(map, rhs.map);
std::swap(free, rhs.free);
}
~MemoryBlock() = default;
public:
VkDeviceMemory memory = VK_NULL_HANDLE;
VkDeviceSize offset = 0;
VkDeviceSize size = 0;
void* map = nullptr; // useless if it's a GPU allocation
private:
bool free = false;
};
constexpr MemoryBlock NULL_MEMORY_BLOCK{};
}
#endif

View File

@@ -0,0 +1,35 @@
#ifndef __SCOP_VULKAN_MEMORY_CHUNK__
#define __SCOP_VULKAN_MEMORY_CHUNK__
#include <vector>
#include <cstdint>
#include <optional>
#include <Renderer/Memory/Block.h>
namespace Scop
{
class MemoryChunk
{
public:
MemoryChunk(VkDevice device, VkPhysicalDevice physical, VkDeviceSize size, std::int32_t memory_type_index);
[[nodiscard]] std::optional<MemoryBlock> Allocate(VkDeviceSize size, VkDeviceSize alignment);
void Deallocate(const MemoryBlock& block);
[[nodiscard]] inline bool Has(const MemoryBlock& block) const noexcept { return block.memory == m_memory; }
[[nodiscard]] inline std::int32_t GetMemoryTypeIndex() const noexcept { return m_memory_type_index; }
~MemoryChunk();
protected:
std::vector<MemoryBlock> m_blocks;
VkDevice m_device = VK_NULL_HANDLE;
VkPhysicalDevice m_physical = VK_NULL_HANDLE;
VkDeviceMemory m_memory = VK_NULL_HANDLE;
void* p_map = nullptr;
VkDeviceSize m_size = 0;
std::int32_t m_memory_type_index;
};
}
#endif

View File

@@ -0,0 +1,35 @@
#ifndef __SCOP_VULKAN_MEMORY_DEVICE_ALLOCATOR__
#define __SCOP_VULKAN_MEMORY_DEVICE_ALLOCATOR__
#include <vector>
#include <memory>
#include <cstdint>
#include <Renderer/Memory/Block.h>
#include <Renderer/Memory/Chunk.h>
namespace Scop
{
class DeviceAllocator
{
public:
DeviceAllocator() = default;
inline void AttachToDevice(VkDevice device, VkPhysicalDevice physical) noexcept { m_device = device; m_physical = physical; }
inline void DetachFromDevice() noexcept { m_chunks.clear(); m_device = VK_NULL_HANDLE; m_physical = VK_NULL_HANDLE; }
[[nodiscard]] inline std::size_t GetAllocationsCount() const noexcept { return m_allocations_count; }
[[nodiscard]] MemoryBlock Allocate(VkDeviceSize size, VkDeviceSize alignment, std::int32_t memory_type_index, bool dedicated_chunk = false);
void Deallocate(const MemoryBlock& block);
~DeviceAllocator() = default;
private:
std::vector<std::unique_ptr<MemoryChunk>> m_chunks;
VkDevice m_device = VK_NULL_HANDLE;
VkPhysicalDevice m_physical = VK_NULL_HANDLE;
std::size_t m_allocations_count = 0;
};
}
#endif

View File

@@ -0,0 +1,67 @@
#ifndef __SCOP_GRAPHICS_PIPELINE__
#define __SCOP_GRAPHICS_PIPELINE__
#include <memory>
#include <vector>
#include <kvf.h>
#include <Renderer/Image.h>
#include <Utils/NonOwningPtr.h>
#include <Renderer/Pipelines/Shader.h>
#include <Renderer/Pipelines/Pipeline.h>
namespace Scop
{
struct GraphicPipelineDescriptor
{
std::shared_ptr<Shader> vertex_shader;
std::shared_ptr<Shader> fragment_shader;
std::vector<NonOwningPtr<Texture>> color_attachments;
NonOwningPtr<DepthImage> depth = nullptr;
NonOwningPtr<class Renderer> renderer = nullptr;
VkCullModeFlagBits culling = VK_CULL_MODE_FRONT_BIT;
VkPolygonMode mode = VK_POLYGON_MODE_FILL;
bool no_vertex_inputs = false;
bool depth_test_equal = false;
bool clear_color_attachments = true;
};
class GraphicPipeline : public Pipeline
{
public:
GraphicPipeline() = default;
void Init(const GraphicPipelineDescriptor& descriptor);
bool BindPipeline(VkCommandBuffer command_buffer, std::size_t framebuffer_index, std::array<float, 4> clear) noexcept;
void EndPipeline(VkCommandBuffer command_buffer) noexcept override;
void Destroy() noexcept;
[[nodiscard]] inline VkPipeline GetPipeline() const override { return m_pipeline; }
[[nodiscard]] inline VkPipelineLayout GetPipelineLayout() const override { return m_pipeline_layout; }
[[nodiscard]] inline VkPipelineBindPoint GetPipelineBindPoint() const override { return VK_PIPELINE_BIND_POINT_GRAPHICS; }
~GraphicPipeline() = default;
private:
void CreateFramebuffers(const std::vector<NonOwningPtr<Texture>>& render_targets, bool clear_attachments);
void TransitionAttachments(VkCommandBuffer cmd = VK_NULL_HANDLE);
// Private override to remove access
bool BindPipeline(VkCommandBuffer) noexcept override { return false; };
private:
std::vector<NonOwningPtr<Texture>> m_attachments;
std::vector<VkFramebuffer> m_framebuffers;
std::vector<VkClearValue> m_clears;
std::shared_ptr<Shader> p_vertex_shader;
std::shared_ptr<Shader> p_fragment_shader;
VkRenderPass m_renderpass = VK_NULL_HANDLE;
VkPipeline m_pipeline = VK_NULL_HANDLE;
VkPipelineLayout m_pipeline_layout = VK_NULL_HANDLE;
NonOwningPtr<class Renderer> p_renderer;
NonOwningPtr<DepthImage> p_depth;
};
}
#endif

View File

@@ -0,0 +1,25 @@
#ifndef __SCOP_PIPELINE__
#define __SCOP_PIPELINE__
#include <kvf.h>
#include <Renderer/RenderCore.h>
namespace Scop
{
class Pipeline
{
public:
Pipeline() = default;
inline virtual bool BindPipeline(VkCommandBuffer command_buffer) noexcept { RenderCore::Get().vkCmdBindPipeline(command_buffer, GetPipelineBindPoint(), GetPipeline()); return true; }
inline virtual void EndPipeline([[maybe_unused]] VkCommandBuffer command_buffer) noexcept {}
virtual VkPipeline GetPipeline() const = 0;
virtual VkPipelineLayout GetPipelineLayout() const = 0;
virtual VkPipelineBindPoint GetPipelineBindPoint() const = 0;
virtual ~Pipeline() = default;
};
}
#endif

View File

@@ -0,0 +1,78 @@
#ifndef __SCOP_SHADER__
#define __SCOP_SHADER__
#include <vector>
#include <cstdint>
#include <filesystem>
#include <kvf.h>
namespace Scop
{
struct ShaderSetLayout
{
std::vector<std::pair<int, VkDescriptorType> > binds;
ShaderSetLayout(std::vector<std::pair<int, VkDescriptorType> > b) : binds(std::move(b)) {}
};
struct ShaderPushConstantLayout
{
std::size_t offset;
std::size_t size;
ShaderPushConstantLayout(std::size_t o, std::size_t s) : offset(o), size(s) {}
};
struct ShaderLayout
{
std::vector<std::pair<int, ShaderSetLayout> > set_layouts;
std::vector<ShaderPushConstantLayout> push_constants;
ShaderLayout(std::vector<std::pair<int, ShaderSetLayout> > s, std::vector<ShaderPushConstantLayout> pc) : set_layouts(std::move(s)), push_constants(std::move(pc)) {}
};
enum class ShaderType
{
Vertex,
Fragment,
Compute
};
struct ShaderPipelineLayoutPart
{
std::vector<VkPushConstantRange> push_constants;
std::vector<VkDescriptorSetLayout> set_layouts;
};
class Shader
{
public:
Shader(const std::vector<std::uint32_t>& bytecode, ShaderType type, ShaderLayout layout);
[[nodiscard]] inline const ShaderLayout& GetShaderLayout() const { return m_layout; }
[[nodiscard]] inline const std::vector<std::uint32_t>& GetByteCode() const noexcept { return m_bytecode; }
[[nodiscard]] inline const ShaderPipelineLayoutPart& GetPipelineLayout() const noexcept { return m_pipeline_layout_part; }
[[nodiscard]] inline VkShaderModule GetShaderModule() const noexcept { return m_module; }
[[nodiscard]] inline VkShaderStageFlagBits GetShaderStage() const noexcept { return m_stage; }
void Destroy();
~Shader();
private:
void GeneratePipelineLayout(ShaderLayout layout);
private:
ShaderLayout m_layout;
ShaderPipelineLayoutPart m_pipeline_layout_part;
std::vector<std::uint32_t> m_bytecode;
std::vector<VkDescriptorSetLayout> m_set_layouts;
VkShaderStageFlagBits m_stage;
VkShaderModule m_module = VK_NULL_HANDLE;
};
std::shared_ptr<Shader> LoadShaderFromFile(const std::filesystem::path& filepath, ShaderType type, ShaderLayout layout);
}
#endif

View File

@@ -0,0 +1,76 @@
#ifndef __SCOP_RENDER_CORE__
#define __SCOP_RENDER_CORE__
#include <array>
#include <memory>
#include <cstdint>
#include <optional>
#ifdef DEBUG
#define KVF_ENABLE_VALIDATION_LAYERS
#endif
#include <kvf.h>
#include <Renderer/Memory/DeviceAllocator.h>
namespace Scop
{
constexpr const int MAX_FRAMES_IN_FLIGHT = 2;
constexpr const int DEFAULT_VERTEX_SHADER_ID = 0;
constexpr const int DEFAULT_FRAGMENT_SHADER_ID = 1;
constexpr const int BASIC_FRAGMENT_SHADER_ID = 2;
std::optional<std::uint32_t> FindMemoryType(std::uint32_t type_filter, VkMemoryPropertyFlags properties, bool error = true);
#if defined(DEBUG) && defined(VK_EXT_debug_utils)
#define SCOP_HAS_DEBUG_UTILS_FUNCTIONS
#endif
class RenderCore
{
public:
RenderCore();
[[nodiscard]] inline VkInstance GetInstance() const noexcept { return m_instance; }
[[nodiscard]] inline VkInstance& GetInstanceRef() noexcept { return m_instance; }
[[nodiscard]] inline VkDevice GetDevice() const noexcept { return m_device; }
[[nodiscard]] inline VkPhysicalDevice GetPhysicalDevice() const noexcept { return m_physical_device; }
[[nodiscard]] inline DeviceAllocator& GetAllocator() noexcept { return m_allocator; }
[[nodiscard]] inline std::shared_ptr<class Shader> GetDefaultVertexShader() const { return m_internal_shaders[DEFAULT_VERTEX_SHADER_ID]; }
[[nodiscard]] inline std::shared_ptr<class Shader> GetBasicFragmentShader() const { return m_internal_shaders[BASIC_FRAGMENT_SHADER_ID]; }
[[nodiscard]] inline std::shared_ptr<class Shader> GetDefaultFragmentShader() const { return m_internal_shaders[DEFAULT_FRAGMENT_SHADER_ID]; }
inline void WaitDeviceIdle() const noexcept { vkDeviceWaitIdle(m_device); }
inline static bool IsInit() noexcept { return s_instance != nullptr; }
inline static RenderCore& Get() noexcept { return *s_instance; }
#define SCOP_VULKAN_GLOBAL_FUNCTION(fn) PFN_##fn fn = nullptr;
#define SCOP_VULKAN_INSTANCE_FUNCTION(fn) PFN_##fn fn = nullptr;
#define SCOP_VULKAN_DEVICE_FUNCTION(fn) PFN_##fn fn = nullptr;
#include <Renderer/Vulkan/VulkanDefs.h>
#undef SCOP_VULKAN_GLOBAL_FUNCTION
#undef SCOP_VULKAN_INSTANCE_FUNCTION
#undef SCOP_VULKAN_DEVICE_FUNCTION
~RenderCore();
private:
void LoadKVFGlobalVulkanFunctionPointers() const noexcept;
void LoadKVFInstanceVulkanFunctionPointers() const noexcept;
void LoadKVFDeviceVulkanFunctionPointers() const noexcept;
private:
static RenderCore* s_instance;
std::array<std::shared_ptr<class Shader>, 3> m_internal_shaders;
DeviceAllocator m_allocator;
VkInstance m_instance = VK_NULL_HANDLE;
VkDevice m_device = VK_NULL_HANDLE;
VkPhysicalDevice m_physical_device = VK_NULL_HANDLE;
};
}
#endif

View File

@@ -0,0 +1,31 @@
#ifndef __SCOP_2D_PASS__
#define __SCOP_2D_PASS__
#include <memory>
#include <Renderer/Descriptor.h>
#include <Renderer/Pipelines/Shader.h>
#include <Renderer/Pipelines/Graphics.h>
namespace Scop
{
class Render2DPass
{
public:
Render2DPass() = default;
void Init();
void Pass(class Scene& scene, class Renderer& renderer, class Texture& render_target);
void Destroy();
~Render2DPass() = default;
private:
GraphicPipeline m_pipeline;
std::shared_ptr<DescriptorSet> p_viewer_data_set;
std::shared_ptr<UniformBuffer> p_viewer_data_buffer;
std::shared_ptr<DescriptorSet> p_texture_set;
std::shared_ptr<Shader> p_vertex_shader;
std::shared_ptr<Shader> p_fragment_shader;
};
}
#endif

View File

@@ -0,0 +1,29 @@
#ifndef __SCOP_FINAL_PASS__
#define __SCOP_FINAL_PASS__
#include <memory>
#include <Renderer/Descriptor.h>
#include <Renderer/Pipelines/Shader.h>
#include <Renderer/Pipelines/Graphics.h>
namespace Scop
{
class FinalPass
{
public:
FinalPass() = default;
void Init();
void Pass(class Scene& scene, class Renderer& renderer, class Texture& render_target);
void Destroy();
~FinalPass() = default;
private:
GraphicPipeline m_pipeline;
std::shared_ptr<DescriptorSet> p_set;
std::shared_ptr<Shader> p_vertex_shader;
std::shared_ptr<Shader> p_fragment_shader;
};
}
#endif

View File

@@ -0,0 +1,15 @@
#ifndef __SCOP_FORWARD_PASS__
#define __SCOP_FORWARD_PASS__
namespace Scop
{
class ForwardPass
{
public:
ForwardPass() = default;
void Pass(class Scene& scene, class Renderer& renderer, class Texture& render_target);
~ForwardPass() = default;
};
}
#endif

View File

@@ -0,0 +1,30 @@
#ifndef __SCOP_PASSES__
#define __SCOP_PASSES__
#include <Renderer/Image.h>
#include <Renderer/RenderPasses/SkyboxPass.h>
#include <Renderer/RenderPasses/ForwardPass.h>
#include <Renderer/RenderPasses/FinalPass.h>
#include <Renderer/RenderPasses/2DPass.h>
namespace Scop
{
class RenderPasses
{
public:
RenderPasses() = default;
void Init();
void Pass(class Scene& scene, class Renderer& renderer);
void Destroy();
~RenderPasses() = default;
private:
SkyboxPass m_skybox;
Render2DPass m_2Dpass;
FinalPass m_final;
Texture m_main_render_texture;
ForwardPass m_forward;
};
}
#endif

View File

@@ -0,0 +1,30 @@
#ifndef __SCOP_SKYBOX_PASS__
#define __SCOP_SKYBOX_PASS__
#include <memory>
#include <Renderer/Descriptor.h>
#include <Renderer/Pipelines/Shader.h>
#include <Renderer/Pipelines/Graphics.h>
namespace Scop
{
class SkyboxPass
{
public:
SkyboxPass() = default;
void Init();
void Pass(class Scene& scene, class Renderer& renderer, class Texture& render_target);
void Destroy();
~SkyboxPass() = default;
private:
GraphicPipeline m_pipeline;
std::shared_ptr<DescriptorSet> p_set;
std::shared_ptr<Shader> p_vertex_shader;
std::shared_ptr<Shader> p_fragment_shader;
std::shared_ptr<class Mesh> m_cube;
};
}
#endif

View File

@@ -0,0 +1,55 @@
#ifndef __SCOP_RENDERER__
#define __SCOP_RENDERER__
#include <Platform/Window.h>
#include <Utils/NonOwningPtr.h>
#include <Renderer/Swapchain.h>
#include <Renderer/RenderCore.h>
#include <Renderer/Image.h>
#include <kvf.h>
#include <array>
#include <Core/EventBus.h>
namespace Scop
{
class Renderer
{
public:
Renderer() = default;
void Init(NonOwningPtr<Window> window);
void BeginFrame();
void EndFrame();
[[nodiscard]] inline VkSemaphore GetImageAvailableSemaphore(int index) const noexcept { return m_image_available_semaphores[index]; }
[[nodiscard]] inline VkSemaphore GetRenderFinishedSemaphore(int index) const noexcept { return m_render_finished_semaphores[index]; }
[[nodiscard]] inline VkCommandBuffer GetCommandBuffer(int index) const noexcept { return m_cmd_buffers[index]; }
[[nodiscard]] inline VkCommandBuffer GetActiveCommandBuffer() const noexcept { return m_cmd_buffers[m_current_frame_index]; }
[[nodiscard]] inline std::size_t& GetDrawCallsCounterRef() noexcept { return m_drawcalls; }
[[nodiscard]] inline std::size_t& GetPolygonDrawnCounterRef() noexcept { return m_polygons_drawn; }
[[nodiscard]] inline std::size_t GetCurrentFrameIndex() const noexcept { return m_current_frame_index; }
[[nodiscard]] inline NonOwningPtr<Window> GetWindow() const noexcept { return p_window; }
[[nodiscard]] inline const Swapchain& GetSwapchain() const noexcept { return m_swapchain; }
void Destroy() noexcept;
~Renderer() = default;
private:
Swapchain m_swapchain;
std::array<VkSemaphore, MAX_FRAMES_IN_FLIGHT> m_image_available_semaphores;
std::array<VkSemaphore, MAX_FRAMES_IN_FLIGHT> m_render_finished_semaphores;
std::array<VkCommandBuffer, MAX_FRAMES_IN_FLIGHT> m_cmd_buffers;
std::array<VkFence, MAX_FRAMES_IN_FLIGHT> m_cmd_fences;
NonOwningPtr<Window> p_window;
std::uint32_t m_current_frame_index = 0;
std::size_t m_polygons_drawn = 0;
std::size_t m_drawcalls = 0;
};
}
#endif

View File

@@ -0,0 +1,22 @@
#ifndef __SCOP_SCENES_RENDERER__
#define __SCOP_SCENES_RENDERER__
#include <Renderer/RenderPasses/Passes.h>
namespace Scop
{
class SceneRenderer
{
public:
SceneRenderer() = default;
void Init();
void Render(class Scene& scene, class Renderer& renderer); // TODO : add RTT support
void Destroy();
~SceneRenderer() = default;
private:
RenderPasses m_passes;
};
}
#endif

View File

@@ -0,0 +1,43 @@
#ifndef __SCOP_SWAPCHAIN__
#define __SCOP_SWAPCHAIN__
#include <Utils/NonOwningPtr.h>
#include <Renderer/Image.h>
namespace Scop
{
class Swapchain
{
public:
Swapchain() = default;
void Init(NonOwningPtr<class Window> window);
void AquireFrame(VkSemaphore signal);
void Present(VkSemaphore wait) noexcept;
void Destroy();
[[nodiscard]] inline VkSwapchainKHR Get() const noexcept { return m_swapchain; }
[[nodiscard]] inline VkSurfaceKHR GetSurface() const noexcept { return m_surface; }
[[nodiscard]] inline std::uint32_t GetImagesCount() const noexcept { return m_images_count; }
[[nodiscard]] inline std::uint32_t GetMinImagesCount() const noexcept { return m_min_images_count; }
[[nodiscard]] inline std::uint32_t GetImageIndex() const noexcept { return m_current_image_index; }
[[nodiscard]] inline const std::vector<Image>& GetSwapchainImages() const { return m_swapchain_images; }
~Swapchain() = default;
private:
void CreateSwapchain();
private:
std::vector<Image> m_swapchain_images;
VkSwapchainKHR m_swapchain = VK_NULL_HANDLE;
VkSurfaceKHR m_surface = VK_NULL_HANDLE;
NonOwningPtr<class Window> p_window;
std::uint32_t m_images_count = 0;
std::uint32_t m_min_images_count = 0;
std::uint32_t m_current_image_index = 0;
bool m_resize = false;
};
}
#endif

28
ScopEngine/Runtime/Includes/Renderer/Vertex.h git.filemode.normal_file
View File

@@ -0,0 +1,28 @@
#ifndef __SCOP_VERTEX__
#define __SCOP_VERTEX__
#include <kvf.h>
#include <array>
#include <Maths/Vec4.h>
#include <Maths/Vec2.h>
namespace Scop
{
struct Vertex
{
alignas(16) Vec4f position = Vec4f{ 0.0f, 0.0f, 0.0f, 1.0f };
alignas(16) Vec4f color = Vec4f{ 1.0f, 1.0f, 1.0f, 1.0f };
alignas(16) Vec4f normal = Vec4f{ 0.0f, 0.0f, 0.0f, 1.0f };
alignas(16) Vec2f uv = Vec2f{ 0.0f, 0.0f };
Vertex() = default;
Vertex(Vec4f p, Vec4f c, Vec4f n, Vec2f u) : position(std::move(p)), color(std::move(c)), normal(std::move(n)), uv(std::move(u)) {}
[[nodiscard]] inline static VkVertexInputBindingDescription GetBindingDescription();
[[nodiscard]] inline static std::array<VkVertexInputAttributeDescription, 4> GetAttributeDescriptions();
};
}
#include <Renderer/Vertex.inl>
#endif

View File

@@ -0,0 +1,41 @@
#pragma once
#include <Renderer/Vertex.h>
namespace Scop
{
VkVertexInputBindingDescription Vertex::GetBindingDescription()
{
VkVertexInputBindingDescription binding_description{};
binding_description.binding = 0;
binding_description.stride = sizeof(Vertex);
binding_description.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
return binding_description;
}
std::array<VkVertexInputAttributeDescription, 4> Vertex::GetAttributeDescriptions()
{
std::array<VkVertexInputAttributeDescription, 4> attribute_descriptions;
attribute_descriptions[0].binding = 0;
attribute_descriptions[0].location = 0;
attribute_descriptions[0].format = VK_FORMAT_R32G32B32A32_SFLOAT;
attribute_descriptions[0].offset = offsetof(Vertex, position);
attribute_descriptions[1].binding = 0;
attribute_descriptions[1].location = 1;
attribute_descriptions[1].format = VK_FORMAT_R32G32B32A32_SFLOAT;
attribute_descriptions[1].offset = offsetof(Vertex, color);
attribute_descriptions[2].binding = 0;
attribute_descriptions[2].location = 2;
attribute_descriptions[2].format = VK_FORMAT_R32G32B32A32_SFLOAT;
attribute_descriptions[2].offset = offsetof(Vertex, normal);
attribute_descriptions[3].binding = 0;
attribute_descriptions[3].location = 3;
attribute_descriptions[3].format = VK_FORMAT_R32G32_SFLOAT;
attribute_descriptions[3].offset = offsetof(Vertex, uv);
return attribute_descriptions;
}
}

View File

@@ -0,0 +1,21 @@
#ifndef __SCOP_VIEWER_DATA__
#define __SCOP_VIEWER_DATA__
#include <Maths/Mat4.h>
#include <Maths/Vec3.h>
namespace Scop
{
struct ViewerData
{
Mat4f projection_matrix;
Mat4f inv_projection_matrix;
Mat4f view_matrix;
Mat4f inv_view_matrix;
Mat4f view_proj_matrix;
Mat4f inv_view_proj_matrix;
alignas(16) Vec3f camera_position;
};
}
#endif

View File

@@ -0,0 +1,127 @@
// No header guard
#ifdef VK_VERSION_1_0
#ifdef SCOP_VULKAN_GLOBAL_FUNCTION
SCOP_VULKAN_GLOBAL_FUNCTION(vkCreateInstance)
SCOP_VULKAN_GLOBAL_FUNCTION(vkEnumerateInstanceExtensionProperties)
SCOP_VULKAN_GLOBAL_FUNCTION(vkEnumerateInstanceLayerProperties)
SCOP_VULKAN_GLOBAL_FUNCTION(vkGetInstanceProcAddr)
#endif
#ifdef SCOP_VULKAN_INSTANCE_FUNCTION
SCOP_VULKAN_INSTANCE_FUNCTION(vkCreateDevice)
SCOP_VULKAN_INSTANCE_FUNCTION(vkDestroyInstance)
SCOP_VULKAN_INSTANCE_FUNCTION(vkEnumerateDeviceExtensionProperties)
SCOP_VULKAN_INSTANCE_FUNCTION(vkEnumeratePhysicalDevices)
SCOP_VULKAN_INSTANCE_FUNCTION(vkGetDeviceProcAddr)
SCOP_VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceFeatures)
SCOP_VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceFormatProperties)
SCOP_VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceImageFormatProperties)
SCOP_VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceMemoryProperties)
SCOP_VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceProperties)
SCOP_VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceQueueFamilyProperties)
#ifdef DEBUG
#ifdef VK_EXT_debug_utils
SCOP_VULKAN_INSTANCE_FUNCTION(vkSetDebugUtilsObjectNameEXT)
//SCOP_VULKAN_INSTANCE_FUNCTION(vkSetDebugUtilsObjectTagEXT)
#endif
#endif
#endif
#ifdef SCOP_VULKAN_DEVICE_FUNCTION
SCOP_VULKAN_DEVICE_FUNCTION(vkAllocateCommandBuffers)
SCOP_VULKAN_DEVICE_FUNCTION(vkAllocateDescriptorSets)
SCOP_VULKAN_DEVICE_FUNCTION(vkAllocateMemory)
SCOP_VULKAN_DEVICE_FUNCTION(vkBeginCommandBuffer)
SCOP_VULKAN_DEVICE_FUNCTION(vkBindBufferMemory)
SCOP_VULKAN_DEVICE_FUNCTION(vkBindImageMemory)
SCOP_VULKAN_DEVICE_FUNCTION(vkCmdBeginRenderPass)
SCOP_VULKAN_DEVICE_FUNCTION(vkCmdBindDescriptorSets)
SCOP_VULKAN_DEVICE_FUNCTION(vkCmdBindIndexBuffer)
SCOP_VULKAN_DEVICE_FUNCTION(vkCmdBindPipeline)
SCOP_VULKAN_DEVICE_FUNCTION(vkCmdBindVertexBuffers)
SCOP_VULKAN_DEVICE_FUNCTION(vkCmdClearAttachments)
SCOP_VULKAN_DEVICE_FUNCTION(vkCmdClearColorImage)
SCOP_VULKAN_DEVICE_FUNCTION(vkCmdClearDepthStencilImage)
SCOP_VULKAN_DEVICE_FUNCTION(vkCmdCopyBuffer)
SCOP_VULKAN_DEVICE_FUNCTION(vkCmdCopyBufferToImage)
SCOP_VULKAN_DEVICE_FUNCTION(vkCmdCopyImage)
SCOP_VULKAN_DEVICE_FUNCTION(vkCmdCopyImageToBuffer)
SCOP_VULKAN_DEVICE_FUNCTION(vkCmdDraw)
SCOP_VULKAN_DEVICE_FUNCTION(vkCmdDrawIndexed)
SCOP_VULKAN_DEVICE_FUNCTION(vkCmdEndRenderPass)
SCOP_VULKAN_DEVICE_FUNCTION(vkCmdPipelineBarrier)
SCOP_VULKAN_DEVICE_FUNCTION(vkCmdPushConstants)
SCOP_VULKAN_DEVICE_FUNCTION(vkCmdSetScissor)
SCOP_VULKAN_DEVICE_FUNCTION(vkCmdSetViewport)
SCOP_VULKAN_DEVICE_FUNCTION(vkCreateBuffer)
SCOP_VULKAN_DEVICE_FUNCTION(vkCreateCommandPool)
SCOP_VULKAN_DEVICE_FUNCTION(vkCreateDescriptorPool)
SCOP_VULKAN_DEVICE_FUNCTION(vkCreateDescriptorSetLayout)
SCOP_VULKAN_DEVICE_FUNCTION(vkCreateFence)
SCOP_VULKAN_DEVICE_FUNCTION(vkCreateFramebuffer)
SCOP_VULKAN_DEVICE_FUNCTION(vkCreateGraphicsPipelines)
SCOP_VULKAN_DEVICE_FUNCTION(vkCreateImage)
SCOP_VULKAN_DEVICE_FUNCTION(vkCreateImageView)
SCOP_VULKAN_DEVICE_FUNCTION(vkCreatePipelineLayout)
SCOP_VULKAN_DEVICE_FUNCTION(vkCreateRenderPass)
SCOP_VULKAN_DEVICE_FUNCTION(vkCreateSampler)
SCOP_VULKAN_DEVICE_FUNCTION(vkCreateSemaphore)
SCOP_VULKAN_DEVICE_FUNCTION(vkCreateShaderModule)
SCOP_VULKAN_DEVICE_FUNCTION(vkDestroyBuffer)
SCOP_VULKAN_DEVICE_FUNCTION(vkDestroyCommandPool)
SCOP_VULKAN_DEVICE_FUNCTION(vkDestroyDescriptorPool)
SCOP_VULKAN_DEVICE_FUNCTION(vkDestroyDescriptorSetLayout)
SCOP_VULKAN_DEVICE_FUNCTION(vkDestroyDevice)
SCOP_VULKAN_DEVICE_FUNCTION(vkDestroyFence)
SCOP_VULKAN_DEVICE_FUNCTION(vkDestroyFramebuffer)
SCOP_VULKAN_DEVICE_FUNCTION(vkDestroyImage)
SCOP_VULKAN_DEVICE_FUNCTION(vkDestroyImageView)
SCOP_VULKAN_DEVICE_FUNCTION(vkDestroyPipeline)
SCOP_VULKAN_DEVICE_FUNCTION(vkDestroyPipelineLayout)
SCOP_VULKAN_DEVICE_FUNCTION(vkDestroyRenderPass)
SCOP_VULKAN_DEVICE_FUNCTION(vkDestroySampler)
SCOP_VULKAN_DEVICE_FUNCTION(vkDestroySemaphore)
SCOP_VULKAN_DEVICE_FUNCTION(vkDestroyShaderModule)
SCOP_VULKAN_DEVICE_FUNCTION(vkDeviceWaitIdle)
SCOP_VULKAN_DEVICE_FUNCTION(vkEndCommandBuffer)
SCOP_VULKAN_DEVICE_FUNCTION(vkFlushMappedMemoryRanges)
SCOP_VULKAN_DEVICE_FUNCTION(vkFreeCommandBuffers)
SCOP_VULKAN_DEVICE_FUNCTION(vkFreeMemory)
SCOP_VULKAN_DEVICE_FUNCTION(vkGetBufferMemoryRequirements)
SCOP_VULKAN_DEVICE_FUNCTION(vkGetDeviceMemoryCommitment)
SCOP_VULKAN_DEVICE_FUNCTION(vkGetDeviceQueue)
SCOP_VULKAN_DEVICE_FUNCTION(vkGetFenceStatus)
SCOP_VULKAN_DEVICE_FUNCTION(vkGetImageMemoryRequirements)
SCOP_VULKAN_DEVICE_FUNCTION(vkGetImageSubresourceLayout)
SCOP_VULKAN_DEVICE_FUNCTION(vkInvalidateMappedMemoryRanges)
SCOP_VULKAN_DEVICE_FUNCTION(vkMapMemory)
SCOP_VULKAN_DEVICE_FUNCTION(vkQueueSubmit)
SCOP_VULKAN_DEVICE_FUNCTION(vkQueueWaitIdle)
SCOP_VULKAN_DEVICE_FUNCTION(vkResetCommandBuffer)
SCOP_VULKAN_DEVICE_FUNCTION(vkResetDescriptorPool)
SCOP_VULKAN_DEVICE_FUNCTION(vkResetEvent)
SCOP_VULKAN_DEVICE_FUNCTION(vkResetFences)
SCOP_VULKAN_DEVICE_FUNCTION(vkUnmapMemory)
SCOP_VULKAN_DEVICE_FUNCTION(vkUpdateDescriptorSets)
SCOP_VULKAN_DEVICE_FUNCTION(vkWaitForFences)
#endif
#endif
#ifdef VK_KHR_swapchain
#ifdef SCOP_VULKAN_DEVICE_FUNCTION
SCOP_VULKAN_DEVICE_FUNCTION(vkAcquireNextImageKHR)
SCOP_VULKAN_DEVICE_FUNCTION(vkCreateSwapchainKHR)
SCOP_VULKAN_DEVICE_FUNCTION(vkDestroySwapchainKHR)
SCOP_VULKAN_DEVICE_FUNCTION(vkGetSwapchainImagesKHR)
SCOP_VULKAN_DEVICE_FUNCTION(vkQueuePresentKHR)
#endif
#endif
#ifdef VK_KHR_surface
#ifdef SCOP_VULKAN_INSTANCE_FUNCTION
SCOP_VULKAN_INSTANCE_FUNCTION(vkDestroySurfaceKHR)
SCOP_VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceSurfaceCapabilitiesKHR)
SCOP_VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceSurfaceFormatsKHR)
SCOP_VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceSurfacePresentModesKHR)
SCOP_VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceSurfaceSupportKHR)
#endif
#endif