Compare commits

...
Sign in to create a new pull request.

22 commits

Author SHA1 Message Date
c9ebd448f9 update 2026-04-16 23:03:24 +02:00
ef8d623525 text rendering fixes 2026-04-15 19:30:21 +02:00
5ffe1404fc vulkan2d fixes 2026-04-13 18:36:07 +02:00
4c93c5535e typo 2026-04-11 23:22:52 +02:00
ea18f32300 vulkan2d fixes 2026-04-11 23:18:41 +02:00
1c1a142f52 rendertargetvulkan 2026-04-11 18:48:00 +02:00
8b12dc39b3 renderingelement2dvulkan load from asset 2026-04-11 13:54:17 +02:00
f4a48b20c6 renderingelement2dvulkan auto buffer size 2026-04-10 22:57:53 +02:00
3fcea6a3d7 writing ui descriptors 2026-04-10 22:26:15 +02:00
3f4ad87746 writing ui descriptors 2026-04-10 22:25:55 +02:00
9d43f2e44b const sbt 2026-04-10 20:53:17 +02:00
92dfe16dac pipeline destructor 2026-04-10 20:51:16 +02:00
5427867fff descriptor heap static offset method 2026-04-10 20:30:58 +02:00
177f873639 vulkan UI 2026-04-09 00:15:09 +02:00
f8e142fb06 descriptor heap rewrite 2026-04-05 22:53:59 +02:00
b4bd0c03c5 fix 2026-04-03 03:29:51 +02:00
22b8af7bfc update 2026-04-02 16:52:10 +02:00
477b7dd087 F16 rendering 2026-04-01 18:43:18 +02:00
c895c266fb vector renderring 2026-03-31 15:22:55 +02:00
80bb04f84a revert 2026-03-24 05:25:53 +01:00
44a2960415 typo 2026-03-24 05:24:46 +01:00
7fdab4f62b rendering update 2026-03-22 21:08:02 +01:00
40 changed files with 1863 additions and 1272 deletions

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

@ -7,11 +7,9 @@
"dependencies": [ "dependencies": [
{ {
"path":"../../project.json", "path":"../../project.json",
"configuration":"lib-win32-vulkan-debug" "configuration":"lib-wayland-vulkan-debug"
} }
], ],
"target": "x86_64-w64-mingw32",
"debug": true,
"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,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));
}

133
examples/VulkanUI/main.cpp Normal file
View file

@ -0,0 +1,133 @@
#include "vulkan/vulkan.h"
#include <cassert>
import Crafter.Graphics;
using namespace Crafter;
import std;
import Crafter.Event;
import Crafter.Math;
int main() {
Device::Initialize();
Window window(1280, 720, "HelloVulkan");
VkCommandBuffer cmd = window.StartInit();
DescriptorHeapVulkan descriptorHeap;
descriptorHeap.Initialize(1,2,0);
VkSpecializationMapEntry entry = {
.constantID = 0,
.offset = 0,
.size = sizeof(uint16_t)
};
VkSpecializationInfo specilizationInfo = {
.mapEntryCount = 1,
.pMapEntries = &entry,
.dataSize = sizeof(uint16_t),
.pData = &descriptorHeap.bufferStartElement
};
std::array<VulkanShader, 1> shaders{{
{"raygen.spv", "main", VK_SHADER_STAGE_RAYGEN_BIT_KHR, &specilizationInfo}
}};
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, 0> missGroups;
std::array<VkRayTracingShaderGroupCreateInfoKHR, 0> hitGroups;
PipelineRTVulkan pipeline;
pipeline.Init(cmd, raygenGroups, missGroups, hitGroups, shaderTable);
window.FinishInit();
RenderingElement2DVulkan<true, true> element(
{
0.5, //anchorX: relative position where this elements x anchor (top-left) is placed to its parent x anchor
0.5, //anchorY: relative position where this elements y anchor (top-left) is placed to its parent y anchor
0.5, //relativeSizeX: the relative x size this element should be scaled to compared to its parent
0.5, //relativeSizeY: the relative y size this element should be scaled to compared to its parent
0.5, //anchorOffsetX: the amount this element's anchor should be offset from the top left corner (0.5 to in the middle)
0.5, //anchorOffsetY: the amount this element's anchor should be offset from the top left corner (0.5 to place it in the middle)
0 //z: this elements Z position
},
2,
1
);
for(std::uint8_t i = 0; i < Window::numFrames; i++) {
reinterpret_cast<VulkanBuffer<Vector<_Float16, 4, 4>, true>*>(element.buffers[i])->value[0] = {1, 0, 0, 1};
reinterpret_cast<VulkanBuffer<Vector<_Float16, 4, 4>, true>*>(element.buffers[i])->value[1] = {0, 1, 0, 1};
reinterpret_cast<VulkanBuffer<Vector<_Float16, 4, 4>, true>*>(element.buffers[i])->FlushDevice();
}
RendertargetVulkan rendertarget(1280, 720, {&element});
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
};
std::array<VkResourceDescriptorInfoEXT, 9> infos;
infos[0] = {
.sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT,
.type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
.data = { .pImage = &imageInfo0 }
};
infos[1] = {
.sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT,
.type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
.data = { .pImage = &imageInfo1 }
};
infos[2] = {
.sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT,
.type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
.data = { .pImage = &imageInfo2 }
};
std::array<VkHostAddressRangeEXT, 9> ranges;
ranges[0] = {
.address = descriptorHeap.resourceHeap[0].value,
.size = Device::descriptorHeapProperties.imageDescriptorSize
};
ranges[1] = {
.address = descriptorHeap.resourceHeap[1].value,
.size = Device::descriptorHeapProperties.imageDescriptorSize
},
ranges[2] = {
.address = descriptorHeap.resourceHeap[2].value,
.size = Device::descriptorHeapProperties.imageDescriptorSize
},
rendertarget.WriteDescriptors(infos, ranges, 3, descriptorHeap.bufferStartOffset, descriptorHeap);
window.pipeline = &pipeline;
window.descriptorHeap = &descriptorHeap;
window.Render();
window.StartSync();
}

View file

@ -7,24 +7,15 @@
"dependencies": [ "dependencies": [
{ {
"path":"../../project.json", "path":"../../project.json",
"configuration":"lib-vulkan-debug" "configuration":"lib-wayland-vulkan-debug"
} }
], ],
"debug": true,
"shaders": [ "shaders": [
{ {
"path":"raygen.glsl", "path":"raygen.glsl",
"type": 6, "type": 6,
"entrypoint":"main" "entrypoint":"main"
},
{
"path":"closesthit.glsl",
"type": 9,
"entrypoint":"main"
},
{
"path":"miss.glsl",
"type": 10,
"entrypoint":"main"
} }
] ]
} }

View file

@ -0,0 +1,48 @@
#version 460
#extension GL_EXT_ray_tracing : 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
#extension GL_EXT_shader_explicit_arithmetic_types_float16 : enable
struct UIScaledData{
int16_t x;
int16_t y;
int16_t sizeX;
int16_t sizeY;
uint16_t bufferX;
uint16_t bufferY;
};
layout(std430, descriptor_heap) buffer UIScaledDataBuffer {
uint16_t count;
uint16_t pad[5];
UIScaledData data[];
} UITransformBuffer[];
layout(std430, descriptor_heap) buffer UIPixelBufferr {
f16vec4 pixels[];
} UIPixelBuffer[];
layout(constant_id = 0) const uint16_t bufferStart = 0us;
layout(descriptor_heap) uniform writeonly image2D image[];
void main()
{
uvec2 pixel = gl_LaunchIDEXT.xy;
uvec2 resolution = gl_LaunchSizeEXT.xy;
vec4 hitValue = vec4(0);
for (uint16_t i = 1us; i < UITransformBuffer[bufferStart].count+1; i++) {
if(pixel.x > UITransformBuffer[bufferStart].data[i].x && pixel.x < UITransformBuffer[bufferStart].data[i].x + UITransformBuffer[bufferStart].data[i].sizeX && pixel.y > UITransformBuffer[bufferStart].data[i].y && pixel.y < UITransformBuffer[bufferStart].data[i].y + UITransformBuffer[bufferStart].data[i].sizeY) {
int16_t srcX = int16_t(float(pixel.x - UITransformBuffer[bufferStart].data[i].x) * float(UITransformBuffer[bufferStart].data[i].bufferX) / float(UITransformBuffer[bufferStart].data[i].sizeX));
int16_t srcY = int16_t(float(pixel.y - UITransformBuffer[bufferStart].data[i].y) * float(UITransformBuffer[bufferStart].data[i].bufferY) / float(UITransformBuffer[bufferStart].data[i].sizeY));
hitValue = vec4(UIPixelBuffer[bufferStart + 1].pixels[srcY * UITransformBuffer[bufferStart].data[i].bufferX + srcX]);
}
}
imageStore(image[0], ivec2(pixel), hitValue);
}

View file

