diff --git a/examples/VulkanTriangle/main.cpp b/examples/VulkanTriangle/main.cpp index 7687e93..74406f8 100644 --- a/examples/VulkanTriangle/main.cpp +++ b/examples/VulkanTriangle/main.cpp @@ -1,5 +1,4 @@ #include "vulkan/vulkan.h" -#include import Crafter.Graphics; using namespace Crafter; @@ -7,69 +6,55 @@ import std; import Crafter.Event; 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 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 +> ShaderGroups; +typedef PipelineRTVulkanConst 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() { Device::Initialize(); Window window(1280, 720, "HelloVulkan"); VkCommandBuffer cmd = window.StartInit(); - DescriptorHeapVulkan descriptorHeap; - descriptorHeap.Initialize(1,1,0); - VkSpecializationMapEntry entry = { - .constantID = 0, - .offset = 0, - .size = sizeof(uint16_t) - }; + Raygenspv::CreateShader(); + Closesthitspv::CreateShader(); + Misspv::CreateShader(); + ShaderBindingTableVulkanConst::Init(); - VkSpecializationInfo specilizationInfo = { - .mapEntryCount = 1, - .pMapEntries = &entry, - .dataSize = sizeof(uint16_t), - .pData = &descriptorHeap.bufferStartElement - }; + descriptorSetLayout::Init(); + std::array layouts {{descriptorSetLayout::layout}}; - std::array shaders{{ - {"raygen.spv", "main", VK_SHADER_STAGE_RAYGEN_BIT_KHR, &specilizationInfo}, - {"miss.spv", "main", VK_SHADER_STAGE_MISS_BIT_KHR, nullptr}, - {"closesthit.spv", "main", VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR, nullptr} - }}; + DescriptorPool pool; + pool.sets.resize(1); + pool.BuildPool(DescriptorPool::GetPoolSizes(), layouts); - ShaderBindingTableVulkan shaderTable; - shaderTable.Init(shaders); - - std::array 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 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 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); + Pipeline::Init(cmd, layouts); Mesh triangleMesh; std::array, 3> verts {{{-150, -150, 100}, {0, 150, 100}, {150, -150, 100}}}; @@ -90,111 +75,59 @@ int main() { MatrixRowMajor transform = MatrixRowMajor::Identity(); std::memcpy(renderer.instance.transform.matrix, transform.m, sizeof(transform.m)); + RenderingElement3D::tlases.resize(1); RenderingElement3D::BuildTLAS(cmd, 0); - RenderingElement3D::BuildTLAS(cmd, 1); - RenderingElement3D::BuildTLAS(cmd, 2); + + 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 + }; + + VulkanBuffer 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(); + window.descriptorsRt = pool.sets; 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.StartSync(); } diff --git a/examples/VulkanTriangle/project.json b/examples/VulkanTriangle/project.json index dd9d5a1..df14df2 100644 --- a/examples/VulkanTriangle/project.json +++ b/examples/VulkanTriangle/project.json @@ -7,9 +7,11 @@ "dependencies": [ { "path":"../../project.json", - "configuration":"lib-wayland-vulkan-debug" + "configuration":"lib-win32-vulkan-debug" } ], + "target": "x86_64-w64-mingw32", + "debug": true, "shaders": [ { "path":"raygen.glsl", diff --git a/examples/VulkanTriangle/raygen.glsl b/examples/VulkanTriangle/raygen.glsl index eff2633..ad7b74c 100644 --- a/examples/VulkanTriangle/raygen.glsl +++ b/examples/VulkanTriangle/raygen.glsl @@ -1,17 +1,14 @@ #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 -layout(constant_id = 0) const uint16_t bufferStart = 0us; -layout(descriptor_heap) uniform accelerationStructureEXT topLevelAS[]; -layout(descriptor_heap) uniform writeonly image2D image[]; +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() { +void main() +{ // Pixel coordinates uvec2 pixel = gl_LaunchIDEXT.xy; uvec2 resolution = gl_LaunchSizeEXT.xy; @@ -35,7 +32,7 @@ void main() { )); traceRayEXT( - topLevelAS[bufferStart], + topLevelAS, gl_RayFlagsNoneEXT, 0xff, 0, 0, 0, @@ -46,5 +43,5 @@ void main() { 0 ); - imageStore(image[0], ivec2(pixel), vec4(hitValue, 1)); + imageStore(image, ivec2(pixel), vec4(hitValue, 1.0)); } diff --git a/examples/VulkanTriangleRuntime/README.md b/examples/VulkanTriangleRuntime/README.md new file mode 100644 index 0000000..6da7987 --- /dev/null +++ b/examples/VulkanTriangleRuntime/README.md @@ -0,0 +1,25 @@ +# 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 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 +``` \ No newline at end of file diff --git a/examples/VulkanTriangleRuntime/closesthit.glsl b/examples/VulkanTriangleRuntime/closesthit.glsl new file mode 100644 index 0000000..9a511b6 --- /dev/null +++ b/examples/VulkanTriangleRuntime/closesthit.glsl @@ -0,0 +1,12 @@ +#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; +} \ No newline at end of file diff --git a/examples/VulkanTriangleRuntime/main.cpp b/examples/VulkanTriangleRuntime/main.cpp new file mode 100644 index 0000000..242971a --- /dev/null +++ b/examples/VulkanTriangleRuntime/main.cpp @@ -0,0 +1,138 @@ +#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 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 layouts {{layout.layout}}; + + DescriptorPool pool; + pool.sets.resize(1); + pool.BuildPool(DescriptorPool::GetPoolSizes({layout}), layouts); + + std::array 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 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 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 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, 3> verts {{{-150, -150, 100}, {0, 150, 100}, {150, -150, 100}}}; + std::array 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 transform = MatrixRowMajor::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(); +} diff --git a/examples/VulkanTriangleRuntime/miss.glsl b/examples/VulkanTriangleRuntime/miss.glsl new file mode 100644 index 0000000..1478ecf --- /dev/null +++ b/examples/VulkanTriangleRuntime/miss.glsl @@ -0,0 +1,9 @@ +#version 460 +#extension GL_EXT_ray_tracing : enable + +layout(location = 0) rayPayloadInEXT vec3 hitValue; + +void main() +{ + hitValue = vec3(1, 1, 1); +} \ No newline at end of file diff --git a/examples/VulkanUI/project.json b/examples/VulkanTriangleRuntime/project.json similarity index 57% rename from examples/VulkanUI/project.json rename to examples/VulkanTriangleRuntime/project.json index 31cecf9..b30faa8 100644 --- a/examples/VulkanUI/project.json +++ b/examples/VulkanTriangleRuntime/project.json @@ -7,15 +7,24 @@ "dependencies": [ { "path":"../../project.json", - "configuration":"lib-wayland-vulkan-debug" + "configuration":"lib-vulkan-debug" } ], - "debug": true, "shaders": [ { "path":"raygen.glsl", "type": 6, "entrypoint":"main" + }, + { + "path":"closesthit.glsl", + "type": 9, + "entrypoint":"main" + }, + { + "path":"miss.glsl", + "type": 10, + "entrypoint":"main" } ] } diff --git a/examples/VulkanTriangleRuntime/raygen.glsl b/examples/VulkanTriangleRuntime/raygen.glsl new file mode 100644 index 0000000..ad7b74c --- /dev/null +++ b/examples/VulkanTriangleRuntime/raygen.glsl @@ -0,0 +1,47 @@ +#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)); +} diff --git a/examples/VulkanUI/main.cpp b/examples/VulkanUI/main.cpp deleted file mode 100644 index 325c38a..0000000 --- a/examples/VulkanUI/main.cpp +++ /dev/null @@ -1,133 +0,0 @@ -#include "vulkan/vulkan.h" -#include - -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 shaders{{ - {"raygen.spv", "main", VK_SHADER_STAGE_RAYGEN_BIT_KHR, &specilizationInfo} - }}; - - ShaderBindingTableVulkan shaderTable; - shaderTable.Init(shaders); - - std::array 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 missGroups; - std::array hitGroups; - - - PipelineRTVulkan pipeline; - pipeline.Init(cmd, raygenGroups, missGroups, hitGroups, shaderTable); - - window.FinishInit(); - - RenderingElement2DVulkan 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, true>*>(element.buffers[i])->value[0] = {1, 0, 0, 1}; - reinterpret_cast, true>*>(element.buffers[i])->value[1] = {0, 1, 0, 1}; - reinterpret_cast, 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 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 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(); -} diff --git a/examples/VulkanUI/raygen.glsl b/examples/VulkanUI/raygen.glsl deleted file mode 100644 index 849b983..0000000 --- a/examples/VulkanUI/raygen.glsl +++ /dev/null @@ -1,48 +0,0 @@ -#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); -} diff --git a/implementations/Crafter.Graphics-Device.cpp b/implementations/Crafter.Graphics-Device.cpp index d65d583..644fb67 100644 --- a/implementations/Crafter.Graphics-Device.cpp +++ b/implementations/Crafter.Graphics-Device.cpp @@ -65,13 +65,11 @@ const char* const deviceExtensionNames[] = { "VK_KHR_swapchain", "VK_KHR_spirv_1_4", "VK_KHR_shader_float_controls", + "VK_KHR_dynamic_rendering", "VK_KHR_acceleration_structure", - "VK_KHR_ray_tracing_pipeline", - "VK_EXT_descriptor_heap", "VK_KHR_deferred_host_operations", - "VK_KHR_maintenance5", - "VK_KHR_shader_untyped_pointers", - "VK_EXT_device_fault" + "VK_KHR_ray_tracing_pipeline", + "VK_KHR_ray_tracing_position_fetch" }; const char* const layerNames[] = { "VK_LAYER_KHRONOS_validation" @@ -81,58 +79,6 @@ const char* const layerNames[] = { void Device::CheckVkResult(VkResult result) { 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 addressInfos(faultCounts.addressInfoCount); - std::vector vendorInfos(faultCounts.vendorInfoCount); - std::vector 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(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)); } } @@ -211,33 +157,6 @@ constexpr CrafterKeys keysym_to_crafter_key(xkb_keysym_t sym) case XKB_KEY_y: return CrafterKeys::Y; 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 case XKB_KEY_0: return CrafterKeys::_0; case XKB_KEY_1: return CrafterKeys::_1; @@ -327,7 +246,8 @@ constexpr CrafterKeys keysym_to_crafter_key(xkb_keysym_t sym) case XKB_KEY_period: return CrafterKeys::period; case XKB_KEY_slash: return CrafterKeys::slash; - default: return CrafterKeys::CrafterKeysMax; + default: + throw std::runtime_error(std::format("Unkown XKB_KEY: {}", sym)); } } @@ -487,39 +407,31 @@ 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) { - xkb_keycode_t keycode = key + 8; - xkb_keysym_t keysym = xkb_state_key_get_one_sym(xkb_state, keycode); + xkb_keycode_t keycode = key + 8; + xkb_keysym_t keysym = xkb_state_key_get_one_sym(xkb_state, keycode); CrafterKeys crafterKey = keysym_to_crafter_key(keysym); - if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { - if (focusedWindow->heldkeys[(std::uint8_t)crafterKey]) { - focusedWindow->onKeyHold[(std::uint8_t)crafterKey].Invoke(); - focusedWindow->onAnyKeyHold.Invoke(crafterKey); - } else { - focusedWindow->heldkeys[(std::uint8_t)crafterKey] = true; - focusedWindow->onKeyDown[(std::uint8_t)crafterKey].Invoke(); - focusedWindow->onAnyKeyDown.Invoke(crafterKey); + if(state == WL_KEYBOARD_KEY_STATE_PRESSED) { + if(Device::focusedWindow->heldkeys[static_cast(crafterKey)]) { + Device::focusedWindow->onKeyHold[static_cast(crafterKey)].Invoke(); + Device::focusedWindow->onAnyKeyHold.Invoke(crafterKey); + } else{ + Device::focusedWindow->heldkeys[static_cast(crafterKey)] = true; + Device::focusedWindow->onKeyDown[static_cast(crafterKey)].Invoke(); + Device::focusedWindow->onAnyKeyDown.Invoke(crafterKey); } - - std::string buf; - buf.resize(16); - 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); + } else{ + Device::focusedWindow->heldkeys[static_cast(crafterKey)] = false; + Device::focusedWindow->onKeyUp[static_cast(crafterKey)].Invoke(); + Device::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) { - 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) { @@ -565,19 +477,8 @@ void Device::Initialize() { app.pEngineName = "Crafter.Graphics"; 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 = {}; instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; - instanceCreateInfo.pNext = &validationFeatures; instanceCreateInfo.pApplicationInfo = &app; instanceCreateInfo.enabledExtensionCount = sizeof(instanceExtensionNames) / sizeof(const char*); instanceCreateInfo.ppEnabledExtensionNames = instanceExtensionNames; @@ -628,10 +529,17 @@ void Device::Initialize() { { VkPhysicalDevice device = physDevices[i]; - uint32_t score; + VkPhysicalDeviceProperties2 properties2{ + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, + .pNext = &rayTracingProperties + }; + vkGetPhysicalDeviceProperties2(device, &properties2); VkPhysicalDeviceProperties properties; vkGetPhysicalDeviceProperties(device, &properties); + + uint32_t score; + switch (properties.deviceType) { default : @@ -660,11 +568,6 @@ void Device::Initialize() { } } - VkPhysicalDeviceProperties2 properties2 { - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, - .pNext = &rayTracingProperties - }; - vkGetPhysicalDeviceProperties2(physDevice, &properties2); uint32_t queueFamilyCount; vkGetPhysicalDeviceQueueFamilyProperties(physDevice, &queueFamilyCount, NULL); @@ -690,50 +593,21 @@ void Device::Initialize() { queueCreateInfo.queueCount = 1; 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 { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES, - .pNext = &bit16, - .shaderFloat16 = VK_TRUE, .runtimeDescriptorArray = 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{ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_FEATURES_KHR, - .pNext = &features12, + .pNext = &vkPhysicalDeviceRayTracingPositionFetchFeatures, .rayTracingPipeline = VK_TRUE }; @@ -743,14 +617,21 @@ void Device::Initialize() { .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 = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, - .pNext = &deviceAccelerationStructureFeature, .features = { - .samplerAnisotropy = VK_TRUE, - .shaderInt16 = VK_TRUE + .samplerAnisotropy = VK_TRUE } }; + physical_features2.pNext = &ext_feature; VkDeviceCreateInfo deviceCreateInfo = {}; deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; @@ -803,11 +684,6 @@ void Device::Initialize() { vkCreateRayTracingPipelinesKHR = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkCreateRayTracingPipelinesKHR")); vkGetRayTracingShaderGroupHandlesKHR = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkGetRayTracingShaderGroupHandlesKHR")); vkCmdTraceRaysKHR = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkCmdTraceRaysKHR")); - vkCmdBindResourceHeapEXT = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkCmdBindResourceHeapEXT")); - vkCmdBindSamplerHeapEXT = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkCmdBindSamplerHeapEXT")); - vkWriteResourceDescriptorsEXT = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkWriteResourceDescriptorsEXT")); - vkGetPhysicalDeviceDescriptorSizeEXT = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceDescriptorSizeEXT")); - vkGetDeviceFaultInfoEXT = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkGetDeviceFaultInfoEXT")); #endif } diff --git a/implementations/Crafter.Graphics-Font.cpp b/implementations/Crafter.Graphics-Font.cpp index 7d7c3ca..e101528 100644 --- a/implementations/Crafter.Graphics-Font.cpp +++ b/implementations/Crafter.Graphics-Font.cpp @@ -56,15 +56,4 @@ Font::Font(const std::filesystem::path& fontFilePath) { this->ascent = ascent; this->descent = descent; 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; } \ No newline at end of file diff --git a/implementations/Crafter.Graphics-RenderingElement3D.cpp b/implementations/Crafter.Graphics-RenderingElement3D.cpp index ca6902f..c1c3a0f 100644 --- a/implementations/Crafter.Graphics-RenderingElement3D.cpp +++ b/implementations/Crafter.Graphics-RenderingElement3D.cpp @@ -114,12 +114,6 @@ void RenderingElement3D::BuildTLAS(VkCommandBuffer cmd, std::uint32_t index) { 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 \ No newline at end of file diff --git a/implementations/Crafter.Graphics-Rendertarget.cpp b/implementations/Crafter.Graphics-Rendertarget.cpp deleted file mode 100644 index 52de2ee..0000000 --- a/implementations/Crafter.Graphics-Rendertarget.cpp +++ /dev/null @@ -1,141 +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 -#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(reinterpret_cast(transformBuffer[frame].value) + sizeof(RenderingElement2DVulkanTransformInfo)); - std::uint16_t* sizePtr = reinterpret_cast(transformBuffer[frame].value); - *sizePtr = static_cast(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(reinterpret_cast(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 infos, std::span 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 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(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 \ No newline at end of file diff --git a/implementations/Crafter.Graphics-Transform2D.cpp b/implementations/Crafter.Graphics-Transform2D.cpp index ff7d4b0..6381950 100644 --- a/implementations/Crafter.Graphics-Transform2D.cpp +++ b/implementations/Crafter.Graphics-Transform2D.cpp @@ -28,6 +28,6 @@ import std; using namespace Crafter; -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) { +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) { } \ No newline at end of file diff --git a/implementations/Crafter.Graphics-Window.cpp b/implementations/Crafter.Graphics-Window.cpp index 57a63ee..fad11c8 100644 --- a/implementations/Crafter.Graphics-Window.cpp +++ b/implementations/Crafter.Graphics-Window.cpp @@ -62,8 +62,6 @@ import :MouseElement; import :Device; #ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN import :VulkanTransition; -import :DescriptorHeapVulkan; -import :PipelineRTVulkan; #endif import std; @@ -272,65 +270,49 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { PostQuitMessage(0); break; } - case WM_KEYDOWN: - case WM_SYSKEYDOWN: { // SYSKEYDOWN catches Alt combos, F10, etc. - CrafterKeys crafterKey = vk_to_crafter_key(wParam); - bool isRepeat = (lParam & (1 << 30)) != 0; - - if (isRepeat) { - window->onKeyHold[(uint8_t)crafterKey].Invoke(); - window->onAnyKeyHold.Invoke(crafterKey); - } else { - window->heldkeys[(uint8_t)crafterKey] = true; - window->onKeyDown[(uint8_t)crafterKey].Invoke(); - window->onAnyKeyDown.Invoke(crafterKey); + case WM_KEYDOWN:{ + if ((lParam & (1 << 30)) == 0) { // only first press + CrafterKeys crafterKey = vk_to_crafter_key(wParam); + if(window->heldkeys[static_cast(crafterKey)]) { + window->onKeyHold[static_cast(crafterKey)].Invoke(); + window->onAnyKeyHold.Invoke(crafterKey); + } else{ + window->heldkeys[static_cast(crafterKey)] = true; + window->onKeyDown[static_cast(crafterKey)].Invoke(); + window->onAnyKeyDown.Invoke(crafterKey); + } } break; } - - case WM_KEYUP: - case WM_SYSKEYUP: { + case WM_KEYUP: { CrafterKeys crafterKey = vk_to_crafter_key(wParam); - window->heldkeys[(uint8_t)crafterKey] = false; - window->onKeyUp[(uint8_t)crafterKey].Invoke(); + window->heldkeys[static_cast(crafterKey)] = false; + window->onKeyUp[static_cast(crafterKey)].Invoke(); window->onAnyKeyUp.Invoke(crafterKey); break; } + case WM_MOUSEMOVE: { + int x = LOWORD(lParam); + int y = HIWORD(lParam); - case WM_CHAR: { - // wParam is a UTF-16 code unit. May be a surrogate — buffer until we have a pair. - wchar_t wc = (wchar_t)wParam; - - // Filter control characters (backspace=0x08, tab=0x09, enter=0x0D, escape=0x1B, etc.) - if (wc < 0x20 || wc == 0x7f) break; - - // Handle UTF-16 surrogate pairs (characters outside the BMP, e.g. emoji). - static wchar_t highSurrogate = 0; - wchar_t utf16[2]; - int utf16Len; - - if (wc >= 0xD800 && wc <= 0xDBFF) { - // High surrogate — stash it and wait for the low surrogate. - highSurrogate = wc; - break; - } else if (wc >= 0xDC00 && wc <= 0xDFFF) { - // 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)); - } + Vector pos(x, y); + window->currentMousePos = pos; + window->onMouseMove.Invoke(); + for(MouseElement* element : window->mouseElements) { + if(element) { + 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) { + element->onMouseMove.Invoke(); + if(!element->mouseHover) { + element->mouseHover = true; + element->onMouseEnter.Invoke(); + } + } else if(element->mouseHover) { + element->mouseHover = false + element->onMouseLeave.Invoke(); + } + } + } + window->mouseElements.erase(std::remove(window->mouseElements.begin(), window->mouseElements.end(), static_cast(nullptr)), window->mouseElements.end()); break; } case WM_LBUTTONDOWN: { @@ -385,14 +367,6 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { break; } - case WM_SETCURSOR: { - if (LOWORD(lParam) == HTCLIENT && window->cursorHandle) { - SetCursor(window->cursorHandle); - return TRUE; - } - break; - } - default: return DefWindowProc(hwnd, msg, wParam, lParam); } return 0; @@ -622,34 +596,6 @@ void Window::SetCusorImage(std::uint16_t sizeX, std::uint16_t sizeY) { wl_surface_damage(cursorSurface, 0, 0, sizeX, sizeY); wl_surface_commit(cursorSurface); #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(&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() { @@ -658,21 +604,9 @@ void Window::SetCusorImageDefault() { wl_surface_destroy(cursorSurface); cursorSurface = nullptr; #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() { - #ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND cursorRenderer.Render(0); for(std::uint32_t i = 0; i < cursorBufferOldSize / 4; i++) { std::swap(cursorRenderer.buffer[0][i].b, cursorRenderer.buffer[0][i].r); @@ -680,35 +614,6 @@ void Window::UpdateCursorImage() { wl_surface_attach(cursorSurface, cursorWlBuffer, 0, 0); wl_surface_damage(cursorSurface, 0, 0, 9999999, 99999999); 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() { @@ -831,31 +736,19 @@ void Window::Render() { } #endif - vkCmdBindPipeline(drawCmdBuffers[currentBuffer], VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, pipeline->pipeline); - - VkBindHeapInfoEXT resourceHeapInfo = { - .sType = VK_STRUCTURE_TYPE_BIND_HEAP_INFO_EXT, - .heapRange = { - .address = descriptorHeap->resourceHeap[currentBuffer].address, - .size = static_cast(descriptorHeap->resourceHeap[currentBuffer].size) - }, - .reservedRangeOffset = (descriptorHeap->resourceHeap[currentBuffer].size - Device::descriptorHeapProperties.minResourceHeapReservedRange) & ~(Device::descriptorHeapProperties.imageDescriptorAlignment - 1), - .reservedRangeSize = Device::descriptorHeapProperties.minResourceHeapReservedRange + vkCmdBindPipeline(drawCmdBuffers[currentBuffer], VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, rtPipeline); + VkBindDescriptorSetsInfo bindDescriptorSetsInfo{ + .sType = VK_STRUCTURE_TYPE_BIND_DESCRIPTOR_SETS_INFO, + .stageFlags = VK_SHADER_STAGE_ALL, + .layout = rtPipelineLayout, + .firstSet = 0, + .descriptorSetCount = static_cast(descriptorsRt.size()), + .pDescriptorSets = descriptorsRt.data() }; - Device::vkCmdBindResourceHeapEXT(drawCmdBuffers[currentBuffer], &resourceHeapInfo); - VkBindHeapInfoEXT samplerHeapInfo = { - .sType = VK_STRUCTURE_TYPE_BIND_HEAP_INFO_EXT, - .heapRange = { - .address = descriptorHeap->samplerHeap[currentBuffer].address, - .size = static_cast(descriptorHeap->samplerHeap[currentBuffer].size) - }, - .reservedRangeOffset = descriptorHeap->samplerHeap[currentBuffer].size - Device::descriptorHeapProperties.minSamplerHeapReservedRange, - .reservedRangeSize = Device::descriptorHeapProperties.minSamplerHeapReservedRange - }; - Device::vkCmdBindSamplerHeapEXT(drawCmdBuffers[currentBuffer], &samplerHeapInfo); + vkCmdBindDescriptorSets2(drawCmdBuffers[currentBuffer], &bindDescriptorSetsInfo); + Device::vkCmdTraceRaysKHR(drawCmdBuffers[currentBuffer], &raygenRegion, &missRegion, &hitRegion, &callableRegion, width, height, 1); - Device::vkCmdTraceRaysKHR(drawCmdBuffers[currentBuffer], &pipeline->raygenRegion, &pipeline->missRegion, &pipeline->hitRegion, &pipeline->callableRegion, width, height, 1); VkImageMemoryBarrier image_memory_barrier2 { .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, @@ -1025,6 +918,9 @@ 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 (oldSwapchain != VK_NULL_HANDLE) { + for (auto i = 0; i < numFrames; i++) { + vkDestroyImageView(Device::device, imageViews[i], nullptr); + } vkDestroySwapchainKHR(Device::device, oldSwapchain, nullptr); } uint32_t imageCount{ 0 }; @@ -1033,27 +929,34 @@ void Window::CreateSwapchain() // Get the swap chain images Device::CheckVkResult(vkGetSwapchainImagesKHR(Device::device, swapChain, &imageCount, images)); - for (std::uint8_t i = 0; i < numFrames; i++) { - imageViews[i] = { - .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, - .flags = 0, - .image = images[i], - .viewType = VK_IMAGE_VIEW_TYPE_2D, - .format = colorFormat, - .components = { - VK_COMPONENT_SWIZZLE_R, - VK_COMPONENT_SWIZZLE_G, - VK_COMPONENT_SWIZZLE_B, - VK_COMPONENT_SWIZZLE_A - }, - .subresourceRange = { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = 1, - }, - }; + for (auto i = 0; i < numFrames; i++) + { + VkImageViewCreateInfo colorAttachmentView = {}; + colorAttachmentView.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + colorAttachmentView.pNext = NULL; + colorAttachmentView.format = colorFormat; + colorAttachmentView.components = { + VK_COMPONENT_SWIZZLE_R, + VK_COMPONENT_SWIZZLE_G, + VK_COMPONENT_SWIZZLE_B, + VK_COMPONENT_SWIZZLE_A + }; + colorAttachmentView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + colorAttachmentView.subresourceRange.baseMipLevel = 0; + colorAttachmentView.subresourceRange.levelCount = 1; + colorAttachmentView.subresourceRange.baseArrayLayer = 0; + colorAttachmentView.subresourceRange.layerCount = 1; + colorAttachmentView.viewType = VK_IMAGE_VIEW_TYPE_2D; + colorAttachmentView.flags = 0; + colorAttachmentView.image = images[i]; + Device::CheckVkResult(vkCreateImageView(Device::device, &colorAttachmentView, nullptr, &imageViews[i])); + + VkImageSubresourceRange range{}; + range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + range.baseMipLevel = 0; + range.levelCount = VK_REMAINING_MIP_LEVELS; + range.baseArrayLayer = 0; + range.layerCount = VK_REMAINING_ARRAY_LAYERS; } } diff --git a/interfaces/Crafter.Graphics-DescriptorHeapVulkan.cppm b/interfaces/Crafter.Graphics-DescriptorHeapVulkan.cppm deleted file mode 100644 index 614c19c..0000000 --- a/interfaces/Crafter.Graphics-DescriptorHeapVulkan.cppm +++ /dev/null @@ -1,75 +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:DescriptorHeapVulkan; -#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN -import std; -import :Device; -import :Window; -import :Types; -import :VulkanBuffer; - -export namespace Crafter { - struct DescriptorHeapVulkan { - VulkanBuffer resourceHeap[Window::numFrames]; - VulkanBuffer 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 \ No newline at end of file diff --git a/interfaces/Crafter.Graphics-DescriptorPoolVulkan.cppm b/interfaces/Crafter.Graphics-DescriptorPoolVulkan.cppm new file mode 100644 index 0000000..4ed941d --- /dev/null +++ b/interfaces/Crafter.Graphics-DescriptorPoolVulkan.cppm @@ -0,0 +1,187 @@ +/* +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 sets; + VkDescriptorPool descriptorPool = VK_NULL_HANDLE; + + template + consteval static void GetOccuringDescriptors(std::array& types) { + for (const VkDescriptorSetLayoutBinding& binding : Shader::descriptors) { + for (DescriptorEntry& type : types) { + if (type.type == binding.descriptorType) { + type.occured = true; + } + } + } + } + + template + consteval static std::uint32_t GetUniqueDiscriptorCount() { + std::array 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(types), ... ); + + std::uint32_t size = 0; + for(DescriptorEntry& type : types) { + if(type.occured) { + size++; + } + } + + return size; + } + + template + consteval static std::array()> GetPoolSizes() { + std::array()> types = {}; + for(std::uint32_t i = 0; i < GetUniqueDiscriptorCount(); 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(); i++){ + if(types[i].descriptorCount == 12345) { + types[i].type = binding.descriptorType; + types[i].descriptorCount = binding.descriptorCount; + break; + } + } + } + } + }(), + ...); + + return types; + } + + constexpr static std::vector GetPoolSizes(const std::span shaders) { + std::vector 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 + constexpr static std::vector GetPoolSizesCombined(const std::span shaders) { + std::vector 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 poolSizes, std::span 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(sets.size()), + .poolSizeCount = static_cast(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(sets.size()), + .pSetLayouts = setLayouts.data(), + }; + + Device::CheckVkResult(vkAllocateDescriptorSets(Device::device, &allocInfo, sets.data())); + } + }; +} + +#endif \ No newline at end of file diff --git a/interfaces/Crafter.Graphics-DescriptorSetLayoutVulkan.cppm b/interfaces/Crafter.Graphics-DescriptorSetLayoutVulkan.cppm new file mode 100644 index 0000000..c499eff --- /dev/null +++ b/interfaces/Crafter.Graphics-DescriptorSetLayoutVulkan.cppm @@ -0,0 +1,62 @@ +/* +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 descriptors; + DescriptorSetLayoutVulkan(std::vector&& layouts) : descriptors(std::move(layouts)) { + VkDescriptorSetLayoutCreateInfo descriptorLayoutInfoMesh = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + .bindingCount = static_cast(descriptors.size()), + .pBindings = descriptors.data() + }; + Device::CheckVkResult(vkCreateDescriptorSetLayout(Device::device, &descriptorLayoutInfoMesh, nullptr, &layout)); + } + }; + + template Descriptors> + class DescriptorSetLayoutVulkanConst { + public: + inline static VkDescriptorSetLayout layout; + constexpr static std::span 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 \ No newline at end of file diff --git a/interfaces/Crafter.Graphics-Device.cppm b/interfaces/Crafter.Graphics-Device.cppm index 214e970..0c5dd50 100644 --- a/interfaces/Crafter.Graphics-Device.cppm +++ b/interfaces/Crafter.Graphics-Device.cppm @@ -114,20 +114,9 @@ export namespace Crafter { inline static PFN_vkCreateRayTracingPipelinesKHR vkCreateRayTracingPipelinesKHR; inline static PFN_vkGetRayTracingShaderGroupHandlesKHR vkGetRayTracingShaderGroupHandlesKHR; inline static PFN_vkCmdTraceRaysKHR vkCmdTraceRaysKHR; - inline static PFN_vkCmdBindResourceHeapEXT vkCmdBindResourceHeapEXT; - inline static PFN_vkCmdBindSamplerHeapEXT vkCmdBindSamplerHeapEXT; - inline static PFN_vkWriteResourceDescriptorsEXT vkWriteResourceDescriptorsEXT; - inline static PFN_vkGetPhysicalDeviceDescriptorSizeEXT vkGetPhysicalDeviceDescriptorSizeEXT; - inline static PFN_vkGetDeviceFaultInfoEXT vkGetDeviceFaultInfoEXT; - inline static VkPhysicalDeviceMemoryProperties memoryProperties; - - inline static VkPhysicalDeviceDescriptorHeapPropertiesEXT descriptorHeapProperties = { - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_HEAP_PROPERTIES_EXT - }; inline static VkPhysicalDeviceRayTracingPipelinePropertiesKHR rayTracingProperties = { - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_PROPERTIES_KHR, - .pNext = &descriptorHeapProperties + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_PROPERTIES_KHR }; static void CheckVkResult(VkResult result); diff --git a/interfaces/Crafter.Graphics-Font.cppm b/interfaces/Crafter.Graphics-Font.cppm index 3b98b35..77d14dd 100644 --- a/interfaces/Crafter.Graphics-Font.cppm +++ b/interfaces/Crafter.Graphics-Font.cppm @@ -33,7 +33,6 @@ namespace Crafter { std::int_fast32_t descent; std::int_fast32_t lineGap; stbtt_fontinfo font; - Font(const std::filesystem::path& font); - std::uint32_t GetLineWidth(const std::string_view text, float size); + Font(const std::filesystem::path& font); }; } \ No newline at end of file diff --git a/interfaces/Crafter.Graphics-ForwardDeclarations.cppm b/interfaces/Crafter.Graphics-ForwardDeclarations.cppm index 3534166..9a5adca 100644 --- a/interfaces/Crafter.Graphics-ForwardDeclarations.cppm +++ b/interfaces/Crafter.Graphics-ForwardDeclarations.cppm @@ -21,7 +21,9 @@ export module Crafter.Graphics:ForwardDeclarations; import std; export namespace Crafter { + template struct RendertargetBase; + struct GridElement; struct Window; } \ No newline at end of file diff --git a/interfaces/Crafter.Graphics-GridElement.cppm b/interfaces/Crafter.Graphics-GridElement.cppm index 30565bf..7465b5e 100644 --- a/interfaces/Crafter.Graphics-GridElement.cppm +++ b/interfaces/Crafter.Graphics-GridElement.cppm @@ -33,7 +33,8 @@ 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) { } - void UpdatePosition(RendertargetBase& window, Transform2D& parent) override { + template + void UpdatePositionImpl(RendertargetBase& window, Transform2D& parent) { ScaleElement(parent); std::int32_t cellWidth = (paddingX * 2) - (spacingX * (columns - 1)) / columns; std::int32_t cellHeight = (paddingY * 2) - (spacingY * (rows - 1)) / rows; @@ -60,5 +61,11 @@ 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); + } }; } \ No newline at end of file diff --git a/interfaces/Crafter.Graphics-ImageVulkan.cppm b/interfaces/Crafter.Graphics-ImageVulkan.cppm index 5ded767..2b95d24 100644 --- a/interfaces/Crafter.Graphics-ImageVulkan.cppm +++ b/interfaces/Crafter.Graphics-ImageVulkan.cppm @@ -37,7 +37,7 @@ export namespace Crafter { std::uint8_t mipLevels; VkImage image; VkDeviceMemory imageMemory; - VulkanBuffer buffer; + VulkanBuffer buffer; VkImageView imageView; VkDescriptorImageInfo descriptor; diff --git a/interfaces/Crafter.Graphics-Mesh.cppm b/interfaces/Crafter.Graphics-Mesh.cppm index 300d8a6..76720b2 100644 --- a/interfaces/Crafter.Graphics-Mesh.cppm +++ b/interfaces/Crafter.Graphics-Mesh.cppm @@ -32,10 +32,10 @@ export namespace Crafter { #ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN class Mesh { public: - VulkanBuffer scratchBuffer; - VulkanBuffer blasBuffer; - VulkanBuffer, true> vertexBuffer; - VulkanBuffer indexBuffer; + VulkanBuffer scratchBuffer; + VulkanBuffer blasBuffer; + VulkanBuffer, true, true, false> vertexBuffer; + VulkanBuffer indexBuffer; VkAccelerationStructureGeometryTrianglesDataKHR blasData; VkAccelerationStructureGeometryKHR blas; VkAccelerationStructureKHR accelerationStructure; diff --git a/interfaces/Crafter.Graphics-PipelineRTVulkan.cppm b/interfaces/Crafter.Graphics-PipelineRTVulkan.cppm index ac34b11..2b312a8 100644 --- a/interfaces/Crafter.Graphics-PipelineRTVulkan.cppm +++ b/interfaces/Crafter.Graphics-PipelineRTVulkan.cppm @@ -30,16 +30,26 @@ import :ShaderBindingTableVulkan; import :Types; export namespace Crafter { - struct PipelineRTVulkan { + class PipelineRTVulkan { + public: VkPipeline pipeline; + VkPipelineLayout pipelineLayout; std::vector shaderHandles; - VulkanBuffer sbtBuffer; + VulkanBuffer sbtBuffer; VkStridedDeviceAddressRegionKHR raygenRegion; VkStridedDeviceAddressRegionKHR missRegion; VkStridedDeviceAddressRegionKHR hitRegion; VkStridedDeviceAddressRegionKHR callableRegion; - void Init(VkCommandBuffer cmd, std::span raygenGroups, std::span missGroups, std::span hitGroups, ShaderBindingTableVulkan& shaderTable) { + void Init(VkCommandBuffer cmd, std::span setLayouts, std::span raygenGroups, std::span missGroups, std::span hitGroups, ShaderBindingTableVulkan& shaderTable) { + VkPipelineLayoutCreateInfo pipelineLayoutInfo { + .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + .setLayoutCount = static_cast(setLayouts.size()), + .pSetLayouts = setLayouts.data() + }; + + Device::CheckVkResult(vkCreatePipelineLayout(Device::device, &pipelineLayoutInfo, nullptr, &pipelineLayout)); + std::vector groups; groups.reserve(raygenGroups.size() + missGroups.size() + hitGroups.size()); @@ -47,21 +57,14 @@ export namespace Crafter { groups.insert(groups.end(), missGroups.begin(), missGroups.end()); groups.insert(groups.end(), hitGroups.begin(), hitGroups.end()); - VkPipelineCreateFlags2CreateInfo flags2 = { - .sType = VK_STRUCTURE_TYPE_PIPELINE_CREATE_FLAGS_2_CREATE_INFO, - .flags = VK_PIPELINE_CREATE_2_DESCRIPTOR_HEAP_BIT_EXT - }; - - VkRayTracingPipelineCreateInfoKHR rtPipelineInfo { + VkRayTracingPipelineCreateInfoKHR rtPipelineInfo{ .sType = VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_KHR, - .pNext = &flags2, - .flags = 0, .stageCount = static_cast(shaderTable.shaderStages.size()), .pStages = shaderTable.shaderStages.data(), .groupCount = static_cast(groups.size()), .pGroups = groups.data(), .maxPipelineRayRecursionDepth = 1, - .layout = VK_NULL_HANDLE + .layout = pipelineLayout }; Device::CheckVkResult(Device::vkCreateRayTracingPipelinesKHR(Device::device, {}, {}, 1, &rtPipelineInfo, nullptr, &pipeline)); @@ -110,10 +113,189 @@ export namespace Crafter { callableRegion.stride = 0; callableRegion.size = 0; } + }; - ~PipelineRTVulkan() { - vkDestroyPipeline(Device::device, pipeline, nullptr); + template + struct ShaderGroup { + static constexpr std::uint32_t generalShader = GeneralShader; + static constexpr std::uint32_t closestHitShader = ClosestHitShader; + static constexpr std::uint32_t anyHitShader = AnyHitShader; + static constexpr std::uint32_t intersectionShader = IntersectionShader; + }; + + template + class PipelineRTVulkanConst { + public: + inline static VkPipeline pipeline; + inline static VkPipelineLayout pipelineLayout; + inline static std::vector shaderHandles; + inline static VulkanBuffer 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 setLayouts) { + VkPipelineLayoutCreateInfo pipelineLayoutInfo { + .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + .setLayoutCount = static_cast(setLayouts.size()), + .pSetLayouts = setLayouts.data() + }; + + Device::CheckVkResult(vkCreatePipelineLayout(Device::device, &pipelineLayoutInfo, nullptr, &pipelineLayout)); + + constexpr auto groupIndexSeq = std::make_index_sequence>{}; + + constexpr std::array> groups = GetShaderGroups(groupIndexSeq); + + VkRayTracingPipelineCreateInfoKHR rtPipelineInfo{ + .sType = VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_KHR, + .stageCount = static_cast(ShaderBindingTableVulkanConst::shaderStages.size()), + .pStages = ShaderBindingTableVulkanConst::shaderStages.data(), + .groupCount = static_cast(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(groupIndexSeq) * sbtStride; + + missRegion.stride = sbtStride; + missRegion.deviceAddress = AlignUp(raygenRegion.size, Device::rayTracingProperties.shaderGroupBaseAlignment); + missRegion.size = GetGroupCount(groupIndexSeq) * sbtStride; + + hitRegion.stride = sbtStride; + hitRegion.deviceAddress = AlignUp(missRegion.deviceAddress + missRegion.size, Device::rayTracingProperties.shaderGroupBaseAlignment); + hitRegion.size = (GetGroupCount(groupIndexSeq) * sbtStride) + (GetGroupCount(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 + consteval static void AddShaderGroup(std::array>& groups) { + using groupTemplate = std::tuple_element_t; + 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 + consteval static std::array> GetShaderGroups(std::index_sequence) { + std::array> groups{}; + (AddShaderGroup(groups), ...); + return groups; + } + + template + consteval static void GetGroupCountImpl(std::uint32_t& count) { + using groupTemplate = std::tuple_element_t; + if constexpr(groupTemplate::generalShader != VK_SHADER_UNUSED_KHR) { + using shaderTemplate = std::tuple_element_t; + if constexpr(shaderTemplate::_stage == stage) { + count++; + } + } else if constexpr(groupTemplate::closestHitShader != VK_SHADER_UNUSED_KHR) { + using shaderTemplate = std::tuple_element_t; + if constexpr(shaderTemplate::_stage == stage) { + count++; + } + } else if constexpr(groupTemplate::anyHitShader != VK_SHADER_UNUSED_KHR) { + using shaderTemplate = std::tuple_element_t; + 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 + consteval static std::uint32_t GetGroupCount(std::index_sequence) { + std::uint32_t count = 0; + (GetGroupCountImpl(count), ...); + return count; + } + + template + static void AddShaderGroupToBuffer(std::uint32_t sbtStride, std::uint32_t& offset) { + using groupTemplate = std::tuple_element_t; + if constexpr(groupTemplate::generalShader != VK_SHADER_UNUSED_KHR) { + using shaderTemplate = std::tuple_element_t; + 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; + 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; + 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 + static void AddShaderGroupsToBuffer(std::uint32_t sbtStride, std::index_sequence) { + std::uint32_t offset = 0; + (AddShaderGroupToBuffer(sbtStride, offset), ...); + offset = AlignUp(offset, Device::rayTracingProperties.shaderGroupBaseAlignment); + (AddShaderGroupToBuffer(sbtStride, offset), ...); + offset = AlignUp(offset, Device::rayTracingProperties.shaderGroupBaseAlignment); + (AddShaderGroupToBuffer(sbtStride, offset), ...); + (AddShaderGroupToBuffer(sbtStride, offset), ...); + } }; } diff --git a/interfaces/Crafter.Graphics-RenderingElement2D.cppm b/interfaces/Crafter.Graphics-RenderingElement2D.cppm index dc2c184..1dc448d 100644 --- a/interfaces/Crafter.Graphics-RenderingElement2D.cppm +++ b/interfaces/Crafter.Graphics-RenderingElement2D.cppm @@ -29,31 +29,31 @@ import :Types; import :Window; export namespace Crafter { - template requires ((!Rotating || Scaling) && (!Owning || Scaling)) - struct RenderingElement2D : RenderingElement2DBase, ScalingBase, RotatingBase { + template requires ((!Rotating || Scaling) && (!Owning || Scaling)) + struct RenderingElement2D : RenderingElement2DBase, ScalingBase, RotatingBase { RenderingElement2D() = default; - RenderingElement2D(Anchor2D anchor, OpaqueType opaque) : RenderingElement2DBase(anchor, opaque) { + RenderingElement2D(Anchor2D anchor, OpaqueType opaque) : RenderingElement2DBase(anchor, opaque) { } - RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t rotation) requires(Rotating) : RenderingElement2DBase(anchor, opaque), RotatingBase(rotation) { + RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t rotation) requires(Rotating) : RenderingElement2DBase(anchor, opaque), RotatingBase(rotation) { } - RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight, Vector* scalingBuffer) requires(Scaling && !Owning) : RenderingElement2DBase(anchor, opaque), ScalingBase(bufferWidth, bufferHeight, scalingBuffer) { + RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight, Vector* scalingBuffer) requires(Scaling && !Owning) : RenderingElement2DBase(anchor, opaque), ScalingBase(bufferWidth, bufferHeight, scalingBuffer) { } - RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight, Vector* scalingBuffer, std::uint32_t rotation) requires(Scaling && !Owning && Rotating) : RenderingElement2DBase(anchor, opaque), ScalingBase(bufferWidth, bufferHeight, scalingBuffer), RotatingBase(rotation) { + RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight, Vector* scalingBuffer, std::uint32_t rotation) requires(Scaling && !Owning && Rotating) : RenderingElement2DBase(anchor, opaque), ScalingBase(bufferWidth, bufferHeight, scalingBuffer), RotatingBase(rotation) { } - RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight) requires(Owning) : RenderingElement2DBase(anchor, opaque), ScalingBase(bufferWidth, bufferHeight) { + RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight) requires(Owning) : RenderingElement2DBase(anchor, opaque), ScalingBase(bufferWidth, bufferHeight) { } - RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight, std::uint32_t rotation) requires(Owning && Rotating) : RenderingElement2DBase(anchor, opaque), ScalingBase(bufferWidth, bufferHeight) , RotatingBase(rotation) { + RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight, std::uint32_t rotation) requires(Owning && Rotating) : RenderingElement2DBase(anchor, opaque), ScalingBase(bufferWidth, bufferHeight) , RotatingBase(rotation) { } - RenderingElement2D(Anchor2D anchor, TextureAsset>& texture) requires(!Owning && Scaling) : RenderingElement2DBase(anchor, texture.opaque), ScalingBase(texture.pixels.data(), texture.sizeX, texture.sizeY) { + RenderingElement2D(Anchor2D anchor, TextureAsset>& texture) requires(!Owning && Scaling) : RenderingElement2DBase(anchor, texture.opaque), ScalingBase(texture.pixels.data(), texture.sizeX, texture.sizeY) { } - RenderingElement2D(Anchor2D anchor, TextureAsset>& texture, std::uint32_t rotation) requires(!Owning && Scaling && Rotating) : RenderingElement2DBase(anchor, texture.opaque), ScalingBase(texture.pixels.data(), texture.sizeX, texture.sizeY), RotatingBase(rotation) { + RenderingElement2D(Anchor2D anchor, TextureAsset>& texture, std::uint32_t rotation) requires(!Owning && Scaling && Rotating) : RenderingElement2DBase(anchor, texture.opaque), ScalingBase(texture.pixels.data(), texture.sizeX, texture.sizeY), RotatingBase(rotation) { } @@ -62,10 +62,10 @@ export namespace Crafter { void ScaleNearestNeighbor() requires(Scaling) { for (std::uint32_t y = 0; y < this->scaled.size.y; y++) { - std::uint32_t srcY = y * ScalingBase::bufferHeight / this->scaled.size.y; + std::uint32_t srcY = y * ScalingBase::bufferHeight / this->scaled.size.y; for (std::uint32_t x = 0; x < this->scaled.size.x; x++) { - std::uint32_t srcX = x * ScalingBase::bufferWidth / this->scaled.size.x; - this->buffer[y * this->scaled.size.x + x] = ScalingBase::scalingBuffer[srcY * ScalingBase::bufferWidth + srcX]; + std::uint32_t srcX = x * ScalingBase::bufferWidth / this->scaled.size.x; + this->buffer[y * this->scaled.size.x + x] = ScalingBase::scalingBuffer[srcY * ScalingBase::bufferWidth + srcX]; } } } @@ -97,15 +97,15 @@ export namespace Crafter { const float dstCy = (this->scaled.size.y - 1.0) * 0.5; // Source center - const float srcCx = (ScalingBase::bufferWidth - 1.0) * 0.5; - const float srcCy = (ScalingBase::bufferHeight - 1.0) * 0.5; + const float srcCx = (ScalingBase::bufferWidth - 1.0) * 0.5; + const float srcCy = (ScalingBase::bufferHeight - 1.0) * 0.5; const float c = std::cos(RotatingBase::rotation); const float s = std::sin(RotatingBase::rotation); // Scale factors (destination → source) - const float scaleX = static_cast(ScalingBase::bufferWidth) / dstWidth; - const float scaleY = static_cast(ScalingBase::bufferHeight) / dstHeight; + const float scaleX = static_cast(ScalingBase::bufferWidth) / dstWidth; + const float scaleY = static_cast(ScalingBase::bufferHeight) / dstHeight; for (std::uint32_t yB = 0; yB < this->scaled.size.y; ++yB) { 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::round(sx)); const std::int32_t srcY = static_cast(std::round(sy)); - if (srcX >= 0 && srcX < ScalingBase::bufferWidth && srcY >= 0 && srcY < ScalingBase::bufferHeight) { - this->buffer[yB * this->scaled.size.x + xB] = ScalingBase::scalingBuffer[srcY * ScalingBase::bufferWidth + srcX]; + if (srcX >= 0 && srcX < ScalingBase::bufferWidth && srcY >= 0 && srcY < ScalingBase::bufferHeight) { + this->buffer[yB * this->scaled.size.x + xB] = ScalingBase::scalingBuffer[srcY * ScalingBase::bufferWidth + srcX]; } } } } - void UpdatePosition(RendertargetBase& window, Transform2D& parent) override { + void UpdatePosition(RendertargetBase& window, Transform2D& parent) override { ScaleData2D oldScale = this->scaled; this->ScaleElement(parent); if constexpr(Scaling && !Rotating) { @@ -153,7 +153,7 @@ export namespace Crafter { } } - std::vector ResizeText(RendertargetBase& window, Transform2D& parent, const std::string_view text, float& size, Font& font, TextOverflowMode overflowMode = TextOverflowMode::Clip, TextScaleMode scaleMode = TextScaleMode::None) { + std::vector 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); int baseline = (int)(font.ascent * scale); @@ -208,7 +208,7 @@ export namespace Crafter { size = std::min(maxFontHeight, maxFontWidth); } else { if constexpr(Scaling) { - lines.resize(ScalingBase::bufferHeight / lines.size()); + lines.resize(ScalingBase::bufferHeight / lines.size()); } else { lines.resize(this->scaled.size.y / lines.size()); } @@ -284,7 +284,7 @@ export namespace Crafter { size = this->scaled.size.y / lineHeightPerFont; } else { if constexpr(Scaling) { - lines.resize(ScalingBase::bufferHeight / lines.size()); + lines.resize(ScalingBase::bufferHeight / lines.size()); } else { lines.resize(this->scaled.size.y / lines.size()); } @@ -293,27 +293,7 @@ export namespace Crafter { return lines; } - - 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 lines, float size, Vector color, Font& font, TextAlignment alignment = TextAlignment::Left, std::uint32_t offsetX = 0, std::uint32_t offsetY = 0, OpaqueType opaque = OpaqueType::FullyOpaque) { + void RenderText(std::span lines, float size, Vector 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); int baseline = (int)(font.ascent * scale); std::uint32_t lineHeight = (font.ascent - font.descent) * scale; @@ -340,13 +320,8 @@ export namespace Crafter { 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; + for (std::size_t i = 0; i < line.size(); ++i) { + int codepoint = line[i]; int ax; int lsb; @@ -371,19 +346,41 @@ export namespace Crafter { // Only draw pixels that are within our scaled buffer bounds if constexpr(Scaling) { - if (bufferX >= 0 && bufferX < ScalingBase::bufferWidth && bufferY >= 0 && bufferY < ScalingBase::bufferHeight) { - ScalingBase::scalingBuffer[bufferY * ScalingBase::bufferWidth + bufferX] = {color.r, color.g, color.b, static_cast(bitmap[j * w + i])}; + if (bufferX >= 0 && bufferX < ScalingBase::bufferWidth && bufferY >= 0 && bufferY < ScalingBase::bufferHeight) { + ScalingBase::scalingBuffer[bufferY * ScalingBase::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) { - this->buffer[bufferY * this->scaled.size.x + bufferX] = {color.r, color.g, color.b, static_cast(bitmap[j * w + i])}; + this->buffer[bufferY * this->scaled.size.x + bufferX] = {color.r, color.g, color.b, 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::bufferWidth && bufferY >= 0 && bufferY < ScalingBase::bufferHeight) { + ScalingBase::scalingBuffer[bufferY * ScalingBase::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; } - case OpaqueType::SemiOpaque: case OpaqueType::Transparent: { for (int j = 0; j < h; j++) { for (int i = 0; i < w; i++) { @@ -392,41 +389,29 @@ export namespace Crafter { // Only draw pixels that are within our scaled buffer bounds if constexpr(Scaling) { - if (bufferX >= 0 && bufferX < ScalingBase::bufferWidth && bufferY >= 0 && bufferY < ScalingBase::bufferHeight) { - ScalingBase::scalingBuffer[bufferY * ScalingBase::bufferWidth + bufferX] = {color.r, color.g, color.b, static_cast(bitmap[j * w + i])}; + if (bufferX >= 0 && bufferX < ScalingBase::bufferWidth && bufferY >= 0 && bufferY < ScalingBase::bufferHeight) { + ScalingBase::scalingBuffer[bufferY * ScalingBase::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) { - if constexpr(std::same_as) { - std::uint8_t alpha = bitmap[j * w + i]; + std::uint8_t alpha = bitmap[j * w + i]; - Vector dst = this->buffer[bufferY * this->scaled.size.x + bufferX]; - - float srcA = (alpha / 255.0f) * (color.a / 255.0f); - float dstA = dst.a / 255.0f; - - float oneMinusSrcA = 1.0f - color.a; - - float outA = srcA + dstA * (1.0f - srcA); - this->buffer[bufferY * this->scaled.size.x + bufferX] = Vector( - static_cast((color.r * srcA + dst.r * dstA * (1.0f - srcA)) / outA), - static_cast((color.g * srcA + dst.g * dstA * (1.0f - srcA)) / outA), - static_cast((color.b * srcA + dst.b * dstA * (1.0f - srcA)) / outA), - static_cast(outA * 255) - ); - } else if constexpr(std::same_as) { - 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 - ); + if(alpha == 0) { + continue; } + + Vector dst = this->buffer[bufferY * this->scaled.size.x + bufferX]; + + float srcA = (alpha / 255.0f) * (color.a / 255.0f); + float dstA = dst.a / 255.0f; + + float outA = srcA + dstA * (1.0f - srcA); + this->buffer[bufferY * this->scaled.size.x + bufferX] = Vector( + static_cast((color.r * srcA + dst.r * dstA * (1.0f - srcA)) / outA), + static_cast((color.g * srcA + dst.g * dstA * (1.0f - srcA)) / outA), + static_cast((color.b * srcA + dst.b * dstA * (1.0f - srcA)) / outA), + static_cast(outA * 255) + ); } } } @@ -437,9 +422,8 @@ export namespace Crafter { x += (int)(ax * scale); - if (p + 1 < end) { - int next; - x += (int)stbtt_GetGlyphKernAdvance(&font.font, codepoint, utf8_decode(p+1, &next)); + if (i + 1 < line.size()) { + x += (int)stbtt_GetGlyphKernAdvance(&font.font, codepoint, line[i+1]); } } currentY += lineHeight; diff --git a/interfaces/Crafter.Graphics-RenderingElement2DBase.cppm b/interfaces/Crafter.Graphics-RenderingElement2DBase.cppm index 37c80ec..df29672 100644 --- a/interfaces/Crafter.Graphics-RenderingElement2DBase.cppm +++ b/interfaces/Crafter.Graphics-RenderingElement2DBase.cppm @@ -30,12 +30,6 @@ export namespace Crafter { Right }; - enum class TextVerticalAlignment { - Top, - Center, - Bottom - }; - enum class TextOverflowMode { Clip, Wrap @@ -48,9 +42,9 @@ export namespace Crafter { Buffer }; - template + template struct RenderElement2DScalingOwning { - std::vector> scalingBuffer; + std::vector> scalingBuffer; std::uint32_t bufferWidth; std::uint32_t bufferHeight; RenderElement2DScalingOwning() = default; @@ -59,13 +53,13 @@ export namespace Crafter { } }; - template + template struct RenderElement2DScalingNonOwning { - Vector* scalingBuffer; + Vector* scalingBuffer; std::uint32_t bufferWidth; std::uint32_t bufferHeight; RenderElement2DScalingNonOwning() = default; - RenderElement2DScalingNonOwning(Vector* scalingBuffer, std::uint32_t bufferWidth, std::uint32_t bufferHeight) : scalingBuffer(scalingBuffer), bufferWidth(bufferWidth), bufferHeight(bufferHeight) { + RenderElement2DScalingNonOwning(Vector* scalingBuffer, std::uint32_t bufferWidth, std::uint32_t bufferHeight) : scalingBuffer(scalingBuffer), bufferWidth(bufferWidth), bufferHeight(bufferHeight) { } }; @@ -82,13 +76,13 @@ export namespace Crafter { struct EmptyScalingBase {}; struct EmptyRotatingBase {}; - template + template using ScalingBase = std::conditional_t< Scaling, std::conditional_t, - RenderElement2DScalingNonOwning>, + RenderElement2DScalingOwning, + RenderElement2DScalingNonOwning>, EmptyScalingBase >; @@ -100,11 +94,11 @@ export namespace Crafter { EmptyRotatingBase >; - template + template struct RenderingElement2DBase : Transform2D { ScaleData2D oldScale[Frames]; bool redraw[Frames]; - std::vector> buffer; + std::vector> buffer; OpaqueType opaque; RenderingElement2DBase(Anchor2D anchor) : Transform2D(anchor) { for(std::uint8_t i = 0; i < Frames; i++) { @@ -121,16 +115,5 @@ export namespace Crafter { redraw[i] = true; } } - void CopyNearestNeighbor(Vector* 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]; - } - } - } }; } \ No newline at end of file diff --git a/interfaces/Crafter.Graphics-RenderingElement2DVulkan.cppm b/interfaces/Crafter.Graphics-RenderingElement2DVulkan.cppm deleted file mode 100644 index 81c5543..0000000 --- a/interfaces/Crafter.Graphics-RenderingElement2DVulkan.cppm +++ /dev/null @@ -1,477 +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; -#include "../lib/stb_truetype.h" -#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN -#include -#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 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&& buffers) : bufferX(bufferX), bufferY(bufferY), buffers(std::move(buffers)), Transform2D(anchor) { - - } - }; - - template - 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, Mapped>(); - static_cast, 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, Mapped>(); - static_cast, 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, Mapped>(); - static_cast, 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, Mapped>(); - static_cast, 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&& 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, Mapped>(); - static_cast, 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>::Load(assetPath, static_cast, Mapped>*>(buffers[0])->value, this->bufferX, this->bufferY); - } else { - for(std::uint8_t i = 0; i < Window::numFrames; i++) { - buffers[i] = new VulkanBuffer, Mapped>(); - static_cast, 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>::Load(assetPath, static_cast, Mapped>*>(buffers[0])->value, this->bufferX, this->bufferY); - for(std::uint8_t i = 1; i < Window::numFrames; i++) { - std::memcpy(static_cast, Mapped>*>(buffers[i])->value, static_cast, Mapped>*>(buffers[0])->value, this->bufferX * this->bufferY * sizeof(_Float16)); - } - } - } - - ~RenderingElement2DVulkan() { - if constexpr(Owning) { - if constexpr(Single) { - delete static_cast, Mapped>*>(buffers[0]); - } else { - for(VulkanBufferBase* buffer : buffers) { - delete static_cast, 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, Mapped>(); - static_cast, 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, Mapped>(); - static_cast, 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, 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, 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(reinterpret_cast(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(window2); - this->ScaleElement(parent); - RenderingElement2DVulkanTransformInfo* val = reinterpret_cast(reinterpret_cast(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 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 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, 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, true>*>(buffers[frame])->value[bufferY * this->bufferX + bufferX]; - - _Float16 outA = srcA + dst.a * (1.0f - srcA); - static_cast, 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 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 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, 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, true>*>(buffers[frame])->value[bufferY * this->bufferX + bufferX]; - - _Float16 outA = srcA + dst.a * (1.0f - srcA); - static_cast, 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 \ No newline at end of file diff --git a/interfaces/Crafter.Graphics-RenderingElement3D.cppm b/interfaces/Crafter.Graphics-RenderingElement3D.cppm index 9a6a031..2931cc4 100644 --- a/interfaces/Crafter.Graphics-RenderingElement3D.cppm +++ b/interfaces/Crafter.Graphics-RenderingElement3D.cppm @@ -27,22 +27,20 @@ import std; import :Mesh; import :VulkanBuffer; import Crafter.Math; -import :Window; export namespace Crafter { struct TlasWithBuffer { - VkDeviceAddress address; - VulkanBuffer buffer; + VulkanBuffer buffer; VkAccelerationStructureKHR accelerationStructure; - VulkanBuffer instanceBuffer; + VulkanBuffer instanceBuffer; }; class RenderingElement3D { public: VkAccelerationStructureInstanceKHR instance; static std::vector elements; - inline static VulkanBuffer scratchBuffer; - inline static TlasWithBuffer tlases[Window::numFrames]; + inline static VulkanBuffer scratchBuffer; + inline static std::vector tlases; static void BuildTLAS(VkCommandBuffer cmd, std::uint32_t index); }; } diff --git a/interfaces/Crafter.Graphics-Rendertarget.cppm b/interfaces/Crafter.Graphics-Rendertarget.cppm index fb00861..5fc70c2 100644 --- a/interfaces/Crafter.Graphics-Rendertarget.cppm +++ b/interfaces/Crafter.Graphics-Rendertarget.cppm @@ -16,12 +16,6 @@ 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 -#endif export module Crafter.Graphics:Rendertarget; import Crafter.Math; import Crafter.Asset; @@ -29,64 +23,34 @@ import std; import :Types; import :Transform2D; import :RenderingElement2DBase; -#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN -import :Device; -import :VulkanBuffer; -#endif export namespace Crafter { + template struct RendertargetBase { #ifdef CRAFTER_TIMING std::vector> renderTimings; #endif Transform2D transform; - std::uint16_t sizeX; - std::uint16_t sizeY; + std::int32_t sizeX; + std::int32_t sizeY; RendertargetBase() = default; - RendertargetBase(std::uint16_t sizeX, std::uint16_t sizeY) : sizeX(sizeX), sizeY(sizeY), transform({0, 0, 1, 1, 0, 0, 0}){ + RendertargetBase(std::int16_t sizeX, std::int16_t sizeY) : sizeX(sizeX), sizeY(sizeY), transform({0, 0, 1, 1, 0, 0, 0}){ transform.scaled.size.x = sizeX; transform.scaled.size.y = sizeY; transform.scaled.position.x = 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 elements; - VulkanBuffer 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 infos, std::span ranges, std::uint16_t start, std::uint32_t bufferOffset, DescriptorHeapVulkan& descriptorHeap); - void SetOrderResursive(Transform2D* elementTransform); - }; - #endif template - struct Rendertarget : RendertargetBase { + struct Rendertarget : RendertargetBase { Vector* buffer[Frames]; Rendertarget() = default; - Rendertarget(std::uint16_t sizeX, std::uint16_t sizeY) : RendertargetBase(sizeX, sizeY) { + Rendertarget(std::int16_t sizeX, std::int16_t sizeY) : RendertargetBase(sizeX, sizeY) { } void RenderElement(Transform2D* elementTransform, std::uint8_t frame, std::vector&& dirtyRects) { - RenderingElement2DBase* element = dynamic_cast*>(elementTransform); + RenderingElement2DBase* element = dynamic_cast*>(elementTransform); if(element) { #ifdef CRAFTER_TIMING auto start = std::chrono::high_resolution_clock::now(); @@ -97,73 +61,68 @@ export namespace Crafter { } for(ClipRect dirty : dirtyRects) { - dirty.left = std::uint16_t(std::max(element->scaled.position.x, std::int16_t(dirty.left))); - dirty.top = std::uint16_t(std::max(element->scaled.position.y,std::int16_t(dirty.top))); - dirty.right = std::min(std::uint16_t(element->scaled.position.x+element->scaled.size.x), dirty.right); - dirty.bottom = std::min(std::uint16_t(element->scaled.position.y+element->scaled.size.y), dirty.bottom); + dirty.left = std::max(element->scaled.position.x, dirty.left); + dirty.top = std::max(element->scaled.position.y, dirty.top); + dirty.right = std::min(element->scaled.position.x+element->scaled.size.x, dirty.right); + dirty.bottom = std::min(element->scaled.position.y+element->scaled.size.y, dirty.bottom); - if(dirty.right <= dirty.left || dirty.bottom <= dirty.top) { - continue; - } - - const Vector* src_buffer = element->buffer.data(); - std::uint16_t src_width = element->scaled.size.x; - std::uint16_t src_height = element->scaled.size.y; + const Vector* src_buffer = element->buffer.data(); + std::int32_t src_width = element->scaled.size.x; + std::int32_t src_height = element->scaled.size.y; switch (element->opaque) { - case OpaqueType::FullyOpaque: { - 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)); + case OpaqueType::FullyOpaque: + 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; + + this->buffer[frame][y * this->sizeX + x] = src_buffer[src_y * src_width + src_x]; + } } break; - } + case OpaqueType::SemiOpaque: - case OpaqueType::Transparent: - if constexpr(std::same_as) { - for (std::uint16_t y = dirty.top; y < dirty.bottom; y++) { - std::uint16_t src_y = y - element->scaled.position.y; - std::uint16_t pixel_width = dirty.right - dirty.left; - - constexpr std::uint32_t simd_width = VectorF16<1, 1>::MaxElement / 4; - 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]); - } - - std::uint32_t remainder = pixel_width - (rows * simd_width); - std::uint16_t remainder_start = dirty.left + rows * simd_width; - - for (std::uint8_t x = 0; x < remainder; x++) { - std::uint16_t px = remainder_start + x; - std::uint16_t src_x = px - element->scaled.position.x; - - Vector src = src_buffer[src_y * src_width + src_x]; - Vector dst = buffer[frame][y * this->sizeX + px]; - _Float16 oneMinusSrcA = (_Float16)1.0f - src.a; - - buffer[frame][y * this->sizeX + px] = Vector( - src.r + dst.r * oneMinusSrcA, - src.g + dst.g * oneMinusSrcA, - src.b + dst.b * oneMinusSrcA, - src.a + dst.a * oneMinusSrcA - ); + // 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 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; } - } 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)); + } + break; + + case OpaqueType::Transparent: + // For transparent, always perform blending + 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 src = src_buffer[src_y * src_width + src_x]; + Vector dst = buffer[frame][y * this->sizeX + x]; + + if(src.a == 0) { + continue; + } + + float srcA = src.a / 255.0f; + float dstA = dst.a / 255.0f; + + float outA = srcA + dstA * (1.0f - srcA); + this->buffer[frame][y * this->sizeX + x] = Vector( + static_cast((src.r * srcA + dst.r * dstA * (1.0f - srcA)) / outA), + static_cast((src.g * srcA + dst.g * dstA * (1.0f - srcA)) / outA), + static_cast((src.b * srcA + dst.b * dstA * (1.0f - srcA)) / outA), + static_cast(outA * 255) + ); } } break; @@ -181,15 +140,11 @@ export namespace Crafter { } void AddOldRects(Transform2D* elementTransform, std::uint8_t frame, std::vector& clipRects) { - RenderingElement2DBase* element = dynamic_cast*>(elementTransform); + RenderingElement2DBase* element = dynamic_cast*>(elementTransform); 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]) { - 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::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))); + 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->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)); element->oldScale[frame] = element->scaled; element->redraw[frame] = false; } diff --git a/interfaces/Crafter.Graphics-ShaderBindingTableVulkan.cppm b/interfaces/Crafter.Graphics-ShaderBindingTableVulkan.cppm index bff2f7b..54b9720 100644 --- a/interfaces/Crafter.Graphics-ShaderBindingTableVulkan.cppm +++ b/interfaces/Crafter.Graphics-ShaderBindingTableVulkan.cppm @@ -33,13 +33,36 @@ export namespace Crafter { class ShaderBindingTableVulkan { public: std::vector shaderStages; - void Init(const std::span shaders) { + void Init(std::span shaders) { shaderStages.reserve(shaders.size()); for(const VulkanShader& shader: shaders) { - shaderStages.emplace_back(VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, nullptr, 0, shader.stage, shader.shader, shader.entrypoint.c_str(), shader.specilizationInfo); + shaderStages.emplace_back(VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, nullptr, 0, shader.stage, shader.shader, shader.entrypoint.c_str(), nullptr); } } }; + + template + class ShaderBindingTableVulkanConst { + public: + inline static std::array> shaderStages; + static void Init() { + AddAllToSBT(std::make_index_sequence>{}); + } + private: + template + static void AddToSBT() { + shaderStages[index] = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .stage = std::tuple_element_t::_stage, + .module = std::tuple_element_t::shader, + .pName = std::tuple_element_t::_entrypoint.value + }; + } + template + static void AddAllToSBT(std::index_sequence) { + (AddToSBT(), ...); + } + }; } #endif \ No newline at end of file diff --git a/interfaces/Crafter.Graphics-ShaderVulkan.cppm b/interfaces/Crafter.Graphics-ShaderVulkan.cppm index 8f71926..a571ded 100644 --- a/interfaces/Crafter.Graphics-ShaderVulkan.cppm +++ b/interfaces/Crafter.Graphics-ShaderVulkan.cppm @@ -28,14 +28,21 @@ import :Device; import :Types; export namespace Crafter { + template + struct StringLiteral { + constexpr StringLiteral(const char (&str)[N]) { + std::copy_n(str, N, value); + } + + char value[N]; + }; + class VulkanShader { public: - std::vector specilizations; - VkSpecializationInfo* specilizationInfo; VkShaderStageFlagBits stage; std::string entrypoint; VkShaderModule shader; - VulkanShader(const std::filesystem::path& path, std::string entrypoint, VkShaderStageFlagBits stage, VkSpecializationInfo* specilizationInfo) : stage(stage), entrypoint(entrypoint), specilizationInfo(specilizationInfo) { + VulkanShader(const std::filesystem::path& path, std::string entrypoint, VkShaderStageFlagBits stage) : stage(stage), entrypoint(entrypoint) { std::ifstream file(path, std::ios::binary); if (!file) { std::cerr << "Error: Could not open file " << path << std::endl; @@ -62,6 +69,44 @@ export namespace Crafter { Device::CheckVkResult(vkCreateShaderModule(Device::device, &module_info, nullptr, &shader)); } }; + + template < + StringLiteral path, + StringLiteral entrypoint, + VkShaderStageFlagBits stage + > + class VulkanShaderConst { + public: + constexpr static VkShaderStageFlagBits _stage = stage; + constexpr static StringLiteral _entrypoint = entrypoint; + inline static VkShaderModule shader; + static void CreateShader() { + std::ifstream file(path.value, std::ios::binary); + if (!file) { + std::cerr << "Error: Could not open file " << path.value << std::endl; + } + + // Move to the end of the file to determine its size + file.seekg(0, std::ios::end); + std::streamsize size = file.tellg(); + file.seekg(0, std::ios::beg); + + std::vector spirv(size / sizeof(std::uint32_t)); + + // Read the data into the vector + if (!file.read(reinterpret_cast(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 \ No newline at end of file diff --git a/interfaces/Crafter.Graphics-Transform2D.cppm b/interfaces/Crafter.Graphics-Transform2D.cppm index f24bb6a..96b4933 100644 --- a/interfaces/Crafter.Graphics-Transform2D.cppm +++ b/interfaces/Crafter.Graphics-Transform2D.cppm @@ -30,10 +30,10 @@ export namespace Crafter { float height; float offsetX; float offsetY; - std::uint8_t z; + std::int32_t z; bool maintainAspectRatio; Anchor2D() = default; - Anchor2D(float x, float y, float width, float height, float offsetX, float offsetY, std::uint8_t z, bool maintainAspectRatio = false); + Anchor2D(float x, float y, float width, float height, float offsetX, float offsetY, std::int32_t z, bool maintainAspectRatio = false); }; struct Transform2D { @@ -49,7 +49,14 @@ export namespace Crafter { Transform2D& operator=(Transform2D&) = delete; virtual ~Transform2D() = default; - virtual void UpdatePosition(RendertargetBase& window, Transform2D& parent) { + virtual void UpdatePosition(RendertargetBase<1>& window, Transform2D& parent) { + ScaleElement(parent); + for(Transform2D* child : children) { + child->UpdatePosition(window, *this); + } + } + + virtual void UpdatePosition(RendertargetBase<3>& window, Transform2D& parent) { ScaleElement(parent); for(Transform2D* child : children) { child->UpdatePosition(window, *this); @@ -66,6 +73,7 @@ export namespace Crafter { scaled.size.y = anchor.height * parent.scaled.size.x; } } else { + scaled.size.x = anchor.width * parent.scaled.size.x; scaled.size.y = anchor.height * parent.scaled.size.y; } diff --git a/interfaces/Crafter.Graphics-Types.cppm b/interfaces/Crafter.Graphics-Types.cppm index 5ae0b8d..0bb3a23 100644 --- a/interfaces/Crafter.Graphics-Types.cppm +++ b/interfaces/Crafter.Graphics-Types.cppm @@ -33,15 +33,15 @@ export namespace Crafter { }; struct ScaleData2D { - Vector position; - Vector size; + Vector position; + Vector size; }; struct ClipRect { - std::uint16_t left; - std::uint16_t right; - std::uint16_t top; - std::uint16_t bottom; + std::int32_t left; + std::int32_t right; + std::int32_t top; + std::int32_t bottom; }; struct FrameTime { @@ -49,7 +49,7 @@ export namespace Crafter { std::chrono::duration delta; }; - enum class CrafterKeys : std::uint8_t { + enum class CrafterKeys { // Alphabetic keys 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, @@ -92,151 +92,6 @@ export namespace Crafter { 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 constexpr T AlignUp(T value, T2 alignment) { return (value + alignment - 1) & ~(alignment - 1); diff --git a/interfaces/Crafter.Graphics-VulkanBuffer.cppm b/interfaces/Crafter.Graphics-VulkanBuffer.cppm index 1b13027..fdaef60 100644 --- a/interfaces/Crafter.Graphics-VulkanBuffer.cppm +++ b/interfaces/Crafter.Graphics-VulkanBuffer.cppm @@ -31,12 +31,24 @@ import :Device; namespace Crafter { export class VulkanBufferBase { public: - VkDeviceAddress address; - std::uint32_t size; + VkDescriptorBufferInfo descriptor; VkBuffer buffer = VK_NULL_HANDLE; VkDeviceMemory memory; }; + export class VulkanBufferAdressable { + public: + VkDeviceAddress address; + }; + export class VulkanBufferAdressableEmpty {}; + template + using VulkanBufferAdressableConditional = + std::conditional_t< + Adressable, + VulkanBufferAdressable, + VulkanBufferAdressableEmpty + >; + export template class VulkanBufferMapped { public: @@ -51,12 +63,33 @@ namespace Crafter { VulkanBufferMappedEmpty >; - export template - class VulkanBuffer : public VulkanBufferBase, public VulkanBufferMappedConditional { + + export template requires ((Mapped && !Staged) || (!Mapped && Staged) || (!Mapped && !Staged)) + class VulkanBuffer; + + export template + class VulkanBufferStaged { + VulkanBuffer* stagingBuffer; + }; + export class VulkanBufferStagedEmpty {}; + template + using VulkanBufferStagedConditional = + std::conditional_t< + Staged, + VulkanBufferStaged, + VulkanBufferStagedEmpty + >; + + + export template requires ((Mapped && !Staged) || (!Mapped && Staged) || (!Mapped && !Staged)) + class VulkanBuffer : public VulkanBufferBase, public VulkanBufferMappedConditional, public VulkanBufferAdressableConditional, public VulkanBufferStagedConditional { public: VulkanBuffer() = default; void Create(VkBufferUsageFlags usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, std::uint32_t count) { - size = count * sizeof(T); + if constexpr(Staged) { + new (&VulkanBufferMappedConditional::stagingBuffer) VulkanBuffer(); + VulkanBufferMappedConditional::stagingBuffer->Create(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, count); + } VkBufferCreateInfo bufferCreateInfo {}; bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; @@ -71,21 +104,30 @@ namespace Crafter { .allocationSize = memReqs.size, .memoryTypeIndex = Device::GetMemoryType(memReqs.memoryTypeBits, memoryPropertyFlags) }; - - VkMemoryAllocateFlagsInfoKHR allocFlagsInfo { - .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR, - .flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR, - }; - memAlloc.pNext = &allocFlagsInfo; - Device::CheckVkResult(vkAllocateMemory(Device::device, &memAlloc, nullptr, &memory)); + if constexpr(Adressable) { + VkMemoryAllocateFlagsInfoKHR allocFlagsInfo { + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR, + .flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR, + }; + memAlloc.pNext = &allocFlagsInfo; + 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)); - VkBufferDeviceAddressInfo addressInfo = { - .sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, - .buffer = buffer - }; - address = vkGetBufferDeviceAddress(Device::device, &addressInfo); + if constexpr(Adressable) { + VkBufferDeviceAddressInfo addressInfo = { + .sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, + .buffer = buffer + }; + VulkanBufferAdressableConditional::address = vkGetBufferDeviceAddress(Device::device, &addressInfo); + } if constexpr(Mapped) { Device::CheckVkResult(vkMapMemory(Device::device, memory, 0, memReqs.size, 0, reinterpret_cast(&(VulkanBufferMappedConditional::value)))); @@ -99,6 +141,9 @@ namespace Crafter { vkDestroyBuffer(Device::device, buffer, nullptr); vkFreeMemory(Device::device, memory, nullptr); buffer = VK_NULL_HANDLE; + if constexpr(Staged) { + delete VulkanBufferMappedConditional::stagingBuffer; + } } void Resize(VkBufferUsageFlags usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, std::uint32_t count) { @@ -112,7 +157,7 @@ namespace Crafter { VkBufferCopy copyRegion = { .srcOffset = 0, .dstOffset = 0, - .size = size + .size = descriptor.range }; vkCmdCopyBuffer( @@ -192,16 +237,37 @@ namespace Crafter { }; vkInvalidateMappedMemoryRanges(Device::device, 1, &range); } + + void FlushDevice(VkCommandBuffer cmd) requires(Staged) { + VulkanBufferStagedConditional::stagingBuffer.FlushDevice(VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); + VulkanBufferStagedConditional::stagingBuffer.Copy(cmd, this); + } + + void FlushDevice(VkCommandBuffer cmd, VkAccessFlags dstAccessMask, VkPipelineStageFlags dstStageMask) requires(Staged) { + VulkanBufferStagedConditional::stagingBuffer.FlushDevice(VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); + VulkanBufferStagedConditional::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::stagingBuffer); + VulkanBufferStagedConditional::stagingBuffer.FlushHost(); + } + VulkanBuffer(VulkanBuffer&& other) { + descriptor = other.descriptor; buffer = other.buffer; memory = other.memory; - size = other.size; other.buffer = VK_NULL_HANDLE; - address = other.address; + if constexpr(Adressable) { + VulkanBufferAdressableConditional::address = other.VulkanBufferAdressableConditional::address; + } if constexpr(Mapped) { VulkanBufferMappedConditional::value = other.VulkanBufferMappedConditional::value; } + if constexpr(Staged) { + VulkanBufferStagedConditional::stagingBuffer = other.VulkanBufferStagedConditional::stagingBuffer; + } }; ~VulkanBuffer() { diff --git a/interfaces/Crafter.Graphics-Window.cppm b/interfaces/Crafter.Graphics-Window.cppm index 2a8f7ab..ba07108 100644 --- a/interfaces/Crafter.Graphics-Window.cppm +++ b/interfaces/Crafter.Graphics-Window.cppm @@ -41,9 +41,6 @@ module; #ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN #include "vulkan/vulkan.h" #endif -#ifdef CRAFTER_GRAPHICS_WINDOW_WIN32 -#include -#endif export module Crafter.Graphics:Window; import std; @@ -51,6 +48,9 @@ import :Types; import :Rendertarget; import :Transform2D; import Crafter.Event; +#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN +import :PipelineRTVulkan; +#endif export namespace Crafter { #ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN @@ -60,8 +60,6 @@ export namespace Crafter { // Command buffer submission and execution VkSemaphore renderComplete; }; - struct PipelineRTVulkan; - struct DescriptorHeapVulkan; #endif struct MouseElement; @@ -82,7 +80,6 @@ export namespace Crafter { Event onAnyKeyDown; Event onAnyKeyHold; Event onAnyKeyUp; - Event onTextInput; Event onMouseRightClick; Event onMouseLeftClick; Event onMouseRightHold; @@ -131,13 +128,6 @@ export namespace Crafter { void LogTiming(); #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 float scale; #ifdef CRAFTER_GRAPHICS_RENDERER_SOFTWARE @@ -178,6 +168,17 @@ export namespace Crafter { #endif #ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN + std::vector descriptorsRt; + template + void SetPipelineRT() { + rtPipeline = Pipeline::pipeline; + rtPipelineLayout = Pipeline::pipelineLayout; + raygenRegion = Pipeline::raygenRegion; + missRegion = Pipeline::missRegion; + hitRegion = Pipeline::hitRegion; + callableRegion = Pipeline::callableRegion; + } + void SetPipelineRT(PipelineRTVulkan& pipeline); VkCommandBuffer StartInit(); void FinishInit(); VkCommandBuffer GetCmd(); @@ -189,15 +190,19 @@ export namespace Crafter { VkFormat colorFormat; VkColorSpaceKHR colorSpace; VkImage images[numFrames]; - VkImageViewCreateInfo imageViews[numFrames]; + VkImageView imageViews[numFrames]; std::thread thread; VkCommandBuffer drawCmdBuffers[numFrames]; VkSubmitInfo submitInfo; Semaphores semaphores; std::uint32_t currentBuffer = 0; VkPipelineStageFlags submitPipelineStages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - PipelineRTVulkan* pipeline; - DescriptorHeapVulkan* descriptorHeap; + VkPipeline rtPipeline; + VkPipelineLayout rtPipelineLayout; + VkStridedDeviceAddressRegionKHR raygenRegion; + VkStridedDeviceAddressRegionKHR missRegion; + VkStridedDeviceAddressRegionKHR hitRegion; + VkStridedDeviceAddressRegionKHR callableRegion; #endif }; } \ No newline at end of file diff --git a/interfaces/Crafter.Graphics.cppm b/interfaces/Crafter.Graphics.cppm index 025f548..6e1d964 100644 --- a/interfaces/Crafter.Graphics.cppm +++ b/interfaces/Crafter.Graphics.cppm @@ -38,14 +38,14 @@ export import :ForwardDeclarations; export import :Device; export import :VulkanTransition; export import :VulkanBuffer; +export import :DescriptorPoolVulkan; export import :ShaderVulkan; export import :ShaderBindingTableVulkan; export import :PipelineRTVulkan; export import :RenderingElement3D; export import :ImageVulkan; export import :SamplerVulkan; -export import :DescriptorHeapVulkan; -export import :RenderingElement2DVulkan; +export import :DescriptorSetLayoutVulkan; #endif // export import :WindowWaylandVulkan; diff --git a/project.json b/project.json index 752dc42..6ba2874 100644 --- a/project.json +++ b/project.json @@ -10,8 +10,7 @@ "implementations/Crafter.Graphics-Transform2D", "implementations/Crafter.Graphics-Device", "implementations/Crafter.Graphics-Mesh", - "implementations/Crafter.Graphics-RenderingElement3D", - "implementations/Crafter.Graphics-Rendertarget" + "implementations/Crafter.Graphics-RenderingElement3D" ], "interfaces": [ "interfaces/Crafter.Graphics-Window", @@ -29,15 +28,15 @@ "interfaces/Crafter.Graphics-Mesh", "interfaces/Crafter.Graphics-VulkanBuffer", "interfaces/Crafter.Graphics-RenderingElement3D", + "interfaces/Crafter.Graphics-DescriptorPoolVulkan", "interfaces/Crafter.Graphics-ShaderVulkan", "interfaces/Crafter.Graphics-PipelineRTVulkan", "interfaces/Crafter.Graphics-ShaderBindingTableVulkan", "interfaces/Crafter.Graphics-ImageVulkan", "interfaces/Crafter.Graphics-SamplerVulkan", - "interfaces/Crafter.Graphics-DescriptorHeapVulkan", + "interfaces/Crafter.Graphics-DescriptorSetLayoutVulkan", "interfaces/Crafter.Graphics-Rendertarget", - "interfaces/Crafter.Graphics-ForwardDeclarations", - "interfaces/Crafter.Graphics-RenderingElement2DVulkan" + "interfaces/Crafter.Graphics-ForwardDeclarations" ], "type": "library" }, @@ -54,7 +53,7 @@ }, { "name": "win32", - "libs": ["kernel32", "user32", "gdi32"], + "libs": ["kernel32", "user32"], "extends": ["base"], "defines": [ {