diff --git a/examples/VulkanTriangle/main.cpp b/examples/VulkanTriangle/main.cpp index 74406f8..7687e93 100644 --- a/examples/VulkanTriangle/main.cpp +++ b/examples/VulkanTriangle/main.cpp @@ -1,4 +1,5 @@ #include "vulkan/vulkan.h" +#include import Crafter.Graphics; using namespace Crafter; @@ -6,55 +7,69 @@ 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); - Raygenspv::CreateShader(); - Closesthitspv::CreateShader(); - Misspv::CreateShader(); - ShaderBindingTableVulkanConst::Init(); + VkSpecializationMapEntry entry = { + .constantID = 0, + .offset = 0, + .size = sizeof(uint16_t) + }; - descriptorSetLayout::Init(); - std::array layouts {{descriptorSetLayout::layout}}; + VkSpecializationInfo specilizationInfo = { + .mapEntryCount = 1, + .pMapEntries = &entry, + .dataSize = sizeof(uint16_t), + .pData = &descriptorHeap.bufferStartElement + }; - DescriptorPool pool; - pool.sets.resize(1); - pool.BuildPool(DescriptorPool::GetPoolSizes(), layouts); + 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} + }}; - Pipeline::Init(cmd, 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); Mesh triangleMesh; std::array, 3> verts {{{-150, -150, 100}, {0, 150, 100}, {150, -150, 100}}}; @@ -75,59 +90,111 @@ 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); - - 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; + RenderingElement3D::BuildTLAS(cmd, 1); + RenderingElement3D::BuildTLAS(cmd, 2); 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 df14df2..dd9d5a1 100644 --- a/examples/VulkanTriangle/project.json +++ b/examples/VulkanTriangle/project.json @@ -7,11 +7,9 @@ "dependencies": [ { "path":"../../project.json", - "configuration":"lib-win32-vulkan-debug" + "configuration":"lib-wayland-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 ad7b74c..eff2633 100644 --- a/examples/VulkanTriangle/raygen.glsl +++ b/examples/VulkanTriangle/raygen.glsl @@ -1,14 +1,17 @@ #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(binding = 0, set = 0) uniform accelerationStructureEXT topLevelAS; -layout(binding = 1, set = 0, rgba8) uniform writeonly image2D image; +layout(constant_id = 0) const uint16_t bufferStart = 0us; +layout(descriptor_heap) uniform accelerationStructureEXT topLevelAS[]; +layout(descriptor_heap) 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; @@ -32,7 +35,7 @@ void main() )); traceRayEXT( - topLevelAS, + topLevelAS[bufferStart], gl_RayFlagsNoneEXT, 0xff, 0, 0, 0, @@ -43,5 +46,5 @@ void main() 0 ); - imageStore(image, ivec2(pixel), vec4(hitValue, 1.0)); + imageStore(image[0], ivec2(pixel), vec4(hitValue, 1)); } diff --git a/examples/VulkanTriangleRuntime/README.md b/examples/VulkanTriangleRuntime/README.md deleted file mode 100644 index 6da7987..0000000 --- a/examples/VulkanTriangleRuntime/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# HelloWindow Example - -## Description - -This example demonstrates how to load shaders and render a triangle. - -## Expected Result - -A blue tinted vulkan window with a white triangle in the center. - -## Highlighted Code Snippet - -```cpp -EventListener 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 deleted file mode 100644 index 9a511b6..0000000 --- a/examples/VulkanTriangleRuntime/closesthit.glsl +++ /dev/null @@ -1,12 +0,0 @@ -#version 460 -#extension GL_EXT_ray_tracing : enable -#extension GL_EXT_nonuniform_qualifier : enable - -layout(location = 0) rayPayloadInEXT vec3 hitValue; -hitAttributeEXT vec2 attribs; - -void main() -{ - const vec3 barycentricCoords = vec3(1.0f - attribs.x - attribs.y, attribs.x, attribs.y); - hitValue = barycentricCoords; -} \ No newline at end of file diff --git a/examples/VulkanTriangleRuntime/main.cpp b/examples/VulkanTriangleRuntime/main.cpp deleted file mode 100644 index 242971a..0000000 --- a/examples/VulkanTriangleRuntime/main.cpp +++ /dev/null @@ -1,138 +0,0 @@ -#include "vulkan/vulkan.h" - -import Crafter.Graphics; -using namespace Crafter; -import std; -import Crafter.Event; -import Crafter.Math; - - -int main() { - Device::CreateDevice(); - WindowVulkan window(1280, 720, "HelloVulkan"); - VkCommandBuffer cmd = window.StartInit(); - - std::vector 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 deleted file mode 100644 index 1478ecf..0000000 --- a/examples/VulkanTriangleRuntime/miss.glsl +++ /dev/null @@ -1,9 +0,0 @@ -#version 460 -#extension GL_EXT_ray_tracing : enable - -layout(location = 0) rayPayloadInEXT vec3 hitValue; - -void main() -{ - hitValue = vec3(1, 1, 1); -} \ No newline at end of file diff --git a/examples/VulkanTriangleRuntime/raygen.glsl b/examples/VulkanTriangleRuntime/raygen.glsl deleted file mode 100644 index ad7b74c..0000000 --- a/examples/VulkanTriangleRuntime/raygen.glsl +++ /dev/null @@ -1,47 +0,0 @@ -#version 460 -#extension GL_EXT_ray_tracing : enable -#extension GL_EXT_shader_image_load_formatted : enable - -layout(binding = 0, set = 0) uniform accelerationStructureEXT topLevelAS; -layout(binding = 1, set = 0, rgba8) uniform writeonly image2D image; - -layout(location = 0) rayPayloadEXT vec3 hitValue; - -void main() -{ - // Pixel coordinates - uvec2 pixel = gl_LaunchIDEXT.xy; - uvec2 resolution = gl_LaunchSizeEXT.xy; - - // Normalized coordinates in range [-1, 1] - vec2 uv = (vec2(pixel) + 0.5) / vec2(resolution); - vec2 ndc = uv * 2.0 - 1.0; - - // Camera parameters - vec3 origin = vec3(0.0, 0.0, -300.0); - - float aspect = float(resolution.x) / float(resolution.y); - float fov = radians(60.0); - float tanHalfFov = tan(fov * 0.5); - - // Simple pinhole camera facing +Z - vec3 direction = normalize(vec3( - ndc.x * aspect * tanHalfFov, - -ndc.y * tanHalfFov, - 1.0 - )); - - traceRayEXT( - topLevelAS, - gl_RayFlagsNoneEXT, - 0xff, - 0, 0, 0, - origin, - 0.001, - direction, - 10000.0, - 0 - ); - - imageStore(image, ivec2(pixel), vec4(hitValue, 1.0)); -} diff --git a/examples/VulkanUI/main.cpp b/examples/VulkanUI/main.cpp new file mode 100644 index 0000000..325c38a --- /dev/null +++ b/examples/VulkanUI/main.cpp @@ -0,0 +1,133 @@ +#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/VulkanTriangleRuntime/project.json b/examples/VulkanUI/project.json similarity index 57% rename from examples/VulkanTriangleRuntime/project.json rename to examples/VulkanUI/project.json index b30faa8..31cecf9 100644 --- a/examples/VulkanTriangleRuntime/project.json +++ b/examples/VulkanUI/project.json @@ -7,24 +7,15 @@ "dependencies": [ { "path":"../../project.json", - "configuration":"lib-vulkan-debug" + "configuration":"lib-wayland-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/VulkanUI/raygen.glsl b/examples/VulkanUI/raygen.glsl new file mode 100644 index 0000000..849b983 --- /dev/null +++ b/examples/VulkanUI/raygen.glsl @@ -0,0 +1,48 @@ +#version 460 +#extension GL_EXT_ray_tracing : enable +#extension GL_EXT_shader_image_load_formatted : enable +#extension GL_EXT_shader_explicit_arithmetic_types_int16 : enable +#extension GL_EXT_descriptor_heap : enable +#extension GL_EXT_nonuniform_qualifier : enable +#extension GL_EXT_shader_explicit_arithmetic_types_float16 : enable + + +struct UIScaledData{ + int16_t x; + int16_t y; + int16_t sizeX; + int16_t sizeY; + uint16_t bufferX; + uint16_t bufferY; +}; + +layout(std430, descriptor_heap) buffer UIScaledDataBuffer { + uint16_t count; + uint16_t pad[5]; + UIScaledData data[]; +} UITransformBuffer[]; + +layout(std430, descriptor_heap) buffer UIPixelBufferr { + f16vec4 pixels[]; +} UIPixelBuffer[]; + +layout(constant_id = 0) const uint16_t bufferStart = 0us; +layout(descriptor_heap) uniform writeonly image2D image[]; + +void main() +{ + uvec2 pixel = gl_LaunchIDEXT.xy; + uvec2 resolution = gl_LaunchSizeEXT.xy; + + vec4 hitValue = vec4(0); + + for (uint16_t i = 1us; i < UITransformBuffer[bufferStart].count+1; i++) { + if(pixel.x > UITransformBuffer[bufferStart].data[i].x && pixel.x < UITransformBuffer[bufferStart].data[i].x + UITransformBuffer[bufferStart].data[i].sizeX && pixel.y > UITransformBuffer[bufferStart].data[i].y && pixel.y < UITransformBuffer[bufferStart].data[i].y + UITransformBuffer[bufferStart].data[i].sizeY) { + int16_t srcX = int16_t(float(pixel.x - UITransformBuffer[bufferStart].data[i].x) * float(UITransformBuffer[bufferStart].data[i].bufferX) / float(UITransformBuffer[bufferStart].data[i].sizeX)); + int16_t srcY = int16_t(float(pixel.y - UITransformBuffer[bufferStart].data[i].y) * float(UITransformBuffer[bufferStart].data[i].bufferY) / float(UITransformBuffer[bufferStart].data[i].sizeY)); + hitValue = vec4(UIPixelBuffer[bufferStart + 1].pixels[srcY * UITransformBuffer[bufferStart].data[i].bufferX + srcX]); + } + } + + imageStore(image[0], ivec2(pixel), hitValue); +} diff --git a/implementations/Crafter.Graphics-Device.cpp b/implementations/Crafter.Graphics-Device.cpp index 644fb67..d65d583 100644 --- a/implementations/Crafter.Graphics-Device.cpp +++ b/implementations/Crafter.Graphics-Device.cpp @@ -65,11 +65,13 @@ 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_deferred_host_operations", "VK_KHR_ray_tracing_pipeline", - "VK_KHR_ray_tracing_position_fetch" + "VK_EXT_descriptor_heap", + "VK_KHR_deferred_host_operations", + "VK_KHR_maintenance5", + "VK_KHR_shader_untyped_pointers", + "VK_EXT_device_fault" }; const char* const layerNames[] = { "VK_LAYER_KHRONOS_validation" @@ -79,6 +81,58 @@ 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)); } } @@ -157,6 +211,33 @@ 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; @@ -246,8 +327,7 @@ 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: - throw std::runtime_error(std::format("Unkown XKB_KEY: {}", sym)); + default: return CrafterKeys::CrafterKeysMax; } } @@ -407,31 +487,39 @@ void Device::keyboard_leave(void *data, wl_keyboard *keyboard, uint32_t serial, } - - void Device::keyboard_key(void *data, wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) { - 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(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); + 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); } - } else{ - Device::focusedWindow->heldkeys[static_cast(crafterKey)] = false; - Device::focusedWindow->onKeyUp[static_cast(crafterKey)].Invoke(); - Device::focusedWindow->onAnyKeyUp.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); } } 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) { @@ -477,8 +565,19 @@ 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; @@ -529,17 +628,10 @@ void Device::Initialize() { { VkPhysicalDevice device = physDevices[i]; - VkPhysicalDeviceProperties2 properties2{ - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, - .pNext = &rayTracingProperties - }; - vkGetPhysicalDeviceProperties2(device, &properties2); + uint32_t score; VkPhysicalDeviceProperties properties; vkGetPhysicalDeviceProperties(device, &properties); - - uint32_t score; - switch (properties.deviceType) { default : @@ -568,6 +660,11 @@ void Device::Initialize() { } } + VkPhysicalDeviceProperties2 properties2 { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, + .pNext = &rayTracingProperties + }; + vkGetPhysicalDeviceProperties2(physDevice, &properties2); uint32_t queueFamilyCount; vkGetPhysicalDeviceQueueFamilyProperties(physDevice, &queueFamilyCount, NULL); @@ -593,21 +690,50 @@ 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 = &vkPhysicalDeviceRayTracingPositionFetchFeatures, + .pNext = &features12, .rayTracingPipeline = VK_TRUE }; @@ -617,21 +743,14 @@ 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 + .samplerAnisotropy = VK_TRUE, + .shaderInt16 = VK_TRUE } }; - physical_features2.pNext = &ext_feature; VkDeviceCreateInfo deviceCreateInfo = {}; deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; @@ -684,6 +803,11 @@ 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 e101528..7d7c3ca 100644 --- a/implementations/Crafter.Graphics-Font.cpp +++ b/implementations/Crafter.Graphics-Font.cpp @@ -56,4 +56,15 @@ 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 c1c3a0f..ca6902f 100644 --- a/implementations/Crafter.Graphics-RenderingElement3D.cpp +++ b/implementations/Crafter.Graphics-RenderingElement3D.cpp @@ -114,6 +114,12 @@ 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 new file mode 100644 index 0000000..52de2ee --- /dev/null +++ b/implementations/Crafter.Graphics-Rendertarget.cpp @@ -0,0 +1,141 @@ +/* +Crafter®.Graphics +Copyright (C) 2026 Catcrafts® +catcrafts.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License version 3.0 as published by the Free Software Foundation; + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +module; +#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN +#include +#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 6381950..ff7d4b0 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::int32_t z, bool maintainAspectRatio): x(x), y(y), width(width), height(height), offsetX(offsetX), offsetY(offsetY), z(z), maintainAspectRatio(maintainAspectRatio) { +Anchor2D::Anchor2D(float x, float y, float width, float height, float offsetX, float offsetY, std::uint8_t z, bool maintainAspectRatio): x(x), y(y), width(width), height(height), offsetX(offsetX), offsetY(offsetY), z(z), maintainAspectRatio(maintainAspectRatio) { } \ No newline at end of file diff --git a/implementations/Crafter.Graphics-Window.cpp b/implementations/Crafter.Graphics-Window.cpp index fad11c8..57a63ee 100644 --- a/implementations/Crafter.Graphics-Window.cpp +++ b/implementations/Crafter.Graphics-Window.cpp @@ -62,6 +62,8 @@ import :MouseElement; import :Device; #ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN import :VulkanTransition; +import :DescriptorHeapVulkan; +import :PipelineRTVulkan; #endif import std; @@ -270,49 +272,65 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { PostQuitMessage(0); break; } - 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); - } + 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); } break; } - case WM_KEYUP: { + + case WM_KEYUP: + case WM_SYSKEYUP: { CrafterKeys crafterKey = vk_to_crafter_key(wParam); - window->heldkeys[static_cast(crafterKey)] = false; - window->onKeyUp[static_cast(crafterKey)].Invoke(); + window->heldkeys[(uint8_t)crafterKey] = false; + window->onKeyUp[(uint8_t)crafterKey].Invoke(); window->onAnyKeyUp.Invoke(crafterKey); break; } - case WM_MOUSEMOVE: { - int x = LOWORD(lParam); - int y = HIWORD(lParam); - 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()); + 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)); + } break; } case WM_LBUTTONDOWN: { @@ -367,6 +385,14 @@ 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; @@ -596,6 +622,34 @@ void Window::SetCusorImage(std::uint16_t sizeX, std::uint16_t sizeY) { wl_surface_damage(cursorSurface, 0, 0, sizeX, sizeY); wl_surface_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() { @@ -604,9 +658,21 @@ 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); @@ -614,6 +680,35 @@ 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() { @@ -736,19 +831,31 @@ void Window::Render() { } #endif - 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() + 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 }; + Device::vkCmdBindResourceHeapEXT(drawCmdBuffers[currentBuffer], &resourceHeapInfo); - vkCmdBindDescriptorSets2(drawCmdBuffers[currentBuffer], &bindDescriptorSetsInfo); - Device::vkCmdTraceRaysKHR(drawCmdBuffers[currentBuffer], &raygenRegion, &missRegion, &hitRegion, &callableRegion, width, height, 1); + 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); + 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, @@ -918,9 +1025,6 @@ void Window::CreateSwapchain() // If an existing swap chain is re-created, destroy the old swap chain and the ressources owned by the application (image views, images are owned by the swap chain) if (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 }; @@ -929,34 +1033,27 @@ void Window::CreateSwapchain() // Get the swap chain images Device::CheckVkResult(vkGetSwapchainImagesKHR(Device::device, swapChain, &imageCount, images)); - 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; + 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, + }, + }; } } diff --git a/interfaces/Crafter.Graphics-DescriptorHeapVulkan.cppm b/interfaces/Crafter.Graphics-DescriptorHeapVulkan.cppm new file mode 100644 index 0000000..614c19c --- /dev/null +++ b/interfaces/Crafter.Graphics-DescriptorHeapVulkan.cppm @@ -0,0 +1,75 @@ +/* +Crafter®.Graphics +Copyright (C) 2026 Catcrafts® +catcrafts.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License version 3.0 as published by the Free Software Foundation; + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +module; +#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN +#include "vulkan/vulkan.h" +#endif +export module Crafter.Graphics:DescriptorHeapVulkan; +#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN +import std; +import :Device; +import :Window; +import :Types; +import :VulkanBuffer; + +export namespace Crafter { + struct DescriptorHeapVulkan { + VulkanBuffer 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 deleted file mode 100644 index 4ed941d..0000000 --- a/interfaces/Crafter.Graphics-DescriptorPoolVulkan.cppm +++ /dev/null @@ -1,187 +0,0 @@ -/* -Crafter®.Graphics -Copyright (C) 2026 Catcrafts® -catcrafts.net - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License version 3.0 as published by the Free Software Foundation; - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -module; -#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN -#include "vulkan/vulkan.h" -#endif -export module Crafter.Graphics:DescriptorPoolVulkan; -#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN -import std; -import :Device; -import :Types; -import :DescriptorSetLayoutVulkan; -import Crafter.Event; - -export namespace Crafter { - struct DescriptorEntry { - VkDescriptorType type; - bool occured = true; - }; - - class DescriptorPool { - public: - std::vector 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 deleted file mode 100644 index c499eff..0000000 --- a/interfaces/Crafter.Graphics-DescriptorSetLayoutVulkan.cppm +++ /dev/null @@ -1,62 +0,0 @@ -/* -Crafter®.Graphics -Copyright (C) 2026 Catcrafts® -catcrafts.net - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License version 3.0 as published by the Free Software Foundation; - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -module; -#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN -#include "vulkan/vulkan.h" -#endif -export module Crafter.Graphics:DescriptorSetLayoutVulkan; -#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN -import std; -import :Device; -import :Types; -import Crafter.Event; - -export namespace Crafter { - class DescriptorSetLayoutVulkan { - public: - VkDescriptorSetLayout layout; - std::vector 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 0c5dd50..214e970 100644 --- a/interfaces/Crafter.Graphics-Device.cppm +++ b/interfaces/Crafter.Graphics-Device.cppm @@ -114,9 +114,20 @@ 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 + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_PROPERTIES_KHR, + .pNext = &descriptorHeapProperties }; static void CheckVkResult(VkResult result); diff --git a/interfaces/Crafter.Graphics-Font.cppm b/interfaces/Crafter.Graphics-Font.cppm index 77d14dd..3b98b35 100644 --- a/interfaces/Crafter.Graphics-Font.cppm +++ b/interfaces/Crafter.Graphics-Font.cppm @@ -33,6 +33,7 @@ namespace Crafter { std::int_fast32_t descent; std::int_fast32_t lineGap; stbtt_fontinfo font; - Font(const std::filesystem::path& font); + Font(const std::filesystem::path& font); + std::uint32_t GetLineWidth(const std::string_view text, float size); }; } \ No newline at end of file diff --git a/interfaces/Crafter.Graphics-ForwardDeclarations.cppm b/interfaces/Crafter.Graphics-ForwardDeclarations.cppm index 9a5adca..3534166 100644 --- a/interfaces/Crafter.Graphics-ForwardDeclarations.cppm +++ b/interfaces/Crafter.Graphics-ForwardDeclarations.cppm @@ -21,9 +21,7 @@ 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 7465b5e..30565bf 100644 --- a/interfaces/Crafter.Graphics-GridElement.cppm +++ b/interfaces/Crafter.Graphics-GridElement.cppm @@ -33,8 +33,7 @@ export namespace Crafter { GridElement(std::uint32_t columns, std::uint32_t rows, std::int32_t spacingX, std::int32_t spacingY, std::int32_t paddingX, std::int32_t paddingY, Anchor2D anchor) : Transform2D(anchor), columns(columns), rows(rows), spacingX(spacingX), spacingY(spacingY), paddingX(paddingX), paddingY(paddingY) { } - template - void UpdatePositionImpl(RendertargetBase& window, Transform2D& parent) { + void UpdatePosition(RendertargetBase& window, Transform2D& parent) override { ScaleElement(parent); std::int32_t cellWidth = (paddingX * 2) - (spacingX * (columns - 1)) / columns; std::int32_t cellHeight = (paddingY * 2) - (spacingY * (rows - 1)) / rows; @@ -61,11 +60,5 @@ export namespace Crafter { } } } - void UpdatePosition(RendertargetBase<1>& window, Transform2D& parent) override { - UpdatePositionImpl<1>(window, parent); - } - void UpdatePosition(RendertargetBase<3>& window, Transform2D& parent) override { - UpdatePositionImpl<3>(window, parent); - } }; } \ No newline at end of file diff --git a/interfaces/Crafter.Graphics-ImageVulkan.cppm b/interfaces/Crafter.Graphics-ImageVulkan.cppm index 2b95d24..5ded767 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 76720b2..300d8a6 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, true, false> vertexBuffer; - VulkanBuffer indexBuffer; + VulkanBuffer scratchBuffer; + VulkanBuffer blasBuffer; + VulkanBuffer, true> vertexBuffer; + VulkanBuffer indexBuffer; VkAccelerationStructureGeometryTrianglesDataKHR blasData; VkAccelerationStructureGeometryKHR blas; VkAccelerationStructureKHR accelerationStructure; diff --git a/interfaces/Crafter.Graphics-PipelineRTVulkan.cppm b/interfaces/Crafter.Graphics-PipelineRTVulkan.cppm index 2b312a8..ac34b11 100644 --- a/interfaces/Crafter.Graphics-PipelineRTVulkan.cppm +++ b/interfaces/Crafter.Graphics-PipelineRTVulkan.cppm @@ -30,26 +30,16 @@ import :ShaderBindingTableVulkan; import :Types; export namespace Crafter { - class PipelineRTVulkan { - public: + struct PipelineRTVulkan { 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 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)); - + void Init(VkCommandBuffer cmd, std::span raygenGroups, std::span missGroups, std::span hitGroups, ShaderBindingTableVulkan& shaderTable) { std::vector groups; groups.reserve(raygenGroups.size() + missGroups.size() + hitGroups.size()); @@ -57,14 +47,21 @@ export namespace Crafter { groups.insert(groups.end(), missGroups.begin(), missGroups.end()); groups.insert(groups.end(), hitGroups.begin(), hitGroups.end()); - VkRayTracingPipelineCreateInfoKHR rtPipelineInfo{ + VkPipelineCreateFlags2CreateInfo flags2 = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_CREATE_FLAGS_2_CREATE_INFO, + .flags = VK_PIPELINE_CREATE_2_DESCRIPTOR_HEAP_BIT_EXT + }; + + VkRayTracingPipelineCreateInfoKHR rtPipelineInfo { .sType = VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_KHR, + .pNext = &flags2, + .flags = 0, .stageCount = static_cast(shaderTable.shaderStages.size()), .pStages = shaderTable.shaderStages.data(), .groupCount = static_cast(groups.size()), .pGroups = groups.data(), .maxPipelineRayRecursionDepth = 1, - .layout = pipelineLayout + .layout = VK_NULL_HANDLE }; Device::CheckVkResult(Device::vkCreateRayTracingPipelinesKHR(Device::device, {}, {}, 1, &rtPipelineInfo, nullptr, &pipeline)); @@ -113,189 +110,10 @@ export namespace Crafter { callableRegion.stride = 0; callableRegion.size = 0; } - }; - 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; + ~PipelineRTVulkan() { + vkDestroyPipeline(Device::device, pipeline, nullptr); } - 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 1dc448d..dc2c184 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,7 +293,27 @@ export namespace Crafter { return lines; } - 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) { + + 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) { float scale = stbtt_ScaleForPixelHeight(&font.font, size); int baseline = (int)(font.ascent * scale); std::uint32_t lineHeight = (font.ascent - font.descent) * scale; @@ -320,8 +340,13 @@ export namespace Crafter { break; } - for (std::size_t i = 0; i < line.size(); ++i) { - int codepoint = line[i]; + 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; @@ -346,41 +371,19 @@ 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, 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, static_cast(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, 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]}; - } + this->buffer[bufferY * this->scaled.size.x + bufferX] = {color.r, color.g, color.b, static_cast(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++) { @@ -389,29 +392,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, 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, static_cast(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 constexpr(std::same_as) { + std::uint8_t alpha = bitmap[j * w + i]; - if(alpha == 0) { - continue; - } - - Vector dst = this->buffer[bufferY * this->scaled.size.x + bufferX]; + 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 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) - ); + 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 + ); + } } } } @@ -422,8 +437,9 @@ export namespace Crafter { x += (int)(ax * scale); - if (i + 1 < line.size()) { - x += (int)stbtt_GetGlyphKernAdvance(&font.font, codepoint, line[i+1]); + if (p + 1 < end) { + int next; + x += (int)stbtt_GetGlyphKernAdvance(&font.font, codepoint, utf8_decode(p+1, &next)); } } currentY += lineHeight; diff --git a/interfaces/Crafter.Graphics-RenderingElement2DBase.cppm b/interfaces/Crafter.Graphics-RenderingElement2DBase.cppm index df29672..37c80ec 100644 --- a/interfaces/Crafter.Graphics-RenderingElement2DBase.cppm +++ b/interfaces/Crafter.Graphics-RenderingElement2DBase.cppm @@ -30,6 +30,12 @@ export namespace Crafter { Right }; + enum class TextVerticalAlignment { + Top, + Center, + Bottom + }; + enum class TextOverflowMode { Clip, Wrap @@ -42,9 +48,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; @@ -53,13 +59,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) { } }; @@ -76,13 +82,13 @@ export namespace Crafter { struct EmptyScalingBase {}; struct EmptyRotatingBase {}; - template + template using ScalingBase = std::conditional_t< Scaling, std::conditional_t, - RenderElement2DScalingNonOwning>, + RenderElement2DScalingOwning, + RenderElement2DScalingNonOwning>, EmptyScalingBase >; @@ -94,11 +100,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++) { @@ -115,5 +121,16 @@ 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 new file mode 100644 index 0000000..81c5543 --- /dev/null +++ b/interfaces/Crafter.Graphics-RenderingElement2DVulkan.cppm @@ -0,0 +1,477 @@ +/* +Crafter®.Graphics +Copyright (C) 2026 Catcrafts® +catcrafts.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License version 3.0 as published by the Free Software Foundation; + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +module; +#include "../lib/stb_truetype.h" +#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN +#include +#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 2931cc4..9a6a031 100644 --- a/interfaces/Crafter.Graphics-RenderingElement3D.cppm +++ b/interfaces/Crafter.Graphics-RenderingElement3D.cppm @@ -27,20 +27,22 @@ import std; import :Mesh; import :VulkanBuffer; import Crafter.Math; +import :Window; export namespace Crafter { struct TlasWithBuffer { - VulkanBuffer buffer; + VkDeviceAddress address; + VulkanBuffer buffer; VkAccelerationStructureKHR accelerationStructure; - VulkanBuffer instanceBuffer; + VulkanBuffer instanceBuffer; }; class RenderingElement3D { public: VkAccelerationStructureInstanceKHR instance; static std::vector elements; - inline static VulkanBuffer scratchBuffer; - inline static std::vector tlases; + inline static VulkanBuffer scratchBuffer; + inline static TlasWithBuffer tlases[Window::numFrames]; static void BuildTLAS(VkCommandBuffer cmd, std::uint32_t index); }; } diff --git a/interfaces/Crafter.Graphics-Rendertarget.cppm b/interfaces/Crafter.Graphics-Rendertarget.cppm index 5fc70c2..fb00861 100644 --- a/interfaces/Crafter.Graphics-Rendertarget.cppm +++ b/interfaces/Crafter.Graphics-Rendertarget.cppm @@ -16,6 +16,12 @@ You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software 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; @@ -23,34 +29,64 @@ 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::int32_t sizeX; - std::int32_t sizeY; + std::uint16_t sizeX; + std::uint16_t sizeY; RendertargetBase() = default; - RendertargetBase(std::int16_t sizeX, std::int16_t sizeY) : sizeX(sizeX), sizeY(sizeY), transform({0, 0, 1, 1, 0, 0, 0}){ + RendertargetBase(std::uint16_t sizeX, std::uint16_t sizeY) : sizeX(sizeX), sizeY(sizeY), transform({0, 0, 1, 1, 0, 0, 0}){ transform.scaled.size.x = sizeX; transform.scaled.size.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::int16_t sizeX, std::int16_t sizeY) : RendertargetBase(sizeX, sizeY) { + Rendertarget(std::uint16_t sizeX, std::uint16_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(); @@ -61,68 +97,73 @@ export namespace Crafter { } for(ClipRect dirty : dirtyRects) { - 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); + 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); - 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; + 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; switch (element->opaque) { - 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]; - } + 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)); } break; - + } case OpaqueType::SemiOpaque: - // For semi-opaque, we can avoid blending when alpha is 0 or 255 - for (std::int32_t y = dirty.top; y < dirty.bottom; y++) { - std::int32_t src_y = y - element->scaled.position.y; - - for (std::int32_t x = dirty.left; x < dirty.right; x++) { - std::int32_t src_x = x - element->scaled.position.x; - Vector src_pixel = src_buffer[src_y * src_width + src_x]; - - if (src_pixel.a == 0) { - continue; - } - this->buffer[frame][y * this->sizeX + x] = src_pixel; - } - } - break; - case OpaqueType::Transparent: - // 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 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; - if(src.a == 0) { - continue; + 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]); } - - 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) - ); + 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 + ); + } + } + } 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; @@ -140,11 +181,15 @@ 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::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)); + 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))); element->oldScale[frame] = element->scaled; element->redraw[frame] = false; } diff --git a/interfaces/Crafter.Graphics-ShaderBindingTableVulkan.cppm b/interfaces/Crafter.Graphics-ShaderBindingTableVulkan.cppm index 54b9720..bff2f7b 100644 --- a/interfaces/Crafter.Graphics-ShaderBindingTableVulkan.cppm +++ b/interfaces/Crafter.Graphics-ShaderBindingTableVulkan.cppm @@ -33,36 +33,13 @@ export namespace Crafter { class ShaderBindingTableVulkan { public: std::vector shaderStages; - void Init(std::span shaders) { + void Init(const 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(), nullptr); + shaderStages.emplace_back(VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, nullptr, 0, shader.stage, shader.shader, shader.entrypoint.c_str(), shader.specilizationInfo); } } }; - - template - 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 a571ded..8f71926 100644 --- a/interfaces/Crafter.Graphics-ShaderVulkan.cppm +++ b/interfaces/Crafter.Graphics-ShaderVulkan.cppm @@ -28,21 +28,14 @@ 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) : stage(stage), entrypoint(entrypoint) { + VulkanShader(const std::filesystem::path& path, std::string entrypoint, VkShaderStageFlagBits stage, VkSpecializationInfo* specilizationInfo) : stage(stage), entrypoint(entrypoint), specilizationInfo(specilizationInfo) { std::ifstream file(path, std::ios::binary); if (!file) { std::cerr << "Error: Could not open file " << path << std::endl; @@ -69,44 +62,6 @@ export namespace Crafter { Device::CheckVkResult(vkCreateShaderModule(Device::device, &module_info, nullptr, &shader)); } }; - - template < - StringLiteral path, - StringLiteral entrypoint, - VkShaderStageFlagBits stage - > - class VulkanShaderConst { - public: - constexpr static VkShaderStageFlagBits _stage = stage; - constexpr static StringLiteral _entrypoint = entrypoint; - inline static VkShaderModule shader; - static void CreateShader() { - std::ifstream file(path.value, std::ios::binary); - if (!file) { - std::cerr << "Error: Could not open file " << path.value << std::endl; - } - - // Move to the end of the file to determine its size - file.seekg(0, std::ios::end); - std::streamsize size = file.tellg(); - file.seekg(0, std::ios::beg); - - std::vector 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 96b4933..f24bb6a 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::int32_t z; + std::uint8_t z; bool maintainAspectRatio; Anchor2D() = default; - Anchor2D(float x, float y, float width, float height, float offsetX, float offsetY, std::int32_t z, bool maintainAspectRatio = false); + Anchor2D(float x, float y, float width, float height, float offsetX, float offsetY, std::uint8_t z, bool maintainAspectRatio = false); }; struct Transform2D { @@ -49,14 +49,7 @@ export namespace Crafter { Transform2D& operator=(Transform2D&) = delete; virtual ~Transform2D() = default; - 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) { + virtual void UpdatePosition(RendertargetBase& window, Transform2D& parent) { ScaleElement(parent); for(Transform2D* child : children) { child->UpdatePosition(window, *this); @@ -73,7 +66,6 @@ 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 0bb3a23..5ae0b8d 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::int32_t left; - std::int32_t right; - std::int32_t top; - std::int32_t bottom; + std::uint16_t left; + std::uint16_t right; + std::uint16_t top; + std::uint16_t bottom; }; struct FrameTime { @@ -49,7 +49,7 @@ export namespace Crafter { std::chrono::duration delta; }; - enum class CrafterKeys { + enum class CrafterKeys : std::uint8_t { // 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,6 +92,151 @@ 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 fdaef60..1b13027 100644 --- a/interfaces/Crafter.Graphics-VulkanBuffer.cppm +++ b/interfaces/Crafter.Graphics-VulkanBuffer.cppm @@ -31,24 +31,12 @@ import :Device; namespace Crafter { export class VulkanBufferBase { public: - VkDescriptorBufferInfo descriptor; + VkDeviceAddress address; + std::uint32_t size; 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: @@ -63,33 +51,12 @@ namespace Crafter { VulkanBufferMappedEmpty >; - - 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 { + export template + class VulkanBuffer : public VulkanBufferBase, public VulkanBufferMappedConditional { public: VulkanBuffer() = default; void Create(VkBufferUsageFlags usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, std::uint32_t count) { - if constexpr(Staged) { - new (&VulkanBufferMappedConditional::stagingBuffer) VulkanBuffer(); - VulkanBufferMappedConditional::stagingBuffer->Create(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, count); - } + size = count * sizeof(T); VkBufferCreateInfo bufferCreateInfo {}; bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; @@ -104,30 +71,21 @@ namespace Crafter { .allocationSize = memReqs.size, .memoryTypeIndex = Device::GetMemoryType(memReqs.memoryTypeBits, memoryPropertyFlags) }; - 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; + + 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)); Device::CheckVkResult(vkBindBufferMemory(Device::device, buffer, memory, 0)); - if constexpr(Adressable) { - VkBufferDeviceAddressInfo addressInfo = { - .sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, - .buffer = buffer - }; - VulkanBufferAdressableConditional::address = vkGetBufferDeviceAddress(Device::device, &addressInfo); - } + VkBufferDeviceAddressInfo addressInfo = { + .sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, + .buffer = buffer + }; + address = vkGetBufferDeviceAddress(Device::device, &addressInfo); if constexpr(Mapped) { Device::CheckVkResult(vkMapMemory(Device::device, memory, 0, memReqs.size, 0, reinterpret_cast(&(VulkanBufferMappedConditional::value)))); @@ -141,9 +99,6 @@ 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) { @@ -157,7 +112,7 @@ namespace Crafter { VkBufferCopy copyRegion = { .srcOffset = 0, .dstOffset = 0, - .size = descriptor.range + .size = size }; vkCmdCopyBuffer( @@ -237,37 +192,16 @@ 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; - if constexpr(Adressable) { - VulkanBufferAdressableConditional::address = other.VulkanBufferAdressableConditional::address; - } + address = other.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 ba07108..2a8f7ab 100644 --- a/interfaces/Crafter.Graphics-Window.cppm +++ b/interfaces/Crafter.Graphics-Window.cppm @@ -41,6 +41,9 @@ 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; @@ -48,9 +51,6 @@ import :Types; import :Rendertarget; import :Transform2D; import Crafter.Event; -#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN -import :PipelineRTVulkan; -#endif export namespace Crafter { #ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN @@ -60,6 +60,8 @@ export namespace Crafter { // Command buffer submission and execution VkSemaphore renderComplete; }; + struct PipelineRTVulkan; + struct DescriptorHeapVulkan; #endif struct MouseElement; @@ -80,6 +82,7 @@ export namespace Crafter { Event onAnyKeyDown; Event onAnyKeyHold; Event onAnyKeyUp; + Event onTextInput; Event onMouseRightClick; Event onMouseLeftClick; Event onMouseRightHold; @@ -128,6 +131,13 @@ 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 @@ -168,17 +178,6 @@ 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(); @@ -190,19 +189,15 @@ export namespace Crafter { VkFormat colorFormat; VkColorSpaceKHR colorSpace; VkImage images[numFrames]; - VkImageView imageViews[numFrames]; + VkImageViewCreateInfo imageViews[numFrames]; std::thread thread; VkCommandBuffer drawCmdBuffers[numFrames]; VkSubmitInfo submitInfo; Semaphores semaphores; std::uint32_t currentBuffer = 0; VkPipelineStageFlags submitPipelineStages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - VkPipeline rtPipeline; - VkPipelineLayout rtPipelineLayout; - VkStridedDeviceAddressRegionKHR raygenRegion; - VkStridedDeviceAddressRegionKHR missRegion; - VkStridedDeviceAddressRegionKHR hitRegion; - VkStridedDeviceAddressRegionKHR callableRegion; + PipelineRTVulkan* pipeline; + DescriptorHeapVulkan* descriptorHeap; #endif }; } \ No newline at end of file diff --git a/interfaces/Crafter.Graphics.cppm b/interfaces/Crafter.Graphics.cppm index 6e1d964..025f548 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 :DescriptorSetLayoutVulkan; +export import :DescriptorHeapVulkan; +export import :RenderingElement2DVulkan; #endif // export import :WindowWaylandVulkan; diff --git a/project.json b/project.json index 6ba2874..752dc42 100644 --- a/project.json +++ b/project.json @@ -10,7 +10,8 @@ "implementations/Crafter.Graphics-Transform2D", "implementations/Crafter.Graphics-Device", "implementations/Crafter.Graphics-Mesh", - "implementations/Crafter.Graphics-RenderingElement3D" + "implementations/Crafter.Graphics-RenderingElement3D", + "implementations/Crafter.Graphics-Rendertarget" ], "interfaces": [ "interfaces/Crafter.Graphics-Window", @@ -28,15 +29,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-DescriptorSetLayoutVulkan", + "interfaces/Crafter.Graphics-DescriptorHeapVulkan", "interfaces/Crafter.Graphics-Rendertarget", - "interfaces/Crafter.Graphics-ForwardDeclarations" + "interfaces/Crafter.Graphics-ForwardDeclarations", + "interfaces/Crafter.Graphics-RenderingElement2DVulkan" ], "type": "library" }, @@ -53,7 +54,7 @@ }, { "name": "win32", - "libs": ["kernel32", "user32"], + "libs": ["kernel32", "user32", "gdi32"], "extends": ["base"], "defines": [ {