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

Binary file not shown.

Binary file not shown.

View file

@ -1,4 +1,5 @@
#include "vulkan/vulkan.h" #include "vulkan/vulkan.h"
#include <cassert>
import Crafter.Graphics; import Crafter.Graphics;
using namespace Crafter; using namespace Crafter;
@ -6,55 +7,69 @@ import std;
import Crafter.Event; import Crafter.Event;
import Crafter.Math; import Crafter.Math;
typedef VulkanShaderConst<"raygen.spv", "main", VK_SHADER_STAGE_RAYGEN_BIT_KHR> Raygenspv;
typedef VulkanShaderConst<"closesthit.spv", "main", VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR> Closesthitspv;
typedef VulkanShaderConst<"miss.spv", "main", VK_SHADER_STAGE_MISS_BIT_KHR> Misspv;
typedef std::tuple<Raygenspv, Misspv, Closesthitspv> AllShaders;
typedef std::tuple<
ShaderGroup<0, VK_SHADER_UNUSED_KHR, VK_SHADER_UNUSED_KHR, VK_SHADER_UNUSED_KHR>,
ShaderGroup<1, VK_SHADER_UNUSED_KHR, VK_SHADER_UNUSED_KHR, VK_SHADER_UNUSED_KHR>,
ShaderGroup<VK_SHADER_UNUSED_KHR, 2, VK_SHADER_UNUSED_KHR, VK_SHADER_UNUSED_KHR>
> ShaderGroups;
typedef PipelineRTVulkanConst<AllShaders, ShaderGroups> Pipeline;
typedef DescriptorSetLayoutVulkanConst<3, {{
{
.binding = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_RAYGEN_BIT_KHR,
},
{
.binding = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_RAYGEN_BIT_KHR,
},
{
.binding = 2,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_RAYGEN_BIT_KHR,
}
}}> descriptorSetLayout;
int main() { int main() {
Device::Initialize(); Device::Initialize();
Window window(1280, 720, "HelloVulkan"); Window window(1280, 720, "HelloVulkan");
VkCommandBuffer cmd = window.StartInit(); VkCommandBuffer cmd = window.StartInit();
DescriptorHeapVulkan descriptorHeap;
descriptorHeap.Initialize(1,1,0);
Raygenspv::CreateShader(); VkSpecializationMapEntry entry = {
Closesthitspv::CreateShader(); .constantID = 0,
Misspv::CreateShader(); .offset = 0,
ShaderBindingTableVulkanConst<AllShaders>::Init(); .size = sizeof(uint16_t)
};
descriptorSetLayout::Init(); VkSpecializationInfo specilizationInfo = {
std::array<VkDescriptorSetLayout, 1> layouts {{descriptorSetLayout::layout}}; .mapEntryCount = 1,
.pMapEntries = &entry,
.dataSize = sizeof(uint16_t),
.pData = &descriptorHeap.bufferStartElement
};
DescriptorPool pool; std::array<VulkanShader, 3> shaders{{
pool.sets.resize(1); {"raygen.spv", "main", VK_SHADER_STAGE_RAYGEN_BIT_KHR, &specilizationInfo},
pool.BuildPool(DescriptorPool::GetPoolSizes<descriptorSetLayout>(), layouts); {"miss.spv", "main", VK_SHADER_STAGE_MISS_BIT_KHR, nullptr},
{"closesthit.spv", "main", VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR, nullptr}
}};
Pipeline::Init(cmd, layouts); ShaderBindingTableVulkan shaderTable;
shaderTable.Init(shaders);
std::array<VkRayTracingShaderGroupCreateInfoKHR, 1> raygenGroups {{
{
.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR,
.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR,
.generalShader = 0,
.closestHitShader = VK_SHADER_UNUSED_KHR,
.anyHitShader = VK_SHADER_UNUSED_KHR,
.intersectionShader = VK_SHADER_UNUSED_KHR,
},
}};
std::array<VkRayTracingShaderGroupCreateInfoKHR, 1> missGroups {{
{
.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR,
.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR,
.generalShader = 1,
.closestHitShader = VK_SHADER_UNUSED_KHR,
.anyHitShader = VK_SHADER_UNUSED_KHR,
.intersectionShader = VK_SHADER_UNUSED_KHR,
},
}};
std::array<VkRayTracingShaderGroupCreateInfoKHR, 1> hitGroups {{
{
.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR,
.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_TRIANGLES_HIT_GROUP_KHR,
.generalShader = VK_SHADER_UNUSED_KHR,
.closestHitShader = 2,
.anyHitShader = VK_SHADER_UNUSED_KHR,
.intersectionShader = VK_SHADER_UNUSED_KHR,
},
}};
PipelineRTVulkan pipeline;
pipeline.Init(cmd, raygenGroups, missGroups, hitGroups, shaderTable);
Mesh triangleMesh; Mesh triangleMesh;
std::array<Vector<float, 3, 3>, 3> verts {{{-150, -150, 100}, {0, 150, 100}, {150, -150, 100}}}; std::array<Vector<float, 3, 3>, 3> verts {{{-150, -150, 100}, {0, 150, 100}, {150, -150, 100}}};
@ -75,59 +90,111 @@ int main() {
MatrixRowMajor<float, 4, 3, 1> transform = MatrixRowMajor<float, 4, 3, 1>::Identity(); MatrixRowMajor<float, 4, 3, 1> transform = MatrixRowMajor<float, 4, 3, 1>::Identity();
std::memcpy(renderer.instance.transform.matrix, transform.m, sizeof(transform.m)); std::memcpy(renderer.instance.transform.matrix, transform.m, sizeof(transform.m));
RenderingElement3D::tlases.resize(1);
RenderingElement3D::BuildTLAS(cmd, 0); RenderingElement3D::BuildTLAS(cmd, 0);
RenderingElement3D::BuildTLAS(cmd, 1);
VkDescriptorImageInfo imageInfo = { RenderingElement3D::BuildTLAS(cmd, 2);
.imageView = window.imageViews[0],
.imageLayout = VK_IMAGE_LAYOUT_GENERAL
};
VkWriteDescriptorSetAccelerationStructureKHR writeDescriptorSetAccelerationStructure {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_KHR,
.accelerationStructureCount = 1,
.pAccelerationStructures = &RenderingElement3D::tlases[0].accelerationStructure
};
VulkanBuffer<std::uint32_t, true, false, false> lightBuffer;
lightBuffer.Create(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, 1);
VkWriteDescriptorSet write[3] = {
{
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.pNext = &writeDescriptorSetAccelerationStructure,
.dstSet = pool.sets[0],
.dstBinding = 0,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR,
},
{
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = pool.sets[0],
.dstBinding = 1,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
.pImageInfo = &imageInfo
},
{
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = pool.sets[0],
.dstBinding = 2,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.pBufferInfo = &lightBuffer.descriptor
}
};
vkUpdateDescriptorSets(Device::device, 3, write, 0, nullptr);
window.SetPipelineRT<Pipeline>();
window.descriptorsRt = pool.sets;
window.FinishInit(); window.FinishInit();
VkDeviceAddressRangeKHR tlasRange0 = {
.address = RenderingElement3D::tlases[0].address,
};
VkDeviceAddressRangeKHR tlasRange1 = {
.address = RenderingElement3D::tlases[1].address,
};
VkDeviceAddressRangeKHR tlasRange2 = {
.address = RenderingElement3D::tlases[2].address,
};
VkImageDescriptorInfoEXT imageInfo0 = {
.sType = VK_STRUCTURE_TYPE_IMAGE_DESCRIPTOR_INFO_EXT,
.pView = &window.imageViews[0],
.layout = VK_IMAGE_LAYOUT_GENERAL
};
VkImageDescriptorInfoEXT imageInfo1 = {
.sType = VK_STRUCTURE_TYPE_IMAGE_DESCRIPTOR_INFO_EXT,
.pView = &window.imageViews[1],
.layout = VK_IMAGE_LAYOUT_GENERAL
};
VkImageDescriptorInfoEXT imageInfo2 = {
.sType = VK_STRUCTURE_TYPE_IMAGE_DESCRIPTOR_INFO_EXT,
.pView = &window.imageViews[2],
.layout = VK_IMAGE_LAYOUT_GENERAL
};
VkResourceDescriptorInfoEXT resources[6] = {
{
.sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT,
.type = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR,
.data = { .pAddressRange = &tlasRange0}
},
{
.sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT,
.type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
.data = { .pImage = &imageInfo0 }
},
{
.sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT,
.type = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR,
.data = { .pAddressRange = &tlasRange1}
},
{
.sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT,
.type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
.data = { .pImage = &imageInfo1 }
},
{
.sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT,
.type = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR,
.data = { .pAddressRange = &tlasRange2}
},
{
.sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT,
.type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
.data = { .pImage = &imageInfo2 }
},
};
VkHostAddressRangeEXT destinations[6] = {
{
.address = descriptorHeap.resourceHeap[0].value + descriptorHeap.bufferStartOffset,
.size = Device::descriptorHeapProperties.bufferDescriptorSize
},
{
.address = descriptorHeap.resourceHeap[0].value,
.size = Device::descriptorHeapProperties.imageDescriptorSize
},
{
.address = descriptorHeap.resourceHeap[1].value + descriptorHeap.bufferStartOffset,
.size = Device::descriptorHeapProperties.bufferDescriptorSize
},
{
.address = descriptorHeap.resourceHeap[1].value,
.size = Device::descriptorHeapProperties.imageDescriptorSize
},
{
.address = descriptorHeap.resourceHeap[2].value + descriptorHeap.bufferStartOffset,
.size = Device::descriptorHeapProperties.bufferDescriptorSize
},
{
.address = descriptorHeap.resourceHeap[2].value,
.size = Device::descriptorHeapProperties.imageDescriptorSize
},
};
Device::vkWriteResourceDescriptorsEXT(Device::device, 6, resources, destinations);
descriptorHeap.resourceHeap[0].FlushDevice();
descriptorHeap.resourceHeap[1].FlushDevice();
descriptorHeap.resourceHeap[2].FlushDevice();
window.pipeline = &pipeline;
window.descriptorHeap = &descriptorHeap;
window.Render(); window.Render();
window.StartSync(); window.StartSync();
} }

View file

@ -10,8 +10,10 @@
"configuration":"lib-win32-vulkan-debug" "configuration":"lib-win32-vulkan-debug"
} }
], ],
"march": "x86-64-v3",
"mtune": "generic",
"target": "x86_64-w64-mingw32", "target": "x86_64-w64-mingw32",
"debug": true, "debug": false,
"shaders": [ "shaders": [
{ {
"path":"raygen.glsl", "path":"raygen.glsl",

View file

@ -1,14 +1,17 @@
#version 460 #version 460
#extension GL_EXT_ray_tracing : enable #extension GL_EXT_ray_tracing : enable
#extension GL_EXT_shader_image_load_formatted : enable #extension GL_EXT_shader_image_load_formatted : enable
#extension GL_EXT_shader_explicit_arithmetic_types_int16 : enable
#extension GL_EXT_descriptor_heap : enable
#extension GL_EXT_nonuniform_qualifier : enable
layout(binding = 0, set = 0) uniform accelerationStructureEXT topLevelAS; layout(constant_id = 0) const uint16_t bufferStart = 0us;
layout(binding = 1, set = 0, rgba8) uniform writeonly image2D image; layout(descriptor_heap) uniform accelerationStructureEXT topLevelAS[];
layout(descriptor_heap) uniform writeonly image2D image[];
layout(location = 0) rayPayloadEXT vec3 hitValue; layout(location = 0) rayPayloadEXT vec3 hitValue;
void main() void main() {
{
// Pixel coordinates // Pixel coordinates
uvec2 pixel = gl_LaunchIDEXT.xy; uvec2 pixel = gl_LaunchIDEXT.xy;
uvec2 resolution = gl_LaunchSizeEXT.xy; uvec2 resolution = gl_LaunchSizeEXT.xy;
@ -32,7 +35,7 @@ void main()
)); ));
traceRayEXT( traceRayEXT(
topLevelAS, topLevelAS[bufferStart],
gl_RayFlagsNoneEXT, gl_RayFlagsNoneEXT,
0xff, 0xff,
0, 0, 0, 0, 0, 0,
@ -43,5 +46,5 @@ void main()
0 0
); );
imageStore(image, ivec2(pixel), vec4(hitValue, 1.0)); imageStore(image[0], ivec2(pixel), vec4(hitValue, 1));
} }

