test(dualgpu): Create separate image for DMA-BUFs

This, again, is a temporary change just for messing around
This commit is contained in:
PancakeTAS 2026-02-10 20:54:29 +01:00
parent d9fcbcd10e
commit 7e07c4ba3a
18 changed files with 679 additions and 36 deletions

View file

@ -52,6 +52,13 @@ namespace lsfgvk::backend {
const std::optional<std::string>& 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<std::pair<uint64_t, uint64_t>> 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<int, int> sourceFds,
const std::vector<int>& destFds,
const std::pair<DmaBufFD, DmaBufFD>& sourceFds,
const std::vector<DmaBufFD>& destFds,
uint32_t width, uint32_t height,
bool hdr, float flow, bool perf
);

View file

@ -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 <cstddef>
@ -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<vk::SharedImage>& 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<vk::SharedImage>& 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<vk::Barrier> 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)
};

View file

@ -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 <cstddef>
#include <vector>
@ -60,6 +61,16 @@ namespace lsfgvk::backend {
[[nodiscard]] ManagedShaderBuilder& sampleds(const std::vector<vk::Image>& 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<vk::SharedImage>& 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<vk::Image>& 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<vk::SharedImage>& 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<ls::R<const vk::Image>> sampledImages;
std::vector<ls::R<const vk::SharedImage>> sampledImagesSh; // always goes first
std::vector<ls::R<const vk::Image>> storageImages;
std::vector<ls::R<const vk::SharedImage>> storageImagesSh;
std::vector<ls::R<const vk::Sampler>> imageSamplers;
std::vector<ls::R<const vk::Buffer>> constantBuffers;
};

View file

@ -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<int, int> sourceFds, const std::vector<int>& destFds,
const std::pair<DmaBufFD, DmaBufFD>& sourceFds,
const std::vector<DmaBufFD>& destFds,
VkExtent2D extent, bool hdr, float flow, bool perf);
/// schedule frames
/// (see lsfg-vk documentation)
void scheduleFrames(int waitFd, std::vector<int>& syncFds);
private:
std::pair<vk::Image, vk::Image> sourceImages;
std::vector<vk::Image> destImages;
std::pair<vk::SharedImage, vk::SharedImage> sourceImages;
std::vector<vk::SharedImage> destImages;
vk::Image blackImage;
ls::lazy<vk::Semaphore> syncSemaphore; // imported
@ -276,7 +278,9 @@ InstanceImpl::InstanceImpl(vk::PhysicalDeviceSelector selectPhysicalDevice,
vk.persistPipelineCache(); // will silently fail
}
Context& Instance::openContext(std::pair<int, int> sourceFds, const std::vector<int>& destFds,
Context& Instance::openContext(
const std::pair<DmaBufFD, DmaBufFD>& sourceFds,
const std::vector<DmaBufFD>& 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<int, int> sourceFds, const std::vector<
namespace {
/// import source images
std::pair<vk::Image, vk::Image> importImages(const vk::Vulkan& vk,
const std::pair<int, int>& sourceFds,
std::pair<vk::SharedImage, vk::SharedImage> importImages(
const vk::Vulkan& vk,
const std::pair<DmaBufFD, DmaBufFD>& 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<vk::Image> importImages(const vk::Vulkan& vk,
const std::vector<int>& destFds,
std::vector<vk::SharedImage> importImages(const vk::Vulkan& vk,
const std::vector<DmaBufFD>& destFds,
VkExtent2D extent, VkFormat format) {
try {
std::vector<vk::Image> destImages;
std::vector<vk::SharedImage> 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<int, int> sourceFds, const std::vector<int>& destFds,
const std::pair<DmaBufFD, DmaBufFD>& sourceFds,
const std::vector<DmaBufFD>& destFds,
VkExtent2D extent, bool hdr, float flow, bool perf) :
sourceImages(importImages(instance.getVulkan(), sourceFds,
extent, hdr ? VK_FORMAT_R16G16B16A16_SFLOAT : VK_FORMAT_R8G8B8A8_UNORM)),

View file

@ -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 <cstddef>
@ -16,11 +17,11 @@
using namespace lsfgvk::backend;
Generate::Generate(const Ctx& ctx, size_t idx,
const std::pair<vk::Image, vk::Image>& sourceImages,
const std::pair<vk::SharedImage, vk::SharedImage>& 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;

View file

@ -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 <cstddef>
@ -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<vk::Image, vk::Image>& sourceImages,
const std::pair<vk::SharedImage, vk::SharedImage>& 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

View file

@ -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 <cstddef>
@ -17,7 +18,7 @@
using namespace lsfgvk::backend;
Mipmaps::Mipmaps(const Ctx& ctx,
const std::pair<vk::Image, vk::Image>& sourceImages) {
const std::pair<vk::SharedImage, vk::SharedImage>& sourceImages) {
// create output images for base and 6 mips
this->images.reserve(7);
for (uint32_t i = 0; i < 7; i++)

View file

@ -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 <cstddef>
@ -23,7 +24,7 @@ namespace lsfgvk::backend {
/// @param ctx context
/// @param sourceImages pair of source images
Mipmaps(const Ctx& ctx,
const std::pair<vk::Image, vk::Image>& sourceImages);
const std::pair<vk::SharedImage, vk::SharedImage>& sourceImages);
/// prepare the shaderchain initially
/// @param images vector to fill with image handles

View file

@ -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")

View file

@ -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<ls::R<const vk::Image>>& sampledImages,
const std::vector<ls::R<const vk::SharedImage>>& sampledImagesSh,
const std::vector<ls::R<const vk::Image>>& storageImages,
const std::vector<ls::R<const vk::SharedImage>>& storageImagesSh,
const std::vector<ls::R<const vk::Sampler>>& samplers,
const std::vector<ls::R<const vk::Buffer>>& buffers);

View file

@ -0,0 +1,76 @@
/* SPDX-License-Identifier: GPL-3.0-or-later */
#pragma once
#include "../helpers/pointers.hpp"
#include "vulkan.hpp"
#include <utility>
#include <vector>
#include <vulkan/vulkan_core.h>
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<uint64_t>& 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<std::pair<uint64_t, uint64_t>>& 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<VkImage> image;
ls::owned_ptr<VkDeviceMemory> memory;
ls::owned_ptr<VkImageView> view;
VkExtent2D extent{};
uint64_t modifier{};
std::vector<std::pair<uint64_t, uint64_t>> layouts;
};
}

View file

@ -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

View file

@ -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 <cstddef>
@ -48,7 +49,9 @@ namespace {
DescriptorSet::DescriptorSet(const vk::Vulkan& vk,
const vk::DescriptorPool& pool, const vk::Shader& shader,
const std::vector<ls::R<const vk::Image>>& sampledImages,
const std::vector<ls::R<const vk::SharedImage>>& sampledImagesSh,
const std::vector<ls::R<const vk::Image>>& storageImages,
const std::vector<ls::R<const vk::SharedImage>>& storageImagesSh,
const std::vector<ls::R<const vk::Sampler>>& samplers,
const std::vector<ls::R<const vk::Buffer>>& 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<VkWriteDescriptorSet> 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<uint32_t>(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<uint32_t>(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<uint32_t>(entries.size()), entries.data(), 0, VK_NULL_HANDLE);
}

View file

@ -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 <bitset>
#include <cstddef>
#include <cstdint>
#include <optional>
#include <utility>
#include <vector>
#include <vulkan/vulkan_core.h>
using namespace vk;
namespace {
/* EXPORTABLE - DMA-BUF */
/// create an image that can be imported from a dma-buf fd
ls::owned_ptr<VkImage> createDMAImportableImage(const vk::Vulkan& vk,
VkExtent2D extent, VkFormat format, VkImageUsageFlags usage,
uint64_t drmModifier,
const std::vector<std::pair<uint64_t, uint64_t>>& drmLayouts) {
VkImage handle{};
// create VkImage
std::vector<VkSubresourceLayout> 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<VkImage>(
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<VkDeviceMemory> 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<VkDeviceMemory>(
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<VkImage> createDMAExportableImage(const vk::Vulkan& vk,
VkExtent2D extent, VkFormat format, VkImageUsageFlags usage,
const std::vector<uint64_t>& drmModifiers) {
VkImage handle{};
// create VkImage
const VkImageDrmFormatModifierListCreateInfoEXT explicitModifierInfo{
.sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_LIST_CREATE_INFO_EXT,
.drmFormatModifierCount = static_cast<uint32_t>(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<VkImage>(
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<VkDeviceMemory> 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<VkDeviceMemory>(
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<VkImageView> 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<VkImageView>(
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<std::pair<uint64_t, uint64_t>> getImageLayouts(const vk::Vulkan& vk,
VkImage image, uint64_t drmModifier) {
std::vector<std::pair<uint64_t, uint64_t>> 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<VkDrmFormatModifierProperties2EXT> formatProps(formats.drmFormatModifierCount);
formats.pDrmFormatModifierProperties = formatProps.data();
vk.fi().GetPhysicalDeviceProperties2(vk.physdev(), &props);
// find plane count for the modifier
std::optional<size_t> 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<VkImageAspectFlagBits> 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<uint64_t>& 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<std::pair<uint64_t, uint64_t>>& 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)) {
}

View file

@ -177,6 +177,8 @@ namespace {
};
const std::vector<const char*> 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<PFN_vkSignalSemaphoreKHR>(f, d, "vkSignalSemaphoreKHR"),
.WaitSemaphoresKHR = dpa<PFN_vkWaitSemaphoresKHR>(f, d, "vkWaitSemaphoresKHR"),
.GetMemoryFdKHR = dpa<PFN_vkGetMemoryFdKHR>(f, d, "vkGetMemoryFdKHR"),
.GetMemoryFdPropertiesKHR = dpa<PFN_vkGetMemoryFdPropertiesKHR>(f, d, "vkGetMemoryFdPropertiesKHR"),
.ImportSemaphoreFdKHR = dpa<PFN_vkImportSemaphoreFdKHR>(f, d, "vkImportSemaphoreFdKHR"),
.GetSemaphoreFdKHR = dpa<PFN_vkGetSemaphoreFdKHR>(f, d, "vkGetSemaphoreFdKHR"),
@ -391,7 +394,11 @@ VulkanDeviceFuncs vk::initVulkanDeviceFuncs(const VulkanInstanceFuncs& f, VkDevi
.QueuePresentKHR = graphical ?
dpa<PFN_vkQueuePresentKHR>(f, d, "vkQueuePresentKHR") : nullptr,
.DestroySwapchainKHR = graphical ?
dpa<PFN_vkDestroySwapchainKHR>(f, d, "vkDestroySwapchainKHR") : nullptr
dpa<PFN_vkDestroySwapchainKHR>(f, d, "vkDestroySwapchainKHR") : nullptr,
.GetImageDrmFormatModifierPropertiesEXT = dpa<PFN_vkGetImageDrmFormatModifierPropertiesEXT>(f, d,
"vkGetImageDrmFormatModifierPropertiesEXT"),
.GetImageSubresourceLayout = dpa<PFN_vkGetImageSubresourceLayout>(f, d, "vkGetImageSubresourceLayout")
};
}

View file

@ -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<uint32_t>(extensions.size());
createInfo.ppEnabledExtensionNames = extensions.data();
finish();
}

View file

@ -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 <algorithm>
#include <cstddef>
#include <cstdint>
#include <drm/drm_fourcc.h>
#include <exception>
#include <functional>
#include <optional>
@ -76,24 +76,46 @@ Swapchain::Swapchain(const vk::Vulkan& vk, backend::Instance& backend,
std::vector<int> sourceFds(2);
std::vector<int> destinationFds(this->profile.multiplier - 1);
const std::vector<uint64_t> 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<backend::DmaBufFD> 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<ls::R<backend::Context>>(
new ls::R<backend::Context>(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
)),

View file

@ -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 <cstdint>
@ -59,8 +58,8 @@ namespace lsfgvk::layer {
void* next_chain, uint32_t imageIdx,
const std::vector<VkSemaphore>& semaphores);
private:
std::vector<vk::Image> sourceImages;
std::vector<vk::Image> destinationImages;
std::vector<vk::SharedImage> sourceImages;
std::vector<vk::SharedImage> destinationImages;
ls::lazy<vk::Semaphore> syncSemaphore;
ls::lazy<vk::CommandBuffer> renderCommandBuffer;