descriptor heap rewrite

This commit is contained in:
Jorijn van der Graaf 2026-04-05 22:53:59 +02:00
commit f8e142fb06
31 changed files with 429 additions and 1017 deletions

View file

@ -0,0 +1,57 @@
/*
Crafter®.Graphics
Copyright (C) 2026 Catcrafts®
catcrafts.net
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License version 3.0 as published by the Free Software Foundation;
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
module;
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
#include "vulkan/vulkan.h"
#endif
export module Crafter.Graphics:DescriptorHeapVulkan;
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
import std;
import :Device;
import :Window;
import :Types;
import :VulkanBuffer;
export namespace Crafter {
struct DescriptorHeapVulkan {
inline static VulkanBuffer<std::uint8_t, true, true> resourceHeap[Window::numFrames];
inline static VulkanBuffer<std::uint8_t, true, true> samplerHeap[Window::numFrames];
inline static std::uint32_t bufferStartOffset;
inline static std::uint16_t bufferStartElement;
void Initialize(std::uint16_t images, std::uint16_t buffers, std::uint16_t samplers) {
std::uint32_t descriptorRegion = images * Device::descriptorHeapProperties.imageDescriptorSize + buffers * Device::descriptorHeapProperties.bufferDescriptorSize;
std::uint32_t alignedDescriptorRegion = (descriptorRegion + Device::descriptorHeapProperties.imageDescriptorAlignment - 1) & ~(Device::descriptorHeapProperties.imageDescriptorAlignment - 1);
std::uint32_t resourceSize = alignedDescriptorRegion + Device::descriptorHeapProperties.minResourceHeapReservedRange;
std::uint32_t samplerSize = samplers * Device::descriptorHeapProperties.samplerDescriptorSize + Device::descriptorHeapProperties.minSamplerHeapReservedRange;
bufferStartElement = images * Device::descriptorHeapProperties.imageDescriptorSize / Device::descriptorHeapProperties.bufferDescriptorSize;
if(images > 0 && bufferStartElement == 0) {
bufferStartElement = 1;
}
bufferStartOffset = bufferStartElement * Device::descriptorHeapProperties.bufferDescriptorSize;
for(std::uint8_t i = 0; i < Window::numFrames; i++) {
resourceHeap[i].Create(VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_DESCRIPTOR_HEAP_BIT_EXT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, resourceSize);
samplerHeap[i].Create(VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_DESCRIPTOR_HEAP_BIT_EXT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, samplerSize);
}
}
};
}
#endif

View file

@ -1,187 +0,0 @@
/*
Crafter®.Graphics
Copyright (C) 2026 Catcrafts®
catcrafts.net
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License version 3.0 as published by the Free Software Foundation;
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
module;
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
#include "vulkan/vulkan.h"
#endif
export module Crafter.Graphics:DescriptorPoolVulkan;
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
import std;
import :Device;
import :Types;
import :DescriptorSetLayoutVulkan;
import Crafter.Event;
export namespace Crafter {
struct DescriptorEntry {
VkDescriptorType type;
bool occured = true;
};
class DescriptorPool {
public:
std::vector<VkDescriptorSet> sets;
VkDescriptorPool descriptorPool = VK_NULL_HANDLE;
template <typename Shader>
consteval static void GetOccuringDescriptors(std::array<DescriptorEntry, 20>& types) {
for (const VkDescriptorSetLayoutBinding& binding : Shader::descriptors) {
for (DescriptorEntry& type : types) {
if (type.type == binding.descriptorType) {
type.occured = true;
}
}
}
}
template <typename... Shaders>
consteval static std::uint32_t GetUniqueDiscriptorCount() {
std::array<DescriptorEntry, 20> types = {{{VK_DESCRIPTOR_TYPE_SAMPLER, 0},{VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 0},{VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 0},{VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 0},{VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 0},{VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 0},{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0},{VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 0},{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 0},{VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 0},{VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 0},{VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK, 0},{VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 0},{VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_NV, 0},{VK_DESCRIPTOR_TYPE_SAMPLE_WEIGHT_IMAGE_QCOM, 0},{VK_DESCRIPTOR_TYPE_BLOCK_MATCH_IMAGE_QCOM, 0},{VK_DESCRIPTOR_TYPE_MUTABLE_EXT, 0},{VK_DESCRIPTOR_TYPE_PARTITIONED_ACCELERATION_STRUCTURE_NV, 0},{VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT, 0},{VK_DESCRIPTOR_TYPE_MUTABLE_VALVE, 0}}};
(GetOccuringDescriptors<Shaders>(types), ... );
std::uint32_t size = 0;
for(DescriptorEntry& type : types) {
if(type.occured) {
size++;
}
}
return size;
}
template <typename... Shaders>
consteval static std::array<VkDescriptorPoolSize, GetUniqueDiscriptorCount<Shaders...>()> GetPoolSizes() {
std::array<VkDescriptorPoolSize, GetUniqueDiscriptorCount<Shaders...>()> types = {};
for(std::uint32_t i = 0; i < GetUniqueDiscriptorCount<Shaders...>(); i++){
types[i].descriptorCount = 12345;
}
([&] {
for (const VkDescriptorSetLayoutBinding& binding : Shaders::descriptors) {
bool found = false;
for(VkDescriptorPoolSize& type : types) {
if(type.type == binding.descriptorType && type.descriptorCount != 12345) {
type.descriptorCount += binding.descriptorCount;
found = true;
}
}
if(!found) {
for(std::uint32_t i = 0; i < GetUniqueDiscriptorCount<Shaders...>(); i++){
if(types[i].descriptorCount == 12345) {
types[i].type = binding.descriptorType;
types[i].descriptorCount = binding.descriptorCount;
break;
}
}
}
}
}(),
...);
return types;
}
constexpr static std::vector<VkDescriptorPoolSize> GetPoolSizes(const std::span<const DescriptorSetLayoutVulkan> shaders) {
std::vector<VkDescriptorPoolSize> types;
for(const DescriptorSetLayoutVulkan& shader : shaders) {
for (const VkDescriptorSetLayoutBinding& binding : shader.descriptors) {
for(VkDescriptorPoolSize& type : types) {
if(type.type == binding.descriptorType) {
type.descriptorCount += binding.descriptorCount;
goto inner;
}
}
types.emplace_back(binding.descriptorType, binding.descriptorCount);
inner:;
}
}
return types;
}
template <typename... Shaders>
constexpr static std::vector<VkDescriptorPoolSize> GetPoolSizesCombined(const std::span<const DescriptorSetLayoutVulkan> shaders) {
std::vector<VkDescriptorPoolSize> types;
for(const DescriptorSetLayoutVulkan& shader : shaders) {
for (const VkDescriptorSetLayoutBinding& binding : shader.descriptors) {
for(VkDescriptorPoolSize& type : types) {
if(type.type == binding.descriptorType) {
type.descriptorCount += binding.descriptorCount;
goto inner;
}
}
types.emplace_back(binding.descriptorType, binding.descriptorCount);
inner:;
}
}
([&] {
for (const VkDescriptorSetLayoutBinding& binding : Shaders::descriptors) {
for(VkDescriptorPoolSize& type : types) {
if(type.type == binding.descriptorType) {
type.descriptorCount += binding.descriptorCount;
goto inner2;
}
}
types.emplace_back(binding.descriptorType, binding.descriptorCount);
inner2:;
}
}(),
...);
return types;
}
public:
~DescriptorPool() {
vkDestroyDescriptorPool(Device::device, descriptorPool, nullptr);
}
void BuildPool(std::span<const VkDescriptorPoolSize> poolSizes, std::span<const VkDescriptorSetLayout> setLayouts) {
if(descriptorPool != VK_NULL_HANDLE) {
vkDestroyDescriptorPool(Device::device, descriptorPool, nullptr);
}
sets.resize(setLayouts.size());
VkDescriptorPoolCreateInfo descriptorPoolInfo {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.maxSets = static_cast<std::uint32_t>(sets.size()),
.poolSizeCount = static_cast<std::uint32_t>(poolSizes.size()),
.pPoolSizes = poolSizes.data()
};
Device::CheckVkResult(vkCreateDescriptorPool(Device::device, &descriptorPoolInfo, nullptr, &descriptorPool));
VkDescriptorSetAllocateInfo allocInfo {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
.descriptorPool = descriptorPool,
.descriptorSetCount = static_cast<std::uint32_t>(sets.size()),
.pSetLayouts = setLayouts.data(),
};
Device::CheckVkResult(vkAllocateDescriptorSets(Device::device, &allocInfo, sets.data()));
}
};
}
#endif

View file

@ -1,62 +0,0 @@
/*
Crafter®.Graphics
Copyright (C) 2026 Catcrafts®
catcrafts.net
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License version 3.0 as published by the Free Software Foundation;
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
module;
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
#include "vulkan/vulkan.h"
#endif
export module Crafter.Graphics:DescriptorSetLayoutVulkan;
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
import std;
import :Device;
import :Types;
import Crafter.Event;
export namespace Crafter {
class DescriptorSetLayoutVulkan {
public:
VkDescriptorSetLayout layout;
std::vector<VkDescriptorSetLayoutBinding> descriptors;
DescriptorSetLayoutVulkan(std::vector<VkDescriptorSetLayoutBinding>&& layouts) : descriptors(std::move(layouts)) {
VkDescriptorSetLayoutCreateInfo descriptorLayoutInfoMesh = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.bindingCount = static_cast<std::uint32_t>(descriptors.size()),
.pBindings = descriptors.data()
};
Device::CheckVkResult(vkCreateDescriptorSetLayout(Device::device, &descriptorLayoutInfoMesh, nullptr, &layout));
}
};
template<std::uint32_t DescriptorCount, const std::array<VkDescriptorSetLayoutBinding, DescriptorCount> Descriptors>
class DescriptorSetLayoutVulkanConst {
public:
inline static VkDescriptorSetLayout layout;
constexpr static std::span<const VkDescriptorSetLayoutBinding> descriptors = Descriptors;
static void Init() {
VkDescriptorSetLayoutCreateInfo descriptorLayoutInfoMesh = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.bindingCount = DescriptorCount,
.pBindings = Descriptors.data()
};
Device::CheckVkResult(vkCreateDescriptorSetLayout(Device::device, &descriptorLayoutInfoMesh, nullptr, &layout));
}
};
}
#endif

View file

@ -114,9 +114,19 @@ export namespace Crafter {
inline static PFN_vkCreateRayTracingPipelinesKHR vkCreateRayTracingPipelinesKHR;
inline static PFN_vkGetRayTracingShaderGroupHandlesKHR vkGetRayTracingShaderGroupHandlesKHR;
inline static PFN_vkCmdTraceRaysKHR vkCmdTraceRaysKHR;
inline static PFN_vkCmdBindResourceHeapEXT vkCmdBindResourceHeapEXT;
inline static PFN_vkCmdBindSamplerHeapEXT vkCmdBindSamplerHeapEXT;
inline static PFN_vkWriteResourceDescriptorsEXT vkWriteResourceDescriptorsEXT;
inline static PFN_vkGetPhysicalDeviceDescriptorSizeEXT vkGetPhysicalDeviceDescriptorSizeEXT;
inline static VkPhysicalDeviceMemoryProperties memoryProperties;
inline static VkPhysicalDeviceDescriptorHeapPropertiesEXT descriptorHeapProperties = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_HEAP_PROPERTIES_EXT
};
inline static VkPhysicalDeviceRayTracingPipelinePropertiesKHR rayTracingProperties = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_PROPERTIES_KHR
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_PROPERTIES_KHR,
.pNext = &descriptorHeapProperties
};
static void CheckVkResult(VkResult result);

View file

@ -37,7 +37,7 @@ export namespace Crafter {
std::uint8_t mipLevels;
VkImage image;
VkDeviceMemory imageMemory;
VulkanBuffer<PixelType, true, false, false> buffer;
VulkanBuffer<PixelType, true, false> buffer;
VkImageView imageView;
VkDescriptorImageInfo descriptor;

View file

@ -32,10 +32,10 @@ export namespace Crafter {
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
class Mesh {
public:
VulkanBuffer<char, false, true, false> scratchBuffer;
VulkanBuffer<char, false, true, false> blasBuffer;
VulkanBuffer<Vector<float, 3, 3>, true, true, false> vertexBuffer;
VulkanBuffer<std::uint32_t, true, true, false> indexBuffer;
VulkanBuffer<char, false, true> scratchBuffer;
VulkanBuffer<char, false, true> blasBuffer;
VulkanBuffer<Vector<float, 3, 3>, true, true> vertexBuffer;
VulkanBuffer<std::uint32_t, true, true> indexBuffer;
VkAccelerationStructureGeometryTrianglesDataKHR blasData;
VkAccelerationStructureGeometryKHR blas;
VkAccelerationStructureKHR accelerationStructure;

View file

@ -30,26 +30,16 @@ import :ShaderBindingTableVulkan;
import :Types;
export namespace Crafter {
class PipelineRTVulkan {
public:
struct PipelineRTVulkan {
VkPipeline pipeline;
VkPipelineLayout pipelineLayout;
std::vector<std::uint8_t> shaderHandles;
VulkanBuffer<std::uint8_t, true, true, false> sbtBuffer;
VulkanBuffer<std::uint8_t, true, true> sbtBuffer;
VkStridedDeviceAddressRegionKHR raygenRegion;
VkStridedDeviceAddressRegionKHR missRegion;
VkStridedDeviceAddressRegionKHR hitRegion;
VkStridedDeviceAddressRegionKHR callableRegion;
void Init(VkCommandBuffer cmd, std::span<VkDescriptorSetLayout> setLayouts, std::span<VkRayTracingShaderGroupCreateInfoKHR> raygenGroups, std::span<VkRayTracingShaderGroupCreateInfoKHR> missGroups, std::span<VkRayTracingShaderGroupCreateInfoKHR> hitGroups, ShaderBindingTableVulkan& shaderTable) {
VkPipelineLayoutCreateInfo pipelineLayoutInfo {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.setLayoutCount = static_cast<std::uint32_t>(setLayouts.size()),
.pSetLayouts = setLayouts.data()
};
Device::CheckVkResult(vkCreatePipelineLayout(Device::device, &pipelineLayoutInfo, nullptr, &pipelineLayout));
void Init(VkCommandBuffer cmd, std::span<VkRayTracingShaderGroupCreateInfoKHR> raygenGroups, std::span<VkRayTracingShaderGroupCreateInfoKHR> missGroups, std::span<VkRayTracingShaderGroupCreateInfoKHR> hitGroups, ShaderBindingTableVulkan& shaderTable) {
std::vector<VkRayTracingShaderGroupCreateInfoKHR> groups;
groups.reserve(raygenGroups.size() + missGroups.size() + hitGroups.size());
@ -57,14 +47,21 @@ export namespace Crafter {
groups.insert(groups.end(), missGroups.begin(), missGroups.end());
groups.insert(groups.end(), hitGroups.begin(), hitGroups.end());
VkRayTracingPipelineCreateInfoKHR rtPipelineInfo{
VkPipelineCreateFlags2CreateInfo flags2 = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_CREATE_FLAGS_2_CREATE_INFO,
.flags = VK_PIPELINE_CREATE_2_DESCRIPTOR_HEAP_BIT_EXT
};
VkRayTracingPipelineCreateInfoKHR rtPipelineInfo {
.sType = VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_KHR,
.pNext = &flags2,
.flags = 0,
.stageCount = static_cast<std::uint32_t>(shaderTable.shaderStages.size()),
.pStages = shaderTable.shaderStages.data(),
.groupCount = static_cast<std::uint32_t>(groups.size()),
.pGroups = groups.data(),
.maxPipelineRayRecursionDepth = 1,
.layout = pipelineLayout
.layout = VK_NULL_HANDLE
};
Device::CheckVkResult(Device::vkCreateRayTracingPipelinesKHR(Device::device, {}, {}, 1, &rtPipelineInfo, nullptr, &pipeline));
@ -114,189 +111,6 @@ export namespace Crafter {
callableRegion.size = 0;
}
};
template <std::uint32_t GeneralShader, std::uint32_t ClosestHitShader, std::uint32_t AnyHitShader, std::uint32_t IntersectionShader>
struct ShaderGroup {
static constexpr std::uint32_t generalShader = GeneralShader;
static constexpr std::uint32_t closestHitShader = ClosestHitShader;
static constexpr std::uint32_t anyHitShader = AnyHitShader;
static constexpr std::uint32_t intersectionShader = IntersectionShader;
};
template <typename Shaders, typename ShaderGroups>
class PipelineRTVulkanConst {
public:
inline static VkPipeline pipeline;
inline static VkPipelineLayout pipelineLayout;
inline static std::vector<std::uint8_t> shaderHandles;
inline static VulkanBuffer<std::uint8_t, true, true, false> sbtBuffer;
inline static VkStridedDeviceAddressRegionKHR raygenRegion;
inline static VkStridedDeviceAddressRegionKHR missRegion;
inline static VkStridedDeviceAddressRegionKHR hitRegion;
inline static VkStridedDeviceAddressRegionKHR callableRegion;
static void Init(VkCommandBuffer cmd, const std::span<const VkDescriptorSetLayout> setLayouts) {
VkPipelineLayoutCreateInfo pipelineLayoutInfo {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.setLayoutCount = static_cast<std::uint32_t>(setLayouts.size()),
.pSetLayouts = setLayouts.data()
};
Device::CheckVkResult(vkCreatePipelineLayout(Device::device, &pipelineLayoutInfo, nullptr, &pipelineLayout));
constexpr auto groupIndexSeq = std::make_index_sequence<std::tuple_size_v<ShaderGroups>>{};
constexpr std::array<VkRayTracingShaderGroupCreateInfoKHR, std::tuple_size_v<ShaderGroups>> groups = GetShaderGroups(groupIndexSeq);
VkRayTracingPipelineCreateInfoKHR rtPipelineInfo{
.sType = VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_KHR,
.stageCount = static_cast<std::uint32_t>(ShaderBindingTableVulkanConst<Shaders>::shaderStages.size()),
.pStages = ShaderBindingTableVulkanConst<Shaders>::shaderStages.data(),
.groupCount = static_cast<std::uint32_t>(groups.size()),
.pGroups = groups.data(),
.maxPipelineRayRecursionDepth = 1,
.layout = pipelineLayout
};
Device::CheckVkResult(Device::vkCreateRayTracingPipelinesKHR(Device::device, {}, {}, 1, &rtPipelineInfo, nullptr, &pipeline));
std::size_t dataSize = Device::rayTracingProperties.shaderGroupHandleSize * rtPipelineInfo.groupCount;
shaderHandles.resize(dataSize);
Device::CheckVkResult(Device::vkGetRayTracingShaderGroupHandlesKHR(Device::device, pipeline, 0, rtPipelineInfo.groupCount, dataSize, shaderHandles.data()));
std::uint32_t sbtStride = AlignUp(Device::rayTracingProperties.shaderGroupHandleSize, Device::rayTracingProperties.shaderGroupHandleAlignment);
raygenRegion.stride = sbtStride;
raygenRegion.deviceAddress = 0;
raygenRegion.size = GetGroupCount<VK_SHADER_STAGE_RAYGEN_BIT_KHR>(groupIndexSeq) * sbtStride;
missRegion.stride = sbtStride;
missRegion.deviceAddress = AlignUp(raygenRegion.size, Device::rayTracingProperties.shaderGroupBaseAlignment);
missRegion.size = GetGroupCount<VK_SHADER_STAGE_MISS_BIT_KHR>(groupIndexSeq) * sbtStride;
hitRegion.stride = sbtStride;
hitRegion.deviceAddress = AlignUp(missRegion.deviceAddress + missRegion.size, Device::rayTracingProperties.shaderGroupBaseAlignment);
hitRegion.size = (GetGroupCount<VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR>(groupIndexSeq) * sbtStride) + (GetGroupCount<VK_SHADER_STAGE_ANY_HIT_BIT_KHR>(groupIndexSeq) * sbtStride);
std::size_t bufferSize = hitRegion.deviceAddress + hitRegion.size;
sbtBuffer.Create(VK_BUFFER_USAGE_2_SHADER_BINDING_TABLE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferSize);
AddShaderGroupsToBuffer(sbtStride, groupIndexSeq);
sbtBuffer.FlushDevice(cmd, VK_ACCESS_MEMORY_READ_BIT, VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR);
raygenRegion.deviceAddress += sbtBuffer.address;
missRegion.deviceAddress += sbtBuffer.address;
hitRegion.deviceAddress += sbtBuffer.address;
callableRegion.deviceAddress = 0;
callableRegion.stride = 0;
callableRegion.size = 0;
}
private:
template<std::size_t index>
consteval static void AddShaderGroup(std::array<VkRayTracingShaderGroupCreateInfoKHR, std::tuple_size_v<ShaderGroups>>& groups) {
using groupTemplate = std::tuple_element_t<index, ShaderGroups>;
VkRayTracingShaderGroupTypeKHR groupType;
if constexpr(groupTemplate::generalShader != VK_SHADER_UNUSED_KHR) {
groupType = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR;
} else if constexpr(groupTemplate::closestHitShader != VK_SHADER_UNUSED_KHR || groupTemplate::anyHitShader != VK_SHADER_UNUSED_KHR) {
groupType = VK_RAY_TRACING_SHADER_GROUP_TYPE_TRIANGLES_HIT_GROUP_KHR;
} else {
static_assert(
groupTemplate::generalShader != VK_SHADER_UNUSED_KHR ||
groupTemplate::closestHitShader != VK_SHADER_UNUSED_KHR,
"Shader group must define either a general or closest-hit shader"
);
}
groups[index] = {
.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR,
.type = groupType,
.generalShader = groupTemplate::generalShader,
.closestHitShader = groupTemplate::closestHitShader,
.anyHitShader = groupTemplate::anyHitShader,
.intersectionShader = groupTemplate::intersectionShader
};
}
template<std::size_t... Is>
consteval static std::array<VkRayTracingShaderGroupCreateInfoKHR, std::tuple_size_v<ShaderGroups>> GetShaderGroups(std::index_sequence<Is...>) {
std::array<VkRayTracingShaderGroupCreateInfoKHR, std::tuple_size_v<ShaderGroups>> groups{};
(AddShaderGroup<Is>(groups), ...);
return groups;
}
template<std::size_t index, VkShaderStageFlagBits stage>
consteval static void GetGroupCountImpl(std::uint32_t& count) {
using groupTemplate = std::tuple_element_t<index, ShaderGroups>;
if constexpr(groupTemplate::generalShader != VK_SHADER_UNUSED_KHR) {
using shaderTemplate = std::tuple_element_t<groupTemplate::generalShader, Shaders>;
if constexpr(shaderTemplate::_stage == stage) {
count++;
}
} else if constexpr(groupTemplate::closestHitShader != VK_SHADER_UNUSED_KHR) {
using shaderTemplate = std::tuple_element_t<groupTemplate::closestHitShader, Shaders>;
if constexpr(shaderTemplate::_stage == stage) {
count++;
}
} else if constexpr(groupTemplate::anyHitShader != VK_SHADER_UNUSED_KHR) {
using shaderTemplate = std::tuple_element_t<groupTemplate::anyHitShader, Shaders>;
if constexpr(shaderTemplate::_stage == stage) {
count++;
}
} else {
static_assert(
groupTemplate::generalShader != VK_SHADER_UNUSED_KHR ||
groupTemplate::closestHitShader != VK_SHADER_UNUSED_KHR,
"Shader group must define either a general or closest-hit shader"
);
}
}
template<VkShaderStageFlagBits stage, std::size_t... Is>
consteval static std::uint32_t GetGroupCount(std::index_sequence<Is...>) {
std::uint32_t count = 0;
(GetGroupCountImpl<Is, stage>(count), ...);
return count;
}
template<std::size_t index, VkShaderStageFlagBits stage>
static void AddShaderGroupToBuffer(std::uint32_t sbtStride, std::uint32_t& offset) {
using groupTemplate = std::tuple_element_t<index, ShaderGroups>;
if constexpr(groupTemplate::generalShader != VK_SHADER_UNUSED_KHR) {
using shaderTemplate = std::tuple_element_t<groupTemplate::generalShader, Shaders>;
if constexpr(shaderTemplate::_stage == stage) {
std::memcpy(sbtBuffer.value + offset, shaderHandles.data() + index * Device::rayTracingProperties.shaderGroupHandleSize, Device::rayTracingProperties.shaderGroupHandleSize);
offset += sbtStride;
}
} else if constexpr(groupTemplate::closestHitShader != VK_SHADER_UNUSED_KHR) {
using shaderTemplate = std::tuple_element_t<groupTemplate::closestHitShader, Shaders>;
if constexpr(shaderTemplate::_stage == stage) {
std::memcpy(sbtBuffer.value + offset, shaderHandles.data() + index * Device::rayTracingProperties.shaderGroupHandleSize, Device::rayTracingProperties.shaderGroupHandleSize);
offset += sbtStride;
}
} else if constexpr(groupTemplate::anyHitShader != VK_SHADER_UNUSED_KHR) {
using shaderTemplate = std::tuple_element_t<groupTemplate::anyHitShader, Shaders>;
if constexpr(shaderTemplate::_stage == stage) {
std::memcpy(sbtBuffer.value + offset, shaderHandles.data() + index * Device::rayTracingProperties.shaderGroupHandleSize, Device::rayTracingProperties.shaderGroupHandleSize);
offset += sbtStride;
}
} else {
static_assert(
groupTemplate::generalShader != VK_SHADER_UNUSED_KHR ||
groupTemplate::closestHitShader != VK_SHADER_UNUSED_KHR,
"Shader group must define either a general or closest-hit shader"
);
}
}
template<std::size_t... Is>
static void AddShaderGroupsToBuffer(std::uint32_t sbtStride, std::index_sequence<Is...>) {
std::uint32_t offset = 0;
(AddShaderGroupToBuffer<Is, VK_SHADER_STAGE_RAYGEN_BIT_KHR>(sbtStride, offset), ...);
offset = AlignUp(offset, Device::rayTracingProperties.shaderGroupBaseAlignment);
(AddShaderGroupToBuffer<Is, VK_SHADER_STAGE_MISS_BIT_KHR>(sbtStride, offset), ...);
offset = AlignUp(offset, Device::rayTracingProperties.shaderGroupBaseAlignment);
(AddShaderGroupToBuffer<Is, VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR>(sbtStride, offset), ...);
(AddShaderGroupToBuffer<Is, VK_SHADER_STAGE_ANY_HIT_BIT_KHR>(sbtStride, offset), ...);
}
};
}
#endif

View file

@ -383,29 +383,7 @@ export namespace Crafter {
}
break;
}
case OpaqueType::SemiOpaque: {
for (int j = 0; j < h; j++) {
for (int i = 0; i < w; i++) {
int bufferX = x + i + c_x1 + offsetX;
int bufferY = currentY + j + c_y1 + offsetY;
// Only draw pixels that are within our scaled buffer bounds
if constexpr(Scaling) {
if (bufferX >= 0 && bufferX < ScalingBase<T, true, Owning, Alignment>::bufferWidth && bufferY >= 0 && bufferY < ScalingBase<T, true, Owning, Alignment>::bufferHeight) {
ScalingBase<T, true, Owning, Alignment>::scalingBuffer[bufferY * ScalingBase<T, true, Owning, Alignment>::bufferWidth + bufferX] = {color.r, color.g, color.b, static_cast<T>(bitmap[j * w + i])};
}
} else {
if (bufferX >= 0 && bufferX < (int)this->scaled.size.x && bufferY >= 0 && bufferY < (int)this->scaled.size.y) {
std::uint8_t alpha = bitmap[j * w + i];
if(alpha != 0) {
this->buffer[bufferY * this->scaled.size.x + bufferX] = {color.r, color.g, color.b, static_cast<T>(bitmap[j * w + i])};
}
}
}
}
}
break;
}
case OpaqueType::SemiOpaque:
case OpaqueType::Transparent: {
for (int j = 0; j < h; j++) {
for (int i = 0; i < w; i++) {

View file

@ -0,0 +1,75 @@
/*
Crafter®.Graphics
Copyright (C) 2026 Catcrafts®
catcrafts.net
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License version 3.0 as published by the Free Software Foundation;
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
module;
#include "../lib/stb_truetype.h"
export module Crafter.Graphics:RenderingElement2DVulkan;
import Crafter.Asset;
import std;
import :Transform2D;
import :Types;
import :Window;
export namespace Crafter {
struct __attribute__((packed)) RenderingElement2DVulkanTransformInfo {
ScaleData2D scaled; // 0 - 8 bytes
std::uint16_t bufferX; // 8 - 2 bytes
std::uint16_t bufferY; // 10 - 2 bytes
//12 bytes total;
}
template<bool Owning, bool Mapped>
struct RenderingElement2DVulkan {
std::uint16_t index;
std:array<VulkanBuffer<_Float16, Mapped, false>*, Window::NumFrames> buffers;
RenderingElement2DVulkan(Anchor2D anchor, std::uint16_t bufferWidth, std::uint16_t bufferHeight) requires(Owning) : index(renderingElement2DVulkans.size()-1) {
renderingElement2DVulkans.push_back(this);
}
RenderingElement2DVulkan(Anchor2D anchor, std::uint16_t bufferWidth, std::uint16_t bufferHeight, std:array<VulkanBuffer<_Float16, Mapped, false>*, Window::NumFrames> buffers) requires(!Owning) : buffers(buffers) {
renderingElement2DVulkans.push_back(this);
}
~RenderingElement2DVulkan() {
if constexpr(Owning) {
for(VulkanBuffer<_Float16, Mapped, false>* buffer : buffers) {
delete buffer;
}
}
}
RenderingElement2DVulkan(RenderingElement2DVulkan&) = delete;
RenderingElement2DVulkan& operator=(RenderingElement2DVulkan&) = delete;
void UpdatePosition(RendertargetBase<Frames>& window, Transform2D& parent) override {
ScaleData2D oldScale = this->scaled;
this->ScaleElement(parent);
if(oldScale.size.x != this->scaled.size.x || oldScale.size.y != this->scaled.size.y) {
this->buffer.resize(this->scaled.size.x * this->scaled.size.y);
}
for(Transform2D* child : this->children) {
child->UpdatePosition(window, *this);
}
}
};
inline static std::vector<RenderingElement2DVulkan*> renderingElement2DVulkans;
inline static std::vector<VkDescriptorBufferInfo> renderingElement2DVulkanDescriptors[Window::NumFrames];
inline static VulkanBuffer<RenderingElement2DVulkanTransformInfo, Mapped, false>* renderingElement2DVulkanTransformBuffer[Window::NumFrames];
}

View file

@ -27,20 +27,22 @@ import std;
import :Mesh;
import :VulkanBuffer;
import Crafter.Math;
import :Window;
export namespace Crafter {
struct TlasWithBuffer {
VulkanBuffer<char, false, true, false> buffer;
VkDeviceAddress address;
VulkanBuffer<char, false, true> buffer;
VkAccelerationStructureKHR accelerationStructure;
VulkanBuffer<VkAccelerationStructureInstanceKHR, true, true, false> instanceBuffer;
VulkanBuffer<VkAccelerationStructureInstanceKHR, true, true> instanceBuffer;
};
class RenderingElement3D {
public:
VkAccelerationStructureInstanceKHR instance;
static std::vector<RenderingElement3D*> elements;
inline static VulkanBuffer<char, false, true, false> scratchBuffer;
inline static std::vector<TlasWithBuffer> tlases;
inline static VulkanBuffer<char, false, true> scratchBuffer;
inline static TlasWithBuffer tlases[Window::numFrames];
static void BuildTLAS(VkCommandBuffer cmd, std::uint32_t index);
};
}

View file

@ -36,33 +36,10 @@ export namespace Crafter {
void Init(std::span<VulkanShader> shaders) {
shaderStages.reserve(shaders.size());
for(const VulkanShader& shader: shaders) {
shaderStages.emplace_back(VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, nullptr, 0, shader.stage, shader.shader, shader.entrypoint.c_str(), nullptr);
shaderStages.emplace_back(VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, nullptr, 0, shader.stage, shader.shader, shader.entrypoint.c_str(), shader.specilizationInfo);
}
}
};
template <typename Shaders>
class ShaderBindingTableVulkanConst {
public:
inline static std::array<VkPipelineShaderStageCreateInfo, std::tuple_size_v<Shaders>> shaderStages;
static void Init() {
AddAllToSBT(std::make_index_sequence<std::tuple_size_v<Shaders>>{});
}
private:
template<std::size_t index>
static void AddToSBT() {
shaderStages[index] = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = std::tuple_element_t<index, Shaders>::_stage,
.module = std::tuple_element_t<index, Shaders>::shader,
.pName = std::tuple_element_t<index, Shaders>::_entrypoint.value
};
}
template<std::size_t... Is>
static void AddAllToSBT(std::index_sequence<Is...>) {
(AddToSBT<Is>(), ...);
}
};
}
#endif

View file

@ -28,21 +28,14 @@ import :Device;
import :Types;
export namespace Crafter {
template<size_t N>
struct StringLiteral {
constexpr StringLiteral(const char (&str)[N]) {
std::copy_n(str, N, value);
}
char value[N];
};
class VulkanShader {
public:
std::vector<VkSpecializationMapEntry> specilizations;
VkSpecializationInfo* specilizationInfo;
VkShaderStageFlagBits stage;
std::string entrypoint;
VkShaderModule shader;
VulkanShader(const std::filesystem::path& path, std::string entrypoint, VkShaderStageFlagBits stage) : stage(stage), entrypoint(entrypoint) {
VulkanShader(const std::filesystem::path& path, std::string entrypoint, VkShaderStageFlagBits stage, VkSpecializationInfo* specilizationInfo) : stage(stage), entrypoint(entrypoint), specilizationInfo(specilizationInfo) {
std::ifstream file(path, std::ios::binary);
if (!file) {
std::cerr << "Error: Could not open file " << path << std::endl;
@ -69,44 +62,6 @@ export namespace Crafter {
Device::CheckVkResult(vkCreateShaderModule(Device::device, &module_info, nullptr, &shader));
}
};
template <
StringLiteral path,
StringLiteral entrypoint,
VkShaderStageFlagBits stage
>
class VulkanShaderConst {
public:
constexpr static VkShaderStageFlagBits _stage = stage;
constexpr static StringLiteral _entrypoint = entrypoint;
inline static VkShaderModule shader;
static void CreateShader() {
std::ifstream file(path.value, std::ios::binary);
if (!file) {
std::cerr << "Error: Could not open file " << path.value << std::endl;
}
// Move to the end of the file to determine its size
file.seekg(0, std::ios::end);
std::streamsize size = file.tellg();
file.seekg(0, std::ios::beg);
std::vector<std::uint32_t> spirv(size / sizeof(std::uint32_t));
// Read the data into the vector
if (!file.read(reinterpret_cast<char*>(spirv.data()), size)) {
std::cerr << "Error: Could not read data from file" << std::endl;
}
file.close();
VkShaderModuleCreateInfo module_info{VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO};
module_info.codeSize = spirv.size() * sizeof(uint32_t);
module_info.pCode = spirv.data();
Device::CheckVkResult(vkCreateShaderModule(Device::device, &module_info, nullptr, &shader));
}
};
}
#endif

View file

@ -33,8 +33,8 @@ export namespace Crafter {
};
struct ScaleData2D {
Vector<std::uint16_t, 2> position;
Vector<std::uint16_t, 2> size;
Vector<std::uint16_t, 2, 2> position;
Vector<std::uint16_t, 2, 2> size;
};
struct ClipRect {

View file

@ -64,32 +64,15 @@ namespace Crafter {
>;
export template <typename T, bool Mapped, bool Adressable, bool Staged> requires ((Mapped && !Staged) || (!Mapped && Staged) || (!Mapped && !Staged))
export template <typename T, bool Mapped, bool Adressable>
class VulkanBuffer;
export template <typename T>
class VulkanBufferStaged {
VulkanBuffer<T, true, false, false>* stagingBuffer;
};
export class VulkanBufferStagedEmpty {};
template<typename T, bool Staged>
using VulkanBufferStagedConditional =
std::conditional_t<
Staged,
VulkanBufferStaged<T>,
VulkanBufferStagedEmpty
>;
export template <typename T, bool Mapped, bool Adressable, bool Staged> requires ((Mapped && !Staged) || (!Mapped && Staged) || (!Mapped && !Staged))
class VulkanBuffer : public VulkanBufferBase, public VulkanBufferMappedConditional<T, Mapped>, public VulkanBufferAdressableConditional<Adressable>, public VulkanBufferStagedConditional<T, Staged> {
export template <typename T, bool Mapped, bool Adressable>
class VulkanBuffer : public VulkanBufferBase, public VulkanBufferMappedConditional<T, Mapped>, public VulkanBufferAdressableConditional<Adressable> {
public:
VulkanBuffer() = default;
void Create(VkBufferUsageFlags usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, std::uint32_t count) {
if constexpr(Staged) {
new (&VulkanBufferMappedConditional<T, true>::stagingBuffer) VulkanBuffer<T, true, false, false>();
VulkanBufferMappedConditional<T, true>::stagingBuffer->Create(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, count);
}
VkBufferCreateInfo bufferCreateInfo {};
bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
@ -117,7 +100,7 @@ namespace Crafter {
descriptor.offset = 0;
descriptor.buffer = buffer;
descriptor.range = VK_WHOLE_SIZE;
descriptor.range = sizeof(T)*count;
Device::CheckVkResult(vkBindBufferMemory(Device::device, buffer, memory, 0));
@ -141,9 +124,6 @@ namespace Crafter {
vkDestroyBuffer(Device::device, buffer, nullptr);
vkFreeMemory(Device::device, memory, nullptr);
buffer = VK_NULL_HANDLE;
if constexpr(Staged) {
delete VulkanBufferMappedConditional<T, true>::stagingBuffer;
}
}
void Resize(VkBufferUsageFlags usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, std::uint32_t count) {
@ -237,22 +217,6 @@ namespace Crafter {
};
vkInvalidateMappedMemoryRanges(Device::device, 1, &range);
}
void FlushDevice(VkCommandBuffer cmd) requires(Staged) {
VulkanBufferStagedConditional<T, true>::stagingBuffer.FlushDevice(VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
VulkanBufferStagedConditional<T, true>::stagingBuffer.Copy(cmd, this);
}
void FlushDevice(VkCommandBuffer cmd, VkAccessFlags dstAccessMask, VkPipelineStageFlags dstStageMask) requires(Staged) {
VulkanBufferStagedConditional<T, true>::stagingBuffer.FlushDevice(VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
VulkanBufferStagedConditional<T, true>::stagingBuffer.Copy(cmd, this, VK_ACCESS_TRANSFER_WRITE_BIT, dstAccessMask, VK_PIPELINE_STAGE_TRANSFER_BIT, dstStageMask);
}
void FlushHost(VkCommandBuffer cmd) requires(Staged) {
Copy(cmd, VulkanBufferStagedConditional<T, true>::stagingBuffer);
VulkanBufferStagedConditional<T, true>::stagingBuffer.FlushHost();
}
VulkanBuffer(VulkanBuffer&& other) {
descriptor = other.descriptor;
@ -265,9 +229,6 @@ namespace Crafter {
if constexpr(Mapped) {
VulkanBufferMappedConditional<T, true>::value = other.VulkanBufferMappedConditional<T, true>::value;
}
if constexpr(Staged) {
VulkanBufferStagedConditional<T, true>::stagingBuffer = other.VulkanBufferStagedConditional<T, true>::stagingBuffer;
}
};
~VulkanBuffer() {

View file

@ -51,9 +51,6 @@ import :Types;
import :Rendertarget;
import :Transform2D;
import Crafter.Event;
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
import :PipelineRTVulkan;
#endif
export namespace Crafter {
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
@ -63,6 +60,8 @@ export namespace Crafter {
// Command buffer submission and execution
VkSemaphore renderComplete;
};
struct PipelineRTVulkan;
struct DescriptorHeapVulkan;
#endif
struct MouseElement;
@ -178,17 +177,6 @@ export namespace Crafter {
#endif
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
std::vector<VkDescriptorSet> descriptorsRt;
template <typename Pipeline>
void SetPipelineRT() {
rtPipeline = Pipeline::pipeline;
rtPipelineLayout = Pipeline::pipelineLayout;
raygenRegion = Pipeline::raygenRegion;
missRegion = Pipeline::missRegion;
hitRegion = Pipeline::hitRegion;
callableRegion = Pipeline::callableRegion;
}
void SetPipelineRT(PipelineRTVulkan& pipeline);
VkCommandBuffer StartInit();
void FinishInit();
VkCommandBuffer GetCmd();
@ -200,19 +188,15 @@ export namespace Crafter {
VkFormat colorFormat;
VkColorSpaceKHR colorSpace;
VkImage images[numFrames];
VkImageView imageViews[numFrames];
VkImageViewCreateInfo imageViews[numFrames];
std::thread thread;
VkCommandBuffer drawCmdBuffers[numFrames];
VkSubmitInfo submitInfo;
Semaphores semaphores;
std::uint32_t currentBuffer = 0;
VkPipelineStageFlags submitPipelineStages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
VkPipeline rtPipeline;
VkPipelineLayout rtPipelineLayout;
VkStridedDeviceAddressRegionKHR raygenRegion;
VkStridedDeviceAddressRegionKHR missRegion;
VkStridedDeviceAddressRegionKHR hitRegion;
VkStridedDeviceAddressRegionKHR callableRegion;
PipelineRTVulkan* pipeline;
DescriptorHeapVulkan* descriptorHeap;
#endif
};
}

View file

@ -38,14 +38,13 @@ export import :ForwardDeclarations;
export import :Device;
export import :VulkanTransition;
export import :VulkanBuffer;
export import :DescriptorPoolVulkan;
export import :ShaderVulkan;
export import :ShaderBindingTableVulkan;
export import :PipelineRTVulkan;
export import :RenderingElement3D;
export import :ImageVulkan;
export import :SamplerVulkan;
export import :DescriptorSetLayoutVulkan;
export import :DescriptorHeapVulkan;
#endif
// export import :WindowWaylandVulkan;