View file

@ -1,25 +0,0 @@
# HelloWindow Example
## Description
This example demonstrates how to load shaders and render a triangle.
## Expected Result
A blue tinted vulkan window with a white triangle in the center.
## Highlighted Code Snippet
```cpp
EventListener<VkCommandBuffer> listener(&window.onDraw, [&descriptors, &meshShader](VkCommandBuffer cmd){
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, Pipeline::pipelineLayout, 0, 2, &descriptors.set[0], 0, NULL);
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, Pipeline::pipeline);
Device::vkCmdDrawMeshTasksEXTProc(cmd, meshShader.threadCount, 1, 1);
});
```
## How to Run
```bash
crafter-build build executable -r
```

View file

@ -1,12 +0,0 @@
#version 460
#extension GL_EXT_ray_tracing : enable
#extension GL_EXT_nonuniform_qualifier : enable
layout(location = 0) rayPayloadInEXT vec3 hitValue;
hitAttributeEXT vec2 attribs;
void main()
{
const vec3 barycentricCoords = vec3(1.0f - attribs.x - attribs.y, attribs.x, attribs.y);
hitValue = barycentricCoords;
}

View file

@ -1,138 +0,0 @@
#include "vulkan/vulkan.h"
import Crafter.Graphics;
using namespace Crafter;
import std;
import Crafter.Event;
import Crafter.Math;
int main() {
Device::CreateDevice();
WindowVulkan window(1280, 720, "HelloVulkan");
VkCommandBuffer cmd = window.StartInit();
std::vector<VkDescriptorSetLayoutBinding> bindings {
{
.binding = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_RAYGEN_BIT_KHR,
},
{
.binding = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_RAYGEN_BIT_KHR,
}
};
DescriptorSetLayoutVulkan layout(std::move(bindings));
std::array<VkDescriptorSetLayout, 1> layouts {{layout.layout}};
DescriptorPool pool;
pool.sets.resize(1);
pool.BuildPool(DescriptorPool::GetPoolSizes({layout}), layouts);
std::array<VulkanShader, 3> shaders{{
{"raygen.spv", "main", VK_SHADER_STAGE_RAYGEN_BIT_KHR},
{"miss.spv", "main", VK_SHADER_STAGE_MISS_BIT_KHR},
{"closesthit.spv", "main", VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR}
}};
ShaderBindingTableVulkan shaderTable;
shaderTable.Init(shaders);
std::array<VkRayTracingShaderGroupCreateInfoKHR, 1> raygenGroups {{
{
.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR,
.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR,
.generalShader = 0,
.closestHitShader = VK_SHADER_UNUSED_KHR,
.anyHitShader = VK_SHADER_UNUSED_KHR,
.intersectionShader = VK_SHADER_UNUSED_KHR,
},
}};
std::array<VkRayTracingShaderGroupCreateInfoKHR, 1> missGroups {{
{
.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR,
.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR,
.generalShader = 1,
.closestHitShader = VK_SHADER_UNUSED_KHR,
.anyHitShader = VK_SHADER_UNUSED_KHR,
.intersectionShader = VK_SHADER_UNUSED_KHR,
},
}};
std::array<VkRayTracingShaderGroupCreateInfoKHR, 1> hitGroups {{
{
.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR,
.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_TRIANGLES_HIT_GROUP_KHR,
.generalShader = VK_SHADER_UNUSED_KHR,
.closestHitShader = 2,
.anyHitShader = VK_SHADER_UNUSED_KHR,
.intersectionShader = VK_SHADER_UNUSED_KHR,
},
}};
PipelineRTVulkan pipeline;
pipeline.Init(cmd, layouts, raygenGroups, missGroups, hitGroups, shaderTable);
Mesh triangleMesh;
std::array<Vector<float, 3, 3>, 3> verts {{{-150, -150, 100}, {0, 150, 100}, {150, -150, 100}}};
std::array<std::uint32_t, 3> index {{2,1,0}};
triangleMesh.Build(verts, index, cmd);
RenderingElement3D renderer = {
.instance = {
.instanceCustomIndex = 0,
.mask = 0xFF,
.instanceShaderBindingTableRecordOffset = 0,
.flags = VK_GEOMETRY_INSTANCE_FORCE_OPAQUE_BIT_KHR,
.accelerationStructureReference = triangleMesh.blasAddr
}
};
MatrixRowMajor<float, 4, 3, 1> transform = MatrixRowMajor<float, 4, 3, 1>::Identity();
std::memcpy(renderer.instance.transform.matrix, transform.m, sizeof(transform.m));
RenderingElement3D::tlases.resize(1);
RenderingElement3D::elements.push_back(&renderer);
RenderingElement3D::BuildTLAS(cmd, 0);
VkDescriptorImageInfo imageInfo = {
.imageView = window.imageViews[0],
.imageLayout = VK_IMAGE_LAYOUT_GENERAL
};
VkWriteDescriptorSetAccelerationStructureKHR writeDescriptorSetAccelerationStructure {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_KHR,
.accelerationStructureCount = 1,
.pAccelerationStructures = &RenderingElement3D::tlases[0].accelerationStructure
};
VkWriteDescriptorSet write[2] = {
{
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.pNext = &writeDescriptorSetAccelerationStructure,
.dstSet = pool.sets[0],
.dstBinding = 0,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR,
},
{
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = pool.sets[0],
.dstBinding = 1,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
.pImageInfo = &imageInfo
}
};
vkUpdateDescriptorSets(Device::device, 2, write, 0, nullptr);
window.SetPipelineRT(pipeline);
window.descriptorsRt = pool.sets;
window.FinishInit();
window.Render();
window.StartSync();
}

