diff --git a/lsfg-vk-backend/include/lsfg-vk-backend/lsfgvk.hpp b/lsfg-vk-backend/include/lsfg-vk-backend/lsfgvk.hpp index df946bb..6fb3ba9 100644 --- a/lsfg-vk-backend/include/lsfg-vk-backend/lsfgvk.hpp +++ b/lsfg-vk-backend/include/lsfg-vk-backend/lsfgvk.hpp @@ -52,6 +52,13 @@ namespace lsfgvk::backend { const std::optional& pci // (bus:slot.func) if available, no padded zeros )>; + /// Helper struct for importing DMA-BUF images + struct DmaBufFD { + int fd; + uint64_t modifier; + std::vector> layouts; // (offset, pitch) per plane + }; + /// /// Main entry point of the library /// @@ -90,8 +97,8 @@ namespace lsfgvk::backend { /// @throws backend::error on failure /// Context& openContext( - std::pair sourceFds, - const std::vector& destFds, + const std::pair& sourceFds, + const std::vector& destFds, uint32_t width, uint32_t height, bool hdr, float flow, bool perf ); diff --git a/lsfg-vk-backend/src/helpers/managed_shader.cpp b/lsfg-vk-backend/src/helpers/managed_shader.cpp index df63450..6f2afb4 100644 --- a/lsfg-vk-backend/src/helpers/managed_shader.cpp +++ b/lsfg-vk-backend/src/helpers/managed_shader.cpp @@ -7,6 +7,7 @@ #include "lsfg-vk-common/vulkan/image.hpp" #include "lsfg-vk-common/vulkan/sampler.hpp" #include "lsfg-vk-common/vulkan/shader.hpp" +#include "lsfg-vk-common/vulkan/shared_image.hpp" #include "lsfg-vk-common/vulkan/vulkan.hpp" #include @@ -35,6 +36,21 @@ ManagedShaderBuilder& ManagedShaderBuilder::sampleds( return *this; } +ManagedShaderBuilder& ManagedShaderBuilder::sampled(const vk::SharedImage& image) { + this->sampledImagesSh.push_back(std::ref(image)); + return *this; +} + +ManagedShaderBuilder& ManagedShaderBuilder::sampleds( + const std::vector& images, + size_t offset, size_t count) { + if (count == 0 || offset + count > images.size()) + count = images.size() - offset; + + for (size_t i = 0; i < count; ++i) + this->sampledImagesSh.push_back(std::ref(images[offset + i])); + return *this; +} ManagedShaderBuilder& ManagedShaderBuilder::storage(const vk::Image& image) { this->storageImages.push_back(std::ref(image)); @@ -52,6 +68,22 @@ ManagedShaderBuilder& ManagedShaderBuilder::storages( return *this; } +ManagedShaderBuilder& ManagedShaderBuilder::storage(const vk::SharedImage& image) { + this->storageImagesSh.push_back(std::ref(image)); + return *this; +} + +ManagedShaderBuilder& ManagedShaderBuilder::storages( + const std::vector& images, + size_t offset, size_t count) { + if (count == 0 || offset + count > images.size()) + count = images.size() - offset; + + for (size_t i = 0; i < count; ++i) + this->storageImagesSh.push_back(std::ref(images[offset + i])); + return *this; +} + ManagedShaderBuilder& ManagedShaderBuilder::sampler(const vk::Sampler& sampler) { this->imageSamplers.push_back(std::ref(sampler)); return *this; @@ -72,8 +104,27 @@ ManagedShaderBuilder& ManagedShaderBuilder::buffer(const vk::Buffer& buffer) { ManagedShader ManagedShaderBuilder::build(const vk::Vulkan& vk, const vk::DescriptorPool& pool, const vk::Shader& shader) const { std::vector barriers; - barriers.reserve(this->storageImages.size() + this->sampledImages.size()); + barriers.reserve( + this->storageImages.size() + this->sampledImages.size() + + this->storageImagesSh.size() + this->sampledImagesSh.size() + ); + for (const auto& img : this->sampledImagesSh) + barriers.push_back({ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, + .oldLayout = VK_IMAGE_LAYOUT_GENERAL, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = img.get().handle(), + .subresourceRange = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .levelCount = 1, + .layerCount = 1 + } + }); for (const auto& img : this->sampledImages) barriers.push_back({ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, @@ -90,6 +141,22 @@ ManagedShader ManagedShaderBuilder::build(const vk::Vulkan& vk, .layerCount = 1 } }); + for (const auto& img : this->storageImagesSh) + barriers.push_back({ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .srcAccessMask = VK_ACCESS_SHADER_READ_BIT, + .dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT, + .oldLayout = VK_IMAGE_LAYOUT_GENERAL, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = img.get().handle(), + .subresourceRange = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .levelCount = 1, + .layerCount = 1 + } + }); for (const auto& img : this->storageImages) barriers.push_back({ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, @@ -112,7 +179,9 @@ ManagedShader ManagedShaderBuilder::build(const vk::Vulkan& vk, std::move(barriers), vk::DescriptorSet(vk, pool, shader, this->sampledImages, + this->sampledImagesSh, this->storageImages, + this->storageImagesSh, this->imageSamplers, this->constantBuffers) }; diff --git a/lsfg-vk-backend/src/helpers/managed_shader.hpp b/lsfg-vk-backend/src/helpers/managed_shader.hpp index e0d673f..378bf70 100644 --- a/lsfg-vk-backend/src/helpers/managed_shader.hpp +++ b/lsfg-vk-backend/src/helpers/managed_shader.hpp @@ -7,6 +7,7 @@ #include "lsfg-vk-common/vulkan/descriptor_pool.hpp" #include "lsfg-vk-common/vulkan/descriptor_set.hpp" #include "lsfg-vk-common/vulkan/shader.hpp" +#include "lsfg-vk-common/vulkan/shared_image.hpp" #include #include @@ -60,6 +61,16 @@ namespace lsfgvk::backend { [[nodiscard]] ManagedShaderBuilder& sampleds(const std::vector& images, size_t offset = 0, size_t count = 0); + // add a sampled shared image + /// @param image image to add + [[nodiscard]] ManagedShaderBuilder& sampled(const vk::SharedImage& image); + /// add multiple sampled shared images + /// @param images images to add + /// @param offset offset into images + /// @param count number of images to add (0 = all) + [[nodiscard]] ManagedShaderBuilder& sampleds(const std::vector& images, + size_t offset = 0, size_t count = 0); + /// add a storage image /// @param image image to add [[nodiscard]] ManagedShaderBuilder& storage(const vk::Image& image); @@ -70,6 +81,16 @@ namespace lsfgvk::backend { [[nodiscard]] ManagedShaderBuilder& storages(const std::vector& images, size_t offset = 0, size_t count = 0); + /// add a storage shared image + /// @param image image to add + [[nodiscard]] ManagedShaderBuilder& storage(const vk::SharedImage& image); + /// add multiple storage shared images + /// @param images images to add + /// @param offset offset into images + /// @param count number of images to add (0 = all) + [[nodiscard]] ManagedShaderBuilder& storages(const std::vector& images, + size_t offset = 0, size_t count = 0); + /// add a sampler /// @param sampler sampler to add [[nodiscard]] ManagedShaderBuilder& sampler(const vk::Sampler& sampler); @@ -90,7 +111,9 @@ namespace lsfgvk::backend { const vk::DescriptorPool& pool, const vk::Shader& shader) const; private: std::vector> sampledImages; + std::vector> sampledImagesSh; // always goes first std::vector> storageImages; + std::vector> storageImagesSh; std::vector> imageSamplers; std::vector> constantBuffers; }; diff --git a/lsfg-vk-backend/src/lsfgvk.cpp b/lsfg-vk-backend/src/lsfgvk.cpp index 8c6c1a1..561778c 100644 --- a/lsfg-vk-backend/src/lsfgvk.cpp +++ b/lsfg-vk-backend/src/lsfgvk.cpp @@ -12,6 +12,7 @@ #include "lsfg-vk-common/vulkan/fence.hpp" #include "lsfg-vk-common/vulkan/image.hpp" #include "lsfg-vk-common/vulkan/semaphore.hpp" +#include "lsfg-vk-common/vulkan/shared_image.hpp" #include "lsfg-vk-common/vulkan/timeline_semaphore.hpp" #include "lsfg-vk-common/vulkan/vulkan.hpp" #include "shaderchains/alpha0.hpp" @@ -100,15 +101,16 @@ namespace lsfgvk::backend { /// create a context /// (see lsfg-vk documentation) ContextImpl(const InstanceImpl& instance, - std::pair sourceFds, const std::vector& destFds, + const std::pair& sourceFds, + const std::vector& destFds, VkExtent2D extent, bool hdr, float flow, bool perf); /// schedule frames /// (see lsfg-vk documentation) void scheduleFrames(int waitFd, std::vector& syncFds); private: - std::pair sourceImages; - std::vector destImages; + std::pair sourceImages; + std::vector destImages; vk::Image blackImage; ls::lazy syncSemaphore; // imported @@ -276,7 +278,9 @@ InstanceImpl::InstanceImpl(vk::PhysicalDeviceSelector selectPhysicalDevice, vk.persistPipelineCache(); // will silently fail } -Context& Instance::openContext(std::pair sourceFds, const std::vector& destFds, +Context& Instance::openContext( + const std::pair& sourceFds, + const std::vector& destFds, uint32_t width, uint32_t height, bool hdr, float flow, bool perf) { const VkExtent2D extent{ width, height }; @@ -288,31 +292,41 @@ Context& Instance::openContext(std::pair sourceFds, const std::vector< namespace { /// import source images - std::pair importImages(const vk::Vulkan& vk, - const std::pair& sourceFds, + std::pair importImages( + const vk::Vulkan& vk, + const std::pair& sourceFds, VkExtent2D extent, VkFormat format) { try { return { - vk::Image(vk, extent, format, - VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, sourceFds.first), - vk::Image(vk, extent, format, - VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, sourceFds.second) + vk::SharedImage(vk, extent, format, + VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, + sourceFds.first.modifier, + sourceFds.first.layouts, + sourceFds.first.fd), + vk::SharedImage(vk, extent, format, + VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, + sourceFds.second.modifier, + sourceFds.second.layouts, + sourceFds.second.fd) }; } catch (const std::exception& e) { throw backend::error("Unable to import destination images", e); } } /// import destination images - std::vector importImages(const vk::Vulkan& vk, - const std::vector& destFds, + std::vector importImages(const vk::Vulkan& vk, + const std::vector& destFds, VkExtent2D extent, VkFormat format) { try { - std::vector destImages; + std::vector destImages; destImages.reserve(destFds.size()); for (const auto& fd : destFds) destImages.emplace_back(vk, extent, format, - VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, fd); + VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, + fd.modifier, + fd.layouts, + fd.fd); return destImages; } catch (const std::exception& e) { @@ -395,7 +409,8 @@ namespace { } ContextImpl::ContextImpl(const InstanceImpl& instance, - std::pair sourceFds, const std::vector& destFds, + const std::pair& sourceFds, + const std::vector& destFds, VkExtent2D extent, bool hdr, float flow, bool perf) : sourceImages(importImages(instance.getVulkan(), sourceFds, extent, hdr ? VK_FORMAT_R16G16B16A16_SFLOAT : VK_FORMAT_R8G8B8A8_UNORM)), diff --git a/lsfg-vk-backend/src/shaderchains/generate.cpp b/lsfg-vk-backend/src/shaderchains/generate.cpp index 45a27e4..8df1da5 100644 --- a/lsfg-vk-backend/src/shaderchains/generate.cpp +++ b/lsfg-vk-backend/src/shaderchains/generate.cpp @@ -5,6 +5,7 @@ #include "lsfg-vk-common/helpers/pointers.hpp" #include "lsfg-vk-common/vulkan/command_buffer.hpp" #include "lsfg-vk-common/vulkan/image.hpp" +#include "lsfg-vk-common/vulkan/shared_image.hpp" #include "lsfg-vk-common/vulkan/vulkan.hpp" #include @@ -16,11 +17,11 @@ using namespace lsfgvk::backend; Generate::Generate(const Ctx& ctx, size_t idx, - const std::pair& sourceImages, + const std::pair& sourceImages, const vk::Image& inputImage1, const vk::Image& inputImage2, const vk::Image& inputImage3, - const vk::Image& outputImage) { + const vk::SharedImage& outputImage) { // create descriptor sets const auto& shader = ctx.hdr ? ctx.shaders.get().generate_hdr : ctx.shaders.get().generate; diff --git a/lsfg-vk-backend/src/shaderchains/generate.hpp b/lsfg-vk-backend/src/shaderchains/generate.hpp index 5e0b349..47e701a 100644 --- a/lsfg-vk-backend/src/shaderchains/generate.hpp +++ b/lsfg-vk-backend/src/shaderchains/generate.hpp @@ -6,6 +6,7 @@ #include "../helpers/utils.hpp" #include "lsfg-vk-common/vulkan/command_buffer.hpp" #include "lsfg-vk-common/vulkan/image.hpp" +#include "lsfg-vk-common/vulkan/shared_image.hpp" #include "lsfg-vk-common/vulkan/vulkan.hpp" #include @@ -27,11 +28,11 @@ namespace lsfgvk::backend { /// @param inputImage2 input image 2 /// @param inputImage3 input image 3 Generate(const Ctx& ctx, size_t idx, - const std::pair& sourceImages, + const std::pair& sourceImages, const vk::Image& inputImage1, const vk::Image& inputImage2, const vk::Image& inputImage3, - const vk::Image& outputImage); + const vk::SharedImage& outputImage); /// render the generate shaderchain /// @param vk the vulkan instance diff --git a/lsfg-vk-backend/src/shaderchains/mipmaps.cpp b/lsfg-vk-backend/src/shaderchains/mipmaps.cpp index a6f4db9..b56c406 100644 --- a/lsfg-vk-backend/src/shaderchains/mipmaps.cpp +++ b/lsfg-vk-backend/src/shaderchains/mipmaps.cpp @@ -5,6 +5,7 @@ #include "lsfg-vk-common/helpers/pointers.hpp" #include "lsfg-vk-common/vulkan/command_buffer.hpp" #include "lsfg-vk-common/vulkan/image.hpp" +#include "lsfg-vk-common/vulkan/shared_image.hpp" #include "lsfg-vk-common/vulkan/vulkan.hpp" #include @@ -17,7 +18,7 @@ using namespace lsfgvk::backend; Mipmaps::Mipmaps(const Ctx& ctx, - const std::pair& sourceImages) { + const std::pair& sourceImages) { // create output images for base and 6 mips this->images.reserve(7); for (uint32_t i = 0; i < 7; i++) diff --git a/lsfg-vk-backend/src/shaderchains/mipmaps.hpp b/lsfg-vk-backend/src/shaderchains/mipmaps.hpp index 8c1f6a2..a50883c 100644 --- a/lsfg-vk-backend/src/shaderchains/mipmaps.hpp +++ b/lsfg-vk-backend/src/shaderchains/mipmaps.hpp @@ -6,6 +6,7 @@ #include "../helpers/utils.hpp" #include "lsfg-vk-common/vulkan/command_buffer.hpp" #include "lsfg-vk-common/vulkan/image.hpp" +#include "lsfg-vk-common/vulkan/shared_image.hpp" #include "lsfg-vk-common/vulkan/vulkan.hpp" #include @@ -23,7 +24,7 @@ namespace lsfgvk::backend { /// @param ctx context /// @param sourceImages pair of source images Mipmaps(const Ctx& ctx, - const std::pair& sourceImages); + const std::pair& sourceImages); /// prepare the shaderchain initially /// @param images vector to fill with image handles diff --git a/lsfg-vk-common/CMakeLists.txt b/lsfg-vk-common/CMakeLists.txt index b03c78d..bfaaa65 100644 --- a/lsfg-vk-common/CMakeLists.txt +++ b/lsfg-vk-common/CMakeLists.txt @@ -12,6 +12,7 @@ set(COMMON_SOURCES "src/vulkan/sampler.cpp" "src/vulkan/semaphore.cpp" "src/vulkan/shader.cpp" + "src/vulkan/shared_image.cpp" "src/vulkan/timeline_semaphore.cpp" "src/vulkan/vulkan.cpp") diff --git a/lsfg-vk-common/include/lsfg-vk-common/vulkan/descriptor_set.hpp b/lsfg-vk-common/include/lsfg-vk-common/vulkan/descriptor_set.hpp index f8703f9..36972f0 100644 --- a/lsfg-vk-common/include/lsfg-vk-common/vulkan/descriptor_set.hpp +++ b/lsfg-vk-common/include/lsfg-vk-common/vulkan/descriptor_set.hpp @@ -6,6 +6,7 @@ #include "buffer.hpp" #include "image.hpp" #include "descriptor_pool.hpp" +#include "shared_image.hpp" #include "sampler.hpp" #include "shader.hpp" #include "vulkan.hpp" @@ -23,14 +24,18 @@ namespace vk { /// @param pool the descriptor pool to allocate from /// @param shader the shader module this descriptor set is for /// @param sampledImages the sampled images to bind + /// @param sampledImagesSh the sampled shared images to bind /// @param storageImages the storage images to bind + /// @param storageImagesSh the storage shared images to bind /// @param samplers the samplers to bind /// @param buffers the buffers to bind /// @throws ls::vulkan_error on failure DescriptorSet(const vk::Vulkan& vk, const vk::DescriptorPool& pool, const vk::Shader& shader, const std::vector>& sampledImages, + const std::vector>& sampledImagesSh, const std::vector>& storageImages, + const std::vector>& storageImagesSh, const std::vector>& samplers, const std::vector>& buffers); diff --git a/lsfg-vk-common/include/lsfg-vk-common/vulkan/shared_image.hpp b/lsfg-vk-common/include/lsfg-vk-common/vulkan/shared_image.hpp new file mode 100644 index 0000000..8a5c3d1 --- /dev/null +++ b/lsfg-vk-common/include/lsfg-vk-common/vulkan/shared_image.hpp @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ + +#pragma once + +#include "../helpers/pointers.hpp" +#include "vulkan.hpp" + +#include +#include + +#include + +namespace vk { + /// vulkan image + class SharedImage { + public: + /// create an exported image + /// @param vk the vulkan instance + /// @param extent extent of the image in pixels + /// @param format vulkan format of the image + /// @param usage usage flags + /// @param drmModifiers list of supported drm format modifiers + /// @param fd output parameter for the dma-buf fd of the image memory + /// @throws ls::vulkan_error on failure + SharedImage(const vk::Vulkan& vk, + VkExtent2D extent, + VkFormat format, + VkImageUsageFlags usage, + const std::vector& drmModifiers, + int& fd); + + /// create an imported image + /// @param vk the vulkan instance + /// @param extent extent of the image in pixels + /// @param format vulkan format of the image + /// @param usage usage flags + /// @param drmModifier the drm format modifier of the image + /// @param drmLayouts the drm offsets and row pitches of the image + /// @param fd the dma-buf fd to import the image memory from + /// @throws ls::vulkan_error on failure + SharedImage(const vk::Vulkan& vk, + VkExtent2D extent, + VkFormat format, + VkImageUsageFlags usage, + uint64_t drmModifier, + const std::vector>& drmLayouts, + int fd); + + /// get the image handle + /// @return the image handle + [[nodiscard]] const auto& handle() const { return this->image.get(); } + /// get the image view handle + /// @return the image view handle + [[nodiscard]] const auto& imageview() const { return this->view.get(); } + + /// get the extent of the image + /// @return the extent of the image + [[nodiscard]] VkExtent2D getExtent() const { return this->extent; } + + /// get the underlying drm format modifier of the image + /// @return the drm format modifier of the image + [[nodiscard]] auto drmModifier() const { return this->modifier; } + /// get the underlying drm offsets and row pitches of the image + /// @return the drm offsets and row pitches of the image + [[nodiscard]] const auto& drmLayouts() const { return this->layouts; } + private: + ls::owned_ptr image; + ls::owned_ptr memory; + ls::owned_ptr view; + + VkExtent2D extent{}; + + uint64_t modifier{}; + std::vector> layouts; + }; +} diff --git a/lsfg-vk-common/include/lsfg-vk-common/vulkan/vulkan.hpp b/lsfg-vk-common/include/lsfg-vk-common/vulkan/vulkan.hpp index 18fe083..07561df 100644 --- a/lsfg-vk-common/include/lsfg-vk-common/vulkan/vulkan.hpp +++ b/lsfg-vk-common/include/lsfg-vk-common/vulkan/vulkan.hpp @@ -109,6 +109,7 @@ namespace vk { PFN_vkSignalSemaphoreKHR SignalSemaphoreKHR; PFN_vkWaitSemaphoresKHR WaitSemaphoresKHR; PFN_vkGetMemoryFdKHR GetMemoryFdKHR; + PFN_vkGetMemoryFdPropertiesKHR GetMemoryFdPropertiesKHR; PFN_vkImportSemaphoreFdKHR ImportSemaphoreFdKHR; PFN_vkGetSemaphoreFdKHR GetSemaphoreFdKHR; PFN_vkCreateSwapchainKHR CreateSwapchainKHR; @@ -116,6 +117,9 @@ namespace vk { PFN_vkAcquireNextImageKHR AcquireNextImageKHR; PFN_vkQueuePresentKHR QueuePresentKHR; PFN_vkDestroySwapchainKHR DestroySwapchainKHR; + + PFN_vkGetImageDrmFormatModifierPropertiesEXT GetImageDrmFormatModifierPropertiesEXT; + PFN_vkGetImageSubresourceLayout GetImageSubresourceLayout; }; /// initialize vulkan device function pointers diff --git a/lsfg-vk-common/src/vulkan/descriptor_set.cpp b/lsfg-vk-common/src/vulkan/descriptor_set.cpp index 03fb874..d7aa669 100644 --- a/lsfg-vk-common/src/vulkan/descriptor_set.cpp +++ b/lsfg-vk-common/src/vulkan/descriptor_set.cpp @@ -8,6 +8,7 @@ #include "lsfg-vk-common/vulkan/image.hpp" #include "lsfg-vk-common/vulkan/sampler.hpp" #include "lsfg-vk-common/vulkan/shader.hpp" +#include "lsfg-vk-common/vulkan/shared_image.hpp" #include "lsfg-vk-common/vulkan/vulkan.hpp" #include @@ -48,7 +49,9 @@ namespace { DescriptorSet::DescriptorSet(const vk::Vulkan& vk, const vk::DescriptorPool& pool, const vk::Shader& shader, const std::vector>& sampledImages, + const std::vector>& sampledImagesSh, const std::vector>& storageImages, + const std::vector>& storageImagesSh, const std::vector>& samplers, const std::vector>& buffers) : descriptorSet(createDescriptorSet(vk, pool, shader)) { @@ -56,7 +59,9 @@ DescriptorSet::DescriptorSet(const vk::Vulkan& vk, const size_t bindingCount = samplers.size() + sampledImages.size() + + sampledImagesSh.size() + storageImages.size() + + storageImagesSh.size() + buffers.size(); std::vector entries; @@ -96,7 +101,20 @@ DescriptorSet::DescriptorSet(const vk::Vulkan& vk, }); size_t sampledIdx{32}; - for (const auto& img : sampledImages) { + for (const auto& img : sampledImages) + entries.push_back({ + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .dstSet = *this->descriptorSet, + .dstBinding = static_cast(sampledIdx++), + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, + .pImageInfo = &(imageInfos.emplace_back(VkDescriptorImageInfo{ + .imageView = img.get().imageview(), + .imageLayout = VK_IMAGE_LAYOUT_GENERAL + })) + }); + + for (const auto& img : sampledImagesSh) entries.push_back({ .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, .dstSet = *this->descriptorSet, @@ -108,7 +126,6 @@ DescriptorSet::DescriptorSet(const vk::Vulkan& vk, .imageLayout = VK_IMAGE_LAYOUT_GENERAL })) }); - } size_t storageIdx{48}; for (const auto& img : storageImages) @@ -124,6 +141,19 @@ DescriptorSet::DescriptorSet(const vk::Vulkan& vk, })) }); + for (const auto& img : storageImagesSh) + entries.push_back({ + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .dstSet = *this->descriptorSet, + .dstBinding = static_cast(storageIdx++), + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .pImageInfo = &(imageInfos.emplace_back(VkDescriptorImageInfo{ + .imageView = img.get().imageview(), + .imageLayout = VK_IMAGE_LAYOUT_GENERAL + })) + }); + vk.df().UpdateDescriptorSets(vk.dev(), static_cast(entries.size()), entries.data(), 0, VK_NULL_HANDLE); } diff --git a/lsfg-vk-common/src/vulkan/shared_image.cpp b/lsfg-vk-common/src/vulkan/shared_image.cpp new file mode 100644 index 0000000..f73c705 --- /dev/null +++ b/lsfg-vk-common/src/vulkan/shared_image.cpp @@ -0,0 +1,374 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ + +#include "lsfg-vk-common/vulkan/shared_image.hpp" +#include "lsfg-vk-common/helpers/errors.hpp" +#include "lsfg-vk-common/helpers/pointers.hpp" +#include "lsfg-vk-common/vulkan/vulkan.hpp" + +#include +#include +#include +#include +#include +#include + +#include + +using namespace vk; + +namespace { + /* EXPORTABLE - DMA-BUF */ + + /// create an image that can be imported from a dma-buf fd + ls::owned_ptr createDMAImportableImage(const vk::Vulkan& vk, + VkExtent2D extent, VkFormat format, VkImageUsageFlags usage, + uint64_t drmModifier, + const std::vector>& drmLayouts) { + VkImage handle{}; + + // create VkImage + std::vector layouts(drmLayouts.size()); + for (size_t i = 0; i < layouts.size(); i++) + layouts[i] = VkSubresourceLayout { + .offset = drmLayouts.at(i).first, + .rowPitch = drmLayouts.at(i).second + }; + const VkImageDrmFormatModifierExplicitCreateInfoEXT explicitModifierInfo{ + .sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_EXPLICIT_CREATE_INFO_EXT, + .drmFormatModifier = drmModifier, + .drmFormatModifierPlaneCount = 1, + .pPlaneLayouts = layouts.data() + }; + const VkExternalMemoryImageCreateInfo externalInfo{ + .sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO, + .pNext = &explicitModifierInfo, + .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT + }; + const VkImageCreateInfo imageInfo{ + .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, + .tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT, + .usage = usage, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE + }; + auto res = vk.df().CreateImage(vk.dev(), &imageInfo, VK_NULL_HANDLE, &handle); + if (res != VK_SUCCESS) + throw ls::vulkan_error(res, "vkCreateImage() failed"); + + return ls::owned_ptr( + new VkImage(handle), + [dev = vk.dev(), defunc = vk.df().DestroyImage](VkImage& image) { + defunc(dev, image, VK_NULL_HANDLE); + } + ); + } + /// import image memory from a dma-buf fd + ls::owned_ptr importDMADeviceMemory(const vk::Vulkan& vk, VkImage image, int fd) { + VkDeviceMemory handle{}; + + // find suitable memory type + VkMemoryFdPropertiesKHR propsFd{ + .sType = VK_STRUCTURE_TYPE_MEMORY_FD_PROPERTIES_KHR + }; + auto res = vk.df().GetMemoryFdPropertiesKHR(vk.dev(), + VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, fd, &propsFd); + if (res != VK_SUCCESS) + throw ls::vulkan_error(res, "vkGetMemoryFdPropertiesKHR() failed"); + + VkMemoryRequirements reqs{}; + vk.df().GetImageMemoryRequirements(vk.dev(), image, &reqs); + + reqs.memoryTypeBits &= propsFd.memoryTypeBits; + + auto mti = vk.findMemoryTypeIndex( + reqs.memoryTypeBits, + false + ); + if (!mti.has_value()) + throw ls::vulkan_error(VK_ERROR_UNKNOWN, "no suitable memory type found"); + + // create VkDeviceMemory + const VkImportMemoryFdInfoKHR importInfo{ + .sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR, + .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, + .fd = fd + }; + const VkMemoryAllocateInfo memoryInfo{ + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .pNext = &importInfo, + .allocationSize = reqs.size, + .memoryTypeIndex = *mti + }; + res = vk.df().AllocateMemory(vk.dev(), &memoryInfo, VK_NULL_HANDLE, &handle); + if (res != VK_SUCCESS) + throw ls::vulkan_error(res, "vkAllocateMemory() failed"); + + // bind memory to image + res = vk.df().BindImageMemory(vk.dev(), image, handle, 0); + if (res != VK_SUCCESS) + throw ls::vulkan_error(res, "vkBindImageMemory() failed"); + + return ls::owned_ptr( + new VkDeviceMemory(handle), + [dev = vk.dev(), defunc = vk.df().FreeMemory](VkDeviceMemory& memory) { + defunc(dev, memory, VK_NULL_HANDLE); + } + ); + } + + /* IMPORTABLE - DMA-BUF */ + + /// create an image that can be exported to a dma-buf fd + ls::owned_ptr createDMAExportableImage(const vk::Vulkan& vk, + VkExtent2D extent, VkFormat format, VkImageUsageFlags usage, + const std::vector& drmModifiers) { + VkImage handle{}; + + // create VkImage + const VkImageDrmFormatModifierListCreateInfoEXT explicitModifierInfo{ + .sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_LIST_CREATE_INFO_EXT, + .drmFormatModifierCount = static_cast(drmModifiers.size()), + .pDrmFormatModifiers = drmModifiers.data() + }; + const VkExternalMemoryImageCreateInfo externalInfo{ + .sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO, + .pNext = &explicitModifierInfo, + .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT + }; + const VkImageCreateInfo imageInfo{ + .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, + .tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT, + .usage = usage, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE + }; + auto res = vk.df().CreateImage(vk.dev(), &imageInfo, VK_NULL_HANDLE, &handle); + if (res != VK_SUCCESS) + throw ls::vulkan_error(res, "vkCreateImage() failed"); + + return ls::owned_ptr( + new VkImage(handle), + [dev = vk.dev(), defunc = vk.df().DestroyImage](VkImage& image) { + defunc(dev, image, VK_NULL_HANDLE); + } + ); + } + /// export image memory to a dma-buf fd + ls::owned_ptr exportDMADeviceMemory(const vk::Vulkan& vk, VkImage image, int& fd) { + VkDeviceMemory handle{}; + + // find suitable memory type + VkMemoryRequirements reqs{}; + vk.df().GetImageMemoryRequirements(vk.dev(), image, &reqs); + + auto mti = vk.findMemoryTypeIndex( + reqs.memoryTypeBits, + false + ); + if (!mti.has_value()) + throw ls::vulkan_error(VK_ERROR_UNKNOWN, "no suitable memory type found"); + + // create VkDeviceMemory + const VkExportMemoryAllocateInfoKHR importInfo{ + .sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR, + .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT + }; + const VkMemoryAllocateInfo memoryInfo{ + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .pNext = &importInfo, + .allocationSize = reqs.size, + .memoryTypeIndex = *mti + }; + auto res = vk.df().AllocateMemory(vk.dev(), &memoryInfo, VK_NULL_HANDLE, &handle); + if (res != VK_SUCCESS) + throw ls::vulkan_error(res, "vkAllocateMemory() failed"); + + // bind memory to image + res = vk.df().BindImageMemory(vk.dev(), image, handle, 0); + if (res != VK_SUCCESS) + throw ls::vulkan_error(res, "vkBindImageMemory() failed"); + + // export dma-buf fd + const VkMemoryGetFdInfoKHR exportInfo{ + .sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR, + .memory = handle, + .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT + }; + res = vk.df().GetMemoryFdKHR(vk.dev(), &exportInfo, &fd); + if (res != VK_SUCCESS) + throw ls::vulkan_error(res, "vkGetMemoryFdKHR() failed"); + + return ls::owned_ptr( + new VkDeviceMemory(handle), + [dev = vk.dev(), defunc = vk.df().FreeMemory](VkDeviceMemory& memory) { + defunc(dev, memory, VK_NULL_HANDLE); + } + ); + } + + /* GENERAL */ + + /// create an image view + ls::owned_ptr createImageView(const vk::Vulkan& vk, + VkImage image, VkFormat format) { + VkImageView handle{}; + + const VkImageViewCreateInfo viewInfo{ + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .image = image, + .viewType = VK_IMAGE_VIEW_TYPE_2D, + .format = format, + .subresourceRange = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .levelCount = 1, + .layerCount = 1 + } + }; + auto res = vk.df().CreateImageView(vk.dev(), &viewInfo, VK_NULL_HANDLE, &handle); + if (res != VK_SUCCESS) + throw ls::vulkan_error(res, "vkCreateImageView() failed"); + + return ls::owned_ptr( + new VkImageView(handle), + [dev = vk.dev(), defunc = vk.df().DestroyImageView](VkImageView& view) { + defunc(dev, view, VK_NULL_HANDLE); + } + ); + } + /// get the drm format modifier of an image + uint64_t getImageDrmModifier(const vk::Vulkan& vk, VkImage image) { + VkImageDrmFormatModifierPropertiesEXT props{ + .sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_PROPERTIES_EXT + }; + auto res = vk.df().GetImageDrmFormatModifierPropertiesEXT(vk.dev(), image, &props); + if (res != VK_SUCCESS) + throw ls::vulkan_error(res, "vkGetImageDrmFormatModifierPropertiesEXT() failed"); + + return props.drmFormatModifier; + } + /// get the drm offsets and row pitches of an image + std::vector> getImageLayouts(const vk::Vulkan& vk, + VkImage image, uint64_t drmModifier) { + std::vector> result; + + // fetch drm modifier information + VkDrmFormatModifierPropertiesList2EXT formats{ + .sType = VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_2_EXT, + }; + VkPhysicalDeviceProperties2 props{ + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, + .pNext = &formats + }; + vk.fi().GetPhysicalDeviceProperties2(vk.physdev(), &props); + + std::vector formatProps(formats.drmFormatModifierCount); + formats.pDrmFormatModifierProperties = formatProps.data(); + + vk.fi().GetPhysicalDeviceProperties2(vk.physdev(), &props); + + // find plane count for the modifier + std::optional planeCount; + for (const auto& fp : formatProps) { + if (fp.drmFormatModifier != drmModifier) + continue; + + planeCount.emplace(fp.drmFormatModifierPlaneCount); + } + + if (!planeCount.has_value()) + throw ls::vulkan_error(VK_ERROR_UNKNOWN, "invalid drm modifier"); + + // get offsets and row pitches + const std::vector bits{ + VK_IMAGE_ASPECT_MEMORY_PLANE_0_BIT_EXT, + VK_IMAGE_ASPECT_MEMORY_PLANE_1_BIT_EXT, + VK_IMAGE_ASPECT_MEMORY_PLANE_2_BIT_EXT, + VK_IMAGE_ASPECT_MEMORY_PLANE_3_BIT_EXT, + }; + + result.reserve(*planeCount); + for (size_t i = 0; i < *planeCount; i++) { + VkSubresourceLayout layout{}; + + const VkImageSubresource subresource{ + .aspectMask = bits.at(i), + }; + vk.df().GetImageSubresourceLayout(vk.dev(), image, &subresource, &layout); + + result.emplace_back( + layout.offset, + layout.rowPitch + ); + } + + return result; + } +} + +SharedImage::SharedImage(const vk::Vulkan& vk, + VkExtent2D extent, + VkFormat format, + VkImageUsageFlags usage, + const std::vector& drmModifiers, + int& fd) : + image(createDMAExportableImage(vk, + extent, format, usage, + drmModifiers + )), + memory(exportDMADeviceMemory(vk, + *this->image, + fd + )), + view(createImageView(vk, + *this->image, + format + )), + extent(extent), + modifier(getImageDrmModifier(vk, *this->image)), + layouts(getImageLayouts(vk, *this->image, this->modifier)) { +} + + +SharedImage::SharedImage(const vk::Vulkan& vk, + VkExtent2D extent, + VkFormat format, + VkImageUsageFlags usage, + uint64_t drmModifier, + const std::vector>& drmLayouts, + int fd) : + image(createDMAImportableImage(vk, + extent, format, usage, + drmModifier, drmLayouts + )), + memory(importDMADeviceMemory(vk, + *this->image, + fd + )), + view(createImageView(vk, + *this->image, + format + )), + extent(extent), + modifier(getImageDrmModifier(vk, *this->image)), + layouts(getImageLayouts(vk, *this->image, this->modifier)) { +} diff --git a/lsfg-vk-common/src/vulkan/vulkan.cpp b/lsfg-vk-common/src/vulkan/vulkan.cpp index b6a2c7a..008e975 100644 --- a/lsfg-vk-common/src/vulkan/vulkan.cpp +++ b/lsfg-vk-common/src/vulkan/vulkan.cpp @@ -177,6 +177,8 @@ namespace { }; const std::vector requestedExtensions{ VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME, + VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME, + VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME, VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME }; @@ -379,6 +381,7 @@ VulkanDeviceFuncs vk::initVulkanDeviceFuncs(const VulkanInstanceFuncs& f, VkDevi .SignalSemaphoreKHR = dpa(f, d, "vkSignalSemaphoreKHR"), .WaitSemaphoresKHR = dpa(f, d, "vkWaitSemaphoresKHR"), .GetMemoryFdKHR = dpa(f, d, "vkGetMemoryFdKHR"), + .GetMemoryFdPropertiesKHR = dpa(f, d, "vkGetMemoryFdPropertiesKHR"), .ImportSemaphoreFdKHR = dpa(f, d, "vkImportSemaphoreFdKHR"), .GetSemaphoreFdKHR = dpa(f, d, "vkGetSemaphoreFdKHR"), @@ -391,7 +394,11 @@ VulkanDeviceFuncs vk::initVulkanDeviceFuncs(const VulkanInstanceFuncs& f, VkDevi .QueuePresentKHR = graphical ? dpa(f, d, "vkQueuePresentKHR") : nullptr, .DestroySwapchainKHR = graphical ? - dpa(f, d, "vkDestroySwapchainKHR") : nullptr + dpa(f, d, "vkDestroySwapchainKHR") : nullptr, + + .GetImageDrmFormatModifierPropertiesEXT = dpa(f, d, + "vkGetImageDrmFormatModifierPropertiesEXT"), + .GetImageSubresourceLayout = dpa(f, d, "vkGetImageSubresourceLayout") }; } diff --git a/lsfg-vk-layer/src/instance.cpp b/lsfg-vk-layer/src/instance.cpp index 3b48daf..19132f3 100644 --- a/lsfg-vk-layer/src/instance.cpp +++ b/lsfg-vk-layer/src/instance.cpp @@ -113,6 +113,13 @@ void Root::modifyDeviceCreateInfo(VkDeviceCreateInfo& createInfo, { "VK_KHR_external_memory", "VK_KHR_external_memory_fd", + "VK_EXT_external_memory_dma_buf", + "VK_KHR_image_format_list", + "VK_KHR_bind_memory2", + "VK_KHR_maintenance1", + "VK_KHR_get_memory_requirements2", + "VK_KHR_sampler_ycbcr_conversion", + "VK_EXT_image_drm_format_modifier", "VK_KHR_external_semaphore", "VK_KHR_external_semaphore_fd", "VK_KHR_timeline_semaphore" @@ -120,7 +127,7 @@ void Root::modifyDeviceCreateInfo(VkDeviceCreateInfo& createInfo, ); createInfo.enabledExtensionCount = static_cast(extensions.size()); createInfo.ppEnabledExtensionNames = extensions.data(); - + finish(); } diff --git a/lsfg-vk-layer/src/swapchain.cpp b/lsfg-vk-layer/src/swapchain.cpp index 873271a..a2cb11c 100644 --- a/lsfg-vk-layer/src/swapchain.cpp +++ b/lsfg-vk-layer/src/swapchain.cpp @@ -6,13 +6,13 @@ #include "lsfg-vk-common/helpers/errors.hpp" #include "lsfg-vk-common/helpers/pointers.hpp" #include "lsfg-vk-common/vulkan/command_buffer.hpp" -#include "lsfg-vk-common/vulkan/image.hpp" #include "lsfg-vk-common/vulkan/semaphore.hpp" #include "lsfg-vk-common/vulkan/vulkan.hpp" #include #include #include +#include #include #include #include @@ -76,24 +76,46 @@ Swapchain::Swapchain(const vk::Vulkan& vk, backend::Instance& backend, std::vector sourceFds(2); std::vector destinationFds(this->profile.multiplier - 1); + const std::vector modifiers = { DRM_FORMAT_MOD_LINEAR }; + this->sourceImages.reserve(sourceFds.size()); for (int& fd : sourceFds) this->sourceImages.emplace_back(vk, extent, hdr ? VK_FORMAT_R16G16B16A16_SFLOAT : VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, - std::nullopt, &fd); + modifiers, fd); this->destinationImages.reserve(destinationFds.size()); for (int& fd : destinationFds) this->destinationImages.emplace_back(vk, extent, hdr ? VK_FORMAT_R16G16B16A16_SFLOAT : VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, - std::nullopt, &fd); + modifiers, fd); try { + const backend::DmaBufFD fd0{ + .fd = sourceFds.at(0), + .modifier = this->sourceImages.at(0).drmModifier(), + .layouts = this->sourceImages.at(0).drmLayouts() + }; + const backend::DmaBufFD fd1{ + .fd = sourceFds.at(1), + .modifier = this->sourceImages.at(1).drmModifier(), + .layouts = this->sourceImages.at(1).drmLayouts() + }; + + std::vector destFdNs; + destFdNs.reserve(this->destinationImages.size()); + for (size_t i = 0; i < this->destinationImages.size(); i++) + destFdNs.push_back(backend::DmaBufFD{ + .fd = destinationFds.at(i), + .modifier = this->destinationImages.at(i).drmModifier(), + .layouts = this->destinationImages.at(i).drmLayouts() + }); + this->ctx = ls::owned_ptr>( new ls::R(backend.openContext( - { sourceFds.at(0), sourceFds.at(1) }, destinationFds, + { fd0, fd1 }, destFdNs, extent.width, extent.height, hdr, 1.0F / this->profile.flow_scale, this->profile.performance_mode )), diff --git a/lsfg-vk-layer/src/swapchain.hpp b/lsfg-vk-layer/src/swapchain.hpp index 0957c9c..fda82de 100644 --- a/lsfg-vk-layer/src/swapchain.hpp +++ b/lsfg-vk-layer/src/swapchain.hpp @@ -7,9 +7,8 @@ #include "lsfg-vk-common/helpers/pointers.hpp" #include "lsfg-vk-common/vulkan/command_buffer.hpp" #include "lsfg-vk-common/vulkan/fence.hpp" -#include "lsfg-vk-common/vulkan/image.hpp" #include "lsfg-vk-common/vulkan/semaphore.hpp" -#include "lsfg-vk-common/vulkan/timeline_semaphore.hpp" +#include "lsfg-vk-common/vulkan/shared_image.hpp" #include "lsfg-vk-common/vulkan/vulkan.hpp" #include @@ -59,8 +58,8 @@ namespace lsfgvk::layer { void* next_chain, uint32_t imageIdx, const std::vector& semaphores); private: - std::vector sourceImages; - std::vector destinationImages; + std::vector sourceImages; + std::vector destinationImages; ls::lazy syncSemaphore; ls::lazy renderCommandBuffer;