From 2083b6c85ec5acbd3a6156186dd02b9135eb57fa Mon Sep 17 00:00:00 2001 From: PancakeTAS Date: Tue, 1 Jul 2025 08:57:44 +0200 Subject: [PATCH] import semaphore and images --- include/core/image.hpp | 15 +++++ include/core/semaphore.hpp | 10 +++ src/context.cpp | 20 ++++++ src/core/device.cpp | 1 + src/core/image.cpp | 125 ++++++++++++++++++++++++++++++++++++- src/core/semaphore.cpp | 36 +++++++++++ 6 files changed, 206 insertions(+), 1 deletion(-) diff --git a/include/core/image.hpp b/include/core/image.hpp index 64dd03c..540e043 100644 --- a/include/core/image.hpp +++ b/include/core/image.hpp @@ -32,6 +32,21 @@ namespace LSFG::Core { Image(const Core::Device& device, VkExtent2D extent, VkFormat format, VkImageUsageFlags usage, VkImageAspectFlags aspectFlags); + /// + /// Create the image with shared backing memory. + /// + /// @param device Vulkan device + /// @param extent Extent of the image in pixels. + /// @param format Vulkan format of the image + /// @param usage Usage flags for the image + /// @param aspectFlags Aspect flags for the image view + /// @param fd File descriptor for shared memory. + /// + /// @throws LSFG::vulkan_error if object creation fails. + /// + Image(const Core::Device& device, VkExtent2D extent, VkFormat format, + VkImageUsageFlags usage, VkImageAspectFlags aspectFlags, int fd); + /// Get the Vulkan handle. [[nodiscard]] auto handle() const { return *this->image; } /// Get the Vulkan device memory handle. diff --git a/include/core/semaphore.hpp b/include/core/semaphore.hpp index b91fdb4..2060c33 100644 --- a/include/core/semaphore.hpp +++ b/include/core/semaphore.hpp @@ -29,6 +29,16 @@ namespace LSFG::Core { /// Semaphore(const Core::Device& device, std::optional initial = std::nullopt); + /// + /// Import a semaphore. + /// + /// @param device Vulkan device + /// @param fd File descriptor to import the semaphore from. + /// + /// @throws LSFG::vulkan_error if object creation fails. + /// + Semaphore(const Core::Device& device, int fd); + /// /// Signal the semaphore to a specific value. /// diff --git a/src/context.cpp b/src/context.cpp index 0afd34c..37cb651 100644 --- a/src/context.cpp +++ b/src/context.cpp @@ -1,4 +1,5 @@ #include "context.hpp" +#include "core/semaphore.hpp" #include "lsfg.hpp" #include @@ -7,6 +8,18 @@ using namespace LSFG; Context::Context(const Core::Device& device, uint32_t width, uint32_t height, int in0, int in1) { + // import images + this->inImg_0 = Core::Image(device, { width, height }, + VK_FORMAT_R8G8B8A8_UNORM, + VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, + VK_IMAGE_ASPECT_COLOR_BIT, + in0); + this->inImg_1 = Core::Image(device, { width, height }, + VK_FORMAT_R8G8B8A8_UNORM, + VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, + VK_IMAGE_ASPECT_COLOR_BIT, + in1); + // create pools this->descPool = Core::DescriptorPool(device); this->cmdPool = Core::CommandPool(device); @@ -81,6 +94,9 @@ Context::Context(const Core::Device& device, uint32_t width, uint32_t height, in } void Context::present(const Core::Device& device, int inSem, int outSem) { + Core::Semaphore inSemaphore(device, inSem); + Core::Semaphore outSemaphore(device, outSem); + Core::CommandBuffer cmdBuffer(device, this->cmdPool); cmdBuffer.begin(); @@ -102,6 +118,10 @@ void Context::present(const Core::Device& device, int inSem, int outSem) { cmdBuffer.end(); + cmdBuffer.submit(device.getComputeQueue(), std::nullopt, + { inSemaphore }, {}, + { outSemaphore }, {}); + fc++; } diff --git a/src/core/device.cpp b/src/core/device.cpp index ef7788b..6c147bb 100644 --- a/src/core/device.cpp +++ b/src/core/device.cpp @@ -8,6 +8,7 @@ using namespace LSFG::Core; const std::vector requiredExtensions = { "VK_KHR_external_memory_fd", + "VK_KHR_external_semaphore_fd", "VK_EXT_robustness2", }; diff --git a/src/core/image.cpp b/src/core/image.cpp index c190bb1..4affb03 100644 --- a/src/core/image.cpp +++ b/src/core/image.cpp @@ -109,5 +109,128 @@ Image::Image(const Core::Device& device, VkExtent2D extent, VkFormat format, vkDestroyImageView(dev, *imgView, nullptr); } ); - +} + +// shared memory constructor + +Image::Image(const Core::Device& device, VkExtent2D extent, VkFormat format, + VkImageUsageFlags usage, VkImageAspectFlags aspectFlags, int fd) + : extent(extent), format(format), aspectFlags(aspectFlags) { + // create image + const VkExternalMemoryImageCreateInfo externalInfo{ + .sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO, + .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR + }; + const VkImageCreateInfo desc{ + .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .pNext = &externalInfo, + .imageType = VK_IMAGE_TYPE_2D, + .format = format, + .extent = { + .width = extent.width, + .height = extent.height, + .depth = 1 + }, + .mipLevels = 1, + .arrayLayers = 1, + .samples = VK_SAMPLE_COUNT_1_BIT, + .usage = usage, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE + }; + VkImage imageHandle{}; + auto res = vkCreateImage(device.handle(), &desc, nullptr, &imageHandle); + if (res != VK_SUCCESS || imageHandle == VK_NULL_HANDLE) + throw LSFG::vulkan_error(res, "Failed to create Vulkan image"); + + // find memory type + VkPhysicalDeviceMemoryProperties memProps; + vkGetPhysicalDeviceMemoryProperties(device.getPhysicalDevice(), &memProps); + + VkMemoryRequirements memReqs; + vkGetImageMemoryRequirements(device.handle(), imageHandle, &memReqs); + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" + std::optional memType{}; + for (uint32_t i = 0; i < memProps.memoryTypeCount; ++i) { + if ((memReqs.memoryTypeBits & (1 << i)) && // NOLINTBEGIN + (memProps.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) { + memType.emplace(i); + break; + } // NOLINTEND + } + if (!memType.has_value()) + throw LSFG::vulkan_error(VK_ERROR_UNKNOWN, "Unable to find memory type for image"); +#pragma clang diagnostic pop + + // ~~allocate~~ and bind memory + const VkMemoryDedicatedAllocateInfoKHR dedicatedInfo2{ + .sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR, + .image = imageHandle, + }; + const VkImportMemoryFdInfoKHR importInfo{ + .sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR, + .pNext = &dedicatedInfo2, + .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR, + .fd = fd // closes the fd + }; + const VkMemoryAllocateInfo allocInfo{ + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .pNext = &importInfo, + .allocationSize = memReqs.size, + .memoryTypeIndex = memType.value() + }; + VkDeviceMemory memoryHandle{}; + res = vkAllocateMemory(device.handle(), &allocInfo, nullptr, &memoryHandle); + if (res != VK_SUCCESS || memoryHandle == VK_NULL_HANDLE) + throw LSFG::vulkan_error(res, "Failed to allocate memory for Vulkan image"); + + res = vkBindImageMemory(device.handle(), imageHandle, memoryHandle, 0); + if (res != VK_SUCCESS) + throw LSFG::vulkan_error(res, "Failed to bind memory to Vulkan image"); + + // create image view + const VkImageViewCreateInfo viewDesc{ + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .image = imageHandle, + .viewType = VK_IMAGE_VIEW_TYPE_2D, + .format = format, + .components = { + .r = VK_COMPONENT_SWIZZLE_IDENTITY, + .g = VK_COMPONENT_SWIZZLE_IDENTITY, + .b = VK_COMPONENT_SWIZZLE_IDENTITY, + .a = VK_COMPONENT_SWIZZLE_IDENTITY + }, + .subresourceRange = { + .aspectMask = aspectFlags, + .levelCount = 1, + .layerCount = 1 + } + }; + + VkImageView viewHandle{}; + res = vkCreateImageView(device.handle(), &viewDesc, nullptr, &viewHandle); + if (res != VK_SUCCESS || viewHandle == VK_NULL_HANDLE) + throw LSFG::vulkan_error(res, "Failed to create image view"); + + // store objects in shared ptr + this->layout = std::make_shared(VK_IMAGE_LAYOUT_UNDEFINED); + this->image = std::shared_ptr( + new VkImage(imageHandle), + [dev = device.handle()](VkImage* img) { + vkDestroyImage(dev, *img, nullptr); + } + ); + this->memory = std::shared_ptr( + new VkDeviceMemory(memoryHandle), + [dev = device.handle()](VkDeviceMemory* mem) { + vkFreeMemory(dev, *mem, nullptr); + } + ); + this->view = std::shared_ptr( + new VkImageView(viewHandle), + [dev = device.handle()](VkImageView* imgView) { + vkDestroyImageView(dev, *imgView, nullptr); + } + ); } diff --git a/src/core/semaphore.cpp b/src/core/semaphore.cpp index d2aee2a..78b66d4 100644 --- a/src/core/semaphore.cpp +++ b/src/core/semaphore.cpp @@ -29,6 +29,42 @@ Semaphore::Semaphore(const Core::Device& device, std::optional initial ); } +Semaphore::Semaphore(const Core::Device& device, int fd) { + // create semaphore + const VkExportSemaphoreCreateInfo exportInfo{ + .sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO, + .handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT + }; + const VkSemaphoreCreateInfo desc{ + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, + .pNext = &exportInfo + }; + VkSemaphore semaphoreHandle{}; + auto res = vkCreateSemaphore(device.handle(), &desc, nullptr, &semaphoreHandle); + if (res != VK_SUCCESS || semaphoreHandle == VK_NULL_HANDLE) + throw LSFG::vulkan_error(res, "Unable to create semaphore"); + + // import semaphore from fd + const VkImportSemaphoreFdInfoKHR importInfo{ + .sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR, + .semaphore = semaphoreHandle, + .handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT, + .fd = fd // closes the fd + }; + res = vkImportSemaphoreFdKHR(device.handle(), &importInfo); + if (res != VK_SUCCESS) + throw LSFG::vulkan_error(res, "Unable to import semaphore from fd"); + + // store semaphore in shared ptr + this->isTimeline = false; + this->semaphore = std::shared_ptr( + new VkSemaphore(semaphoreHandle), + [dev = device.handle()](VkSemaphore* semaphoreHandle) { + vkDestroySemaphore(dev, *semaphoreHandle, nullptr); + } + ); +} + void Semaphore::signal(const Core::Device& device, uint64_t value) const { if (!this->isTimeline) throw std::logic_error("Invalid timeline semaphore");