View file

@ -1,9 +0,0 @@
#version 460
#extension GL_EXT_ray_tracing : enable
layout(location = 0) rayPayloadInEXT vec3 hitValue;
void main()
{
hitValue = vec3(1, 1, 1);
}

View file

@ -1,32 +0,0 @@
{
"name": "crafter-graphics",
"configurations": [
{
"name": "executable",
"implementations": ["main"],
"dependencies": [
{
"path":"../../project.json",
"configuration":"lib-vulkan-debug"
}
],
"shaders": [
{
"path":"raygen.glsl",
"type": 6,
"entrypoint":"main"
},
{
"path":"closesthit.glsl",
"type": 9,
"entrypoint":"main"
},
{
"path":"miss.glsl",
"type": 10,
"entrypoint":"main"
}
]
}
]
}

View file

@ -1,47 +0,0 @@
#version 460
#extension GL_EXT_ray_tracing : enable
#extension GL_EXT_shader_image_load_formatted : enable
layout(binding = 0, set = 0) uniform accelerationStructureEXT topLevelAS;
layout(binding = 1, set = 0, rgba8) uniform writeonly image2D image;
layout(location = 0) rayPayloadEXT vec3 hitValue;
void main()
{
// Pixel coordinates
uvec2 pixel = gl_LaunchIDEXT.xy;
uvec2 resolution = gl_LaunchSizeEXT.xy;
// Normalized coordinates in range [-1, 1]
vec2 uv = (vec2(pixel) + 0.5) / vec2(resolution);
vec2 ndc = uv * 2.0 - 1.0;
// Camera parameters
vec3 origin = vec3(0.0, 0.0, -300.0);
float aspect = float(resolution.x) / float(resolution.y);
float fov = radians(60.0);
float tanHalfFov = tan(fov * 0.5);
// Simple pinhole camera facing +Z
vec3 direction = normalize(vec3(
ndc.x * aspect * tanHalfFov,
-ndc.y * tanHalfFov,
1.0
));
traceRayEXT(
topLevelAS,
gl_RayFlagsNoneEXT,
0xff,
0, 0, 0,
origin,
0.001,
direction,
10000.0,
0
);
imageStore(image, ivec2(pixel), vec4(hitValue, 1.0));
}

