/* 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:VulkanBuffer; #ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN import std; import :Device; namespace Crafter { export class VulkanBufferBase { public: VkDescriptorBufferInfo descriptor; VkBuffer buffer = VK_NULL_HANDLE; VkDeviceMemory memory; }; export class VulkanBufferAdressable { public: VkDeviceAddress address; }; export class VulkanBufferAdressableEmpty {}; template using VulkanBufferAdressableConditional = std::conditional_t< Adressable, VulkanBufferAdressable, VulkanBufferAdressableEmpty >; export template class VulkanBufferMapped { public: T* value; }; export class VulkanBufferMappedEmpty {}; template using VulkanBufferMappedConditional = std::conditional_t< Mapped, VulkanBufferMapped, VulkanBufferMappedEmpty >; export template class VulkanBuffer; export template class VulkanBuffer : public VulkanBufferBase, public VulkanBufferMappedConditional, public VulkanBufferAdressableConditional { public: VulkanBuffer() = default; void Create(VkBufferUsageFlags usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, std::uint32_t count) { VkBufferCreateInfo bufferCreateInfo {}; bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; bufferCreateInfo.usage = usageFlags; bufferCreateInfo.size = sizeof(T)*count; Device::CheckVkResult(vkCreateBuffer(Device::device, &bufferCreateInfo, nullptr, &buffer)); VkMemoryRequirements memReqs; vkGetBufferMemoryRequirements(Device::device, buffer, &memReqs); VkMemoryAllocateInfo memAlloc { .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .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 = sizeof(T)*count; 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); } if constexpr(Mapped) { Device::CheckVkResult(vkMapMemory(Device::device, memory, 0, memReqs.size, 0, reinterpret_cast(&(VulkanBufferMappedConditional::value)))); } } void Clear() { if constexpr(Mapped) { vkUnmapMemory(Device::device, memory); } vkDestroyBuffer(Device::device, buffer, nullptr); vkFreeMemory(Device::device, memory, nullptr); buffer = VK_NULL_HANDLE; } void Resize(VkBufferUsageFlags usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, std::uint32_t count) { if(buffer != VK_NULL_HANDLE) { Clear(); } Create(usageFlags, memoryPropertyFlags, count); } void Copy(VkCommandBuffer cmd, VulkanBuffer& dst) { VkBufferCopy copyRegion = { .srcOffset = 0, .dstOffset = 0, .size = descriptor.range }; vkCmdCopyBuffer( cmd, buffer, dst.buffer, 1, ©Region ); } void Copy(VkCommandBuffer cmd, VulkanBuffer& dst, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask) { Copy(cmd, dst); VkBufferMemoryBarrier barrier = { .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, .srcAccessMask = srcAccessMask, .dstAccessMask = dstAccessMask, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .buffer = buffer, .offset = 0, .size = VK_WHOLE_SIZE }; vkCmdPipelineBarrier( cmd, srcStageMask, dstStageMask, 0, 0, NULL, 1, &barrier, 0, NULL ); } void FlushDevice() requires(Mapped) { VkMappedMemoryRange range = { .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, .memory = memory, .offset = 0, .size = VK_WHOLE_SIZE }; vkFlushMappedMemoryRanges(Device::device, 1, &range); } void FlushDevice(VkCommandBuffer cmd, VkAccessFlags dstAccessMask, VkPipelineStageFlags dstStageMask) requires(Mapped) { FlushDevice(); VkBufferMemoryBarrier barrier = { .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, .srcAccessMask = VK_ACCESS_HOST_WRITE_BIT, .dstAccessMask = dstAccessMask, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .buffer = buffer, .offset = 0, .size = VK_WHOLE_SIZE }; vkCmdPipelineBarrier( cmd, VK_PIPELINE_STAGE_HOST_BIT, dstStageMask, 0, 0, NULL, 1, &barrier, 0, NULL ); } void FlushHost() requires(Mapped) { VkMappedMemoryRange range = { .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, .memory = memory, .offset = 0, .size = VK_WHOLE_SIZE }; vkInvalidateMappedMemoryRanges(Device::device, 1, &range); } VulkanBuffer(VulkanBuffer&& other) { descriptor = other.descriptor; buffer = other.buffer; memory = other.memory; other.buffer = VK_NULL_HANDLE; if constexpr(Adressable) { VulkanBufferAdressableConditional::address = other.VulkanBufferAdressableConditional::address; } if constexpr(Mapped) { VulkanBufferMappedConditional::value = other.VulkanBufferMappedConditional::value; } }; ~VulkanBuffer() { if(buffer != VK_NULL_HANDLE) { Clear(); } } VulkanBuffer(VulkanBuffer&) = delete; VulkanBuffer& operator=(const VulkanBuffer&) = delete; }; } #endif