From 819517d150251c1646af698b0afdef9a3a239ea9 Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Wed, 28 Jan 2026 01:07:41 +0100 Subject: [PATCH] BLAS --- .../Crafter.Graphics-Mesh_vulkan.cpp | 198 ++++++ .../Crafter.Graphics-VulkanDevice.cpp | 19 +- .../Crafter.Graphics-Window_vulkan.cpp | 2 +- interfaces/Crafter.Graphics-Mesh.cppm | 52 ++ ...ter.Graphics-RenderingElement3DVulkan.cppm | 653 ++++++++++++++++++ interfaces/Crafter.Graphics-Types.cppm | 66 +- interfaces/Crafter.Graphics-VulkanBuffer.cppm | 100 +++ interfaces/Crafter.Graphics-VulkanDevice.cppm | 3 + interfaces/Crafter.Graphics.cppm | 2 + project.json | 4 +- 10 files changed, 1062 insertions(+), 37 deletions(-) create mode 100644 implementations/Crafter.Graphics-Mesh_vulkan.cpp create mode 100644 interfaces/Crafter.Graphics-Mesh.cppm create mode 100644 interfaces/Crafter.Graphics-RenderingElement3DVulkan.cppm create mode 100644 interfaces/Crafter.Graphics-VulkanBuffer.cppm diff --git a/implementations/Crafter.Graphics-Mesh_vulkan.cpp b/implementations/Crafter.Graphics-Mesh_vulkan.cpp new file mode 100644 index 0000000..d8d0d3a --- /dev/null +++ b/implementations/Crafter.Graphics-Mesh_vulkan.cpp @@ -0,0 +1,198 @@ +/* +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 + +module Crafter.Graphics:Mesh_impl; +import :Mesh; +import :VulkanDevice; +import std; + +using namespace Crafter; + +constexpr std::size_t alignUp(std::size_t value, std::size_t alignment) { + return (value + alignment - 1) & ~(alignment - 1); +} + +void Mesh::Build(std::span verticies, std::span indicies, VkCommandBuffer cmd) { + new (&vertexStaging) VulkanBuffer(VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, verticies.size()); + new (&indexStaging) VulkanBuffer(VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, indicies.size()); + + std::memcpy(vertexStaging.value, verticies.data(), verticies.size() * sizeof(Vertex)); + std::memcpy(indexStaging.value, indicies.data(), indicies.size() * sizeof(std::uint32_t)); + + new (&vertexBuffer) VulkanBuffer(VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_2_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, verticies.size()); + new (&indexBuffer) VulkanBuffer(VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_2_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, indicies.size()); + + VkBufferCopy copyRegion = { + .srcOffset = 0, + .dstOffset = 0, + .size = verticies.size() * sizeof(Vertex) + }; + + vkCmdCopyBuffer( + cmd, + vertexStaging.buffer, + vertexBuffer.buffer, + 1, + ©Region + ); + + VkBufferCopy copyRegion2 = { + .srcOffset = 0, + .dstOffset = 0, + .size = indicies.size() * sizeof(std::uint32_t) + }; + + vkCmdCopyBuffer( + cmd, + indexStaging.buffer, + indexBuffer.buffer, + 1, + ©Region2 + ); + + VkBufferMemoryBarrier barrier = { + .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, + .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .buffer = vertexBuffer.buffer, + .offset = 0, + .size = VK_WHOLE_SIZE + }; + + vkCmdPipelineBarrier( + cmd, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, + 0, + 0, NULL, + 1, &barrier, + 0, NULL + ); + + VkBufferMemoryBarrier barrier2 = { + .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, + .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .buffer = indexStaging.buffer, + .offset = 0, + .size = VK_WHOLE_SIZE + }; + + vkCmdPipelineBarrier( + cmd, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, + 0, + 0, NULL, + 1, &barrier2, + 0, NULL + ); + + + VkDeviceOrHostAddressConstKHR vertexAddr; + vertexAddr.deviceAddress = vertexBuffer.address; + + VkDeviceOrHostAddressConstKHR indexAddr; + indexAddr.deviceAddress = indexBuffer.address; + + auto trianglesData = VkAccelerationStructureGeometryTrianglesDataKHR { + .sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_TRIANGLES_DATA_KHR, + .vertexFormat = VK_FORMAT_R32G32B32_SFLOAT, + .vertexData = vertexAddr, + .vertexStride = sizeof(Vertex), + .maxVertex = static_cast(verticies.size())-1, + .indexType = VK_INDEX_TYPE_UINT32, + .indexData = indexAddr, + .transformData = {.deviceAddress = 0} + }; + VkAccelerationStructureGeometryDataKHR geometryData(trianglesData); + VkAccelerationStructureGeometryKHR blasGeometry { + .sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR, + .geometryType = VK_GEOMETRY_TYPE_TRIANGLES_KHR, + .geometry = geometryData, + .flags = VK_GEOMETRY_OPAQUE_BIT_KHR + }; + + VkAccelerationStructureBuildGeometryInfoKHR blasBuildGeometryInfo{ + .sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR, + .type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR, + .mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR, + .geometryCount = 1, + .pGeometries = &blasGeometry, + }; + + // Query the memory sizes that will be needed for this BLAS + auto primitiveCount = static_cast(indicies.size() / 3); + + VkAccelerationStructureBuildSizesInfoKHR blasBuildSizes = { + .sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR + }; + + VulkanDevice::vkGetAccelerationStructureBuildSizesKHR( + VulkanDevice::device, + VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR, + &blasBuildGeometryInfo, + &primitiveCount, + &blasBuildSizes + ); + + new (&scratchBuffer) VulkanBuffer(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, blasBuildSizes.buildScratchSize); + + VkPhysicalDeviceAccelerationStructurePropertiesKHR asProps{ + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_PROPERTIES_KHR + }; + + VkDeviceAddress scratchAddr = scratchBuffer.address; + blasBuildGeometryInfo.scratchData.deviceAddress = scratchAddr; + + new (&blasBuffer) VulkanBuffer(VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, blasBuildSizes.accelerationStructureSize); + + // Create and store the BLAS handle + VkAccelerationStructureCreateInfoKHR blasCreateInfo{ + .sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR, + .buffer = blasBuffer.buffer, + .offset = 0, + .size = blasBuildSizes.accelerationStructureSize, + .type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR, + }; + + VkAccelerationStructureKHR aclereationStructure {}; + + VulkanDevice::vkCreateAccelerationStructureKHR(VulkanDevice::device, &blasCreateInfo, nullptr, &aclereationStructure); + blasBuildGeometryInfo.dstAccelerationStructure = aclereationStructure; + + // Prepare the build range for the BLAS + VkAccelerationStructureBuildRangeInfoKHR blasRangeInfo { + .primitiveCount = primitiveCount, + .primitiveOffset = 0, + .firstVertex = 0, + .transformOffset = 0 + }; + + VkAccelerationStructureBuildRangeInfoKHR* blasRangeInfoPP = &blasRangeInfo; + VulkanDevice::vkCmdBuildAccelerationStructuresKHR(cmd, 1, &blasBuildGeometryInfo, &blasRangeInfoPP); +} \ No newline at end of file diff --git a/implementations/Crafter.Graphics-VulkanDevice.cpp b/implementations/Crafter.Graphics-VulkanDevice.cpp index fcb9abc..ca7d323 100644 --- a/implementations/Crafter.Graphics-VulkanDevice.cpp +++ b/implementations/Crafter.Graphics-VulkanDevice.cpp @@ -42,7 +42,9 @@ const char* const deviceExtensionNames[] = { "VK_KHR_spirv_1_4", "VK_EXT_mesh_shader", "VK_KHR_shader_float_controls", - "VK_KHR_dynamic_rendering" + "VK_KHR_dynamic_rendering", + "VK_KHR_acceleration_structure", + "VK_KHR_deferred_host_operations", }; const char* const layerNames[] = { "VK_LAYER_KHRONOS_validation" @@ -211,8 +213,20 @@ void VulkanDevice::CreateDevice() { queueCreateInfo.queueCount = 1; queueCreateInfo.pQueuePriorities = &priority; + VkPhysicalDeviceBufferDeviceAddressFeatures deviceBufferDeviceAddressFeature = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES, + .bufferDeviceAddress = VK_TRUE + }; + + VkPhysicalDeviceAccelerationStructureFeaturesKHR deviceAccelerationStructureFeature = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR, + .pNext = &deviceBufferDeviceAddressFeature, + .accelerationStructure = VK_TRUE + }; + VkPhysicalDeviceDynamicRenderingFeaturesKHR dynamicRenderingFeature = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES_KHR}; dynamicRenderingFeature.dynamicRendering = VK_TRUE; + dynamicRenderingFeature.pNext = &deviceAccelerationStructureFeature; VkPhysicalDeviceMeshShaderFeaturesEXT ext_feature = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_FEATURES_EXT}; ext_feature.meshShader = VK_TRUE; @@ -282,6 +296,9 @@ void VulkanDevice::CreateDevice() { vkCmdDrawMeshTasksEXTProc = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCmdDrawMeshTasksEXT")); vkCmdBeginRenderingKHRProc = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkCmdBeginRenderingKHR")); vkCmdEndRenderingKHRProc = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkCmdEndRenderingKHR")); + vkGetAccelerationStructureBuildSizesKHR = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkGetAccelerationStructureBuildSizesKHR")); + vkCreateAccelerationStructureKHR = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkCreateAccelerationStructureKHR")); + vkCmdBuildAccelerationStructuresKHR = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkCmdBuildAccelerationStructuresKHR")); } std::uint32_t VulkanDevice::GetMemoryType(uint32_t typeBits, VkMemoryPropertyFlags properties) { diff --git a/implementations/Crafter.Graphics-Window_vulkan.cpp b/implementations/Crafter.Graphics-Window_vulkan.cpp index d24baec..1dd3bca 100644 --- a/implementations/Crafter.Graphics-Window_vulkan.cpp +++ b/implementations/Crafter.Graphics-Window_vulkan.cpp @@ -199,7 +199,7 @@ void WindowVulkan::CreateSwapchain() } WindowVulkan::WindowVulkan(std::uint32_t width, std::uint32_t height) : Window(width, height) { - + display = wl_display_connect(NULL); if (display == NULL) { std::cerr << "failed to create display" << std::endl; diff --git a/interfaces/Crafter.Graphics-Mesh.cppm b/interfaces/Crafter.Graphics-Mesh.cppm new file mode 100644 index 0000000..5a8dce4 --- /dev/null +++ b/interfaces/Crafter.Graphics-Mesh.cppm @@ -0,0 +1,52 @@ +/* +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_VULKAN +#include +#endif + +export module Crafter.Graphics:Mesh; +import std; +import :VulkanBuffer; + +export namespace Crafter { + struct __attribute__((packed)) Vertex { + float x; + float y; + float z; + }; + + #ifdef CRAFTER_GRAPHICS_VULKAN + class Mesh { + public: + VulkanBuffer scratchBuffer; + VulkanBuffer blasBuffer; + VulkanBuffer vertexStaging; + VulkanBuffer indexStaging; + VulkanBuffer vertexBuffer; + VulkanBuffer indexBuffer; + VkAccelerationStructureGeometryTrianglesDataKHR blasData; + VkAccelerationStructureGeometryKHR blas; + bool opaque; + void Build(std::span verticies, std::span indicies, VkCommandBuffer cmd); + }; + #endif +} \ No newline at end of file diff --git a/interfaces/Crafter.Graphics-RenderingElement3DVulkan.cppm b/interfaces/Crafter.Graphics-RenderingElement3DVulkan.cppm new file mode 100644 index 0000000..5a8640d --- /dev/null +++ b/interfaces/Crafter.Graphics-RenderingElement3DVulkan.cppm @@ -0,0 +1,653 @@ +/* +Crafter®.Graphics +Copyright (C) 2025 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; +#define STB_IMAGE_IMPLEMENTATION +#include "../lib/stb_image.h" +#include "../lib/stb_truetype.h" +export module Crafter.Graphics:RenderingElement; +import std; +import :Transform; +import :Font; +import :Types; +import :Image; +import :Window; + +export namespace Crafter { + enum class TextAlignment { + Left, + Center, + Right + }; + + enum class TextOverflowMode { + Clip, + Wrap + }; + + enum class TextScaleMode { + None, + Font, + Element, + Buffer + }; + + struct RenderElementScalingOwning { + std::vector scalingBuffer; + std::uint32_t bufferWidth; + std::uint32_t bufferHeight; + bool bufferUpdated = true; + RenderElementScalingOwning() = default; + RenderElementScalingOwning(std::uint32_t bufferWidth, std::uint32_t bufferHeight) : scalingBuffer(bufferWidth*bufferHeight), bufferWidth(bufferWidth), bufferHeight(bufferHeight) { + + } + }; + + struct RenderElementScalingNonOwning { + Pixel_BU8_GU8_RU8_AU8* scalingBuffer; + std::uint32_t bufferWidth; + std::uint32_t bufferHeight; + bool bufferUpdated = true; + RenderElementScalingNonOwning() = default; + RenderElementScalingNonOwning(Pixel_BU8_GU8_RU8_AU8* scalingBuffer, std::uint32_t bufferWidth, std::uint32_t bufferHeight) : scalingBuffer(scalingBuffer), bufferWidth(bufferWidth), bufferHeight(bufferHeight) { + + } + }; + + struct RenderElementRotating { + std::uint32_t rotation; + bool rotationUpdated = true; + RenderElementRotating() = default; + RenderElementRotating(std::uint32_t rotation) : rotation(rotation) { + + } + }; + + + struct EmptyScalingBase {}; + struct EmptyRotatingBase {}; + + template + using ScalingBase = + std::conditional_t< + Scaling, + std::conditional_t, + EmptyScalingBase + >; + + template + using RotatingBase = + std::conditional_t< + Rotating, + RenderElementRotating, + EmptyRotatingBase + >; + + class RenderingElementBase : public Transform { + public: + std::vector buffer; + OpaqueType opaque; + RenderingElementBase(Anchor anchor) : Transform(anchor) { + scaled.width = 0; + } + RenderingElementBase(Anchor anchor, OpaqueType opaque) : Transform(anchor), opaque(opaque) { + scaled.width = 0; + } + }; + + template requires ((!Rotating || Scaling) && (!Owning || Scaling)) + class RenderingElement : public RenderingElementBase, public ScalingBase, public RotatingBase { + public: + RenderingElement() = default; + RenderingElement(Anchor anchor, OpaqueType opaque) : RenderingElementBase(anchor, opaque) { + + } + RenderingElement(Anchor anchor, OpaqueType opaque, std::uint32_t rotation) requires(Rotating) : RenderingElementBase(anchor, opaque), RotatingBase(rotation) { + + } + + RenderingElement(Anchor anchor, const std::string_view imagePath) : RenderingElementBase(anchor) { + LoadImage(imagePath); + } + RenderingElement(Anchor anchor, const std::string_view imagePath, std::uint32_t rotation) requires(Rotating) : RenderingElementBase(anchor), RotatingBase(rotation) { + LoadImage(imagePath); + } + + RenderingElement(Anchor anchor, const std::string_view imagePath, OpaqueType opaque) : RenderingElementBase(anchor, opaque) { + LoadImageNoOpaqueCheck(imagePath); + } + RenderingElement(Anchor anchor, const std::string_view imagePath, OpaqueType opaque, std::uint32_t rotation) requires(Rotating) : RenderingElementBase(anchor, opaque), RotatingBase(rotation) { + LoadImageNoOpaqueCheck(imagePath); + } + + RenderingElement(Anchor anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight, Pixel_BU8_GU8_RU8_AU8* scalingBuffer) requires(Scaling && !Owning) : RenderingElementBase(anchor, opaque), ScalingBase(bufferWidth, bufferHeight, scalingBuffer) { + + } + RenderingElement(Anchor anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight, Pixel_BU8_GU8_RU8_AU8* scalingBuffer, std::uint32_t rotation) requires(Scaling && !Owning && Rotating) : RenderingElementBase(anchor, opaque), ScalingBase(bufferWidth, bufferHeight, scalingBuffer), RotatingBase(rotation) { + + } + + RenderingElement(Anchor anchor, OpaqueType opaque, Image& image) requires(Scaling && !Owning) : RenderingElementBase(anchor, opaque), ScalingBase(image.buffer.data(), image.width, image.height) { + + } + RenderingElement(Anchor anchor, OpaqueType opaque, Image& image, std::uint32_t rotation) requires(Scaling && !Owning && Rotating) : RenderingElementBase(anchor, opaque), ScalingBase(image.buffer.data(), image.width, image.height), RotatingBase(rotation) { + + } + + RenderingElement(Anchor anchor, Image& image) requires(Scaling && !Owning) : RenderingElementBase(anchor, image.opaque), ScalingBase(image.buffer.data(), image.width, image.height) { + + } + RenderingElement(Anchor anchor, Image& image, std::uint32_t rotation) requires(Scaling && !Owning && Rotating) : RenderingElementBase(anchor, image.opaque), ScalingBase(image.buffer.data(), image.width, image.height), RotatingBase(rotation) { + + } + + RenderingElement(Anchor anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight) requires(Owning) : RenderingElementBase(anchor, opaque), ScalingBase(bufferWidth, bufferHeight) { + + } + RenderingElement(Anchor anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight, std::uint32_t rotation) requires(Owning && Rotating) : RenderingElementBase(anchor, opaque), ScalingBase(bufferWidth, bufferHeight) , RotatingBase(rotation) { + + } + + RenderingElement(RenderingElement&) = delete; + RenderingElement& operator=(RenderingElement&) = delete; + + void ScaleNearestNeighbor() requires(Scaling) { + for (std::uint32_t y = 0; y < scaled.height; y++) { + std::uint32_t srcY = y * ScalingBase::bufferHeight / scaled.height; + for (std::uint32_t x = 0; x < scaled.width; x++) { + std::uint32_t srcX = x * ScalingBase::bufferWidth / scaled.width; + buffer[y * scaled.width + x] = ScalingBase::scalingBuffer[srcY * ScalingBase::bufferWidth + srcX]; + } + } + } + + void ScaleRotating() requires(Scaling) { + const double rad = (static_cast(RotatingBase::rotation) / static_cast(std::numeric_limits::max())) * 2.0 * std::numbers::pi; + + const std::uint32_t dstWidth = scaled.width; + const std::uint32_t dstHeight = scaled.height; + + const double c2 = std::abs(std::cos(rad)); + const double s2 = std::abs(std::sin(rad)); + + const double rotatedWidth = dstWidth * c2 + dstHeight * s2; + const double rotatedHeight = dstWidth * s2 + dstHeight * c2; + + const std::uint32_t diffX = static_cast(std::ceil((rotatedWidth - dstWidth) * 0.5)); + const std::uint32_t diffY = static_cast(std::ceil((rotatedHeight - dstHeight) * 0.5)); + + scaled.width += diffX + diffX; + scaled.height += diffY + diffY; + + scaled.x -= diffX; + scaled.y -= diffY; + + buffer.clear(); + buffer.resize(scaled.width * scaled.height); + + // Destination center + const double dstCx = (static_cast(scaled.width) - 1.0) * 0.5; + const double dstCy = (static_cast(scaled.height) - 1.0) * 0.5; + + // Source center + const double srcCx = (static_cast(ScalingBase::bufferWidth) - 1.0) * 0.5; + const double srcCy = (static_cast(ScalingBase::bufferHeight) - 1.0) * 0.5; + + const double c = std::cos(rad); + const double s = std::sin(rad); + + // Scale factors (destination → source) + const double scaleX = static_cast(ScalingBase::bufferWidth) / dstWidth; + const double scaleY = static_cast(ScalingBase::bufferHeight) / dstHeight; + + for (std::uint32_t yB = 0; yB < scaled.height; ++yB) { + for (std::uint32_t xB = 0; xB < scaled.width; ++xB) { + + // ---- Destination pixel relative to center ---- + const double dx = (static_cast(xB) - dstCx) * scaleX; + const double dy = (static_cast(yB) - dstCy) * scaleY; + + // ---- Inverse rotation ---- + const double sx = (c * dx - s * dy) + srcCx; + const double sy = (s * dx + c * dy) + srcCy; + + // ---- Nearest neighbour sampling ---- + 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) { + buffer[yB * scaled.width + xB] = ScalingBase::scalingBuffer[srcY * ScalingBase::bufferWidth + srcX]; + } + } + } + } + + void UpdatePosition(Window& window, ScaleData oldScale) { + if constexpr(Scaling && !Rotating) { + if(oldScale.width != scaled.width || oldScale.height != scaled.height) { + buffer.resize(scaled.width * scaled.height); + ScaleNearestNeighbor(); + window.AddDirtyRect(oldScale); + window.AddDirtyRect(scaled); + } else if(oldScale.x != scaled.x || oldScale.y != scaled.y) { + window.AddDirtyRect(oldScale); + window.AddDirtyRect(scaled); + if(ScalingBase::bufferUpdated) { + ScaleNearestNeighbor(); + ScalingBase::bufferUpdated = false; + } + } else if(ScalingBase::bufferUpdated) { + ScaleNearestNeighbor(); + ScalingBase::bufferUpdated = false; + } + } else if constexpr(Rotating) { + if(oldScale.width != scaled.width || oldScale.height != scaled.height) { + buffer.resize(scaled.width * scaled.height); + ScaleRotating(); + window.AddDirtyRect(oldScale); + window.AddDirtyRect(scaled); + } else if(oldScale.x != scaled.x || oldScale.y != scaled.y) { + window.AddDirtyRect(oldScale); + window.AddDirtyRect(scaled); + if(ScalingBase::bufferUpdated || RotatingBase::rotationUpdated) { + ScaleRotating(); + ScalingBase::bufferUpdated = false; + RotatingBase::rotationUpdated = false; + } + } else if(ScalingBase::bufferUpdated || RotatingBase::rotationUpdated) { + ScaleRotating(); + ScalingBase::bufferUpdated = false; + RotatingBase::rotationUpdated = false; + } + } else { + if(oldScale.width != scaled.width || oldScale.height != scaled.height) { + buffer.resize(scaled.width * scaled.height); + window.AddDirtyRect(oldScale); + window.AddDirtyRect(scaled); + } + if(oldScale.x != scaled.x || oldScale.y != scaled.y) { + window.AddDirtyRect(oldScale); + window.AddDirtyRect(scaled); + } + } + } + + void UpdatePosition(Window& window) override { + ScaleData oldScale = scaled; + window.ScaleElement(*this); + UpdatePosition(window, oldScale); + for(Transform* child : children) { + child->UpdatePosition(window, *this); + } + } + + void UpdatePosition(Window& window, Transform& parent) override { + ScaleData oldScale = scaled; + window.ScaleElement(*this, parent); + UpdatePosition(window, oldScale); + for(Transform* child : children) { + child->UpdatePosition(window, *this); + } +} + + void LoadImage(const std::string_view imagePath) { + std::filesystem::path abs = std::filesystem::absolute(imagePath); + int xSize; + int ySize; + unsigned char* bgData = stbi_load(abs.string().c_str(), &xSize, &ySize, nullptr, 4); + + if constexpr(Scaling && !Owning) { + ScalingBase::bufferUpdated = true; + } else if constexpr(Scaling && Owning) { + ScalingBase::bufferWidth = xSize; + ScalingBase::bufferHeight = ySize; + ScalingBase::bufferUpdated = true; + ScalingBase::scalingBuffer.resize(xSize*ySize); + } else { + buffer.resize(xSize*ySize); + } + + opaque = OpaqueType::FullyOpaque; + + if constexpr(Scaling) { + for(std::uint32_t x = 0; x < xSize; x++) { + for(std::uint32_t y = 0; y < ySize; y++) { + std::uint32_t idx = (x*ySize+y)*4; + ScalingBase::scalingBuffer[x*ySize+y].r = bgData[idx]; + ScalingBase::scalingBuffer[x*ySize+y].g = bgData[idx+1]; + ScalingBase::scalingBuffer[x*ySize+y].b = bgData[idx+2]; + ScalingBase::scalingBuffer[x*ySize+y].a = bgData[idx+3]; + } + } + + for(std::uint32_t i = 0; i < xSize*ySize; i++) { + if(ScalingBase::scalingBuffer[i].a != 255) { + opaque = OpaqueType::SemiOpaque; + for(std::uint32_t i2 = 0; i2 < xSize*ySize; i2++) { + if(ScalingBase::scalingBuffer[i2].a != 0 && ScalingBase::scalingBuffer[i2].a != 255) { + opaque = OpaqueType::Transparent; + return; + } + } + return; + } + } + } else { + for(std::uint32_t x = 0; x < xSize; x++) { + for(std::uint32_t y = 0; y < ySize; y++) { + std::uint32_t idx = (x*ySize+y)*4; + buffer[x*ySize+y].r = bgData[idx]; + buffer[x*ySize+y].g = bgData[idx+1]; + buffer[x*ySize+y].b = bgData[idx+2]; + buffer[x*ySize+y].a = bgData[idx+3]; + } + } + + for(std::uint32_t i = 0; i < xSize*ySize; i++) { + if(buffer[i].a != 255) { + opaque = OpaqueType::SemiOpaque; + for(std::uint32_t i2 = 0; i2 < xSize*ySize; i2++) { + if(buffer[i2].a != 0 && buffer[i2].a != 255) { + opaque = OpaqueType::Transparent; + return; + } + } + return; + } + } + } + } + void LoadImageNoOpaqueCheck(const std::string_view imagePath) { + std::filesystem::path abs = std::filesystem::absolute(imagePath); + int xSize; + int ySize; + unsigned char* bgData = stbi_load(abs.string().c_str(), &xSize, &ySize, nullptr, 4); + + + if constexpr(Scaling && !Owning) { + ScalingBase::bufferUpdated = true; + } else if constexpr(Scaling && Owning) { + ScalingBase::bufferWidth = xSize; + ScalingBase::bufferHeight = ySize; + ScalingBase::bufferUpdated = true; + ScalingBase::scalingBuffer.resize(xSize*ySize); + } else { + buffer.resize(xSize*ySize); + } + + + if constexpr(Scaling) { + for(std::uint32_t x = 0; x < xSize; x++) { + for(std::uint32_t y = 0; y < ySize; y++) { + std::uint32_t idx = (x*ySize+y)*4; + ScalingBase::scalingBuffer[x*ySize+y].r = bgData[idx]; + ScalingBase::scalingBuffer[x*ySize+y].g = bgData[idx+1]; + ScalingBase::scalingBuffer[x*ySize+y].b = bgData[idx+2]; + ScalingBase::scalingBuffer[x*ySize+y].a = bgData[idx+3]; + } + } + } else { + for(std::uint32_t x = 0; x < xSize; x++) { + for(std::uint32_t y = 0; y < ySize; y++) { + std::uint32_t idx = (x*ySize+y)*4; + buffer[x*ySize+y].r = bgData[idx]; + buffer[x*ySize+y].g = bgData[idx+1]; + buffer[x*ySize+y].b = bgData[idx+2]; + buffer[x*ySize+y].a = bgData[idx+3]; + } + } + } + } + std::vector ResizeText(Window& window, const std::string_view text, float size, Font& font, TextOverflowMode overflowMode = TextOverflowMode::Clip, TextScaleMode scaleMode = TextScaleMode::None, Transform* parent = nullptr) { + float scale = stbtt_ScaleForPixelHeight(&font.font, size); + int baseline = (int)(font.ascent * scale); + + std::vector lines; + std::string_view remaining = text; + + std::uint32_t lineHeight = (font.ascent - font.descent) * scale; + + if(overflowMode == TextOverflowMode::Clip) { + while (!remaining.empty()) { + // Find next newline or end of string + auto newlinePos = remaining.find('\n'); + if (newlinePos != std::string_view::npos) { + lines.emplace_back(remaining.substr(0, newlinePos)); + remaining = remaining.substr(newlinePos + 1); + } else { + lines.emplace_back(remaining); + break; + } + } + std::uint32_t maxWidth = 0; + + for(const 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); + } + if(lineWidth > maxWidth) { + maxWidth = lineWidth; + } + } + + + if(scaleMode == TextScaleMode::Element) { + std::int32_t logicalPerPixelY = anchor.height / scaled.height; + std::int32_t oldHeight = anchor.height; + std::int32_t logicalPerPixelX = anchor.width / scaled.width; + std::int32_t oldwidth = anchor.width; + anchor.height = lineHeight * logicalPerPixelY; + anchor.width = maxWidth * logicalPerPixelX; + if(oldHeight != anchor.height || oldwidth != anchor.width) { + if(parent) { + UpdatePosition(window, *parent); + } else { + UpdatePosition(window); + } + } + } else if(scaleMode == TextScaleMode::Font) { + //todo + } else if(scaleMode == TextScaleMode::Buffer) { + if constexpr(Scaling && Owning) { + std::uint32_t neededHeight = lines.size() * lineHeight; + if(neededHeight != ScalingBase::bufferHeight || maxWidth != ScalingBase::bufferWidth) { + ScalingBase::bufferHeight = neededHeight; + ScalingBase::bufferWidth = maxWidth; + ScalingBase::bufferUpdated = true; + ScalingBase::scalingBuffer.resize(neededHeight*maxWidth); + } + } + } else { + if constexpr(Scaling) { + lines.resize(ScalingBase::bufferHeight / lines.size()); + } else { + lines.resize(scaled.height / lines.size()); + } + } + } else { + while (!remaining.empty()) { + std::string_view line; + auto newlinePos = remaining.find('\n'); + if (newlinePos != std::string_view::npos) { + line = remaining.substr(0, newlinePos); + remaining = remaining.substr(newlinePos + 1); + } else { + line = remaining; + remaining = ""; + } + + std::uint32_t lineWidth = 0; + std::size_t lastWrapPos = 0; // position of last space that can be used to wrap + std::size_t startPos = 0; + + for (std::size_t i = 0; i < line.size(); ++i) { + char c = line[i]; + + // get width of this character + int advance, lsb; + stbtt_GetCodepointHMetrics(&font.font, c, &advance, &lsb); + lineWidth += (std::uint32_t)(advance * scale); + + // remember last space for wrapping + if (c == ' ') { + lastWrapPos = i; + } + + // if line exceeds width, wrap + if (lineWidth > scaled.width) { + std::size_t wrapPos; + if (lastWrapPos > startPos) { + wrapPos = lastWrapPos; // wrap at last space + } else { + wrapPos = i; // no space, hard wrap + } + + // push the line up to wrapPos + lines.push_back(line.substr(startPos, wrapPos - startPos)); + + // skip any spaces at the beginning of next line + startPos = wrapPos; + while (startPos < line.size() && line[startPos] == ' ') { + ++startPos; + } + + // reset width and i + lineWidth = 0; + i = startPos - 1; // -1 because loop will increment i + } + } + + // add the remaining part of the line + if (startPos < line.size()) { + lines.push_back(line.substr(startPos)); + } + } + + if(scaleMode == TextScaleMode::Element) { + std::int32_t logicalPerPixelY = anchor.height / scaled.height; + std::int32_t oldHeight = anchor.height; + anchor.height = lineHeight * logicalPerPixelY; + if(oldHeight != anchor.height) { + if(parent) { + UpdatePosition(window, *parent); + } else { + UpdatePosition(window); + } + } + } else if(scaleMode == TextScaleMode::Font) { + //todo + } else if(scaleMode == TextScaleMode::Buffer) { + if constexpr(Scaling && Owning) { + std::uint32_t neededHeight = lines.size() * lineHeight; + if(neededHeight != ScalingBase::bufferHeight) { + ScalingBase::bufferHeight = neededHeight; + ScalingBase::bufferUpdated = true; + ScalingBase::scalingBuffer.resize(neededHeight*ScalingBase::bufferWidth); + } + } + } else { + if constexpr(Scaling) { + lines.resize(ScalingBase::bufferHeight / lines.size()); + } else { + lines.resize(scaled.height / lines.size()); + } + } + } + + return lines; + } + void RenderText(Window& window, std::span lines, float size, Pixel_BU8_GU8_RU8_AU8 color, Font& font, TextAlignment alignment = TextAlignment::Left, std::uint32_t offsetX = 0, std::uint32_t offsetY = 0) { + 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 startX = 0; + switch (alignment) { + case TextAlignment::Left: + startX = 0; + break; + case TextAlignment::Center: + startX = (scaled.width - lineWidth) / 2; + break; + case TextAlignment::Right: + startX = scaled.width - lineWidth; + break; + } + std::uint32_t x = startX; + + for (std::size_t i = 0; i < line.size(); ++i) { + int codepoint = line[i]; + + 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 + 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)scaled.width && bufferY >= 0 && bufferY < (int)scaled.height) { + buffer[bufferY * scaled.width + bufferX] = {color.r, color.g, color.b, bitmap[j * w + i]}; + } + } + } + } + + x += (int)(ax * scale); + + if (i + 1 < line.size()) { + x += (int)stbtt_GetCodepointKernAdvance(&font.font, codepoint, line[i+1]); + } + } + currentY += lineHeight; + } + } + }; +} \ No newline at end of file diff --git a/interfaces/Crafter.Graphics-Types.cppm b/interfaces/Crafter.Graphics-Types.cppm index fb00ed9..59b3296 100644 --- a/interfaces/Crafter.Graphics-Types.cppm +++ b/interfaces/Crafter.Graphics-Types.cppm @@ -73,46 +73,46 @@ export namespace Crafter { std::uint8_t a; }; - struct __attribute__((packed)) Vertex { - float x; - float y; - float z; - float w; - }; - struct __attribute__((packed)) VertexUV { - float x; - float y; - float z; - float w; + // struct __attribute__((packed)) Vertex { + // float x; + // float y; + // float z; + // float w; + // }; + // struct __attribute__((packed)) VertexUV { + // float x; + // float y; + // float z; + // float w; - float u; - float v; + // float u; + // float v; - float pad[2]; - }; + // float pad[2]; + // }; - struct __attribute__((packed)) VertexRGBA { - float x; - float y; - float z; - float w; + // struct __attribute__((packed)) VertexRGBA { + // float x; + // float y; + // float z; + // float w; - float r; - float g; - float b; - float a; - }; + // float r; + // float g; + // float b; + // float a; + // }; - struct __attribute__((packed)) HeightRGBA { - float height; + // struct __attribute__((packed)) HeightRGBA { + // float height; - float pad[3]; + // float pad[3]; - float r; - float g; - float b; - float a; - }; + // float r; + // float g; + // float b; + // float a; + // }; struct FrameTime { std::chrono::time_point now; diff --git a/interfaces/Crafter.Graphics-VulkanBuffer.cppm b/interfaces/Crafter.Graphics-VulkanBuffer.cppm new file mode 100644 index 0000000..5d580e8 --- /dev/null +++ b/interfaces/Crafter.Graphics-VulkanBuffer.cppm @@ -0,0 +1,100 @@ +/* +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_VULKAN +#include +#endif + +export module Crafter.Graphics:VulkanBuffer; +#ifdef CRAFTER_GRAPHICS_VULKAN +import std; +import :VulkanDevice; + +namespace Crafter { + export template + class VulkanBuffer { + public: + T* value; + VkDescriptorBufferInfo descriptor; + VkDeviceSize alignment = 0; + VkMemoryPropertyFlags memoryPropertyFlags; + VkBufferUsageFlags usageFlags; + VkBuffer buffer = VK_NULL_HANDLE; + VkDeviceMemory memory = VK_NULL_HANDLE; + VkDeviceAddress address; + public: + VulkanBuffer() : value(nullptr) {}; + VulkanBuffer(VkBufferUsageFlags usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, std::uint32_t count = 1) { + VkBufferCreateInfo bufferCreateInfo {}; + bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bufferCreateInfo.usage = usageFlags; + bufferCreateInfo.size = sizeof(T)*count; + VulkanDevice::CheckVkResult(vkCreateBuffer(VulkanDevice::device, &bufferCreateInfo, nullptr, &buffer)); + + // Create the memory backing up the buffer handle + VkMemoryRequirements memReqs; + VkMemoryAllocateInfo memAlloc {}; + memAlloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + vkGetBufferMemoryRequirements(VulkanDevice::device, buffer, &memReqs); + memAlloc.allocationSize = memReqs.size; + // Find a memory type index that fits the properties of the buffer + memAlloc.memoryTypeIndex = VulkanDevice::GetMemoryType(memReqs.memoryTypeBits, memoryPropertyFlags); + // If the buffer has VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT set we also need to enable the appropriate flag during allocation + VkMemoryAllocateFlagsInfoKHR allocFlagsInfo{}; + if (usageFlags & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT) { + allocFlagsInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR; + allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR; + memAlloc.pNext = &allocFlagsInfo; + } + VulkanDevice::CheckVkResult(vkAllocateMemory(VulkanDevice::device, &memAlloc, nullptr, &memory)); + + alignment = memReqs.alignment; + usageFlags = usageFlags; + memoryPropertyFlags = memoryPropertyFlags; + + descriptor.offset = 0; + descriptor.buffer = buffer; + descriptor.range = sizeof(T)*count; + + VulkanDevice::CheckVkResult(vkBindBufferMemory(VulkanDevice::device, buffer, memory, 0)); + if(memoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) { + VulkanDevice::CheckVkResult(vkMapMemory(VulkanDevice::device, memory, 0, sizeof(T)*count, 0, reinterpret_cast(&value))); + } + + VkBufferDeviceAddressInfo addressInfo = { + .sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, + .buffer = buffer + }; + + address = vkGetBufferDeviceAddress(VulkanDevice::device, &addressInfo); + } + ~VulkanBuffer() { + if(value != nullptr) { + if(memoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) { + vkUnmapMemory(VulkanDevice::device, memory); + } + vkDestroyBuffer(VulkanDevice::device, buffer, nullptr); + vkFreeMemory(VulkanDevice::device, memory, nullptr); + } + } + }; +} +#endif \ No newline at end of file diff --git a/interfaces/Crafter.Graphics-VulkanDevice.cppm b/interfaces/Crafter.Graphics-VulkanDevice.cppm index 7725dcb..23c309d 100644 --- a/interfaces/Crafter.Graphics-VulkanDevice.cppm +++ b/interfaces/Crafter.Graphics-VulkanDevice.cppm @@ -40,6 +40,9 @@ export namespace Crafter { inline static PFN_vkCmdDrawMeshTasksEXT vkCmdDrawMeshTasksEXTProc; inline static PFN_vkCmdBeginRenderingKHR vkCmdBeginRenderingKHRProc; inline static PFN_vkCmdEndRenderingKHR vkCmdEndRenderingKHRProc; + inline static PFN_vkGetAccelerationStructureBuildSizesKHR vkGetAccelerationStructureBuildSizesKHR; + inline static PFN_vkCreateAccelerationStructureKHR vkCreateAccelerationStructureKHR; + inline static PFN_vkCmdBuildAccelerationStructuresKHR vkCmdBuildAccelerationStructuresKHR; inline static VkPhysicalDeviceMemoryProperties memoryProperties; inline static VkFormat depthFormat = VK_FORMAT_UNDEFINED; static void CreateDevice(); diff --git a/interfaces/Crafter.Graphics.cppm b/interfaces/Crafter.Graphics.cppm index 9834ac1..457a111 100644 --- a/interfaces/Crafter.Graphics.cppm +++ b/interfaces/Crafter.Graphics.cppm @@ -30,10 +30,12 @@ export import :Font; export import :Image; export import :Shm; export import :Animation; +export import :Mesh; #ifdef CRAFTER_GRAPHICS_VULKAN export import :VulkanDevice; export import :VulkanTransition; +export import :VulkanBuffer; #endif // export import :WindowWaylandVulkan; diff --git a/project.json b/project.json index aeb505d..9d2550c 100644 --- a/project.json +++ b/project.json @@ -4,7 +4,7 @@ { "name": "base", "implementations": ["implementations/Crafter.Graphics-Font", "implementations/Crafter.Graphics-Shm", "implementations/Crafter.Graphics-Window", "implementations/Crafter.Graphics-MouseElement", "implementations/Crafter.Graphics-Transform", "implementations/Crafter.Graphics-GridElement", "implementations/Crafter.Graphics-Image"], - "interfaces": ["interfaces/Crafter.Graphics-Window", "interfaces/Crafter.Graphics", "interfaces/Crafter.Graphics-Types", "interfaces/Crafter.Graphics-Font", "interfaces/Crafter.Graphics-Image", "interfaces/Crafter.Graphics-Shm", "interfaces/Crafter.Graphics-Animation", "interfaces/Crafter.Graphics-RenderingElement", "interfaces/Crafter.Graphics-MouseElement", "interfaces/Crafter.Graphics-Transform", "interfaces/Crafter.Graphics-GridElement", "interfaces/Crafter.Graphics-VulkanDevice", "interfaces/Crafter.Graphics-VulkanTransition"], + "interfaces": ["interfaces/Crafter.Graphics-Window", "interfaces/Crafter.Graphics", "interfaces/Crafter.Graphics-Types", "interfaces/Crafter.Graphics-Font", "interfaces/Crafter.Graphics-Image", "interfaces/Crafter.Graphics-Shm", "interfaces/Crafter.Graphics-Animation", "interfaces/Crafter.Graphics-RenderingElement", "interfaces/Crafter.Graphics-MouseElement", "interfaces/Crafter.Graphics-Transform", "interfaces/Crafter.Graphics-GridElement", "interfaces/Crafter.Graphics-VulkanDevice", "interfaces/Crafter.Graphics-VulkanTransition", "interfaces/Crafter.Graphics-Mesh", "interfaces/Crafter.Graphics-VulkanBuffer"], "type": "library" }, { @@ -22,7 +22,7 @@ }, { "name": "vulkan", - "implementations": ["implementations/Crafter.Graphics-VulkanDevice", "implementations/Crafter.Graphics-Window_vulkan"], + "implementations": ["implementations/Crafter.Graphics-VulkanDevice", "implementations/Crafter.Graphics-Window_vulkan", "implementations/Crafter.Graphics-Mesh_vulkan"], "interfaces": [], "libs": ["wayland-client", "xkbcommon", "vulkan"], "c_files": ["lib/xdg-shell-protocol", "lib/wayland-xdg-decoration-unstable-v1-client-protocol", "lib/fractional-scale-v1", "lib/viewporter"],