View file

@ -65,10 +65,12 @@ const char* const deviceExtensionNames[] = {
"VK_KHR_swapchain", "VK_KHR_swapchain",
"VK_KHR_spirv_1_4", "VK_KHR_spirv_1_4",
"VK_KHR_shader_float_controls", "VK_KHR_shader_float_controls",
"VK_KHR_dynamic_rendering",
"VK_KHR_acceleration_structure", "VK_KHR_acceleration_structure",
"VK_KHR_deferred_host_operations",
"VK_KHR_ray_tracing_pipeline", "VK_KHR_ray_tracing_pipeline",
"VK_EXT_descriptor_heap",
"VK_KHR_deferred_host_operations",
"VK_KHR_maintenance5",
"VK_KHR_shader_untyped_pointers"
}; };
const char* const layerNames[] = { const char* const layerNames[] = {
"VK_LAYER_KHRONOS_validation" "VK_LAYER_KHRONOS_validation"
@ -474,10 +476,21 @@ void Device::Initialize() {
VkApplicationInfo app{VK_STRUCTURE_TYPE_APPLICATION_INFO}; VkApplicationInfo app{VK_STRUCTURE_TYPE_APPLICATION_INFO};
app.pApplicationName = ""; app.pApplicationName = "";
app.pEngineName = "Crafter.Graphics"; app.pEngineName = "Crafter.Graphics";
app.apiVersion = VK_MAKE_VERSION(1, 3, 0); app.apiVersion = VK_MAKE_VERSION(1, 4, 0);
VkValidationFeatureEnableEXT enables[] = {
VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT
};
VkValidationFeaturesEXT validationFeatures = {
.sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT,
.enabledValidationFeatureCount = 1,
.pEnabledValidationFeatures = enables
};
VkInstanceCreateInfo instanceCreateInfo = {}; VkInstanceCreateInfo instanceCreateInfo = {};
instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
instanceCreateInfo.pNext = &validationFeatures;
instanceCreateInfo.pApplicationInfo = &app; instanceCreateInfo.pApplicationInfo = &app;
instanceCreateInfo.enabledExtensionCount = sizeof(instanceExtensionNames) / sizeof(const char*); instanceCreateInfo.enabledExtensionCount = sizeof(instanceExtensionNames) / sizeof(const char*);
instanceCreateInfo.ppEnabledExtensionNames = instanceExtensionNames; instanceCreateInfo.ppEnabledExtensionNames = instanceExtensionNames;
@ -590,8 +603,20 @@ void Device::Initialize() {
queueCreateInfo.queueCount = 1; queueCreateInfo.queueCount = 1;
queueCreateInfo.pQueuePriorities = &priority; queueCreateInfo.pQueuePriorities = &priority;
VkPhysicalDeviceShaderUntypedPointersFeaturesKHR untypedPointersFeatures {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_UNTYPED_POINTERS_FEATURES_KHR,
.shaderUntypedPointers = VK_TRUE,
};
VkPhysicalDeviceDescriptorHeapFeaturesEXT desciptorHeapFeatures {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_HEAP_FEATURES_EXT,
.pNext = &untypedPointersFeatures,
.descriptorHeap = VK_TRUE,
};
VkPhysicalDevice16BitStorageFeatures bit16 { VkPhysicalDevice16BitStorageFeatures bit16 {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES, .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES,
.pNext = &desciptorHeapFeatures,
.storageBuffer16BitAccess = VK_TRUE, .storageBuffer16BitAccess = VK_TRUE,
}; };
@ -614,21 +639,14 @@ void Device::Initialize() {
.accelerationStructure = VK_TRUE .accelerationStructure = VK_TRUE
}; };
VkPhysicalDeviceDynamicRenderingFeaturesKHR dynamicRenderingFeature = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES_KHR};
dynamicRenderingFeature.dynamicRendering = VK_FALSE;
dynamicRenderingFeature.pNext = &deviceAccelerationStructureFeature;
VkPhysicalDeviceMeshShaderFeaturesEXT ext_feature = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_FEATURES_EXT};
ext_feature.meshShader = VK_FALSE;
ext_feature.pNext = &dynamicRenderingFeature;
VkPhysicalDeviceFeatures2 physical_features2 = { VkPhysicalDeviceFeatures2 physical_features2 = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
.pNext = &deviceAccelerationStructureFeature,
.features = { .features = {
.samplerAnisotropy = VK_TRUE .samplerAnisotropy = VK_TRUE,
.shaderInt16 = VK_TRUE
} }
}; };
physical_features2.pNext = &ext_feature;
VkDeviceCreateInfo deviceCreateInfo = {}; VkDeviceCreateInfo deviceCreateInfo = {};
deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
@ -681,6 +699,10 @@ void Device::Initialize() {
vkCreateRayTracingPipelinesKHR = reinterpret_cast<PFN_vkCreateRayTracingPipelinesKHR>(vkGetInstanceProcAddr(instance, "vkCreateRayTracingPipelinesKHR")); vkCreateRayTracingPipelinesKHR = reinterpret_cast<PFN_vkCreateRayTracingPipelinesKHR>(vkGetInstanceProcAddr(instance, "vkCreateRayTracingPipelinesKHR"));
vkGetRayTracingShaderGroupHandlesKHR = reinterpret_cast<PFN_vkGetRayTracingShaderGroupHandlesKHR>(vkGetInstanceProcAddr(instance, "vkGetRayTracingShaderGroupHandlesKHR")); vkGetRayTracingShaderGroupHandlesKHR = reinterpret_cast<PFN_vkGetRayTracingShaderGroupHandlesKHR>(vkGetInstanceProcAddr(instance, "vkGetRayTracingShaderGroupHandlesKHR"));
vkCmdTraceRaysKHR = reinterpret_cast<PFN_vkCmdTraceRaysKHR>(vkGetInstanceProcAddr(instance, "vkCmdTraceRaysKHR")); vkCmdTraceRaysKHR = reinterpret_cast<PFN_vkCmdTraceRaysKHR>(vkGetInstanceProcAddr(instance, "vkCmdTraceRaysKHR"));
vkCmdBindResourceHeapEXT = reinterpret_cast<PFN_vkCmdBindResourceHeapEXT>(vkGetInstanceProcAddr(instance, "vkCmdBindResourceHeapEXT"));
vkCmdBindSamplerHeapEXT = reinterpret_cast<PFN_vkCmdBindSamplerHeapEXT>(vkGetInstanceProcAddr(instance, "vkCmdBindSamplerHeapEXT"));
vkWriteResourceDescriptorsEXT = reinterpret_cast<PFN_vkWriteResourceDescriptorsEXT>(vkGetInstanceProcAddr(instance, "vkWriteResourceDescriptorsEXT"));
vkGetPhysicalDeviceDescriptorSizeEXT = reinterpret_cast<PFN_vkGetPhysicalDeviceDescriptorSizeEXT>(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceDescriptorSizeEXT"));
#endif #endif
} }

