From d1b8e45fb6367763d7bec606b837f7e9cf2dbcd6 Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Tue, 3 Feb 2026 21:03:11 +0100 Subject: [PATCH] new descriptor layout 2 --- examples/VulkanAnimation/main.cpp | 127 +++++++------ examples/VulkanAnimation/raygen.glsl | 2 +- examples/VulkanTriangle/main.cpp | 30 ++- .../Crafter.Graphics-Window_vulkan.cpp | 36 +--- ...Crafter.Graphics-DescriptorPoolVulkan.cppm | 17 +- ...er.Graphics-DescriptorSetLayoutVulkan.cppm | 48 +++++ interfaces/Crafter.Graphics-ImageVulkan.cppm | 174 ++++++++++++++++++ .../Crafter.Graphics-SamplerVulkan.cppm | 64 +++++++ interfaces/Crafter.Graphics-ShaderVulkan.cppm | 24 +-- interfaces/Crafter.Graphics-Types.cppm | 6 + interfaces/Crafter.Graphics-Window.cppm | 7 +- interfaces/Crafter.Graphics.cppm | 3 + project.json | 2 +- 13 files changed, 412 insertions(+), 128 deletions(-) create mode 100644 interfaces/Crafter.Graphics-DescriptorSetLayoutVulkan.cppm create mode 100644 interfaces/Crafter.Graphics-ImageVulkan.cppm create mode 100644 interfaces/Crafter.Graphics-SamplerVulkan.cppm diff --git a/examples/VulkanAnimation/main.cpp b/examples/VulkanAnimation/main.cpp index d77262c..39647a3 100644 --- a/examples/VulkanAnimation/main.cpp +++ b/examples/VulkanAnimation/main.cpp @@ -6,9 +6,9 @@ import std; import Crafter.Event; import Crafter.Math; -typedef VulkanShader<"raygen.spv", "main", VK_SHADER_STAGE_RAYGEN_BIT_KHR, 2, {{{VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 0}, {VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1}}}> Raygenspv; -typedef VulkanShader<"closesthit.spv", "main", VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR, 0, {{}}> Closesthitspv; -typedef VulkanShader<"miss.spv", "main", VK_SHADER_STAGE_MISS_BIT_KHR, 0, {{}}> Misspv; +typedef VulkanShader<"raygen.spv", "main", VK_SHADER_STAGE_RAYGEN_BIT_KHR> Raygenspv; +typedef VulkanShader<"closesthit.spv", "main", VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR> Closesthitspv; +typedef VulkanShader<"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>, @@ -16,6 +16,22 @@ typedef std::tuple< ShaderGroup > ShaderGroups; typedef PipelineRTVulkan Pipeline; +typedef DescriptorSetLayoutVulkan<1, {{ + { + .binding = 0, + .descriptorType = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_RAYGEN_BIT_KHR, + }, +}}> descriptorSetLayoutTlas; +typedef DescriptorSetLayoutVulkan<1, {{ + { + .binding = 0, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_RAYGEN_BIT_KHR, + } +}}> descriptorSetLayoutImage; int main() { VulkanDevice::CreateDevice(); @@ -27,13 +43,17 @@ int main() { Misspv::CreateShader(); ShaderBindingTableVulkan::Init(); - std::array layouts {{Raygenspv::layout, Raygenspv::layout, Raygenspv::layout}}; - Pipeline::Init(cmd, layouts); - window.SetPipelineRT(); + + descriptorSetLayoutTlas::Init(); + descriptorSetLayoutImage::Init(); + std::array layouts {{descriptorSetLayoutTlas::layout, descriptorSetLayoutImage::layout, descriptorSetLayoutImage::layout, descriptorSetLayoutImage::layout}}; DescriptorPool pool; - pool.sets.resize(3); - pool.BuildPool(DescriptorPool::GetPoolSizes(), layouts); + pool.sets.resize(4); + pool.BuildPool(DescriptorPool::GetPoolSizes(), layouts); + + Pipeline::Init(cmd, layouts); + window.SetPipelineRT(); Mesh triangleMesh; std::array verts {{{-150, -150, 100}, {0, 150, 100}, {150, -150, 100}}}; @@ -43,14 +63,24 @@ int main() { MatrixRowMajor transform = MatrixRowMajor::Identity(); std::memcpy(el.instance.transform.matrix, transform.m, sizeof(transform.m)); - RenderingElement3DVulkan::tlases.resize(3); + RenderingElement3DVulkan::tlases.resize(WindowVulkan::numFrames); RenderingElement3DVulkan::BuildTLAS(cmd, 0); RenderingElement3DVulkan::BuildTLAS(cmd, 1); RenderingElement3DVulkan::BuildTLAS(cmd, 2); - VkDescriptorImageInfo imageInfo = { - .imageView = window.imageViews[0], - .imageLayout = VK_IMAGE_LAYOUT_GENERAL + VkDescriptorImageInfo imageInfo[WindowVulkan::numFrames] = { + { + .imageView = window.imageViews[0], + .imageLayout = VK_IMAGE_LAYOUT_GENERAL + }, + { + .imageView = window.imageViews[1], + .imageLayout = VK_IMAGE_LAYOUT_GENERAL + }, + { + .imageView = window.imageViews[2], + .imageLayout = VK_IMAGE_LAYOUT_GENERAL + }, }; VkWriteDescriptorSetAccelerationStructureKHR writeDescriptorSetAccelerationStructure { @@ -59,28 +89,31 @@ int main() { .pAccelerationStructures = &RenderingElement3DVulkan::tlases[0].accelerationStructure }; - VkWriteDescriptorSet write[2] = { - { + VkWriteDescriptorSet write[4]; + + write[0] = { + .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, + }; + + for(std::uint32_t i = 0; i < WindowVulkan::numFrames; i++) { + write[i+1] = { .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .pNext = &writeDescriptorSetAccelerationStructure, - .dstSet = pool.sets[0], + .dstSet = pool.sets[i+1], .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(VulkanDevice::device, 2, write, 0, nullptr); - window.descriptorsRt = {pool.sets[0]}; + .pImageInfo = &imageInfo[i] + }; + } + vkUpdateDescriptorSets(VulkanDevice::device, 4, write, 0, nullptr); + window.descriptorsRt = {pool.sets[0], pool.sets[1]}; /* FinishInit executes all commands recorded to StartInit. @@ -104,10 +137,6 @@ int main() { MatrixRowMajor transform = MatrixRowMajor::Translation(value, 0, 0); std::memcpy(el.instance.transform.matrix, transform.m, sizeof(transform.m)); RenderingElement3DVulkan::BuildTLAS(window.drawCmdBuffers[window.currentBuffer], window.currentBuffer); - VkDescriptorImageInfo imageInfo = { - .imageView = window.imageViews[window.currentBuffer], - .imageLayout = VK_IMAGE_LAYOUT_GENERAL - }; VkWriteDescriptorSetAccelerationStructureKHR writeDescriptorSetAccelerationStructure { .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_KHR, @@ -115,28 +144,18 @@ int main() { .pAccelerationStructures = &RenderingElement3DVulkan::tlases[window.currentBuffer].accelerationStructure }; - VkWriteDescriptorSet write[2] = { - { - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .pNext = &writeDescriptorSetAccelerationStructure, - .dstSet = pool.sets[window.currentBuffer], - .dstBinding = 0, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, - }, - { - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .dstSet = pool.sets[window.currentBuffer], - .dstBinding = 1, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, - .pImageInfo = &imageInfo - } + VkWriteDescriptorSet write = { + .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, }; - vkUpdateDescriptorSets(VulkanDevice::device, 2, write, 0, nullptr); - window.descriptorsRt[0] = pool.sets[window.currentBuffer]; + vkUpdateDescriptorSets(VulkanDevice::device, 1, &write, 0, nullptr); + + window.descriptorsRt[1] = pool.sets[window.currentBuffer+1]; }); window.Render(); diff --git a/examples/VulkanAnimation/raygen.glsl b/examples/VulkanAnimation/raygen.glsl index ad7b74c..1cbdb98 100644 --- a/examples/VulkanAnimation/raygen.glsl +++ b/examples/VulkanAnimation/raygen.glsl @@ -3,7 +3,7 @@ #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(binding = 0, set = 1, rgba8) uniform writeonly image2D image; layout(location = 0) rayPayloadEXT vec3 hitValue; diff --git a/examples/VulkanTriangle/main.cpp b/examples/VulkanTriangle/main.cpp index bec4d46..a35c950 100644 --- a/examples/VulkanTriangle/main.cpp +++ b/examples/VulkanTriangle/main.cpp @@ -6,9 +6,9 @@ import std; import Crafter.Event; import Crafter.Math; -typedef VulkanShader<"raygen.spv", "main", VK_SHADER_STAGE_RAYGEN_BIT_KHR, 2, {{{VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 0}, {VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1}}}> Raygenspv; -typedef VulkanShader<"closesthit.spv", "main", VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR, 0, {{}}> Closesthitspv; -typedef VulkanShader<"miss.spv", "main", VK_SHADER_STAGE_MISS_BIT_KHR, 0, {{}}> Misspv; +typedef VulkanShader<"raygen.spv", "main", VK_SHADER_STAGE_RAYGEN_BIT_KHR> Raygenspv; +typedef VulkanShader<"closesthit.spv", "main", VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR> Closesthitspv; +typedef VulkanShader<"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>, @@ -16,6 +16,20 @@ typedef std::tuple< ShaderGroup > ShaderGroups; typedef PipelineRTVulkan Pipeline; +typedef DescriptorSetLayoutVulkan<2, {{ + { + .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, + } +}}> descriptorSetLayout; int main() { VulkanDevice::CreateDevice(); @@ -26,11 +40,15 @@ int main() { Closesthitspv::CreateShader(); Misspv::CreateShader(); ShaderBindingTableVulkan::Init(); - std::array layouts {{Raygenspv::layout}}; - Pipeline::Init(cmd, layouts); + + descriptorSetLayout::Init(); + std::array layouts {{descriptorSetLayout::layout}}; + DescriptorPool pool; pool.sets.resize(1); - pool.BuildPool(DescriptorPool::GetPoolSizes(), layouts); + pool.BuildPool(DescriptorPool::GetPoolSizes(), layouts); + + Pipeline::Init(cmd, layouts); Mesh triangleMesh; std::array verts {{{-150, -150, 100}, {0, 150, 100}, {150, -150, 100}}}; diff --git a/implementations/Crafter.Graphics-Window_vulkan.cpp b/implementations/Crafter.Graphics-Window_vulkan.cpp index e8dac5a..1cd2710 100644 --- a/implementations/Crafter.Graphics-Window_vulkan.cpp +++ b/implementations/Crafter.Graphics-Window_vulkan.cpp @@ -92,13 +92,6 @@ void WindowVulkan::CreateSwapchain() // This mode waits for the vertical blank ("v-sync") VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR; - // Determine the number of images - uint32_t desiredNumberOfSwapchainImages = surfCaps.minImageCount + 1; - if ((surfCaps.maxImageCount > 0) && (desiredNumberOfSwapchainImages > surfCaps.maxImageCount)) - { - desiredNumberOfSwapchainImages = surfCaps.maxImageCount; - } - // Find the transformation of the surface VkSurfaceTransformFlagsKHR preTransform; if (surfCaps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) @@ -130,7 +123,7 @@ void WindowVulkan::CreateSwapchain() VkSwapchainCreateInfoKHR swapchainCI = {}; swapchainCI.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; swapchainCI.surface = vulkanSurface; - swapchainCI.minImageCount = desiredNumberOfSwapchainImages; + swapchainCI.minImageCount = numFrames; swapchainCI.imageFormat = colorFormat; swapchainCI.imageColorSpace = colorSpace; swapchainCI.imageExtent = { swapchainExtent.width, swapchainExtent.height }; @@ -145,22 +138,14 @@ void WindowVulkan::CreateSwapchain() // Setting clipped to VK_TRUE allows the implementation to discard rendering outside of the surface area swapchainCI.clipped = VK_TRUE; swapchainCI.compositeAlpha = compositeAlpha; - - // Enable transfer source on swap chain images if supported - if (surfCaps.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_SRC_BIT) { - swapchainCI.imageUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_STORAGE_BIT; - } - - // Enable transfer destination on swap chain images if supported - if (surfCaps.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT) { - swapchainCI.imageUsage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_STORAGE_BIT; - } + swapchainCI.imageUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_STORAGE_BIT; + swapchainCI.imageUsage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_STORAGE_BIT; VulkanDevice::CheckVkResult(vkCreateSwapchainKHR(VulkanDevice::device, &swapchainCI, nullptr, &swapChain)); // 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 < images.size(); i++) { + for (auto i = 0; i < numFrames; i++) { vkDestroyImageView(VulkanDevice::device, imageViews[i], nullptr); } vkDestroySwapchainKHR(VulkanDevice::device, oldSwapchain, nullptr); @@ -169,12 +154,9 @@ void WindowVulkan::CreateSwapchain() VulkanDevice::CheckVkResult(vkGetSwapchainImagesKHR(VulkanDevice::device, swapChain, &imageCount, nullptr)); // Get the swap chain images - images.resize(imageCount); - VulkanDevice::CheckVkResult(vkGetSwapchainImagesKHR(VulkanDevice::device, swapChain, &imageCount, images.data())); + VulkanDevice::CheckVkResult(vkGetSwapchainImagesKHR(VulkanDevice::device, swapChain, &imageCount, images)); - // Get the swap chain buffers containing the image and imageview - imageViews.resize(imageCount); - for (auto i = 0; i < images.size(); i++) + for (auto i = 0; i < numFrames; i++) { VkImageViewCreateInfo colorAttachmentView = {}; colorAttachmentView.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; @@ -297,14 +279,12 @@ WindowVulkan::WindowVulkan(std::uint32_t width, std::uint32_t height) : Window(w subpassDescription.pPreserveAttachments = nullptr; subpassDescription.pResolveAttachments = nullptr; - drawCmdBuffers.resize(images.size()); - VkCommandBufferAllocateInfo cmdBufAllocateInfo {}; cmdBufAllocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; cmdBufAllocateInfo.commandPool = VulkanDevice::commandPool; cmdBufAllocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - cmdBufAllocateInfo.commandBufferCount = static_cast(drawCmdBuffers.size()); - VulkanDevice::CheckVkResult(vkAllocateCommandBuffers(VulkanDevice::device, &cmdBufAllocateInfo, drawCmdBuffers.data())); + cmdBufAllocateInfo.commandBufferCount = numFrames; + VulkanDevice::CheckVkResult(vkAllocateCommandBuffers(VulkanDevice::device, &cmdBufAllocateInfo, drawCmdBuffers)); VkSemaphoreCreateInfo semaphoreCreateInfo {}; semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; diff --git a/interfaces/Crafter.Graphics-DescriptorPoolVulkan.cppm b/interfaces/Crafter.Graphics-DescriptorPoolVulkan.cppm index 24d6e90..ad8e2ef 100644 --- a/interfaces/Crafter.Graphics-DescriptorPoolVulkan.cppm +++ b/interfaces/Crafter.Graphics-DescriptorPoolVulkan.cppm @@ -34,19 +34,16 @@ export namespace Crafter { bool occured = true; }; - class DescriptorPool { public: - Event onDescriptorRefresh; - std::uint32_t setIndex = 0; std::vector sets; VkDescriptorPool descriptorPool = VK_NULL_HANDLE; template consteval static void GetOccuringDescriptors(std::array& types) { - for (const DescriptorBinding& binding : Shader::descriptors) { + for (const VkDescriptorSetLayoutBinding& binding : Shader::descriptors) { for (DescriptorEntry& type : types) { - if (type.type == binding.type) { + if (type.type == binding.descriptorType) { type.occured = true; } } @@ -77,10 +74,10 @@ export namespace Crafter { } ([&] { - for (const DescriptorBinding& binding : Shaders::descriptors) { + for (const VkDescriptorSetLayoutBinding& binding : Shaders::descriptors) { bool found = false; for(VkDescriptorPoolSize& type : types) { - if(type.type == binding.type && type.descriptorCount != 12345) { + if(type.type == binding.descriptorType && type.descriptorCount != 12345) { type.descriptorCount += 1; found = true; } @@ -88,7 +85,7 @@ export namespace Crafter { if(!found) { for(std::uint32_t i = 0; i < GetUniqueDiscriptorCount(); i++){ if(types[i].descriptorCount == 12345) { - types[i].type = binding.type; + types[i].type = binding.descriptorType; types[i].descriptorCount = 1; break; } @@ -125,10 +122,6 @@ export namespace Crafter { }; VulkanDevice::CheckVkResult(vkAllocateDescriptorSets(VulkanDevice::device, &allocInfo, sets.data())); - - setIndex = 0; - - onDescriptorRefresh.Invoke(); } }; } diff --git a/interfaces/Crafter.Graphics-DescriptorSetLayoutVulkan.cppm b/interfaces/Crafter.Graphics-DescriptorSetLayoutVulkan.cppm new file mode 100644 index 0000000..076d081 --- /dev/null +++ b/interfaces/Crafter.Graphics-DescriptorSetLayoutVulkan.cppm @@ -0,0 +1,48 @@ +/* +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:DescriptorSetLayoutVulkan; +#ifdef CRAFTER_GRAPHICS_VULKAN +import std; +import :VulkanDevice; +import :Types; +import Crafter.Event; + +export namespace Crafter { + template Descriptors> + class DescriptorSetLayoutVulkan { + 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() + }; + VulkanDevice::CheckVkResult(vkCreateDescriptorSetLayout(VulkanDevice::device, &descriptorLayoutInfoMesh, nullptr, &layout)); + } + }; +} + +#endif \ No newline at end of file diff --git a/interfaces/Crafter.Graphics-ImageVulkan.cppm b/interfaces/Crafter.Graphics-ImageVulkan.cppm new file mode 100644 index 0000000..a3fed17 --- /dev/null +++ b/interfaces/Crafter.Graphics-ImageVulkan.cppm @@ -0,0 +1,174 @@ +/* +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:ImageVulkan; +import std; +import :VulkanBuffer; + +export namespace Crafter { + #ifdef CRAFTER_GRAPHICS_VULKAN + template + class ImageVulkan { + public: + std::uint32_t width; + std::uint32_t height; + VkImage image; + VkDeviceMemory imageMemory; + VulkanBuffer buffer; + VkImageView imageView; + VkDescriptorImageInfo descr; + ImageVulkan(std::uint32_t width, std::uint32_t height, VkCommandBuffer cmd, VkFormat format) : width(width), height(height) { + buffer.Create(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, width * height); + VkImageCreateInfo imageInfo{}; + imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + imageInfo.imageType = VK_IMAGE_TYPE_2D; + imageInfo.extent.width = width; + imageInfo.extent.height = height; + imageInfo.extent.depth = 1; + imageInfo.mipLevels = 1; + imageInfo.arrayLayers = 1; + imageInfo.format = format; + imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; + imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; + imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + VulkanDevice::CheckVkResult(vkCreateImage(VulkanDevice::device, &imageInfo, nullptr, &image)); + + VkMemoryRequirements memRequirements; + vkGetImageMemoryRequirements(VulkanDevice::device, image, &memRequirements); + + VkMemoryAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocInfo.allocationSize = memRequirements.size; + allocInfo.memoryTypeIndex = VulkanDevice::GetMemoryType(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + + VulkanDevice::CheckVkResult(vkAllocateMemory(VulkanDevice::device, &allocInfo, nullptr, &imageMemory)); + + vkBindImageMemory(VulkanDevice::device, image, imageMemory, 0); + + VkImageViewCreateInfo viewInfo{}; + viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + viewInfo.image = image; + viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + viewInfo.format = format; + viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + viewInfo.subresourceRange.baseMipLevel = 0; + viewInfo.subresourceRange.levelCount = 1; + viewInfo.subresourceRange.baseArrayLayer = 0; + viewInfo.subresourceRange.layerCount = 1; + + VulkanDevice::CheckVkResult(vkCreateImageView(VulkanDevice::device, &viewInfo, nullptr, &imageView)); + + TransitionImageLayout(cmd, image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + } + + void Update(const PixelType* bufferdata, VkCommandBuffer cmd) { + std::memcpy(buffer.value, bufferdata, height*width*sizeof(PixelType)); + buffer.FlushDevice(cmd, VK_ACCESS_MEMORY_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); + TransitionImageLayout(cmd, image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + + VkBufferImageCopy region{}; + region.bufferOffset = 0; + region.bufferRowLength = 0; + region.bufferImageHeight = 0; + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.imageSubresource.mipLevel = 0; + region.imageSubresource.baseArrayLayer = 0; + region.imageSubresource.layerCount = 1; + region.imageOffset = {0, 0, 0}; + region.imageExtent = { width, height, 1}; + vkCmdCopyBufferToImage( + cmd, + buffer.buffer, + image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, + ®ion + ); + + TransitionImageLayout(cmd, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + } + + private: + void TransitionImageLayout(VkCommandBuffer cmd, VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout) { + VkImageMemoryBarrier barrier{}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.oldLayout = oldLayout; + barrier.newLayout = newLayout; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.image = image; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.subresourceRange.baseMipLevel = 0; + barrier.subresourceRange.levelCount = 1; + barrier.subresourceRange.baseArrayLayer = 0; + barrier.subresourceRange.layerCount = 1; + barrier.srcAccessMask = 0; + barrier.dstAccessMask = 0; + + VkPipelineStageFlags sourceStage; + VkPipelineStageFlags destinationStage; + + if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + barrier.srcAccessMask = 0; + barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + + sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + } else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { + barrier.srcAccessMask = 0; + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + + sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + } else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + + sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + } else if (oldLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + + sourceStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + } else { + throw std::invalid_argument("unsupported layout transition!"); + } + + vkCmdPipelineBarrier( + cmd, + sourceStage, destinationStage, + 0, + 0, nullptr, + 0, nullptr, + 1, &barrier + ); + } + }; + #endif +} \ No newline at end of file diff --git a/interfaces/Crafter.Graphics-SamplerVulkan.cppm b/interfaces/Crafter.Graphics-SamplerVulkan.cppm new file mode 100644 index 0000000..9057b7f --- /dev/null +++ b/interfaces/Crafter.Graphics-SamplerVulkan.cppm @@ -0,0 +1,64 @@ +/* +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:SamplerVulkan; +import std; +import :VulkanBuffer; +import :ImageVulkan; + +export namespace Crafter { + #ifdef CRAFTER_GRAPHICS_VULKAN + template + class SamplerVulkan { + public: + VkSampler textureSampler; + VkDescriptorImageInfo imageInfo; + SamplerVulkan(ImageVulkan& texture) { + VkSamplerCreateInfo samplerInfo{}; + samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + samplerInfo.magFilter = VK_FILTER_LINEAR; + samplerInfo.minFilter = VK_FILTER_LINEAR; + samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerInfo.anisotropyEnable = VK_FALSE; + samplerInfo.maxAnisotropy = 1; + samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; + samplerInfo.unnormalizedCoordinates = VK_FALSE; + samplerInfo.compareEnable = VK_FALSE; + samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS; + samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + samplerInfo.mipLodBias = 0.0f; + samplerInfo.minLod = 0.0f; + samplerInfo.maxLod = 0.0f; + VulkanDevice::CheckVkResult(vkCreateSampler(VulkanDevice::device, &samplerInfo, nullptr, &textureSampler)); + + imageInfo = { + .sampler = textureSampler + }; + } + }; + #endif +} \ No newline at end of file diff --git a/interfaces/Crafter.Graphics-ShaderVulkan.cppm b/interfaces/Crafter.Graphics-ShaderVulkan.cppm index 16e6b08..a0429bd 100644 --- a/interfaces/Crafter.Graphics-ShaderVulkan.cppm +++ b/interfaces/Crafter.Graphics-ShaderVulkan.cppm @@ -40,27 +40,13 @@ export namespace Crafter { template < StringLiteral path, StringLiteral entrypoint, - VkShaderStageFlagBits stage, - std::uint32_t DescriptorCount, - const std::array Descriptors + VkShaderStageFlagBits stage > class VulkanShader { public: constexpr static VkShaderStageFlagBits _stage = stage; constexpr static StringLiteral _entrypoint = entrypoint; - constexpr static std::span descriptors = Descriptors; inline static VkShaderModule shader; - inline static VkDescriptorSetLayout layout; - - consteval static std::array GetDescriptorSet() { - std::array set; - - for(std::uint32_t i = 0; i < Descriptors.size(); i++) { - set[i] = {Descriptors[i].slot, Descriptors[i].type, 1, stage, nullptr}; - } - - return set; - } static void CreateShader() { std::ifstream file(path.value, std::ios::binary); @@ -88,14 +74,6 @@ export namespace Crafter { VkShaderModule shader_module; VulkanDevice::CheckVkResult(vkCreateShaderModule(VulkanDevice::device, &module_info, nullptr, &shader)); - - constexpr std::array setLayoutBindingsMesh = GetDescriptorSet(); - VkDescriptorSetLayoutCreateInfo descriptorLayoutInfoMesh = { - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, - .bindingCount = DescriptorCount, - .pBindings = setLayoutBindingsMesh.data() - }; - VulkanDevice::CheckVkResult(vkCreateDescriptorSetLayout(VulkanDevice::device, &descriptorLayoutInfoMesh, nullptr, &layout)); } }; } diff --git a/interfaces/Crafter.Graphics-Types.cppm b/interfaces/Crafter.Graphics-Types.cppm index 606595f..bbc4ece 100644 --- a/interfaces/Crafter.Graphics-Types.cppm +++ b/interfaces/Crafter.Graphics-Types.cppm @@ -76,6 +76,12 @@ export namespace Crafter { std::uint8_t a; }; + struct __attribute__((packed)) Pixel_RU8_GU8_BU8 { + std::uint8_t r; + std::uint8_t g; + std::uint8_t b; + }; + // struct __attribute__((packed)) Vertex { // float x; // float y; diff --git a/interfaces/Crafter.Graphics-Window.cppm b/interfaces/Crafter.Graphics-Window.cppm index 465904c..5d772a4 100644 --- a/interfaces/Crafter.Graphics-Window.cppm +++ b/interfaces/Crafter.Graphics-Window.cppm @@ -383,14 +383,15 @@ export namespace Crafter { .preferred_scale = xdg_surface_handle_preferred_scale, }; void CreateSwapchain(); + static constexpr std::uint32_t numFrames = 3; VkSurfaceKHR vulkanSurface = VK_NULL_HANDLE; VkSwapchainKHR swapChain = VK_NULL_HANDLE; VkFormat colorFormat; VkColorSpaceKHR colorSpace; - std::vector images; - std::vector imageViews; + VkImage images[numFrames]; + VkImageView imageViews[numFrames]; std::thread thread; - std::vector drawCmdBuffers; + VkCommandBuffer drawCmdBuffers[numFrames]; VkSubmitInfo submitInfo; Semaphores semaphores; uint32_t currentBuffer = 0; diff --git a/interfaces/Crafter.Graphics.cppm b/interfaces/Crafter.Graphics.cppm index e51d857..0bd48e4 100644 --- a/interfaces/Crafter.Graphics.cppm +++ b/interfaces/Crafter.Graphics.cppm @@ -41,6 +41,9 @@ export import :ShaderVulkan; export import :ShaderBindingTableVulkan; export import :PipelineRTVulkan; export import :RenderingElement3DVulkan; +export import :ImageVulkan; +export import :SamplerVulkan; +export import :DescriptorSetLayoutVulkan; #endif // export import :WindowWaylandVulkan; diff --git a/project.json b/project.json index 8f925d5..dc0f8e7 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/Crafter.Graphics-Mesh", "interfaces/Crafter.Graphics-VulkanBuffer", "interfaces/Crafter.Graphics-RenderingElement3DVulkan", "interfaces/Crafter.Graphics-DescriptorPoolVulkan", "interfaces/Crafter.Graphics-ShaderVulkan", "interfaces/Crafter.Graphics-PipelineRTVulkan", "interfaces/Crafter.Graphics-ShaderBindingTableVulkan"], + "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", "interfaces/Crafter.Graphics-RenderingElement3DVulkan", "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"], "type": "library" }, {