From 405733d50e804d086ca578a10aaceaeaf724502d Mon Sep 17 00:00:00 2001 From: PancakeTAS Date: Fri, 8 Aug 2025 17:37:14 +0200 Subject: [PATCH] feat(test): write main test --- framegen/include/core/image.hpp | 15 ++++ framegen/src/core/image.cpp | 133 ++++++++++++++++++++++++++++++++ test/CMakeLists.txt | 3 +- test/src/main.cpp | 44 ++++++++--- 4 files changed, 185 insertions(+), 10 deletions(-) diff --git a/framegen/include/core/image.hpp b/framegen/include/core/image.hpp index 9731bdf..b89fea5 100644 --- a/framegen/include/core/image.hpp +++ b/framegen/include/core/image.hpp @@ -48,6 +48,21 @@ namespace LSFG::Core { Image(const Core::Device& device, VkExtent2D extent, VkFormat format, VkImageUsageFlags usage, VkImageAspectFlags aspectFlags, int fd); + /// + /// Create the image and export the backing fd + /// + /// @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 Pointer to an integer where the file descriptor will be stored. + /// + /// @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/framegen/src/core/image.cpp b/framegen/src/core/image.cpp index cfbf2e2..b9c4f17 100644 --- a/framegen/src/core/image.cpp +++ b/framegen/src/core/image.cpp @@ -240,3 +240,136 @@ Image::Image(const Core::Device& device, VkExtent2D extent, VkFormat format, } ); } + +// second shared memory constructors + +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 dedicatedInfo{ + .sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR, + .image = imageHandle, + }; + const VkExportMemoryAllocateInfo exportInfo{ + .sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO, + .pNext = &dedicatedInfo, + .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR + }; + const VkMemoryAllocateInfo allocInfo{ + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .pNext = &exportInfo, + .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"); + + // obtain the sharing fd + const VkMemoryGetFdInfoKHR fdInfo{ + .sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR, + .memory = memoryHandle, + .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR, + }; + res = vkGetMemoryFdKHR(device.handle(), &fdInfo, fd); + if (res != VK_SUCCESS || *fd < 0) + throw LSFG::vulkan_error(res, "Failed to obtain sharing fd for 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/test/CMakeLists.txt b/test/CMakeLists.txt index b3d81f7..d38d6f0 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -22,7 +22,8 @@ set_target_properties(lsfg-vk-test PROPERTIES target_include_directories(lsfg-vk-test PRIVATE include) target_link_libraries(lsfg-vk-test PUBLIC - vulkan lsfg-vk) + lsfg-vk lsfg-vk-framegen + vulkan) # diagnostics if(CMAKE_BUILD_TYPE STREQUAL "Debug") diff --git a/test/src/main.cpp b/test/src/main.cpp index 4216edf..dc5b4e0 100644 --- a/test/src/main.cpp +++ b/test/src/main.cpp @@ -1,13 +1,16 @@ +#include "common/utils.hpp" +#include "core/commandpool.hpp" #include "core/device.hpp" +#include "core/image.hpp" #include "core/instance.hpp" #include "extract/extract.hpp" -#include "mini/image.hpp" -#include "utils.hpp" #include #include +#include #include +#include #include #include #include @@ -20,6 +23,7 @@ using namespace LSFG; const VkExtent2D SRC_EXTENT = { 2560 , 1440 }; const VkFormat SRC_FORMAT = VK_FORMAT_R8G8B8A8_UNORM; +const std::string EMPTY_FILE = "test/empty.dds"; const std::array SRC_FILES = { "test/f0.dds", "test/f1.dds" @@ -53,26 +57,28 @@ namespace { } /// Create images for frame generation - void create_images(const Core::Device& device, + std::pair create_images(const Core::Device& device, std::array& fds, - std::vector& outFds, std::vector& out_n) { - const Mini::Image frame_0{device.handle(), device.getPhysicalDevice(), + std::vector& outFds, std::vector& out_n) { + const Core::Image frame_0{device, SRC_EXTENT, SRC_FORMAT, VK_IMAGE_USAGE_TRANSFER_DST_BIT, VK_IMAGE_ASPECT_COLOR_BIT, &fds.at(0) }; - const Mini::Image frame_1{device.handle(), device.getPhysicalDevice(), + const Core::Image frame_1{device, SRC_EXTENT, SRC_FORMAT, VK_IMAGE_USAGE_TRANSFER_DST_BIT, VK_IMAGE_ASPECT_COLOR_BIT, &fds.at(1) }; for (size_t i = 0; i < (MULTIPLIER - 1); i++) - out_n.at(i) = Mini::Image{device.handle(), device.getPhysicalDevice(), + out_n.at(i) = Core::Image{device, SRC_EXTENT, SRC_FORMAT, VK_IMAGE_USAGE_TRANSFER_SRC_BIT, VK_IMAGE_ASPECT_COLOR_BIT, &outFds.at(i) }; + + return { frame_0, frame_1 }; } /// Create the LSFG context. @@ -99,7 +105,8 @@ namespace { namespace { std::array fds{}; std::vector outFds(MULTIPLIER - 1); - std::vector out_n(MULTIPLIER - 1); + std::pair frames{}; + std::vector out_n(MULTIPLIER - 1); int32_t lsfg_id{}; } @@ -107,14 +114,33 @@ int main() { // initialize host Vulkan const Core::Instance instance{}; const Core::Device device{instance, 0x1463ABAC}; + const Core::CommandPool commandPool{device}; // setup test setup_renderdoc(); - create_images(device, fds, outFds, out_n); + frames = create_images(device, fds, outFds, out_n); lsfg_id = create_lsfg(fds, outFds); + Utils::clearImage(device, frames.first); + Utils::clearImage(device, frames.second); + // run for (size_t fc = 0; fc < SRC_FILES.size(); fc++) { + if (fc % 2 == 0) + Utils::uploadImage(device, commandPool, frames.first, SRC_FILES.at(fc)); + else + Utils::uploadImage(device, commandPool, frames.second, SRC_FILES.at(fc)); + + if (rdoc) rdoc->StartFrameCapture(nullptr, nullptr); + + // run the present + const std::vector null_sems(MULTIPLIER - 1, -1); + presentContext(lsfg_id, -1, null_sems); + + // wait until the present is done + usleep(1000 * 100); + + if (rdoc) rdoc->EndFrameCapture(nullptr, nullptr); } // destroy test