View file

@ -114,6 +114,12 @@ void RenderingElement3D::BuildTLAS(VkCommandBuffer cmd, std::uint32_t index) {
0, nullptr, 0, nullptr,
0, nullptr 0, nullptr
); );
VkAccelerationStructureDeviceAddressInfoKHR addrInfo {
.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_DEVICE_ADDRESS_INFO_KHR,
.accelerationStructure = tlases[index].accelerationStructure
};
tlases[index].address = Device::vkGetAccelerationStructureDeviceAddressKHR(Device::device, &addrInfo);
} }
#endif #endif

View file

@ -62,6 +62,8 @@ import :MouseElement;
import :Device; import :Device;
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN #ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
import :VulkanTransition; import :VulkanTransition;
import :DescriptorHeapVulkan;
import :PipelineRTVulkan;
#endif #endif
import std; import std;
@ -813,10 +815,31 @@ void Window::Render() {
} }
#endif #endif
vkCmdBindPipeline(drawCmdBuffers[currentBuffer], VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, rtPipeline); vkCmdBindPipeline(drawCmdBuffers[currentBuffer], VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, pipeline->pipeline);
vkCmdBindDescriptorSets(drawCmdBuffers[currentBuffer], VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, rtPipelineLayout, 0, static_cast<std::uint32_t>(descriptorsRt.size()), descriptorsRt.data(), 0, nullptr);
Device::vkCmdTraceRaysKHR(drawCmdBuffers[currentBuffer], &raygenRegion, &missRegion, &hitRegion, &callableRegion, width, height, 1);
VkBindHeapInfoEXT resourceHeapInfo = {
.sType = VK_STRUCTURE_TYPE_BIND_HEAP_INFO_EXT,
.heapRange = {
.address = descriptorHeap->resourceHeap[currentBuffer].address,
.size = static_cast<std::uint32_t>(descriptorHeap->resourceHeap[currentBuffer].descriptor.range)
},
.reservedRangeOffset = (descriptorHeap->resourceHeap[currentBuffer].descriptor.range - Device::descriptorHeapProperties.minResourceHeapReservedRange) & ~(Device::descriptorHeapProperties.imageDescriptorAlignment - 1),
.reservedRangeSize = Device::descriptorHeapProperties.minResourceHeapReservedRange
};
Device::vkCmdBindResourceHeapEXT(drawCmdBuffers[currentBuffer], &resourceHeapInfo);
VkBindHeapInfoEXT samplerHeapInfo = {
.sType = VK_STRUCTURE_TYPE_BIND_HEAP_INFO_EXT,
.heapRange = {
.address = descriptorHeap->samplerHeap[currentBuffer].address,
.size = static_cast<std::uint32_t>(descriptorHeap->samplerHeap[currentBuffer].descriptor.range)
},
.reservedRangeOffset = descriptorHeap->samplerHeap[currentBuffer].descriptor.range - Device::descriptorHeapProperties.minSamplerHeapReservedRange,
.reservedRangeSize = Device::descriptorHeapProperties.minSamplerHeapReservedRange
};
Device::vkCmdBindSamplerHeapEXT(drawCmdBuffers[currentBuffer], &samplerHeapInfo);
Device::vkCmdTraceRaysKHR(drawCmdBuffers[currentBuffer], &pipeline->raygenRegion, &pipeline->missRegion, &pipeline->hitRegion, &pipeline->callableRegion, width, height, 1);
VkImageMemoryBarrier image_memory_barrier2 { VkImageMemoryBarrier image_memory_barrier2 {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
@ -986,9 +1009,6 @@ void Window::CreateSwapchain()
// If an existing swap chain is re-created, destroy the old swap chain and the ressources owned by the application (image views, images are owned by the swap chain) // If an existing swap chain is re-created, destroy the old swap chain and the ressources owned by the application (image views, images are owned by the swap chain)
if (oldSwapchain != VK_NULL_HANDLE) { if (oldSwapchain != VK_NULL_HANDLE) {
for (auto i = 0; i < numFrames; i++) {
vkDestroyImageView(Device::device, imageViews[i], nullptr);
}
vkDestroySwapchainKHR(Device::device, oldSwapchain, nullptr); vkDestroySwapchainKHR(Device::device, oldSwapchain, nullptr);
} }
uint32_t imageCount{ 0 }; uint32_t imageCount{ 0 };
@ -997,34 +1017,27 @@ void Window::CreateSwapchain()
// Get the swap chain images // Get the swap chain images
Device::CheckVkResult(vkGetSwapchainImagesKHR(Device::device, swapChain, &imageCount, images)); Device::CheckVkResult(vkGetSwapchainImagesKHR(Device::device, swapChain, &imageCount, images));
for (auto i = 0; i < numFrames; i++) for (std::uint8_t i = 0; i < numFrames; i++) {
{ imageViews[i] = {
VkImageViewCreateInfo colorAttachmentView = {}; .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
colorAttachmentView.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; .flags = 0,
colorAttachmentView.pNext = NULL; .image = images[i],
colorAttachmentView.format = colorFormat; .viewType = VK_IMAGE_VIEW_TYPE_2D,
colorAttachmentView.components = { .format = colorFormat,
.components = {
VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R,
VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_G,
VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_B,
VK_COMPONENT_SWIZZLE_A VK_COMPONENT_SWIZZLE_A
},
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
}; };
colorAttachmentView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
colorAttachmentView.subresourceRange.baseMipLevel = 0;
colorAttachmentView.subresourceRange.levelCount = 1;
colorAttachmentView.subresourceRange.baseArrayLayer = 0;
colorAttachmentView.subresourceRange.layerCount = 1;
colorAttachmentView.viewType = VK_IMAGE_VIEW_TYPE_2D;
colorAttachmentView.flags = 0;
colorAttachmentView.image = images[i];
Device::CheckVkResult(vkCreateImageView(Device::device, &colorAttachmentView, nullptr, &imageViews[i]));
VkImageSubresourceRange range{};
range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
range.baseMipLevel = 0;
range.levelCount = VK_REMAINING_MIP_LEVELS;
range.baseArrayLayer = 0;
range.layerCount = VK_REMAINING_ARRAY_LAYERS;
} }
} }

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_vkCreateRayTracingPipelinesKHR vkCreateRayTracingPipelinesKHR;
inline static PFN_vkGetRayTracingShaderGroupHandlesKHR vkGetRayTracingShaderGroupHandlesKHR; inline static PFN_vkGetRayTracingShaderGroupHandlesKHR vkGetRayTracingShaderGroupHandlesKHR;
inline static PFN_vkCmdTraceRaysKHR vkCmdTraceRaysKHR; 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 VkPhysicalDeviceMemoryProperties memoryProperties;
inline static VkPhysicalDeviceDescriptorHeapPropertiesEXT descriptorHeapProperties = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_HEAP_PROPERTIES_EXT
};
inline static VkPhysicalDeviceRayTracingPipelinePropertiesKHR rayTracingProperties = { 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); static void CheckVkResult(VkResult result);