@ -65,11 +65,13 @@ 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_KHR_ray_tracing_position_fetch" "VK_EXT_descriptor_heap",
"VK_KHR_deferred_host_operations",
"VK_KHR_maintenance5",
"VK_KHR_shader_untyped_pointers",
"VK_EXT_device_fault"
}; };
const char* const layerNames[] = { const char* const layerNames[] = {
"VK_LAYER_KHRONOS_validation" "VK_LAYER_KHRONOS_validation"
@ -79,6 +81,58 @@ const char* const layerNames[] = {
void Device::CheckVkResult(VkResult result) { void Device::CheckVkResult(VkResult result) {
if (result != VK_SUCCESS) if (result != VK_SUCCESS)
{ {
if(result == VK_ERROR_DEVICE_LOST) {
VkDeviceFaultCountsEXT faultCounts = {
.sType = VK_STRUCTURE_TYPE_DEVICE_FAULT_COUNTS_EXT,
.pNext = NULL,
};
Device::vkGetDeviceFaultInfoEXT(device, &faultCounts, NULL);
std::vector<VkDeviceFaultAddressInfoEXT> addressInfos(faultCounts.addressInfoCount);
std::vector<VkDeviceFaultVendorInfoEXT> vendorInfos(faultCounts.vendorInfoCount);
std::vector<char> vendorBinaryData(faultCounts.vendorBinarySize);
VkDeviceFaultInfoEXT faultInfo = {
.sType = VK_STRUCTURE_TYPE_DEVICE_FAULT_INFO_EXT,
.pNext = NULL,
.pAddressInfos = addressInfos.data(),
.pVendorInfos = vendorInfos.data(),
.pVendorBinaryData = vendorBinaryData.data(),
};
Device::vkGetDeviceFaultInfoEXT(device, &faultCounts, &faultInfo);
std::println("{}", faultInfo.description);
std::println("{} AddressInfos:", addressInfos.size());
for(const VkDeviceFaultAddressInfoEXT& info : addressInfos) {
std::println("\t{} {}", static_cast<uint32_t>(info.addressType), info.reportedAddress);
}
std::println("{} vendorInfos:", vendorInfos.size());
for(const VkDeviceFaultVendorInfoEXT& info : vendorInfos) {
std::println("\t{} {} {}", info.description, info.vendorFaultCode, info.vendorFaultData);
}
if(!vendorBinaryData.empty()) {
std::string ext = ".bin";
if(vendorBinaryData.size() >= sizeof(VkDeviceFaultVendorBinaryHeaderVersionOneEXT)) {
VkDeviceFaultVendorBinaryHeaderVersionOneEXT header;
std::memcpy(&header, vendorBinaryData.data(), sizeof(header));
if(header.vendorID == 0x10DE) { // NVIDIA
ext = ".nv-gpudmp";
}
}
const auto now = std::chrono::system_clock::now();
const std::string dumpPath = std::format("gpu_crash_dump-{:%Y%m%d-%H%M%S}{}", now, ext);
std::ofstream file(dumpPath, std::ios::binary);
if(file.write(vendorBinaryData.data(), vendorBinaryData.size())) {
std::println("Vendor binary saved to: {}", std::filesystem::canonical(dumpPath).string());
} else {
std::println(stderr, "Failed to write vendor binary to: {}", dumpPath);
}
}
}
throw std::runtime_error(string_VkResult(result)); throw std::runtime_error(string_VkResult(result));
} }
} }
@ -157,6 +211,33 @@ constexpr CrafterKeys keysym_to_crafter_key(xkb_keysym_t sym)
case XKB_KEY_y: return CrafterKeys::Y; case XKB_KEY_y: return CrafterKeys::Y;
case XKB_KEY_z: return CrafterKeys::Z; case XKB_KEY_z: return CrafterKeys::Z;
case XKB_KEY_A: return CrafterKeys::A;
case XKB_KEY_B: return CrafterKeys::B;
case XKB_KEY_C: return CrafterKeys::C;
case XKB_KEY_D: return CrafterKeys::D;
case XKB_KEY_E: return CrafterKeys::E;
case XKB_KEY_F: return CrafterKeys::F;
case XKB_KEY_G: return CrafterKeys::G;
case XKB_KEY_H: return CrafterKeys::H;
case XKB_KEY_I: return CrafterKeys::I;
case XKB_KEY_J: return CrafterKeys::J;
case XKB_KEY_K: return CrafterKeys::K;
case XKB_KEY_L: return CrafterKeys::L;
case XKB_KEY_M: return CrafterKeys::M;
case XKB_KEY_N: return CrafterKeys::N;
case XKB_KEY_O: return CrafterKeys::O;
case XKB_KEY_P: return CrafterKeys::P;
case XKB_KEY_Q: return CrafterKeys::Q;
case XKB_KEY_R: return CrafterKeys::R;
case XKB_KEY_S: return CrafterKeys::S;
case XKB_KEY_T: return CrafterKeys::T;
case XKB_KEY_U: return CrafterKeys::U;
case XKB_KEY_V: return CrafterKeys::V;
case XKB_KEY_W: return CrafterKeys::W;
case XKB_KEY_X: return CrafterKeys::X;
case XKB_KEY_Y: return CrafterKeys::Y;
case XKB_KEY_Z: return CrafterKeys::Z;
// Numbers // Numbers
case XKB_KEY_0: return CrafterKeys::_0; case XKB_KEY_0: return CrafterKeys::_0;
case XKB_KEY_1: return CrafterKeys::_1; case XKB_KEY_1: return CrafterKeys::_1;
@ -246,8 +327,7 @@ constexpr CrafterKeys keysym_to_crafter_key(xkb_keysym_t sym)
case XKB_KEY_period: return CrafterKeys::period; case XKB_KEY_period: return CrafterKeys::period;
case XKB_KEY_slash: return CrafterKeys::slash; case XKB_KEY_slash: return CrafterKeys::slash;
default: default: return CrafterKeys::CrafterKeysMax;
throw std::runtime_error(std::format("Unkown XKB_KEY: {}", sym));
} }
} }
@ -407,31 +487,39 @@ void Device::keyboard_leave(void *data, wl_keyboard *keyboard, uint32_t serial,
} }
void Device::keyboard_key(void *data, wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) { void Device::keyboard_key(void *data, wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) {
xkb_keycode_t keycode = key + 8; xkb_keycode_t keycode = key + 8;
xkb_keysym_t keysym = xkb_state_key_get_one_sym(xkb_state, keycode); xkb_keysym_t keysym = xkb_state_key_get_one_sym(xkb_state, keycode);
CrafterKeys crafterKey = keysym_to_crafter_key(keysym); CrafterKeys crafterKey = keysym_to_crafter_key(keysym);
if(state == WL_KEYBOARD_KEY_STATE_PRESSED) { if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
if(Device::focusedWindow->heldkeys[static_cast<std::uint8_t>(crafterKey)]) { if (focusedWindow->heldkeys[(std::uint8_t)crafterKey]) {
Device::focusedWindow->onKeyHold[static_cast<std::uint8_t>(crafterKey)].Invoke(); focusedWindow->onKeyHold[(std::uint8_t)crafterKey].Invoke();
Device::focusedWindow->onAnyKeyHold.Invoke(crafterKey); focusedWindow->onAnyKeyHold.Invoke(crafterKey);
} else{ } else {
Device::focusedWindow->heldkeys[static_cast<std::uint8_t>(crafterKey)] = true; focusedWindow->heldkeys[(std::uint8_t)crafterKey] = true;
Device::focusedWindow->onKeyDown[static_cast<std::uint8_t>(crafterKey)].Invoke(); focusedWindow->onKeyDown[(std::uint8_t)crafterKey].Invoke();
Device::focusedWindow->onAnyKeyDown.Invoke(crafterKey); focusedWindow->onAnyKeyDown.Invoke(crafterKey);
} }
} else{
Device::focusedWindow->heldkeys[static_cast<std::uint8_t>(crafterKey)] = false; std::string buf;
Device::focusedWindow->onKeyUp[static_cast<std::uint8_t>(crafterKey)].Invoke(); buf.resize(16);
Device::focusedWindow->onAnyKeyUp.Invoke(crafterKey); int n = xkb_state_key_get_utf8(xkb_state, keycode, buf.data(), 16);
if (n > 0) {
if ((unsigned char)buf[0] >= 0x20 && buf[0] != 0x7f) {
buf.resize(n);
focusedWindow->onTextInput.Invoke(buf);
}
}
} else {
focusedWindow->heldkeys[(std::uint8_t)crafterKey] = false;
focusedWindow->onKeyUp[(std::uint8_t)crafterKey].Invoke();
focusedWindow->onAnyKeyUp.Invoke(crafterKey);
} }
} }
void Device::keyboard_modifiers(void *data, wl_keyboard *keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group) { void Device::keyboard_modifiers(void *data, wl_keyboard *keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group) {
xkb_state_update_mask(xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group);
} }
void Device::keyboard_repeat_info(void *data, wl_keyboard *keyboard, int32_t rate, int32_t delay) { void Device::keyboard_repeat_info(void *data, wl_keyboard *keyboard, int32_t rate, int32_t delay) {
@ -477,8 +565,19 @@ void Device::Initialize() {
app.pEngineName = "Crafter.Graphics"; app.pEngineName = "Crafter.Graphics";
app.apiVersion = VK_MAKE_VERSION(1, 4, 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;
@ -529,17 +628,10 @@ void Device::Initialize() {
{ {
VkPhysicalDevice device = physDevices[i]; VkPhysicalDevice device = physDevices[i];
VkPhysicalDeviceProperties2 properties2{ uint32_t score;
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
.pNext = &rayTracingProperties
};
vkGetPhysicalDeviceProperties2(device, &properties2);
VkPhysicalDeviceProperties properties; VkPhysicalDeviceProperties properties;
vkGetPhysicalDeviceProperties(device, &properties); vkGetPhysicalDeviceProperties(device, &properties);
uint32_t score;
switch (properties.deviceType) switch (properties.deviceType)
{ {
default : default :
@ -568,6 +660,11 @@ void Device::Initialize() {
} }
} }
VkPhysicalDeviceProperties2 properties2 {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
.pNext = &rayTracingProperties
};
vkGetPhysicalDeviceProperties2(physDevice, &properties2);
uint32_t queueFamilyCount; uint32_t queueFamilyCount;
vkGetPhysicalDeviceQueueFamilyProperties(physDevice, &queueFamilyCount, NULL); vkGetPhysicalDeviceQueueFamilyProperties(physDevice, &queueFamilyCount, NULL);
@ -593,21 +690,50 @@ void Device::Initialize() {
queueCreateInfo.queueCount = 1; queueCreateInfo.queueCount = 1;
queueCreateInfo.pQueuePriorities = &priority; queueCreateInfo.pQueuePriorities = &priority;
VkPhysicalDeviceFaultFeaturesEXT faultFeatures2 = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FAULT_FEATURES_EXT,
};
VkPhysicalDeviceFeatures2 features22 = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
.pNext = &faultFeatures2,
};
vkGetPhysicalDeviceFeatures2(physDevice, &features22);
VkPhysicalDeviceFaultFeaturesEXT faultFeatures = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FAULT_FEATURES_EXT,
.deviceFault = VK_TRUE,
.deviceFaultVendorBinary = faultFeatures2.deviceFaultVendorBinary,
};
VkPhysicalDeviceShaderUntypedPointersFeaturesKHR untypedPointersFeatures {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_UNTYPED_POINTERS_FEATURES_KHR,
.pNext = &faultFeatures,
.shaderUntypedPointers = VK_TRUE,
};
VkPhysicalDeviceDescriptorHeapFeaturesEXT desciptorHeapFeatures {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_HEAP_FEATURES_EXT,
.pNext = &untypedPointersFeatures,
.descriptorHeap = VK_TRUE,
};
VkPhysicalDevice16BitStorageFeatures bit16 {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES,
.pNext = &desciptorHeapFeatures,
.storageBuffer16BitAccess = VK_TRUE,
};
VkPhysicalDeviceVulkan12Features features12 { VkPhysicalDeviceVulkan12Features features12 {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES, .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES,
.pNext = &bit16,
.shaderFloat16 = VK_TRUE,
.runtimeDescriptorArray = VK_TRUE, .runtimeDescriptorArray = VK_TRUE,
.bufferDeviceAddress = VK_TRUE .bufferDeviceAddress = VK_TRUE
}; };
VkPhysicalDeviceRayTracingPositionFetchFeaturesKHR vkPhysicalDeviceRayTracingPositionFetchFeatures {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_POSITION_FETCH_FEATURES_KHR,
.pNext = &features12,
.rayTracingPositionFetch = VK_TRUE
};
VkPhysicalDeviceRayTracingPipelineFeaturesKHR physicalDeviceRayTracingPipelineFeatures{ VkPhysicalDeviceRayTracingPipelineFeaturesKHR physicalDeviceRayTracingPipelineFeatures{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_FEATURES_KHR, .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_FEATURES_KHR,
.pNext = &vkPhysicalDeviceRayTracingPositionFetchFeatures, .pNext = &features12,
.rayTracingPipeline = VK_TRUE .rayTracingPipeline = VK_TRUE
}; };
@ -617,21 +743,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;
@ -684,6 +803,11 @@ 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"));
vkGetDeviceFaultInfoEXT = reinterpret_cast<PFN_vkGetDeviceFaultInfoEXT>(vkGetInstanceProcAddr(instance, "vkGetDeviceFaultInfoEXT"));
#endif #endif
} }

View file

@ -56,4 +56,15 @@ Font::Font(const std::filesystem::path& fontFilePath) {
this->ascent = ascent; this->ascent = ascent;
this->descent = descent; this->descent = descent;
this->lineGap = lineGap; this->lineGap = lineGap;
}
std::uint32_t Font::GetLineWidth(const std::string_view text, float size) {
float scale = stbtt_ScaleForPixelHeight(&font, size);
std::uint32_t lineWidth = 0;
for (const char c : text) {
int advance, lsb;
stbtt_GetCodepointHMetrics(&font, c, &advance, &lsb);
lineWidth += (int)(advance * scale);
}
return lineWidth;
} }

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

@ -0,0 +1,141 @@
/*
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
module Crafter.Graphics:Rendertarget_impl;
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
import :Rendertarget;
import :Window;
import :DescriptorHeapVulkan;
import :RenderingElement2DVulkan;
import std;
using namespace Crafter;
RendertargetVulkan::RendertargetVulkan(std::uint16_t sizeX, std::uint16_t sizeY) : RendertargetBase(sizeX, sizeY) {
}
void RendertargetVulkan::UpdateElements() {
elements.clear();
std::sort(transform.children.begin(), transform.children.end(), [](Transform2D* a, Transform2D* b){ return a->anchor.z < b->anchor.z; });
for(Transform2D* child : transform.children) {
SetOrderResursive(child);
}
}
void RendertargetVulkan::CreateBuffer(std::uint8_t frame) {
transformBuffer[frame].Resize(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, elements.size()+1);
RenderingElement2DVulkanTransformInfo* val = reinterpret_cast<RenderingElement2DVulkanTransformInfo*>(reinterpret_cast<char*>(transformBuffer[frame].value) + sizeof(RenderingElement2DVulkanTransformInfo));
std::uint16_t* sizePtr = reinterpret_cast<std::uint16_t*>(transformBuffer[frame].value);
*sizePtr = static_cast<std::uint16_t>(elements.size());
for(std::uint16_t i = 0; i < elements.size(); i++) {
val[i].bufferX = elements[i]->bufferX;
val[i].bufferY = elements[i]->bufferY;
}
transformBuffer[frame].FlushDevice();
}
void RendertargetVulkan::ReorderBuffer(std::uint8_t frame) {
RenderingElement2DVulkanTransformInfo* val = reinterpret_cast<RenderingElement2DVulkanTransformInfo*>(reinterpret_cast<char*>(transformBuffer[frame].value) + sizeof(RenderingElement2DVulkanTransformInfo));
for(std::uint16_t i = 0; i < elements.size(); i++) {
val[i].scaled = elements[i]->scaled;
val[i].bufferX = elements[i]->bufferX;
val[i].bufferY = elements[i]->bufferY;
}
transformBuffer[frame].FlushDevice();
}
void RendertargetVulkan::WriteDescriptors(std::span<VkResourceDescriptorInfoEXT> infos, std::span<VkHostAddressRangeEXT> ranges, std::uint16_t start, std::uint32_t bufferOffset, DescriptorHeapVulkan& descriptorHeap) {
VkDeviceAddressRangeKHR transformRanges[Window::numFrames] = {
{
.address = transformBuffer[0].address,
.size = transformBuffer[0].size
},
{
.address = transformBuffer[1].address,
.size = transformBuffer[1].size
},
{
.address = transformBuffer[2].address,
.size = transformBuffer[2].size
}
};
for(std::uint8_t i = 0; i < Window::numFrames; i++) {
ranges[start + i] = {
.address = descriptorHeap.resourceHeap[i].value + bufferOffset,
.size = Device::descriptorHeapProperties.bufferDescriptorSize
};
infos[start + i] = {
.sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT,
.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.data = { .pAddressRange = &transformRanges[i]}
};
}
start += 3;
bufferOffset += Device::descriptorHeapProperties.bufferDescriptorSize;
std::vector<VkDeviceAddressRangeKHR> bufferRanges(elements.size() * Window::numFrames);
std::uint16_t rangeOffset = 0;
for(std::uint8_t i2 = 0; i2 < Window::numFrames; i2++) {
for(std::uint16_t i = 0; i < elements.size(); i++) {
ranges[start + i] = {
.address = descriptorHeap.resourceHeap[i2].value + bufferOffset + Device::descriptorHeapProperties.bufferDescriptorSize * i,
.size = Device::descriptorHeapProperties.bufferDescriptorSize
};
infos[start + i] = {
.sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT,
.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.data = { .pAddressRange = &bufferRanges[i]}
};
bufferRanges[rangeOffset + i] = {
.address = elements[i]->buffers[i2]->address,
.size = elements[i]->buffers[i2]->size
};
}
start += elements.size();
rangeOffset += elements.size();
}
Device::vkWriteResourceDescriptorsEXT(Device::device, start, infos.data(), ranges.data());
for(std::uint8_t i = 0; i < Window::numFrames; i++) {
descriptorHeap.resourceHeap[i].FlushDevice();
}
}
void RendertargetVulkan::SetOrderResursive(Transform2D* elementTransform) {
RenderingElement2DVulkanBase* renderer = dynamic_cast<RenderingElement2DVulkanBase*>(elementTransform);
if(renderer) {
renderer->index = elements.size();
elements.push_back(renderer);
}
std::sort(elementTransform->children.begin(), elementTransform->children.end(), [](Transform2D* a, Transform2D* b){ return a->anchor.z < b->anchor.z; });
for(Transform2D* childTransform : elementTransform->children) {
SetOrderResursive(childTransform);
}
}
#endif

View file

@ -28,6 +28,6 @@ import std;
using namespace Crafter; using namespace Crafter;
Anchor2D::Anchor2D(float x, float y, float width, float height, float offsetX, float offsetY, std::int32_t z, bool maintainAspectRatio): x(x), y(y), width(width), height(height), offsetX(offsetX), offsetY(offsetY), z(z), maintainAspectRatio(maintainAspectRatio) { Anchor2D::Anchor2D(float x, float y, float width, float height, float offsetX, float offsetY, std::uint8_t z, bool maintainAspectRatio): x(x), y(y), width(width), height(height), offsetX(offsetX), offsetY(offsetY), z(z), maintainAspectRatio(maintainAspectRatio) {
} }

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;
@ -270,49 +272,65 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
PostQuitMessage(0); PostQuitMessage(0);
break; break;
} }
case WM_KEYDOWN:{ case WM_KEYDOWN:
if ((lParam & (1 << 30)) == 0) { // only first press case WM_SYSKEYDOWN: { // SYSKEYDOWN catches Alt combos, F10, etc.
CrafterKeys crafterKey = vk_to_crafter_key(wParam); CrafterKeys crafterKey = vk_to_crafter_key(wParam);
if(window->heldkeys[static_cast<std::uint8_t>(crafterKey)]) { bool isRepeat = (lParam & (1 << 30)) != 0;
window->onKeyHold[static_cast<std::uint8_t>(crafterKey)].Invoke();
window->onAnyKeyHold.Invoke(crafterKey); if (isRepeat) {
} else{ window->onKeyHold[(uint8_t)crafterKey].Invoke();
window->heldkeys[static_cast<std::uint8_t>(crafterKey)] = true; window->onAnyKeyHold.Invoke(crafterKey);
window->onKeyDown[static_cast<std::uint8_t>(crafterKey)].Invoke(); } else {
window->onAnyKeyDown.Invoke(crafterKey); window->heldkeys[(uint8_t)crafterKey] = true;
} window->onKeyDown[(uint8_t)crafterKey].Invoke();
window->onAnyKeyDown.Invoke(crafterKey);
} }
break; break;
} }
case WM_KEYUP: {
case WM_KEYUP:
case WM_SYSKEYUP: {
CrafterKeys crafterKey = vk_to_crafter_key(wParam); CrafterKeys crafterKey = vk_to_crafter_key(wParam);
window->heldkeys[static_cast<std::uint8_t>(crafterKey)] = false; window->heldkeys[(uint8_t)crafterKey] = false;
window->onKeyUp[static_cast<std::uint8_t>(crafterKey)].Invoke(); window->onKeyUp[(uint8_t)crafterKey].Invoke();
window->onAnyKeyUp.Invoke(crafterKey); window->onAnyKeyUp.Invoke(crafterKey);
break; break;
} }
case WM_MOUSEMOVE: {
int x = LOWORD(lParam);
int y = HIWORD(lParam);
Vector<float, 2> pos(x, y); case WM_CHAR: {
window->currentMousePos = pos; // wParam is a UTF-16 code unit. May be a surrogate — buffer until we have a pair.
window->onMouseMove.Invoke(); wchar_t wc = (wchar_t)wParam;
for(MouseElement* element : window->mouseElements) {
if(element) { // Filter control characters (backspace=0x08, tab=0x09, enter=0x0D, escape=0x1B, etc.)
if(window->currentMousePos.x >= element->scaled.position.x && window->currentMousePos.x <= element->scaled.position.x+element->scaled.size.x && window->currentMousePos.y > element->scaled.position.y && window->currentMousePos.y < element->scaled.position.y+element->scaled.size.y) { if (wc < 0x20 || wc == 0x7f) break;
element->onMouseMove.Invoke();
if(!element->mouseHover) { // Handle UTF-16 surrogate pairs (characters outside the BMP, e.g. emoji).
element->mouseHover = true; static wchar_t highSurrogate = 0;
element->onMouseEnter.Invoke(); wchar_t utf16[2];
} int utf16Len;
} else if(element->mouseHover) {
element->mouseHover = false if (wc >= 0xD800 && wc <= 0xDBFF) {
element->onMouseLeave.Invoke(); // High surrogate — stash it and wait for the low surrogate.
} highSurrogate = wc;
} break;
} } else if (wc >= 0xDC00 && wc <= 0xDFFF) {
window->mouseElements.erase(std::remove(window->mouseElements.begin(), window->mouseElements.end(), static_cast<MouseElement*>(nullptr)), window->mouseElements.end()); // Low surrogate — pair with the stashed high surrogate.
if (highSurrogate == 0) break; // orphaned low surrogate, ignore
utf16[0] = highSurrogate;
utf16[1] = wc;
utf16Len = 2;
highSurrogate = 0;
} else {
utf16[0] = wc;
utf16Len = 1;
}
// Convert UTF-16 to UTF-8.
char utf8[8];
int n = WideCharToMultiByte(CP_UTF8, 0, utf16, utf16Len, utf8, sizeof(utf8), nullptr, nullptr);
if (n > 0) {
window->onTextInput.Invoke(std::string(utf8, n));
}
break; break;
} }
case WM_LBUTTONDOWN: { case WM_LBUTTONDOWN: {
@ -367,6 +385,14 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
break; break;
} }
case WM_SETCURSOR: {
if (LOWORD(lParam) == HTCLIENT && window->cursorHandle) {
SetCursor(window->cursorHandle);
return TRUE;
}
break;
}
default: return DefWindowProc(hwnd, msg, wParam, lParam); default: return DefWindowProc(hwnd, msg, wParam, lParam);
} }
return 0; return 0;
@ -596,6 +622,34 @@ void Window::SetCusorImage(std::uint16_t sizeX, std::uint16_t sizeY) {
wl_surface_damage(cursorSurface, 0, 0, sizeX, sizeY); wl_surface_damage(cursorSurface, 0, 0, sizeX, sizeY);
wl_surface_commit(cursorSurface); wl_surface_commit(cursorSurface);
#endif #endif
#ifdef CRAFTER_GRAPHICS_WINDOW_WIN32
if (cursorBitmap) {
DeleteObject(cursorBitmap);
}
if (cursorHandle) {
DestroyCursor(cursorHandle);
cursorHandle = nullptr;
}
BITMAPINFO bmi = {};
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = sizeX;
bmi.bmiHeader.biHeight = -(int)sizeY; // top-down
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
HDC hdc = GetDC(nullptr);
cursorBitmap = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, reinterpret_cast<void**>(&cursorRenderer.buffer[0]), nullptr, 0);
ReleaseDC(nullptr, hdc);
if (!cursorBitmap) {
throw std::runtime_error("CreateDIBSection failed for cursor");
}
cursorSizeX = sizeX;
cursorSizeY = sizeY;
#endif
} }
void Window::SetCusorImageDefault() { void Window::SetCusorImageDefault() {
@ -604,9 +658,21 @@ void Window::SetCusorImageDefault() {
wl_surface_destroy(cursorSurface); wl_surface_destroy(cursorSurface);
cursorSurface = nullptr; cursorSurface = nullptr;
#endif #endif
#ifdef CRAFTER_GRAPHICS_WINDOW_WIN32
if (cursorHandle) {
DestroyCursor(cursorHandle);
cursorHandle = nullptr;
}
if (cursorBitmap) {
DeleteObject(cursorBitmap);
cursorBitmap = nullptr;
}
// Setting nullptr will make WM_SETCURSOR fall through to the default
#endif
} }
void Window::UpdateCursorImage() { void Window::UpdateCursorImage() {
#ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND
cursorRenderer.Render(0); cursorRenderer.Render(0);
for(std::uint32_t i = 0; i < cursorBufferOldSize / 4; i++) { for(std::uint32_t i = 0; i < cursorBufferOldSize / 4; i++) {
std::swap(cursorRenderer.buffer[0][i].b, cursorRenderer.buffer[0][i].r); std::swap(cursorRenderer.buffer[0][i].b, cursorRenderer.buffer[0][i].r);
@ -614,6 +680,35 @@ void Window::UpdateCursorImage() {
wl_surface_attach(cursorSurface, cursorWlBuffer, 0, 0); wl_surface_attach(cursorSurface, cursorWlBuffer, 0, 0);
wl_surface_damage(cursorSurface, 0, 0, 9999999, 99999999); wl_surface_damage(cursorSurface, 0, 0, 9999999, 99999999);
wl_surface_commit(cursorSurface); wl_surface_commit(cursorSurface);
#endif
#ifdef CRAFTER_GRAPHICS_WINDOW_WIN32
cursorRenderer.Render(0);
// Swap R and B channels (renderer is RGBA, GDI DIB is BGRA)
for (std::uint32_t i = 0; i < (std::uint32_t)(cursorSizeX * cursorSizeY); i++) {
std::swap(cursorRenderer.buffer[0][i].r, cursorRenderer.buffer[0][i].b);
}
// Create a mask bitmap (all zeros = fully opaque, alpha comes from color bitmap)
HBITMAP hMask = CreateBitmap(cursorSizeX, cursorSizeY, 1, 1, nullptr);
ICONINFO ii = {};
ii.fIcon = FALSE;
ii.xHotspot = 0;
ii.yHotspot = 0;
ii.hbmMask = hMask;
ii.hbmColor = cursorBitmap;
if (cursorHandle) {
DestroyCursor(cursorHandle);
}
cursorHandle = (HCURSOR)CreateIconIndirect(&ii);
DeleteObject(hMask);
if (cursorHandle) {
SetCursor(cursorHandle);
}
#endif
} }
void Window::StartSync() { void Window::StartSync() {
@ -736,19 +831,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);
VkBindDescriptorSetsInfo bindDescriptorSetsInfo{
.sType = VK_STRUCTURE_TYPE_BIND_DESCRIPTOR_SETS_INFO, VkBindHeapInfoEXT resourceHeapInfo = {
.stageFlags = VK_SHADER_STAGE_ALL, .sType = VK_STRUCTURE_TYPE_BIND_HEAP_INFO_EXT,
.layout = rtPipelineLayout, .heapRange = {
.firstSet = 0, .address = descriptorHeap->resourceHeap[currentBuffer].address,
.descriptorSetCount = static_cast<std::uint32_t>(descriptorsRt.size()), .size = static_cast<std::uint32_t>(descriptorHeap->resourceHeap[currentBuffer].size)
.pDescriptorSets = descriptorsRt.data() },
.reservedRangeOffset = (descriptorHeap->resourceHeap[currentBuffer].size - Device::descriptorHeapProperties.minResourceHeapReservedRange) & ~(Device::descriptorHeapProperties.imageDescriptorAlignment - 1),
.reservedRangeSize = Device::descriptorHeapProperties.minResourceHeapReservedRange
}; };
Device::vkCmdBindResourceHeapEXT(drawCmdBuffers[currentBuffer], &resourceHeapInfo);
vkCmdBindDescriptorSets2(drawCmdBuffers[currentBuffer], &bindDescriptorSetsInfo); VkBindHeapInfoEXT samplerHeapInfo = {
Device::vkCmdTraceRaysKHR(drawCmdBuffers[currentBuffer], &raygenRegion, &missRegion, &hitRegion, &callableRegion, width, height, 1); .sType = VK_STRUCTURE_TYPE_BIND_HEAP_INFO_EXT,
.heapRange = {
.address = descriptorHeap->samplerHeap[currentBuffer].address,
.size = static_cast<std::uint32_t>(descriptorHeap->samplerHeap[currentBuffer].size)
},
.reservedRangeOffset = descriptorHeap->samplerHeap[currentBuffer].size - 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,
@ -918,9 +1025,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 };
@ -929,34 +1033,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,
VK_COMPONENT_SWIZZLE_R, .components = {
VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_R,
VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_G,
VK_COMPONENT_SWIZZLE_A VK_COMPONENT_SWIZZLE_B,
}; VK_COMPONENT_SWIZZLE_A
colorAttachmentView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; },
colorAttachmentView.subresourceRange.baseMipLevel = 0; .subresourceRange = {
colorAttachmentView.subresourceRange.levelCount = 1; .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
colorAttachmentView.subresourceRange.baseArrayLayer = 0; .baseMipLevel = 0,
colorAttachmentView.subresourceRange.layerCount = 1; .levelCount = 1,
colorAttachmentView.viewType = VK_IMAGE_VIEW_TYPE_2D; .baseArrayLayer = 0,
colorAttachmentView.flags = 0; .layerCount = 1,
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,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;
#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 {
VulkanBuffer<std::uint8_t, true> resourceHeap[Window::numFrames];
VulkanBuffer<std::uint8_t, true> samplerHeap[Window::numFrames];
std::uint32_t bufferStartOffset;
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].Resize(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].Resize(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);
}
}
inline static std::uint32_t GetBufferOffset(std::uint16_t images, std::uint16_t buffers) {
std::uint32_t bufferStartElement = images * Device::descriptorHeapProperties.imageDescriptorSize / Device::descriptorHeapProperties.bufferDescriptorSize;
if(images > 0 && bufferStartElement == 0) {
bufferStartElement = 1;
}
return bufferStartElement * Device::descriptorHeapProperties.bufferDescriptorSize;
}
inline static std::uint16_t GetBufferOffsetElement(std::uint16_t images, std::uint16_t buffers) {
std::uint16_t bufferStartElement = images * Device::descriptorHeapProperties.imageDescriptorSize / Device::descriptorHeapProperties.bufferDescriptorSize;
if(images > 0 && bufferStartElement == 0) {
bufferStartElement = 1;
}
return bufferStartElement;
}
};
}
#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,20 @@ 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 PFN_vkGetDeviceFaultInfoEXT vkGetDeviceFaultInfoEXT;
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

@ -33,6 +33,7 @@ namespace Crafter {
std::int_fast32_t descent; std::int_fast32_t descent;
std::int_fast32_t lineGap; std::int_fast32_t lineGap;
stbtt_fontinfo font; stbtt_fontinfo font;
Font(const std::filesystem::path& font); Font(const std::filesystem::path& font);
std::uint32_t GetLineWidth(const std::string_view text, float size);
}; };
} }

View file

@ -21,9 +21,7 @@ export module Crafter.Graphics:ForwardDeclarations;
import std; import std;
export namespace Crafter { export namespace Crafter {
template<std::uint8_t Frames = 1>
struct RendertargetBase; struct RendertargetBase;
struct GridElement; struct GridElement;
struct Window; struct Window;
} }

View file

@ -33,8 +33,7 @@ export namespace Crafter {
GridElement(std::uint32_t columns, std::uint32_t rows, std::int32_t spacingX, std::int32_t spacingY, std::int32_t paddingX, std::int32_t paddingY, Anchor2D anchor) : Transform2D(anchor), columns(columns), rows(rows), spacingX(spacingX), spacingY(spacingY), paddingX(paddingX), paddingY(paddingY) { GridElement(std::uint32_t columns, std::uint32_t rows, std::int32_t spacingX, std::int32_t spacingY, std::int32_t paddingX, std::int32_t paddingY, Anchor2D anchor) : Transform2D(anchor), columns(columns), rows(rows), spacingX(spacingX), spacingY(spacingY), paddingX(paddingX), paddingY(paddingY) {
} }
template<std::uint8_t Frames> void UpdatePosition(RendertargetBase& window, Transform2D& parent) override {
void UpdatePositionImpl(RendertargetBase<Frames>& window, Transform2D& parent) {
ScaleElement(parent); ScaleElement(parent);
std::int32_t cellWidth = (paddingX * 2) - (spacingX * (columns - 1)) / columns; std::int32_t cellWidth = (paddingX * 2) - (spacingX * (columns - 1)) / columns;
std::int32_t cellHeight = (paddingY * 2) - (spacingY * (rows - 1)) / rows; std::int32_t cellHeight = (paddingY * 2) - (spacingY * (rows - 1)) / rows;
@ -61,11 +60,5 @@ export namespace Crafter {
} }
} }
} }
void UpdatePosition(RendertargetBase<1>& window, Transform2D& parent) override {
UpdatePositionImpl<1>(window, parent);
}
void UpdatePosition(RendertargetBase<3>& window, Transform2D& parent) override {
UpdatePositionImpl<3>(window, parent);
}
}; };
} }

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> 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> scratchBuffer;
VulkanBuffer<char, false, true, false> blasBuffer; VulkanBuffer<char, false> blasBuffer;
VulkanBuffer<Vector<float, 3, 3>, true, true, false> vertexBuffer; VulkanBuffer<Vector<float, 3, 3>, true> vertexBuffer;
VulkanBuffer<std::uint32_t, true, true, false> indexBuffer; VulkanBuffer<std::uint32_t, 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> 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());
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, .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));
@ -113,189 +110,10 @@ export namespace Crafter {
callableRegion.stride = 0; callableRegion.stride = 0;
callableRegion.size = 0; callableRegion.size = 0;
} }
};
template <std::uint32_t GeneralShader, std::uint32_t ClosestHitShader, std::uint32_t AnyHitShader, std::uint32_t IntersectionShader> ~PipelineRTVulkan() {
struct ShaderGroup { vkDestroyPipeline(Device::device, pipeline, nullptr);
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), ...);
}
}; };
} }

View file

@ -29,31 +29,31 @@ import :Types;
import :Window; import :Window;
export namespace Crafter { export namespace Crafter {
template<bool Scaling, bool Owning, bool Rotating, std::uint8_t Alignment = 0, std::uint8_t Frames = 1> requires ((!Rotating || Scaling) && (!Owning || Scaling)) template<typename T, bool Scaling, bool Owning, bool Rotating, std::uint8_t Alignment = 0, std::uint8_t Frames = 1> requires ((!Rotating || Scaling) && (!Owning || Scaling))
struct RenderingElement2D : RenderingElement2DBase<Frames>, ScalingBase<Scaling, Owning, Alignment>, RotatingBase<Rotating> { struct RenderingElement2D : RenderingElement2DBase<T, Frames>, ScalingBase<T, Scaling, Owning, Alignment>, RotatingBase<Rotating> {
RenderingElement2D() = default; RenderingElement2D() = default;
RenderingElement2D(Anchor2D anchor, OpaqueType opaque) : RenderingElement2DBase<Frames>(anchor, opaque) { RenderingElement2D(Anchor2D anchor, OpaqueType opaque) : RenderingElement2DBase<T, Frames>(anchor, opaque) {
} }
RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t rotation) requires(Rotating) : RenderingElement2DBase<Frames>(anchor, opaque), RotatingBase<Rotating>(rotation) { RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t rotation) requires(Rotating) : RenderingElement2DBase<T, Frames>(anchor, opaque), RotatingBase<Rotating>(rotation) {
} }
RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight, Vector<std::uint8_t, 4, Alignment>* scalingBuffer) requires(Scaling && !Owning) : RenderingElement2DBase<Frames>(anchor, opaque), ScalingBase<Scaling, Owning, Alignment>(bufferWidth, bufferHeight, scalingBuffer) { RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight, Vector<T, 4, Alignment>* scalingBuffer) requires(Scaling && !Owning) : RenderingElement2DBase<T, Frames>(anchor, opaque), ScalingBase<T, Scaling, Owning, Alignment>(bufferWidth, bufferHeight, scalingBuffer) {
} }
RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight, Vector<std::uint8_t, 4, Alignment>* scalingBuffer, std::uint32_t rotation) requires(Scaling && !Owning && Rotating) : RenderingElement2DBase<Frames>(anchor, opaque), ScalingBase<Scaling, Owning, Alignment>(bufferWidth, bufferHeight, scalingBuffer), RotatingBase<Rotating>(rotation) { RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight, Vector<T, 4, Alignment>* scalingBuffer, std::uint32_t rotation) requires(Scaling && !Owning && Rotating) : RenderingElement2DBase<T, Frames>(anchor, opaque), ScalingBase<T, Scaling, Owning, Alignment>(bufferWidth, bufferHeight, scalingBuffer), RotatingBase<Rotating>(rotation) {
} }
RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight) requires(Owning) : RenderingElement2DBase<Frames>(anchor, opaque), ScalingBase<Scaling, Owning, Alignment>(bufferWidth, bufferHeight) { RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight) requires(Owning) : RenderingElement2DBase<T, Frames>(anchor, opaque), ScalingBase<T, Scaling, Owning, Alignment>(bufferWidth, bufferHeight) {
} }
RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight, std::uint32_t rotation) requires(Owning && Rotating) : RenderingElement2DBase<Frames>(anchor, opaque), ScalingBase<Scaling, Owning, Alignment>(bufferWidth, bufferHeight) , RotatingBase<Rotating>(rotation) { RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight, std::uint32_t rotation) requires(Owning && Rotating) : RenderingElement2DBase<T, Frames>(anchor, opaque), ScalingBase<T, Scaling, Owning, Alignment>(bufferWidth, bufferHeight) , RotatingBase<Rotating>(rotation) {
} }
RenderingElement2D(Anchor2D anchor, TextureAsset<Vector<std::uint8_t, 4, Alignment>>& texture) requires(!Owning && Scaling) : RenderingElement2DBase<Frames>(anchor, texture.opaque), ScalingBase<Scaling, Owning, Alignment>(texture.pixels.data(), texture.sizeX, texture.sizeY) { RenderingElement2D(Anchor2D anchor, TextureAsset<Vector<T, 4, Alignment>>& texture) requires(!Owning && Scaling) : RenderingElement2DBase<T, Frames>(anchor, texture.opaque), ScalingBase<T, Scaling, Owning, Alignment>(texture.pixels.data(), texture.sizeX, texture.sizeY) {
} }
RenderingElement2D(Anchor2D anchor, TextureAsset<Vector<std::uint8_t, 4, Alignment>>& texture, std::uint32_t rotation) requires(!Owning && Scaling && Rotating) : RenderingElement2DBase<Frames>(anchor, texture.opaque), ScalingBase<Scaling, Owning, Alignment>(texture.pixels.data(), texture.sizeX, texture.sizeY), RotatingBase<Rotating>(rotation) { RenderingElement2D(Anchor2D anchor, TextureAsset<Vector<T, 4, Alignment>>& texture, std::uint32_t rotation) requires(!Owning && Scaling && Rotating) : RenderingElement2DBase<T, Frames>(anchor, texture.opaque), ScalingBase<T, Scaling, Owning, Alignment>(texture.pixels.data(), texture.sizeX, texture.sizeY), RotatingBase<Rotating>(rotation) {
} }
@ -62,10 +62,10 @@ export namespace Crafter {
void ScaleNearestNeighbor() requires(Scaling) { void ScaleNearestNeighbor() requires(Scaling) {
for (std::uint32_t y = 0; y < this->scaled.size.y; y++) { for (std::uint32_t y = 0; y < this->scaled.size.y; y++) {
std::uint32_t srcY = y * ScalingBase<true, Owning, Alignment>::bufferHeight / this->scaled.size.y; std::uint32_t srcY = y * ScalingBase<T, true, Owning, Alignment>::bufferHeight / this->scaled.size.y;
for (std::uint32_t x = 0; x < this->scaled.size.x; x++) { for (std::uint32_t x = 0; x < this->scaled.size.x; x++) {
std::uint32_t srcX = x * ScalingBase<true, Owning, Alignment>::bufferWidth / this->scaled.size.x; std::uint32_t srcX = x * ScalingBase<T, true, Owning, Alignment>::bufferWidth / this->scaled.size.x;
this->buffer[y * this->scaled.size.x + x] = ScalingBase<true, Owning, Alignment>::scalingBuffer[srcY * ScalingBase<true, Owning, Alignment>::bufferWidth + srcX]; this->buffer[y * this->scaled.size.x + x] = ScalingBase<T, true, Owning, Alignment>::scalingBuffer[srcY * ScalingBase<T, true, Owning, Alignment>::bufferWidth + srcX];
} }
} }
} }
@ -97,15 +97,15 @@ export namespace Crafter {
const float dstCy = (this->scaled.size.y - 1.0) * 0.5; const float dstCy = (this->scaled.size.y - 1.0) * 0.5;
// Source center // Source center
const float srcCx = (ScalingBase<true, Owning, Alignment>::bufferWidth - 1.0) * 0.5; const float srcCx = (ScalingBase<T, true, Owning, Alignment>::bufferWidth - 1.0) * 0.5;
const float srcCy = (ScalingBase<true, Owning, Alignment>::bufferHeight - 1.0) * 0.5; const float srcCy = (ScalingBase<T, true, Owning, Alignment>::bufferHeight - 1.0) * 0.5;
const float c = std::cos(RotatingBase<true>::rotation); const float c = std::cos(RotatingBase<true>::rotation);
const float s = std::sin(RotatingBase<true>::rotation); const float s = std::sin(RotatingBase<true>::rotation);
// Scale factors (destination → source) // Scale factors (destination → source)
const float scaleX = static_cast<float>(ScalingBase<true, Owning, Alignment>::bufferWidth) / dstWidth; const float scaleX = static_cast<float>(ScalingBase<T, true, Owning, Alignment>::bufferWidth) / dstWidth;
const float scaleY = static_cast<float>(ScalingBase<true, Owning, Alignment>::bufferHeight) / dstHeight; const float scaleY = static_cast<float>(ScalingBase<T, true, Owning, Alignment>::bufferHeight) / dstHeight;
for (std::uint32_t yB = 0; yB < this->scaled.size.y; ++yB) { for (std::uint32_t yB = 0; yB < this->scaled.size.y; ++yB) {
for (std::uint32_t xB = 0; xB < this->scaled.size.x; ++xB) { for (std::uint32_t xB = 0; xB < this->scaled.size.x; ++xB) {
@ -122,15 +122,15 @@ export namespace Crafter {
const std::int32_t srcX = static_cast<std::int32_t>(std::round(sx)); const std::int32_t srcX = static_cast<std::int32_t>(std::round(sx));
const std::int32_t srcY = static_cast<std::int32_t>(std::round(sy)); const std::int32_t srcY = static_cast<std::int32_t>(std::round(sy));
if (srcX >= 0 && srcX < ScalingBase<true, Owning, Alignment>::bufferWidth && srcY >= 0 && srcY < ScalingBase<true, Owning, Alignment>::bufferHeight) { if (srcX >= 0 && srcX < ScalingBase<T, true, Owning, Alignment>::bufferWidth && srcY >= 0 && srcY < ScalingBase<T, true, Owning, Alignment>::bufferHeight) {
this->buffer[yB * this->scaled.size.x + xB] = ScalingBase<true, Owning, Alignment>::scalingBuffer[srcY * ScalingBase<true, Owning, Alignment>::bufferWidth + srcX]; this->buffer[yB * this->scaled.size.x + xB] = ScalingBase<T, true, Owning, Alignment>::scalingBuffer[srcY * ScalingBase<T, true, Owning, Alignment>::bufferWidth + srcX];
} }
} }
} }
} }
void UpdatePosition(RendertargetBase<Frames>& window, Transform2D& parent) override { void UpdatePosition(RendertargetBase& window, Transform2D& parent) override {
ScaleData2D oldScale = this->scaled; ScaleData2D oldScale = this->scaled;
this->ScaleElement(parent); this->ScaleElement(parent);
if constexpr(Scaling && !Rotating) { if constexpr(Scaling && !Rotating) {
@ -153,7 +153,7 @@ export namespace Crafter {
} }
} }
std::vector<std::string_view> ResizeText(RendertargetBase<Frames>& window, Transform2D& parent, const std::string_view text, float& size, Font& font, TextOverflowMode overflowMode = TextOverflowMode::Clip, TextScaleMode scaleMode = TextScaleMode::None) { std::vector<std::string_view> ResizeText(RendertargetBase& window, Transform2D& parent, const std::string_view text, float& size, Font& font, TextOverflowMode overflowMode = TextOverflowMode::Clip, TextScaleMode scaleMode = TextScaleMode::None) {
float scale = stbtt_ScaleForPixelHeight(&font.font, size); float scale = stbtt_ScaleForPixelHeight(&font.font, size);
int baseline = (int)(font.ascent * scale); int baseline = (int)(font.ascent * scale);
@ -208,7 +208,7 @@ export namespace Crafter {
size = std::min(maxFontHeight, maxFontWidth); size = std::min(maxFontHeight, maxFontWidth);
} else { } else {
if constexpr(Scaling) { if constexpr(Scaling) {
lines.resize(ScalingBase<true, Owning, Alignment>::bufferHeight / lines.size()); lines.resize(ScalingBase<T, true, Owning, Alignment>::bufferHeight / lines.size());
} else { } else {
lines.resize(this->scaled.size.y / lines.size()); lines.resize(this->scaled.size.y / lines.size());
} }
@ -284,7 +284,7 @@ export namespace Crafter {
size = this->scaled.size.y / lineHeightPerFont; size = this->scaled.size.y / lineHeightPerFont;
} else { } else {
if constexpr(Scaling) { if constexpr(Scaling) {
lines.resize(ScalingBase<true, Owning, Alignment>::bufferHeight / lines.size()); lines.resize(ScalingBase<T, true, Owning, Alignment>::bufferHeight / lines.size());
} else { } else {
lines.resize(this->scaled.size.y / lines.size()); lines.resize(this->scaled.size.y / lines.size());
} }
@ -293,7 +293,27 @@ export namespace Crafter {
return lines; return lines;
} }
void RenderText(std::span<const std::string_view> lines, float size, Vector<std::uint8_t, 4> color, Font& font, TextAlignment alignment = TextAlignment::Left, std::uint32_t offsetX = 0, std::uint32_t offsetY = 0, OpaqueType opaque = OpaqueType::FullyOpaque) {
int utf8_decode(const char* s, int* bytes_consumed) {
unsigned char c = s[0];
if (c < 0x80) {
*bytes_consumed = 1;
return c;
} else if ((c & 0xE0) == 0xC0) {
*bytes_consumed = 2;
return ((c & 0x1F) << 6) | (s[1] & 0x3F);
} else if ((c & 0xF0) == 0xE0) {
*bytes_consumed = 3;
return ((c & 0x0F) << 12) | ((s[1] & 0x3F) << 6) | (s[2] & 0x3F);
} else if ((c & 0xF8) == 0xF0) {
*bytes_consumed = 4;
return ((c & 0x07) << 18) | ((s[1] & 0x3F) << 12) | ((s[2] & 0x3F) << 6) | (s[3] & 0x3F);
}
*bytes_consumed = 1;
return 0xFFFD; // replacement char
}
void RenderText(std::span<const std::string_view> lines, float size, Vector<T, 4> color, Font& font, TextAlignment alignment = TextAlignment::Left, std::uint32_t offsetX = 0, std::uint32_t offsetY = 0, OpaqueType opaque = OpaqueType::FullyOpaque) {
float scale = stbtt_ScaleForPixelHeight(&font.font, size); float scale = stbtt_ScaleForPixelHeight(&font.font, size);
int baseline = (int)(font.ascent * scale); int baseline = (int)(font.ascent * scale);
std::uint32_t lineHeight = (font.ascent - font.descent) * scale; std::uint32_t lineHeight = (font.ascent - font.descent) * scale;
@ -320,8 +340,13 @@ export namespace Crafter {
break; break;
} }
for (std::size_t i = 0; i < line.size(); ++i) { const char* p = line.data();
int codepoint = line[i]; const char* end = p + line.size();
while (p < end) {
int bytes;
int codepoint = utf8_decode(p, &bytes);
p += bytes;
int ax; int ax;
int lsb; int lsb;
@ -346,41 +371,19 @@ export namespace Crafter {
// Only draw pixels that are within our scaled buffer bounds // Only draw pixels that are within our scaled buffer bounds
if constexpr(Scaling) { if constexpr(Scaling) {
if (bufferX >= 0 && bufferX < ScalingBase<true, Owning, Alignment>::bufferWidth && bufferY >= 0 && bufferY < ScalingBase<true, Owning, Alignment>::bufferHeight) { if (bufferX >= 0 && bufferX < ScalingBase<T, true, Owning, Alignment>::bufferWidth && bufferY >= 0 && bufferY < ScalingBase<T, true, Owning, Alignment>::bufferHeight) {
ScalingBase<true, Owning, Alignment>::scalingBuffer[bufferY * ScalingBase<true, Owning, Alignment>::bufferWidth + bufferX] = {color.r, color.g, color.b, bitmap[j * w + i]}; 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 { } else {
if (bufferX >= 0 && bufferX < (int)this->scaled.size.x && bufferY >= 0 && bufferY < (int)this->scaled.size.y) { if (bufferX >= 0 && bufferX < (int)this->scaled.size.x && bufferY >= 0 && bufferY < (int)this->scaled.size.y) {
this->buffer[bufferY * this->scaled.size.x + bufferX] = {color.r, color.g, color.b, bitmap[j * w + i]}; 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: {
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<true, Owning, Alignment>::bufferWidth && bufferY >= 0 && bufferY < ScalingBase<true, Owning, Alignment>::bufferHeight) {
ScalingBase<true, Owning, Alignment>::scalingBuffer[bufferY * ScalingBase<true, Owning, Alignment>::bufferWidth + bufferX] = {color.r, color.g, color.b, 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, bitmap[j * w + i]};
}
} }
} }
} }
} }
break; break;
} }
case OpaqueType::SemiOpaque:
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++) {
@ -389,29 +392,41 @@ export namespace Crafter {
// Only draw pixels that are within our scaled buffer bounds // Only draw pixels that are within our scaled buffer bounds
if constexpr(Scaling) { if constexpr(Scaling) {
if (bufferX >= 0 && bufferX < ScalingBase<true, Owning, Alignment>::bufferWidth && bufferY >= 0 && bufferY < ScalingBase<true, Owning, Alignment>::bufferHeight) { if (bufferX >= 0 && bufferX < ScalingBase<T, true, Owning, Alignment>::bufferWidth && bufferY >= 0 && bufferY < ScalingBase<T, true, Owning, Alignment>::bufferHeight) {
ScalingBase<true, Owning, Alignment>::scalingBuffer[bufferY * ScalingBase<true, Owning, Alignment>::bufferWidth + bufferX] = {color.r, color.g, color.b, bitmap[j * w + i]}; 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 { } else {
if (bufferX >= 0 && bufferX < (int)this->scaled.size.x && bufferY >= 0 && bufferY < (int)this->scaled.size.y) { 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 constexpr(std::same_as<T, std::uint8_t>) {
std::uint8_t alpha = bitmap[j * w + i];
if(alpha == 0) { Vector<T, 4, Alignment> dst = this->buffer[bufferY * this->scaled.size.x + bufferX];
continue;
}
Vector<std::uint8_t, 4, Alignment> dst = this->buffer[bufferY * this->scaled.size.x + bufferX];
float srcA = (alpha / 255.0f) * (color.a / 255.0f); float srcA = (alpha / 255.0f) * (color.a / 255.0f);
float dstA = dst.a / 255.0f; float dstA = dst.a / 255.0f;
float outA = srcA + dstA * (1.0f - srcA); float oneMinusSrcA = 1.0f - color.a;
this->buffer[bufferY * this->scaled.size.x + bufferX] = Vector<std::uint8_t, 4, Alignment>(
static_cast<std::uint8_t>((color.r * srcA + dst.r * dstA * (1.0f - srcA)) / outA), float outA = srcA + dstA * (1.0f - srcA);
static_cast<std::uint8_t>((color.g * srcA + dst.g * dstA * (1.0f - srcA)) / outA), this->buffer[bufferY * this->scaled.size.x + bufferX] = Vector<std::uint8_t, 4, Alignment>(
static_cast<std::uint8_t>((color.b * srcA + dst.b * dstA * (1.0f - srcA)) / outA), static_cast<std::uint8_t>((color.r * srcA + dst.r * dstA * (1.0f - srcA)) / outA),
static_cast<std::uint8_t>(outA * 255) static_cast<std::uint8_t>((color.g * srcA + dst.g * dstA * (1.0f - srcA)) / outA),
); static_cast<std::uint8_t>((color.b * srcA + dst.b * dstA * (1.0f - srcA)) / outA),
static_cast<std::uint8_t>(outA * 255)
);
} else if constexpr(std::same_as<T, _Float16>) {
std::uint8_t alpha = bitmap[j * w + i];
_Float16 srcA = (_Float16(alpha)/_Float16(255.0f))*color.a;
Vector<_Float16, 4, Alignment> dst = this->buffer[bufferY * this->scaled.size.x + bufferX];
_Float16 outA = srcA + dst.a * (1.0f - srcA);
this->buffer[bufferY * this->scaled.size.x + bufferX] = Vector<_Float16, 4, Alignment>(
(color.r * srcA + dst.r * dst.a * (1.0f - srcA)),
(color.g * srcA + dst.g * dst.a * (1.0f - srcA)),
(color.b * srcA + dst.b * dst.a * (1.0f - srcA)),
outA
);
}
} }
} }
} }
@ -422,8 +437,9 @@ export namespace Crafter {
x += (int)(ax * scale); x += (int)(ax * scale);
if (i + 1 < line.size()) { if (p + 1 < end) {
x += (int)stbtt_GetGlyphKernAdvance(&font.font, codepoint, line[i+1]); int next;
x += (int)stbtt_GetGlyphKernAdvance(&font.font, codepoint, utf8_decode(p+1, &next));
} }
} }
currentY += lineHeight; currentY += lineHeight;

View file

@ -30,6 +30,12 @@ export namespace Crafter {
Right Right
}; };
enum class TextVerticalAlignment {
Top,
Center,
Bottom
};
enum class TextOverflowMode { enum class TextOverflowMode {
Clip, Clip,
Wrap Wrap
@ -42,9 +48,9 @@ export namespace Crafter {
Buffer Buffer
}; };
template<std::uint8_t Alignment = 0> template<typename T, std::uint8_t Alignment = 0>
struct RenderElement2DScalingOwning { struct RenderElement2DScalingOwning {
std::vector<Vector<std::uint8_t, 4, Alignment>> scalingBuffer; std::vector<Vector<T, 4, Alignment>> scalingBuffer;
std::uint32_t bufferWidth; std::uint32_t bufferWidth;
std::uint32_t bufferHeight; std::uint32_t bufferHeight;
RenderElement2DScalingOwning() = default; RenderElement2DScalingOwning() = default;
@ -53,13 +59,13 @@ export namespace Crafter {
} }
}; };
template<std::uint8_t Alignment = 0> template<typename T, std::uint8_t Alignment = 0>
struct RenderElement2DScalingNonOwning { struct RenderElement2DScalingNonOwning {
Vector<std::uint8_t, 4, Alignment>* scalingBuffer; Vector<T, 4, Alignment>* scalingBuffer;
std::uint32_t bufferWidth; std::uint32_t bufferWidth;
std::uint32_t bufferHeight; std::uint32_t bufferHeight;
RenderElement2DScalingNonOwning() = default; RenderElement2DScalingNonOwning() = default;
RenderElement2DScalingNonOwning(Vector<std::uint8_t, 4, Alignment>* scalingBuffer, std::uint32_t bufferWidth, std::uint32_t bufferHeight) : scalingBuffer(scalingBuffer), bufferWidth(bufferWidth), bufferHeight(bufferHeight) { RenderElement2DScalingNonOwning(Vector<T, 4, Alignment>* scalingBuffer, std::uint32_t bufferWidth, std::uint32_t bufferHeight) : scalingBuffer(scalingBuffer), bufferWidth(bufferWidth), bufferHeight(bufferHeight) {
} }
}; };
@ -76,13 +82,13 @@ export namespace Crafter {
struct EmptyScalingBase {}; struct EmptyScalingBase {};
struct EmptyRotatingBase {}; struct EmptyRotatingBase {};
template<bool Scaling, bool Owning, std::uint8_t Alignment = 0> template<typename T, bool Scaling, bool Owning, std::uint8_t Alignment = 0>
using ScalingBase = using ScalingBase =
std::conditional_t< std::conditional_t<
Scaling, Scaling,
std::conditional_t<Owning, std::conditional_t<Owning,
RenderElement2DScalingOwning<Alignment>, RenderElement2DScalingOwning<T, Alignment>,
RenderElement2DScalingNonOwning<Alignment>>, RenderElement2DScalingNonOwning<T, Alignment>>,
EmptyScalingBase EmptyScalingBase
>; >;
@ -94,11 +100,11 @@ export namespace Crafter {
EmptyRotatingBase EmptyRotatingBase
>; >;
template<std::uint8_t Frames = 1> template<typename T, std::uint8_t Frames = 1>
struct RenderingElement2DBase : Transform2D { struct RenderingElement2DBase : Transform2D {
ScaleData2D oldScale[Frames]; ScaleData2D oldScale[Frames];
bool redraw[Frames]; bool redraw[Frames];
std::vector<Vector<std::uint8_t, 4>> buffer; std::vector<Vector<T, 4, 4>> buffer;
OpaqueType opaque; OpaqueType opaque;
RenderingElement2DBase(Anchor2D anchor) : Transform2D(anchor) { RenderingElement2DBase(Anchor2D anchor) : Transform2D(anchor) {
for(std::uint8_t i = 0; i < Frames; i++) { for(std::uint8_t i = 0; i < Frames; i++) {
@ -115,5 +121,16 @@ export namespace Crafter {
redraw[i] = true; redraw[i] = true;
} }
} }
void CopyNearestNeighbor(Vector<T, 4>* dst, std::uint16_t dstSizeX, std::uint16_t dstScaledSizeX, std::uint16_t dstScaledSizeY, std::uint16_t offsetX, std::uint16_t offsetY) {
for (std::uint16_t y = 0; y < dstScaledSizeY; y++) {
std::uint16_t srcY = y * scaled.size.y / dstScaledSizeY;
std::uint16_t dstY = y + offsetY;
for (std::uint16_t x = 0; x < dstScaledSizeX; x++) {
std::uint16_t srcX = x * scaled.size.x / dstScaledSizeX;
std::uint16_t dstX = x + offsetX;
dst[dstY * dstSizeX + dstX] = buffer[srcY * this->scaled.size.x + srcX];
}
}
}
}; };
} }

View file

@ -0,0 +1,477 @@
/*
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"
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
#include <vulkan/vulkan.h>
#endif
export module Crafter.Graphics:RenderingElement2DVulkan;
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
import Crafter.Asset;
import std;
import :Transform2D;
import :VulkanBuffer;
import :Types;
import :Window;
import :DescriptorHeapVulkan;
import :Font;
export namespace Crafter {
struct RenderingElement2DVulkanBase : Transform2D {
std::uint16_t index;
std::uint16_t bufferX;
std::uint16_t bufferY;
std::array<VulkanBufferBase*, Window::numFrames> buffers;
RenderingElement2DVulkanBase(Anchor2D anchor) : Transform2D(anchor) {
}
RenderingElement2DVulkanBase(Anchor2D anchor, std::uint16_t bufferX, std::uint16_t bufferY) : bufferX(bufferX), bufferY(bufferY), Transform2D(anchor) {
}
RenderingElement2DVulkanBase(Anchor2D anchor, std::uint16_t bufferX, std::uint16_t bufferY, std::array<VulkanBufferBase*, Window::numFrames>&& buffers) : bufferX(bufferX), bufferY(bufferY), buffers(std::move(buffers)), Transform2D(anchor) {
}
};
template<bool Owning, bool Mapped, bool Single = false>
struct RenderingElement2DVulkan : RenderingElement2DVulkanBase {
RenderingElement2DVulkan(Anchor2D anchor) : RenderingElement2DVulkanBase(anchor) {
}
RenderingElement2DVulkan(Anchor2D anchor, RendertargetBase& target, Transform2D& parent) requires(Owning) : RenderingElement2DVulkanBase(anchor) {
GetScale(target, parent);
this->bufferX = this->scaled.size.x;
this->bufferY = this->scaled.size.y;
if(Single) {
buffers[0] = new VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>();
static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffers[0])->Create(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferX * bufferY);
for(std::uint8_t i = 1; i < Window::numFrames; i++) {
buffers[i] = buffers[0];
}
} else {
for(std::uint8_t i = 0; i < Window::numFrames; i++) {
buffers[i] = new VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>();
static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffers[i])->Create(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferX * bufferY);
}
}
}
RenderingElement2DVulkan(Anchor2D anchor, std::uint16_t bufferX, std::uint16_t bufferY) requires(Owning) : RenderingElement2DVulkanBase(anchor, bufferX, bufferY) {
if constexpr(Single) {
buffers[0] = new VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>();
static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffers[0])->Create(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferX * bufferY);
for(std::uint8_t i = 1; i < Window::numFrames; i++) {
buffers[i] = buffers[0];
}
} else {
for(std::uint8_t i = 0; i < Window::numFrames; i++) {
buffers[i] = new VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>();
static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffers[i])->Create(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferX * bufferY);
}
}
}
RenderingElement2DVulkan(Anchor2D anchor, std::uint16_t bufferX, std::uint16_t bufferY, std::array<VulkanBufferBase*, Window::numFrames>&& buffers) requires(!Owning) : RenderingElement2DVulkanBase(anchor, bufferX, bufferY, std::move(buffers)) {
}
RenderingElement2DVulkan(Anchor2D anchor, const std::filesystem::path& assetPath) requires(Owning && Mapped) : RenderingElement2DVulkanBase(anchor) {
TextureAssetInfo info = TextureAsset<_Float16>::LoadInfo(assetPath);
this->bufferX = info.sizeX;
this->bufferY = info.sizeY;
if constexpr(Single) {
buffers[0] = new VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>();
static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffers[0])->Create(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferX * bufferY);
for(std::uint8_t i = 1; i < Window::numFrames; i++) {
buffers[i] = buffers[0];
}
TextureAsset<Vector<_Float16, 4, 4>>::Load(assetPath, static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffers[0])->value, this->bufferX, this->bufferY);
} else {
for(std::uint8_t i = 0; i < Window::numFrames; i++) {
buffers[i] = new VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>();
static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffers[i])->Create(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferX * bufferY);
}
TextureAsset<Vector<_Float16, 4, 4>>::Load(assetPath, static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffers[0])->value, this->bufferX, this->bufferY);
for(std::uint8_t i = 1; i < Window::numFrames; i++) {
std::memcpy(static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffers[i])->value, static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffers[0])->value, this->bufferX * this->bufferY * sizeof(_Float16));
}
}
}
~RenderingElement2DVulkan() {
if constexpr(Owning) {
if constexpr(Single) {
delete static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffers[0]);
} else {
for(VulkanBufferBase* buffer : buffers) {
delete static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffer);
}
}
}
}
RenderingElement2DVulkan(RenderingElement2DVulkan&) = delete;
RenderingElement2DVulkan& operator=(RenderingElement2DVulkan&) = delete;
void CreateBuffer(std::uint16_t bufferX, std::uint16_t bufferY) requires(Owning) {
this->bufferX = this->scaled.size.x;
this->bufferY = this->scaled.size.y;
if constexpr(Single) {
buffers[0] = new VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>();
static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffers[0])->Create(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferX * bufferY);
for(std::uint8_t i = 1; i < Window::numFrames; i++) {
buffers[i] = buffers[0];
}
} else {
for(std::uint8_t i = 0; i < Window::numFrames; i++) {
buffers[i] = new VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>();
static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffers[i])->Create(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferX * bufferY);
}
}
}
void ResizeBuffer(RendertargetVulkan& window, DescriptorHeapVulkan& descriptorHeap, std::uint16_t bufferOffset, std::uint16_t bufferX, std::uint16_t bufferY) requires(Owning) {
if constexpr(Single) {
static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffers[0])->Resize(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferX * bufferY);
} else {
for(VulkanBufferBase* buffer : buffers) {
delete static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffer)->Resize(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferX * bufferY);
}
}
this->bufferX = bufferX;
this->bufferY = bufferY;
for(std::uint8_t frame = 0; frame < Window::numFrames; frame++) {
RenderingElement2DVulkanTransformInfo* val = reinterpret_cast<RenderingElement2DVulkanTransformInfo*>(reinterpret_cast<char*>(window.transformBuffer[frame].value) + sizeof(RenderingElement2DVulkanTransformInfo));
val[index].bufferX = this->bufferX;
val[index].bufferY = this->bufferY;
window.transformBuffer[frame].FlushDevice();
}
VkHostAddressRangeEXT ranges[3] = {
{
.address = descriptorHeap.resourceHeap[0].value + bufferOffset + Device::descriptorHeapProperties.bufferDescriptorSize * index,
.size = Device::descriptorHeapProperties.bufferDescriptorSize
},
{
.address = descriptorHeap.resourceHeap[1].value + bufferOffset + Device::descriptorHeapProperties.bufferDescriptorSize * index,
.size = Device::descriptorHeapProperties.bufferDescriptorSize
},
{
.address = descriptorHeap.resourceHeap[2].value + bufferOffset + Device::descriptorHeapProperties.bufferDescriptorSize * index,
.size = Device::descriptorHeapProperties.bufferDescriptorSize
},
};
VkDeviceAddressRangeKHR bufferRanges[3] {
{
.address = buffers[0]->address,
.size = buffers[0]->size
},
{
.address = buffers[1]->address,
.size = buffers[1]->size
},
{
.address = buffers[2]->address,
.size = buffers[2]->size
},
};
VkResourceDescriptorInfoEXT infos[3] = {
{
.sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT,
.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.data = { .pAddressRange = &bufferRanges[0]}
},
{
.sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT,
.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.data = { .pAddressRange = &bufferRanges[1]}
},
{
.sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT,
.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.data = { .pAddressRange = &bufferRanges[2]}
},
};
Device::vkWriteResourceDescriptorsEXT(Device::device, 3, infos, ranges);
for(std::uint8_t i = 0; i < Window::numFrames; i++) {
descriptorHeap.resourceHeap[i].FlushDevice();
}
}
void UpdatePosition(RendertargetBase& window2, Transform2D& parent) override {
RendertargetVulkan& window = static_cast<RendertargetVulkan&>(window2);
this->ScaleElement(parent);
RenderingElement2DVulkanTransformInfo* val = reinterpret_cast<RenderingElement2DVulkanTransformInfo*>(reinterpret_cast<char*>(window.transformBuffer[window.frame].value) + sizeof(RenderingElement2DVulkanTransformInfo));
val[index].scaled = this->scaled;
for(Transform2D* child : this->children) {
child->UpdatePosition(window, *this);
}
}
void GetScale(RendertargetBase& window, Transform2D& parent) {
this->ScaleElement(parent);
for(Transform2D* child : this->children) {
child->UpdatePosition(window, *this);
}
}
int utf8_decode(const char* s, int* bytes_consumed) {
unsigned char c = s[0];
if (c < 0x80) {
*bytes_consumed = 1;
return c;
} else if ((c & 0xE0) == 0xC0) {
*bytes_consumed = 2;
return ((c & 0x1F) << 6) | (s[1] & 0x3F);
} else if ((c & 0xF0) == 0xE0) {
*bytes_consumed = 3;
return ((c & 0x0F) << 12) | ((s[1] & 0x3F) << 6) | (s[2] & 0x3F);
} else if ((c & 0xF8) == 0xF0) {
*bytes_consumed = 4;
return ((c & 0x07) << 18) | ((s[1] & 0x3F) << 12) | ((s[2] & 0x3F) << 6) | (s[3] & 0x3F);
}
*bytes_consumed = 1;
return 0xFFFD; // replacement char
}
void RenderText(std::span<const std::string_view> lines, float size, Vector<_Float16, 4> color, Font& font, TextAlignment alignment = TextAlignment::Left, std::uint32_t offsetX = 0, std::uint32_t offsetY = 0, OpaqueType opaque = OpaqueType::FullyOpaque) requires(Mapped) {
float scale = stbtt_ScaleForPixelHeight(&font.font, size);
int baseline = (int)(font.ascent * scale);
std::uint32_t lineHeight = (font.ascent - font.descent) * scale;
std::uint32_t currentY = baseline;
for(std::string_view line : lines) {
std::uint32_t lineWidth = 0;
for (const char c : line) {
int advance, lsb;
stbtt_GetCodepointHMetrics(&font.font, c, &advance, &lsb);
lineWidth += (int)(advance * scale);
}
std::uint32_t x = 0;
switch (alignment) {
case TextAlignment::Left:
x = 0;
break;
case TextAlignment::Center:
x = (this->scaled.size.x - lineWidth) / 2;
break;
case TextAlignment::Right:
x = this->scaled.size.x - lineWidth;
break;
}
const char* p = line.data();
const char* end = p + line.size();
while (p < end) {
int bytes;
int codepoint = utf8_decode(p, &bytes);
p += bytes;
int ax;
int lsb;
stbtt_GetCodepointHMetrics(&font.font, codepoint, &ax, &lsb);
int c_x1, c_y1, c_x2, c_y2;
stbtt_GetCodepointBitmapBox(&font.font, codepoint, scale, scale, &c_x1, &c_y1, &c_x2, &c_y2);
int w = c_x2 - c_x1;
int h = c_y2 - c_y1;
std::vector<unsigned char> bitmap(w * h);
stbtt_MakeCodepointBitmap(&font.font, bitmap.data(), w, h, w, scale, scale, codepoint);
// Only render characters that fit within the scaled bounds
switch(opaque) {
case OpaqueType::FullyOpaque: {
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;
if (bufferX >= 0 && bufferX < (int)this->bufferX && bufferY >= 0 && bufferY < (int)this->bufferY) {
for(std::uint8_t frame = 0; frame < Window::numFrames; frame++) {
static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, true>*>(buffers[frame])->value[bufferY * this->bufferX + bufferX] = {color.r, color.g, color.b, static_cast<_Float16>(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++) {
int bufferX = x + i + c_x1 + offsetX;
int bufferY = currentY + j + c_y1 + offsetY;
if (bufferX >= 0 && bufferX < (int)this->bufferX && bufferY >= 0 && bufferY < (int)this->bufferY) {
std::uint8_t alpha = bitmap[j * w + i];
_Float16 srcA = (_Float16(alpha)/_Float16(255.0f))*color.a;
for(std::uint8_t frame = 0; frame < Window::numFrames; frame++) {
Vector<_Float16, 4, 4> dst = static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, true>*>(buffers[frame])->value[bufferY * this->bufferX + bufferX];
_Float16 outA = srcA + dst.a * (1.0f - srcA);
static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, true>*>(buffers[frame])->value[bufferY * this->bufferX + bufferX] = Vector<_Float16, 4, 4>(
(color.r * srcA + dst.r * dst.a * (1.0f - srcA)),
(color.g * srcA + dst.g * dst.a * (1.0f - srcA)),
(color.b * srcA + dst.b * dst.a * (1.0f - srcA)),
outA
);
}
}
}
}
break;
}
}
x += (int)(ax * scale);
if (p + 1 < end) {
int next;
x += (int)stbtt_GetGlyphKernAdvance(&font.font, codepoint, utf8_decode(p+1, &next));
}
}
currentY += lineHeight;
}
}
void RenderText(std::span<const std::string_view> lines, float size, Vector<_Float16, 4> color, Font& font, std::uint8_t frame, TextAlignment alignment = TextAlignment::Left, TextVerticalAlignment verticalAlignment = TextVerticalAlignment::Top, std::int32_t offsetX = 0, std::int32_t offsetY = 0, OpaqueType opaque = OpaqueType::FullyOpaque) requires(Mapped) {
float scale = stbtt_ScaleForPixelHeight(&font.font, size);
int baseline = (int)(font.ascent * scale);
std::uint32_t lineHeight = (font.ascent - font.descent) * scale;
std::uint32_t currentY = baseline;
std::uint32_t ogOffsetX = offsetX;
std::uint32_t ogOffsetY = offsetY;
for(std::string_view line : lines) {
offsetX = ogOffsetX;
offsetY = ogOffsetY;
std::int32_t lineWidth = 0;
for (const char c : line) {
int advance, lsb;
stbtt_GetCodepointHMetrics(&font.font, c, &advance, &lsb);
lineWidth += (int)(advance * scale);
}
switch (alignment) {
case TextAlignment::Left:
break;
case TextAlignment::Center:
offsetX -= lineWidth / 2;
break;
case TextAlignment::Right:
offsetX -= lineWidth;
break;
}
switch (verticalAlignment) {
case TextVerticalAlignment::Top:
break;
case TextVerticalAlignment::Center:
offsetY += (lineHeight / 2) - (size);
break;
case TextVerticalAlignment::Bottom:
offsetY += lineHeight;
break;
}
const char* p = line.data();
const char* end = p + line.size();
while (p < end) {
int bytes;
int codepoint = utf8_decode(p, &bytes);
p += bytes;
int ax;
int lsb;
stbtt_GetCodepointHMetrics(&font.font, codepoint, &ax, &lsb);
int c_x1, c_y1, c_x2, c_y2;
stbtt_GetCodepointBitmapBox(&font.font, codepoint, scale, scale, &c_x1, &c_y1, &c_x2, &c_y2);
int w = c_x2 - c_x1;
int h = c_y2 - c_y1;
std::vector<unsigned char> bitmap(w * h);
stbtt_MakeCodepointBitmap(&font.font, bitmap.data(), w, h, w, scale, scale, codepoint);
// Only render characters that fit within the scaled bounds
switch(opaque) {
case OpaqueType::FullyOpaque: {
for (int j = 0; j < h; j++) {
for (int i = 0; i < w; i++) {
int bufferX = offsetX + i + c_x1;
int bufferY = currentY + j + c_y1 + offsetY;
if (bufferX >= 0 && bufferX < (int)this->bufferX && bufferY >= 0 && bufferY < (int)this->bufferY) {
static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, true>*>(buffers[frame])->value[bufferY * this->bufferX + bufferX] = {color.r, color.g, color.b, static_cast<_Float16>(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++) {
int bufferX = offsetX + i + c_x1;
int bufferY = currentY + j + c_y1 + offsetY;
if (bufferX >= 0 && bufferX < (int)this->bufferX && bufferY >= 0 && bufferY < (int)this->bufferY) {
std::uint8_t alpha = bitmap[j * w + i];
_Float16 srcA = (_Float16(alpha)/_Float16(255.0f))*color.a;
Vector<_Float16, 4, 4> dst = static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, true>*>(buffers[frame])->value[bufferY * this->bufferX + bufferX];
_Float16 outA = srcA + dst.a * (1.0f - srcA);
static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, true>*>(buffers[frame])->value[bufferY * this->bufferX + bufferX] = Vector<_Float16, 4, 4>(
(color.r * srcA + dst.r * dst.a * (1.0f - srcA)),
(color.g * srcA + dst.g * dst.a * (1.0f - srcA)),
(color.b * srcA + dst.b * dst.a * (1.0f - srcA)),
outA
);
}
}
}
break;
}
}
offsetX += (int)(ax * scale);
if (p + 1 < end) {
int next;
offsetX += (int)stbtt_GetGlyphKernAdvance(&font.font, codepoint, utf8_decode(p+1, &next));
}
}
currentY += lineHeight;
}
}
};
}
#endif

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> buffer;
VkAccelerationStructureKHR accelerationStructure; VkAccelerationStructureKHR accelerationStructure;
VulkanBuffer<VkAccelerationStructureInstanceKHR, true, true, false> instanceBuffer; VulkanBuffer<VkAccelerationStructureInstanceKHR, 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> 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

@ -16,6 +16,12 @@ You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
module;
#include "../lib/stb_truetype.h"
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
#include <vulkan/vulkan.h>
#endif
export module Crafter.Graphics:Rendertarget; export module Crafter.Graphics:Rendertarget;
import Crafter.Math; import Crafter.Math;
import Crafter.Asset; import Crafter.Asset;
@ -23,34 +29,64 @@ import std;
import :Types; import :Types;
import :Transform2D; import :Transform2D;
import :RenderingElement2DBase; import :RenderingElement2DBase;
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
import :Device;
import :VulkanBuffer;
#endif
export namespace Crafter { export namespace Crafter {
template<std::uint8_t Frames>
struct RendertargetBase { struct RendertargetBase {
#ifdef CRAFTER_TIMING #ifdef CRAFTER_TIMING
std::vector<std::tuple<const Transform*, std::uint32_t, std::uint32_t, std::chrono::nanoseconds>> renderTimings; std::vector<std::tuple<const Transform*, std::uint32_t, std::uint32_t, std::chrono::nanoseconds>> renderTimings;
#endif #endif
Transform2D transform; Transform2D transform;
std::int32_t sizeX; std::uint16_t sizeX;
std::int32_t sizeY; std::uint16_t sizeY;
RendertargetBase() = default; RendertargetBase() = default;
RendertargetBase(std::int16_t sizeX, std::int16_t sizeY) : sizeX(sizeX), sizeY(sizeY), transform({0, 0, 1, 1, 0, 0, 0}){ RendertargetBase(std::uint16_t sizeX, std::uint16_t sizeY) : sizeX(sizeX), sizeY(sizeY), transform({0, 0, 1, 1, 0, 0, 0}){
transform.scaled.size.x = sizeX; transform.scaled.size.x = sizeX;
transform.scaled.size.y = sizeY; transform.scaled.size.y = sizeY;
transform.scaled.position.x = 0; transform.scaled.position.x = 0;
transform.scaled.position.y = 0; transform.scaled.position.y = 0;
} }
}; };
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
struct RenderingElement2DVulkanBase;
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;
};
struct DescriptorHeapVulkan;
struct RendertargetVulkan : RendertargetBase {
std::uint8_t frame;
std::vector<RenderingElement2DVulkanBase*> elements;
VulkanBuffer<RenderingElement2DVulkanTransformInfo, true> transformBuffer[3];
RendertargetVulkan() = default;
RendertargetVulkan(std::uint16_t sizeX, std::uint16_t sizeY);
void UpdateElements();
void CreateBuffer(std::uint8_t frame);
void ReorderBuffer(std::uint8_t frame);
void WriteDescriptors(std::span<VkResourceDescriptorInfoEXT> infos, std::span<VkHostAddressRangeEXT> ranges, std::uint16_t start, std::uint32_t bufferOffset, DescriptorHeapVulkan& descriptorHeap);
void SetOrderResursive(Transform2D* elementTransform);
};
#endif
template<typename T, std::uint8_t Channels, std::uint8_t Alignment, std::uint8_t Frames> template<typename T, std::uint8_t Channels, std::uint8_t Alignment, std::uint8_t Frames>
struct Rendertarget : RendertargetBase<Frames> { struct Rendertarget : RendertargetBase {
Vector<T, Channels, Alignment>* buffer[Frames]; Vector<T, Channels, Alignment>* buffer[Frames];
Rendertarget() = default; Rendertarget() = default;
Rendertarget(std::int16_t sizeX, std::int16_t sizeY) : RendertargetBase<Frames>(sizeX, sizeY) { Rendertarget(std::uint16_t sizeX, std::uint16_t sizeY) : RendertargetBase(sizeX, sizeY) {
} }
void RenderElement(Transform2D* elementTransform, std::uint8_t frame, std::vector<ClipRect>&& dirtyRects) { void RenderElement(Transform2D* elementTransform, std::uint8_t frame, std::vector<ClipRect>&& dirtyRects) {
RenderingElement2DBase<Frames>* element = dynamic_cast<RenderingElement2DBase<Frames>*>(elementTransform); RenderingElement2DBase<T, Frames>* element = dynamic_cast<RenderingElement2DBase<T, Frames>*>(elementTransform);
if(element) { if(element) {
#ifdef CRAFTER_TIMING #ifdef CRAFTER_TIMING
auto start = std::chrono::high_resolution_clock::now(); auto start = std::chrono::high_resolution_clock::now();
@ -61,68 +97,73 @@ export namespace Crafter {
} }
for(ClipRect dirty : dirtyRects) { for(ClipRect dirty : dirtyRects) {
dirty.left = std::max(element->scaled.position.x, dirty.left); dirty.left = std::uint16_t(std::max(element->scaled.position.x, std::int16_t(dirty.left)));
dirty.top = std::max(element->scaled.position.y, dirty.top); dirty.top = std::uint16_t(std::max(element->scaled.position.y,std::int16_t(dirty.top)));
dirty.right = std::min(element->scaled.position.x+element->scaled.size.x, dirty.right); dirty.right = std::min(std::uint16_t(element->scaled.position.x+element->scaled.size.x), dirty.right);
dirty.bottom = std::min(element->scaled.position.y+element->scaled.size.y, dirty.bottom); dirty.bottom = std::min(std::uint16_t(element->scaled.position.y+element->scaled.size.y), dirty.bottom);
const Vector<std::uint8_t, 4>* src_buffer = element->buffer.data(); if(dirty.right <= dirty.left || dirty.bottom <= dirty.top) {
std::int32_t src_width = element->scaled.size.x; continue;
std::int32_t src_height = element->scaled.size.y; }
const Vector<T, 4, 4>* src_buffer = element->buffer.data();
std::uint16_t src_width = element->scaled.size.x;
std::uint16_t src_height = element->scaled.size.y;
switch (element->opaque) { switch (element->opaque) {
case OpaqueType::FullyOpaque: case OpaqueType::FullyOpaque: {
for (std::int32_t y = dirty.top; y < dirty.bottom; y++) { for (std::uint16_t y = dirty.top; y < dirty.bottom; y++) {
std::int32_t src_y = y - element->scaled.position.y; std::uint16_t src_y = y - element->scaled.position.y;
std::uint16_t src_x = dirty.left - element->scaled.position.x;
for (std::int32_t x = dirty.left; x < dirty.right; x++) { std::memcpy(&this->buffer[frame][y * this->sizeX + dirty.left], &src_buffer[src_y * src_width + src_x], (dirty.right - dirty.left) * sizeof(Vector<T, Channels, Alignment>));
std::int32_t src_x = x - element->scaled.position.x;
this->buffer[frame][y * this->sizeX + x] = src_buffer[src_y * src_width + src_x];
}
} }
break; break;
}
case OpaqueType::SemiOpaque: case OpaqueType::SemiOpaque:
// For semi-opaque, we can avoid blending when alpha is 0 or 255
for (std::int32_t y = dirty.top; y < dirty.bottom; y++) {
std::int32_t src_y = y - element->scaled.position.y;
for (std::int32_t x = dirty.left; x < dirty.right; x++) {
std::int32_t src_x = x - element->scaled.position.x;
Vector<std::uint8_t, 4> src_pixel = src_buffer[src_y * src_width + src_x];
if (src_pixel.a == 0) {
continue;
}
this->buffer[frame][y * this->sizeX + x] = src_pixel;
}
}
break;
case OpaqueType::Transparent: case OpaqueType::Transparent:
// For transparent, always perform blending if constexpr(std::same_as<T, _Float16>) {
for (std::int32_t y = dirty.top; y < dirty.bottom; y++) { for (std::uint16_t y = dirty.top; y < dirty.bottom; y++) {
std::int32_t src_y = y - element->scaled.position.y; std::uint16_t src_y = y - element->scaled.position.y;
for (std::int32_t x = dirty.left; x < dirty.right; x++) { std::uint16_t pixel_width = dirty.right - dirty.left;
std::int32_t src_x = x - element->scaled.position.x;
Vector<T, Channels, Alignment> src = src_buffer[src_y * src_width + src_x];
Vector<T, Channels, Alignment> dst = buffer[frame][y * this->sizeX + x];
if(src.a == 0) { constexpr std::uint32_t simd_width = VectorF16<1, 1>::MaxElement / 4;
continue; std::uint32_t rows = pixel_width / simd_width;
for (std::uint32_t x = 0; x < rows; x++) {
std::uint16_t px = dirty.left + x * simd_width;
std::uint16_t src_x = px - element->scaled.position.x;
VectorF16<4, simd_width> src(&src_buffer[src_y * src_width + src_x].v[0]);
VectorF16<4, simd_width> dst(&buffer[frame][y * this->sizeX + px].v[0]);
VectorF16<4, simd_width> oneMinusSrcA = VectorF16<4, simd_width>(1) - src.Shuffle<{{3, 3, 3, 3}}>();
VectorF16<4, simd_width> result = VectorF16<4, simd_width>::MulitplyAdd(dst, oneMinusSrcA, src);
result.Store(&buffer[frame][y * this->sizeX + px].v[0]);
} }
float srcA = src.a / 255.0f;
float dstA = dst.a / 255.0f;
float outA = srcA + dstA * (1.0f - srcA); std::uint32_t remainder = pixel_width - (rows * simd_width);
this->buffer[frame][y * this->sizeX + x] = Vector<T, Channels, Alignment>( std::uint16_t remainder_start = dirty.left + rows * simd_width;
static_cast<T>((src.r * srcA + dst.r * dstA * (1.0f - srcA)) / outA),
static_cast<T>((src.g * srcA + dst.g * dstA * (1.0f - srcA)) / outA), for (std::uint8_t x = 0; x < remainder; x++) {
static_cast<T>((src.b * srcA + dst.b * dstA * (1.0f - srcA)) / outA), std::uint16_t px = remainder_start + x;
static_cast<T>(outA * 255) std::uint16_t src_x = px - element->scaled.position.x;
);
Vector<T, Channels, Alignment> src = src_buffer[src_y * src_width + src_x];
Vector<T, Channels, Alignment> dst = buffer[frame][y * this->sizeX + px];
_Float16 oneMinusSrcA = (_Float16)1.0f - src.a;
buffer[frame][y * this->sizeX + px] = Vector<T, Channels, Alignment>(
src.r + dst.r * oneMinusSrcA,
src.g + dst.g * oneMinusSrcA,
src.b + dst.b * oneMinusSrcA,
src.a + dst.a * oneMinusSrcA
);
}
}
} else {
for (std::uint16_t y = dirty.top; y < dirty.bottom; y++) {
std::uint16_t src_y = y - element->scaled.position.y;
std::uint16_t src_x = dirty.left - element->scaled.position.x;
std::memcpy(&this->buffer[frame][y * this->sizeX + dirty.left], &src_buffer[src_y * src_width + src_x], (dirty.right - dirty.left) * sizeof(Vector<T, Channels, Alignment>));
} }
} }
break; break;
@ -140,11 +181,15 @@ export namespace Crafter {
} }
void AddOldRects(Transform2D* elementTransform, std::uint8_t frame, std::vector<ClipRect>& clipRects) { void AddOldRects(Transform2D* elementTransform, std::uint8_t frame, std::vector<ClipRect>& clipRects) {
RenderingElement2DBase<Frames>* element = dynamic_cast<RenderingElement2DBase<Frames>*>(elementTransform); RenderingElement2DBase<T, Frames>* element = dynamic_cast<RenderingElement2DBase<T, Frames>*>(elementTransform);
if(element) { if(element) {
if(element->scaled.position.x != element->oldScale[frame].position.x || element->scaled.position.y != element->oldScale[frame].position.y || element->scaled.size.x != element->oldScale[frame].size.x || element->scaled.size.y != element->oldScale[frame].size.y || element->redraw[frame]) { if(element->scaled.position.x != element->oldScale[frame].position.x || element->scaled.position.y != element->oldScale[frame].position.y || element->scaled.size.x != element->oldScale[frame].size.x || element->scaled.size.y != element->oldScale[frame].size.y || element->redraw[frame]) {
clipRects.emplace_back(std::max(element->scaled.position.x, std::int32_t(0)), std::min(element->scaled.position.x + element->scaled.size.x, this->sizeX), std::max(element->scaled.position.y, std::int32_t(0)), std::min(element->scaled.position.y + element->scaled.size.y, this->sizeY)); clipRects.emplace_back(std::max(element->scaled.position.x, std::int16_t(0)), std::min(std::int16_t(element->scaled.position.x + element->scaled.size.x), std::int16_t(this->sizeX)), std::max(element->scaled.position.y, std::int16_t(0)), std::min(std::int16_t(element->scaled.position.y + element->scaled.size.y), std::int16_t(this->sizeY)));
clipRects.emplace_back(std::max(element->oldScale[frame].position.x, std::int32_t(0)), std::min(element->oldScale[frame].position.x + element->oldScale[frame].size.x, this->sizeX), std::max(element->oldScale[frame].position.y, std::int32_t(0)), std::min(element->oldScale[frame].position.y + element->oldScale[frame].size.y, this->sizeY)); clipRects.emplace_back(std::max(element->oldScale[frame].position.x, std::int16_t(0)), std::min(std::int16_t(element->oldScale[frame].position.x + element->oldScale[frame].size.x), std::int16_t(this->sizeX)), std::max(element->oldScale[frame].position.y, std::int16_t(0)), std::min(std::int16_t(element->oldScale[frame].position.y + element->oldScale[frame].size.y), std::int16_t(this->sizeY)));
element->oldScale[frame] = element->scaled;
element->redraw[frame] = false;
} else if(element->redraw[frame]) {
clipRects.emplace_back(std::max(element->scaled.position.x, std::int16_t(0)), std::min(std::int16_t(element->scaled.position.x + element->scaled.size.x), std::int16_t(this->sizeX)), std::max(element->scaled.position.y, std::int16_t(0)), std::min(std::int16_t(element->scaled.position.y + element->scaled.size.y), std::int16_t(this->sizeY)));
element->oldScale[frame] = element->scaled; element->oldScale[frame] = element->scaled;
element->redraw[frame] = false; element->redraw[frame] = false;
} }

View file

@ -33,36 +33,13 @@ export namespace Crafter {
class ShaderBindingTableVulkan { class ShaderBindingTableVulkan {
public: public:
std::vector<VkPipelineShaderStageCreateInfo> shaderStages; std::vector<VkPipelineShaderStageCreateInfo> shaderStages;
void Init(std::span<VulkanShader> shaders) { void Init(const std::span<const 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

@ -30,10 +30,10 @@ export namespace Crafter {
float height; float height;
float offsetX; float offsetX;
float offsetY; float offsetY;
std::int32_t z; std::uint8_t z;
bool maintainAspectRatio; bool maintainAspectRatio;
Anchor2D() = default; Anchor2D() = default;
Anchor2D(float x, float y, float width, float height, float offsetX, float offsetY, std::int32_t z, bool maintainAspectRatio = false); Anchor2D(float x, float y, float width, float height, float offsetX, float offsetY, std::uint8_t z, bool maintainAspectRatio = false);
}; };
struct Transform2D { struct Transform2D {
@ -49,14 +49,7 @@ export namespace Crafter {
Transform2D& operator=(Transform2D&) = delete; Transform2D& operator=(Transform2D&) = delete;
virtual ~Transform2D() = default; virtual ~Transform2D() = default;
virtual void UpdatePosition(RendertargetBase<1>& window, Transform2D& parent) { virtual void UpdatePosition(RendertargetBase& window, Transform2D& parent) {
ScaleElement(parent);
for(Transform2D* child : children) {
child->UpdatePosition(window, *this);
}
}
virtual void UpdatePosition(RendertargetBase<3>& window, Transform2D& parent) {
ScaleElement(parent); ScaleElement(parent);
for(Transform2D* child : children) { for(Transform2D* child : children) {
child->UpdatePosition(window, *this); child->UpdatePosition(window, *this);
@ -73,7 +66,6 @@ export namespace Crafter {
scaled.size.y = anchor.height * parent.scaled.size.x; scaled.size.y = anchor.height * parent.scaled.size.x;
} }
} else { } else {
scaled.size.x = anchor.width * parent.scaled.size.x; scaled.size.x = anchor.width * parent.scaled.size.x;
scaled.size.y = anchor.height * parent.scaled.size.y; scaled.size.y = anchor.height * parent.scaled.size.y;
} }

View file

@ -33,15 +33,15 @@ export namespace Crafter {
}; };
struct ScaleData2D { struct ScaleData2D {
Vector<std::int32_t, 2> position; Vector<std::int16_t, 2, 2> position;
Vector<std::int32_t, 2> size; Vector<std::int16_t, 2, 2> size;
}; };
struct ClipRect { struct ClipRect {
std::int32_t left; std::uint16_t left;
std::int32_t right; std::uint16_t right;
std::int32_t top; std::uint16_t top;
std::int32_t bottom; std::uint16_t bottom;
}; };
struct FrameTime { struct FrameTime {
@ -49,7 +49,7 @@ export namespace Crafter {
std::chrono::duration<double> delta; std::chrono::duration<double> delta;
}; };
enum class CrafterKeys { enum class CrafterKeys : std::uint8_t {
// Alphabetic keys // Alphabetic keys
A, B, C, D, E, F, G, H, I, J, K, L, M, A, B, C, D, E, F, G, H, I, J, K, L, M,
N, O, P, Q, R, S, T, U, V, W, X, Y, Z, N, O, P, Q, R, S, T, U, V, W, X, Y, Z,
@ -92,6 +92,151 @@ export namespace Crafter {
CrafterKeysMax CrafterKeysMax
}; };
constexpr std::string CrafterKeyToString(CrafterKeys key) {
switch (key) {
// Alphabetic keys
case CrafterKeys::A: return "A";
case CrafterKeys::B: return "B";
case CrafterKeys::C: return "C";
case CrafterKeys::D: return "D";
case CrafterKeys::E: return "E";
case CrafterKeys::F: return "F";
case CrafterKeys::G: return "G";
case CrafterKeys::H: return "H";
case CrafterKeys::I: return "I";
case CrafterKeys::J: return "J";
case CrafterKeys::K: return "K";
case CrafterKeys::L: return "L";
case CrafterKeys::M: return "M";
case CrafterKeys::N: return "N";
case CrafterKeys::O: return "O";
case CrafterKeys::P: return "P";
case CrafterKeys::Q: return "Q";
case CrafterKeys::R: return "R";
case CrafterKeys::S: return "S";
case CrafterKeys::T: return "T";
case CrafterKeys::U: return "U";
case CrafterKeys::V: return "V";
case CrafterKeys::W: return "W";
case CrafterKeys::X: return "X";
case CrafterKeys::Y: return "Y";
case CrafterKeys::Z: return "Z";
// Numeric keys
case CrafterKeys::_0: return "0";
case CrafterKeys::_1: return "1";
case CrafterKeys::_2: return "2";
case CrafterKeys::_3: return "3";
case CrafterKeys::_4: return "4";
case CrafterKeys::_5: return "5";
case CrafterKeys::_6: return "6";
case CrafterKeys::_7: return "7";
case CrafterKeys::_8: return "8";
case CrafterKeys::_9: return "9";
// Function keys
case CrafterKeys::F1: return "F1";
case CrafterKeys::F2: return "F2";
case CrafterKeys::F3: return "F3";
case CrafterKeys::F4: return "F4";
case CrafterKeys::F5: return "F5";
case CrafterKeys::F6: return "F6";
case CrafterKeys::F7: return "F7";
case CrafterKeys::F8: return "F8";
case CrafterKeys::F9: return "F9";
case CrafterKeys::F10: return "F10";
case CrafterKeys::F11: return "F11";
case CrafterKeys::F12: return "F12";
// Control keys
case CrafterKeys::Escape: return "Escape";
case CrafterKeys::Tab: return "Tab";
case CrafterKeys::Enter: return "Enter";
case CrafterKeys::Space: return "Space";
case CrafterKeys::Backspace: return "Backspace";
case CrafterKeys::Delete: return "Delete";
case CrafterKeys::Insert: return "Insert";
case CrafterKeys::Home: return "Home";
case CrafterKeys::End: return "End";
case CrafterKeys::PageUp: return "PageUp";
case CrafterKeys::PageDown: return "PageDown";
case CrafterKeys::CapsLock: return "CapsLock";
case CrafterKeys::NumLock: return "NumLock";
case CrafterKeys::ScrollLock: return "ScrollLock";
// Modifier keys
case CrafterKeys::LeftShift: return "LeftShift";
case CrafterKeys::RightShift: return "RightShift";
case CrafterKeys::LeftCtrl: return "LeftCtrl";
case CrafterKeys::RightCtrl: return "RightCtrl";
case CrafterKeys::LeftAlt: return "LeftAlt";
case CrafterKeys::RightAlt: return "RightAlt";
case CrafterKeys::LeftSuper: return "LeftSuper";
case CrafterKeys::RightSuper: return "RightSuper";
// Arrow keys
case CrafterKeys::Up: return "Up";
case CrafterKeys::Down: return "Down";
case CrafterKeys::Left: return "Left";
case CrafterKeys::Right: return "Right";
// Keypad keys
case CrafterKeys::keypad_0: return "Keypad0";
case CrafterKeys::keypad_1: return "Keypad1";
case CrafterKeys::keypad_2: return "Keypad2";
case CrafterKeys::keypad_3: return "Keypad3";
case CrafterKeys::keypad_4: return "Keypad4";
case CrafterKeys::keypad_5: return "Keypad5";
case CrafterKeys::keypad_6: return "Keypad6";
case CrafterKeys::keypad_7: return "Keypad7";
case CrafterKeys::keypad_8: return "Keypad8";
case CrafterKeys::keypad_9: return "Keypad9";
case CrafterKeys::keypad_enter: return "KeypadEnter";
case CrafterKeys::keypad_plus: return "KeypadPlus";
case CrafterKeys::keypad_minus: return "KeypadMinus";
case CrafterKeys::keypad_multiply: return "KeypadMultiply";
case CrafterKeys::keypad_divide: return "KeypadDivide";
case CrafterKeys::keypad_decimal: return "KeypadDecimal";
// Punctuation and special keys
case CrafterKeys::grave: return "Grave";
case CrafterKeys::minus: return "Minus";
case CrafterKeys::equal: return "Equal";
case CrafterKeys::bracket_left: return "BracketLeft";
case CrafterKeys::bracket_right: return "BracketRight";
case CrafterKeys::backslash: return "Backslash";
case CrafterKeys::semicolon: return "Semicolon";
case CrafterKeys::quote: return "Quote";
case CrafterKeys::comma: return "Comma";
case CrafterKeys::period: return "Period";
case CrafterKeys::slash: return "Slash";
case CrafterKeys::print_screen: return "PrintScreen";
case CrafterKeys::pause: return "Pause";
case CrafterKeys::menu: return "Menu";
// Additional keys
case CrafterKeys::volume_up: return "VolumeUp";
case CrafterKeys::volume_down: return "VolumeDown";
case CrafterKeys::volume_mute: return "VolumeMute";
case CrafterKeys::media_play: return "MediaPlay";
case CrafterKeys::media_stop: return "MediaStop";
case CrafterKeys::media_prev: return "MediaPrev";
case CrafterKeys::media_next: return "MediaNext";
case CrafterKeys::browser_back: return "BrowserBack";
case CrafterKeys::browser_forward: return "BrowserForward";
case CrafterKeys::browser_refresh: return "BrowserRefresh";
case CrafterKeys::browser_stop: return "BrowserStop";
case CrafterKeys::browser_search: return "BrowserSearch";
case CrafterKeys::browser_home: return "BrowserHome";
case CrafterKeys::launch_mail: return "LaunchMail";
case CrafterKeys::launch_calculator: return "LaunchCalculator";
case CrafterKeys::launch_media_player:return "LaunchMediaPlayer";
case CrafterKeys::CrafterKeysMax: return "Unknown";
}
return "Unknown";
}
template <typename T, typename T2> template <typename T, typename T2>
constexpr T AlignUp(T value, T2 alignment) { constexpr T AlignUp(T value, T2 alignment) {
return (value + alignment - 1) & ~(alignment - 1); return (value + alignment - 1) & ~(alignment - 1);

View file

@ -31,24 +31,12 @@ import :Device;
namespace Crafter { namespace Crafter {
export class VulkanBufferBase { export class VulkanBufferBase {
public: public:
VkDescriptorBufferInfo descriptor; VkDeviceAddress address;
std::uint32_t size;
VkBuffer buffer = VK_NULL_HANDLE; VkBuffer buffer = VK_NULL_HANDLE;
VkDeviceMemory memory; VkDeviceMemory memory;
}; };
export class VulkanBufferAdressable {
public:
VkDeviceAddress address;
};
export class VulkanBufferAdressableEmpty {};
template<bool Adressable>
using VulkanBufferAdressableConditional =
std::conditional_t<
Adressable,
VulkanBufferAdressable,
VulkanBufferAdressableEmpty
>;
export template<typename T> export template<typename T>
class VulkanBufferMapped { class VulkanBufferMapped {
public: public:
@ -63,33 +51,12 @@ namespace Crafter {
VulkanBufferMappedEmpty VulkanBufferMappedEmpty
>; >;
export template <typename T, bool Mapped>
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> {
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> {
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) { size = count * sizeof(T);
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;
@ -104,30 +71,21 @@ namespace Crafter {
.allocationSize = memReqs.size, .allocationSize = memReqs.size,
.memoryTypeIndex = Device::GetMemoryType(memReqs.memoryTypeBits, memoryPropertyFlags) .memoryTypeIndex = Device::GetMemoryType(memReqs.memoryTypeBits, memoryPropertyFlags)
}; };
if constexpr(Adressable) {
VkMemoryAllocateFlagsInfoKHR allocFlagsInfo { VkMemoryAllocateFlagsInfoKHR allocFlagsInfo {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR, .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR,
.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR, .flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR,
}; };
memAlloc.pNext = &allocFlagsInfo; memAlloc.pNext = &allocFlagsInfo;
Device::CheckVkResult(vkAllocateMemory(Device::device, &memAlloc, nullptr, &memory)); Device::CheckVkResult(vkAllocateMemory(Device::device, &memAlloc, nullptr, &memory));
} else {
Device::CheckVkResult(vkAllocateMemory(Device::device, &memAlloc, nullptr, &memory));
}
descriptor.offset = 0;
descriptor.buffer = buffer;
descriptor.range = VK_WHOLE_SIZE;
Device::CheckVkResult(vkBindBufferMemory(Device::device, buffer, memory, 0)); Device::CheckVkResult(vkBindBufferMemory(Device::device, buffer, memory, 0));
if constexpr(Adressable) { VkBufferDeviceAddressInfo addressInfo = {
VkBufferDeviceAddressInfo addressInfo = { .sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO,
.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, .buffer = buffer
.buffer = buffer };
}; address = vkGetBufferDeviceAddress(Device::device, &addressInfo);
VulkanBufferAdressableConditional<true>::address = vkGetBufferDeviceAddress(Device::device, &addressInfo);
}
if constexpr(Mapped) { if constexpr(Mapped) {
Device::CheckVkResult(vkMapMemory(Device::device, memory, 0, memReqs.size, 0, reinterpret_cast<void**>(&(VulkanBufferMappedConditional<T, true>::value)))); Device::CheckVkResult(vkMapMemory(Device::device, memory, 0, memReqs.size, 0, reinterpret_cast<void**>(&(VulkanBufferMappedConditional<T, true>::value))));
@ -141,9 +99,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) {
@ -157,7 +112,7 @@ namespace Crafter {
VkBufferCopy copyRegion = { VkBufferCopy copyRegion = {
.srcOffset = 0, .srcOffset = 0,
.dstOffset = 0, .dstOffset = 0,
.size = descriptor.range .size = size
}; };
vkCmdCopyBuffer( vkCmdCopyBuffer(
@ -237,37 +192,16 @@ 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;
buffer = other.buffer; buffer = other.buffer;
memory = other.memory; memory = other.memory;
size = other.size;
other.buffer = VK_NULL_HANDLE; other.buffer = VK_NULL_HANDLE;
if constexpr(Adressable) { address = other.address;
VulkanBufferAdressableConditional<true>::address = other.VulkanBufferAdressableConditional<true>::address;
}
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

@ -41,6 +41,9 @@ module;
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN #ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
#include "vulkan/vulkan.h" #include "vulkan/vulkan.h"
#endif #endif
#ifdef CRAFTER_GRAPHICS_WINDOW_WIN32
#include <windows.h>
#endif
export module Crafter.Graphics:Window; export module Crafter.Graphics:Window;
import std; import std;
@ -48,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
@ -60,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;
@ -80,6 +82,7 @@ export namespace Crafter {
Event<CrafterKeys> onAnyKeyDown; Event<CrafterKeys> onAnyKeyDown;
Event<CrafterKeys> onAnyKeyHold; Event<CrafterKeys> onAnyKeyHold;
Event<CrafterKeys> onAnyKeyUp; Event<CrafterKeys> onAnyKeyUp;
Event<const std::string_view> onTextInput;
Event<void> onMouseRightClick; Event<void> onMouseRightClick;
Event<void> onMouseLeftClick; Event<void> onMouseLeftClick;
Event<void> onMouseRightHold; Event<void> onMouseRightHold;
@ -128,6 +131,13 @@ export namespace Crafter {
void LogTiming(); void LogTiming();
#endif #endif
#ifdef CRAFTER_GRAPHICS_WINDOW_WIN32
HBITMAP cursorBitmap = nullptr;
HCURSOR cursorHandle = nullptr;
std::uint16_t cursorSizeX = 0;
std::uint16_t cursorSizeY = 0;
#endif
#ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND #ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND
float scale; float scale;
#ifdef CRAFTER_GRAPHICS_RENDERER_SOFTWARE #ifdef CRAFTER_GRAPHICS_RENDERER_SOFTWARE
@ -168,17 +178,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();
@ -190,19 +189,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,14 @@ 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;
export import :RenderingElement2DVulkan;
#endif #endif
// export import :WindowWaylandVulkan; // export import :WindowWaylandVulkan;

View file

@ -10,7 +10,8 @@
"implementations/Crafter.Graphics-Transform2D", "implementations/Crafter.Graphics-Transform2D",
"implementations/Crafter.Graphics-Device", "implementations/Crafter.Graphics-Device",
"implementations/Crafter.Graphics-Mesh", "implementations/Crafter.Graphics-Mesh",
"implementations/Crafter.Graphics-RenderingElement3D" "implementations/Crafter.Graphics-RenderingElement3D",
"implementations/Crafter.Graphics-Rendertarget"
], ],
"interfaces": [ "interfaces": [
"interfaces/Crafter.Graphics-Window", "interfaces/Crafter.Graphics-Window",
@ -28,15 +29,15 @@
"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",
"interfaces/Crafter.Graphics-RenderingElement2DVulkan"
], ],
"type": "library" "type": "library"
}, },
@ -53,7 +54,7 @@
}, },
{ {
"name": "win32", "name": "win32",
"libs": ["kernel32", "user32"], "libs": ["kernel32", "user32", "gdi32"],
"extends": ["base"], "extends": ["base"],
"defines": [ "defines": [
{ {