View file

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

View file

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

View file

@ -30,26 +30,16 @@ import :ShaderBindingTableVulkan;
import :Types; import :Types;
export namespace Crafter { export namespace Crafter {
class PipelineRTVulkan { struct PipelineRTVulkan {
public:
VkPipeline pipeline; VkPipeline pipeline;
VkPipelineLayout pipelineLayout;
std::vector<std::uint8_t> shaderHandles; std::vector<std::uint8_t> shaderHandles;
VulkanBuffer<std::uint8_t, true, true, false> sbtBuffer; VulkanBuffer<std::uint8_t, true, true> sbtBuffer;
VkStridedDeviceAddressRegionKHR raygenRegion; VkStridedDeviceAddressRegionKHR raygenRegion;
VkStridedDeviceAddressRegionKHR missRegion; VkStridedDeviceAddressRegionKHR missRegion;
VkStridedDeviceAddressRegionKHR hitRegion; VkStridedDeviceAddressRegionKHR hitRegion;
VkStridedDeviceAddressRegionKHR callableRegion; VkStridedDeviceAddressRegionKHR callableRegion;
void Init(VkCommandBuffer cmd, std::span<VkDescriptorSetLayout> setLayouts, std::span<VkRayTracingShaderGroupCreateInfoKHR> raygenGroups, std::span<VkRayTracingShaderGroupCreateInfoKHR> missGroups, std::span<VkRayTracingShaderGroupCreateInfoKHR> hitGroups, ShaderBindingTableVulkan& shaderTable) { void Init(VkCommandBuffer cmd, 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));
std::vector<VkRayTracingShaderGroupCreateInfoKHR> groups; std::vector<VkRayTracingShaderGroupCreateInfoKHR> groups;
groups.reserve(raygenGroups.size() + missGroups.size() + hitGroups.size()); 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(), missGroups.begin(), missGroups.end());
groups.insert(groups.end(), hitGroups.begin(), hitGroups.end()); groups.insert(groups.end(), hitGroups.begin(), hitGroups.end());
VkPipelineCreateFlags2CreateInfo flags2 = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_CREATE_FLAGS_2_CREATE_INFO,
.flags = VK_PIPELINE_CREATE_2_DESCRIPTOR_HEAP_BIT_EXT
};
VkRayTracingPipelineCreateInfoKHR rtPipelineInfo { VkRayTracingPipelineCreateInfoKHR rtPipelineInfo {
.sType = VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_KHR, .sType = VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_KHR,
.pNext = &flags2,
.flags = 0,
.stageCount = static_cast<std::uint32_t>(shaderTable.shaderStages.size()), .stageCount = static_cast<std::uint32_t>(shaderTable.shaderStages.size()),
.pStages = shaderTable.shaderStages.data(), .pStages = shaderTable.shaderStages.data(),
.groupCount = static_cast<std::uint32_t>(groups.size()), .groupCount = static_cast<std::uint32_t>(groups.size()),
.pGroups = groups.data(), .pGroups = groups.data(),
.maxPipelineRayRecursionDepth = 1, .maxPipelineRayRecursionDepth = 1,
.layout = pipelineLayout .layout = VK_NULL_HANDLE
}; };
Device::CheckVkResult(Device::vkCreateRayTracingPipelinesKHR(Device::device, {}, {}, 1, &rtPipelineInfo, nullptr, &pipeline)); Device::CheckVkResult(Device::vkCreateRayTracingPipelinesKHR(Device::device, {}, {}, 1, &rtPipelineInfo, nullptr, &pipeline));
@ -114,189 +111,6 @@ export namespace Crafter {
callableRegion.size = 0; 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 #endif

View file

@ -383,29 +383,7 @@ export namespace Crafter {
} }
break; break;
} }
case OpaqueType::SemiOpaque: { 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::Transparent: { case OpaqueType::Transparent: {
for (int j = 0; j < h; j++) { for (int j = 0; j < h; j++) {
for (int i = 0; i < w; i++) { 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 :Mesh;
import :VulkanBuffer; import :VulkanBuffer;
import Crafter.Math; import Crafter.Math;
import :Window;
export namespace Crafter { export namespace Crafter {
struct TlasWithBuffer { struct TlasWithBuffer {
VulkanBuffer<char, false, true, false> buffer; VkDeviceAddress address;
VulkanBuffer<char, false, true> buffer;
VkAccelerationStructureKHR accelerationStructure; VkAccelerationStructureKHR accelerationStructure;
VulkanBuffer<VkAccelerationStructureInstanceKHR, true, true, false> instanceBuffer; VulkanBuffer<VkAccelerationStructureInstanceKHR, true, true> instanceBuffer;
}; };
class RenderingElement3D { class RenderingElement3D {
public: public:
VkAccelerationStructureInstanceKHR instance; VkAccelerationStructureInstanceKHR instance;
static std::vector<RenderingElement3D*> elements; static std::vector<RenderingElement3D*> elements;
inline static VulkanBuffer<char, false, true, false> scratchBuffer; inline static VulkanBuffer<char, false, true> scratchBuffer;
inline static std::vector<TlasWithBuffer> tlases; inline static TlasWithBuffer tlases[Window::numFrames];
static void BuildTLAS(VkCommandBuffer cmd, std::uint32_t index); static void BuildTLAS(VkCommandBuffer cmd, std::uint32_t index);
}; };
} }

View file

@ -36,33 +36,10 @@ export namespace Crafter {
void Init(std::span<VulkanShader> shaders) { void Init(std::span<VulkanShader> shaders) {
shaderStages.reserve(shaders.size()); shaderStages.reserve(shaders.size());
for(const VulkanShader& shader: shaders) { 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 #endif

View file

@ -28,21 +28,14 @@ import :Device;
import :Types; import :Types;
export namespace Crafter { 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 { class VulkanShader {
public: public:
std::vector<VkSpecializationMapEntry> specilizations;
VkSpecializationInfo* specilizationInfo;
VkShaderStageFlagBits stage; VkShaderStageFlagBits stage;
std::string entrypoint; std::string entrypoint;
VkShaderModule shader; 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); std::ifstream file(path, std::ios::binary);
if (!file) { if (!file) {
std::cerr << "Error: Could not open file " << path << std::endl; 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)); 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 #endif

View file

@ -33,8 +33,8 @@ export namespace Crafter {
}; };
struct ScaleData2D { struct ScaleData2D {
Vector<std::uint16_t, 2> position; Vector<std::uint16_t, 2, 2> position;
Vector<std::uint16_t, 2> size; Vector<std::uint16_t, 2, 2> size;
}; };
struct ClipRect { 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; 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>
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> {
class VulkanBuffer : public VulkanBufferBase, public VulkanBufferMappedConditional<T, Mapped>, public VulkanBufferAdressableConditional<Adressable>, public VulkanBufferStagedConditional<T, Staged> {
public: public:
VulkanBuffer() = default; VulkanBuffer() = default;
void Create(VkBufferUsageFlags usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, std::uint32_t count) { 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 {}; VkBufferCreateInfo bufferCreateInfo {};
bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
@ -117,7 +100,7 @@ namespace Crafter {
descriptor.offset = 0; descriptor.offset = 0;
descriptor.buffer = buffer; descriptor.buffer = buffer;
descriptor.range = VK_WHOLE_SIZE; descriptor.range = sizeof(T)*count;
Device::CheckVkResult(vkBindBufferMemory(Device::device, buffer, memory, 0)); Device::CheckVkResult(vkBindBufferMemory(Device::device, buffer, memory, 0));
@ -141,9 +124,6 @@ namespace Crafter {
vkDestroyBuffer(Device::device, buffer, nullptr); vkDestroyBuffer(Device::device, buffer, nullptr);
vkFreeMemory(Device::device, memory, nullptr); vkFreeMemory(Device::device, memory, nullptr);
buffer = VK_NULL_HANDLE; buffer = VK_NULL_HANDLE;
if constexpr(Staged) {
delete VulkanBufferMappedConditional<T, true>::stagingBuffer;
}
} }
void Resize(VkBufferUsageFlags usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, std::uint32_t count) { void Resize(VkBufferUsageFlags usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, std::uint32_t count) {
@ -238,22 +218,6 @@ namespace Crafter {
vkInvalidateMappedMemoryRanges(Device::device, 1, &range); 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) { VulkanBuffer(VulkanBuffer&& other) {
descriptor = other.descriptor; descriptor = other.descriptor;
buffer = other.buffer; buffer = other.buffer;
@ -265,9 +229,6 @@ namespace Crafter {
if constexpr(Mapped) { if constexpr(Mapped) {
VulkanBufferMappedConditional<T, true>::value = other.VulkanBufferMappedConditional<T, true>::value; VulkanBufferMappedConditional<T, true>::value = other.VulkanBufferMappedConditional<T, true>::value;
} }
if constexpr(Staged) {
VulkanBufferStagedConditional<T, true>::stagingBuffer = other.VulkanBufferStagedConditional<T, true>::stagingBuffer;
}
}; };
~VulkanBuffer() { ~VulkanBuffer() {

View file

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

View file

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

View file

@ -28,13 +28,12 @@
"interfaces/Crafter.Graphics-Mesh", "interfaces/Crafter.Graphics-Mesh",
"interfaces/Crafter.Graphics-VulkanBuffer", "interfaces/Crafter.Graphics-VulkanBuffer",
"interfaces/Crafter.Graphics-RenderingElement3D", "interfaces/Crafter.Graphics-RenderingElement3D",
"interfaces/Crafter.Graphics-DescriptorPoolVulkan",
"interfaces/Crafter.Graphics-ShaderVulkan", "interfaces/Crafter.Graphics-ShaderVulkan",
"interfaces/Crafter.Graphics-PipelineRTVulkan", "interfaces/Crafter.Graphics-PipelineRTVulkan",
"interfaces/Crafter.Graphics-ShaderBindingTableVulkan", "interfaces/Crafter.Graphics-ShaderBindingTableVulkan",
"interfaces/Crafter.Graphics-ImageVulkan", "interfaces/Crafter.Graphics-ImageVulkan",
"interfaces/Crafter.Graphics-SamplerVulkan", "interfaces/Crafter.Graphics-SamplerVulkan",
"interfaces/Crafter.Graphics-DescriptorSetLayoutVulkan", "interfaces/Crafter.Graphics-DescriptorHeapVulkan",
"interfaces/Crafter.Graphics-Rendertarget", "interfaces/Crafter.Graphics-Rendertarget",
"interfaces/Crafter.Graphics-ForwardDeclarations" "interfaces/Crafter.Graphics-ForwardDeclarations"
], ],