mirror of
https://github.com/PancakeTAS/lsfg-vk.git
synced 2026-05-10 19:21:42 +00:00
feat(bindless): Build images for pipelines
This commit is contained in:
parent
8e7dd4e4aa
commit
310f53e373
4 changed files with 333 additions and 0 deletions
|
|
@ -8,7 +8,13 @@
|
|||
#include "utility/logger.hpp"
|
||||
#include "utility/vkhelper.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <ios>
|
||||
#include <stdexcept>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
|
|
@ -108,5 +114,119 @@ Pipeline::Pipeline(
|
|||
<< sampledImageCount << " sampled images, "
|
||||
<< storageImageCount << " storage images)")
|
||||
|
||||
// Create the Vulkan images
|
||||
size_t alignment{};
|
||||
uint32_t types{~0U};
|
||||
|
||||
const vk::Extent2D flowExtent{
|
||||
static_cast<uint32_t>(static_cast<float>(extent.width) * flow),
|
||||
static_cast<uint32_t>(static_cast<float>(extent.height) * flow)
|
||||
};
|
||||
for (const auto& imageSignature : signature.images) {
|
||||
const auto imageIdx{this->m_images.size()};
|
||||
auto& image{this->m_images.emplace_back()};
|
||||
image = {
|
||||
.signature = imageSignature
|
||||
};
|
||||
|
||||
const bool hasHdrVariant{image.signature.flags & ImageFlag::HdrVariant};
|
||||
const vk::Format format{
|
||||
(hasHdrVariant && hdr) ?
|
||||
static_cast<vk::Format>(image.signature.hdrFormat) :
|
||||
static_cast<vk::Format>(image.signature.format)
|
||||
};
|
||||
const vk::Extent2D baseExtent{apply(extent, flowExtent, image.signature.extentOp)};
|
||||
const vk::ImageUsageFlags usage{
|
||||
vk::ImageUsageFlagBits::eStorage | vk::ImageUsageFlagBits::eSampled
|
||||
};
|
||||
|
||||
const bool isMipmapped{image.signature.flags & ImageFlag::Mipmaps};
|
||||
for (uint32_t i = 0; i < image.signature.count; i++) {
|
||||
const vk::Extent2D imageExtent{
|
||||
.width = std::max(baseExtent.width >> i, 1U),
|
||||
.height = std::max(baseExtent.height >> i, 1U)
|
||||
};
|
||||
|
||||
if (image.signature.flags & (ImageFlag::ExternalInput | ImageFlag::ExternalOutput)) {
|
||||
const bool isInputOr{image.signature.flags & ImageFlag::ExternalInput};
|
||||
|
||||
auto [subimage, allocation] = vkhelper::createExternalImage(
|
||||
dld,
|
||||
device,
|
||||
physdev,
|
||||
imageExtent,
|
||||
format,
|
||||
image.signature.count,
|
||||
usage |
|
||||
(isInputOr ?
|
||||
vk::ImageUsageFlagBits::eTransferDst
|
||||
: vk::ImageUsageFlagBits::eTransferSrc)
|
||||
);
|
||||
|
||||
if (isInputOr) {
|
||||
this->m_externalInputs.push_back({
|
||||
.extent = imageExtent,
|
||||
.format = format,
|
||||
.layers = image.signature.count,
|
||||
.image = *subimage,
|
||||
.memory = *allocation
|
||||
});
|
||||
} else {
|
||||
this->m_externalOutputs.push_back({
|
||||
.extent = imageExtent,
|
||||
.format = format,
|
||||
.layers = image.signature.count,
|
||||
.image = *subimage,
|
||||
.memory = *allocation
|
||||
});
|
||||
}
|
||||
|
||||
LOG_DEBUG(" Allocated memory of size "
|
||||
<< [&]() {
|
||||
const auto& reqs{device.getImageMemoryRequirements(*subimage, dld)};
|
||||
return reqs.size;
|
||||
}() << " for external image " << imageIdx)
|
||||
|
||||
image.subimages.push_back({
|
||||
.image = std::move(subimage)
|
||||
});
|
||||
this->m_externalAllocations[imageIdx] = std::move(allocation);
|
||||
|
||||
break; // There can only be one image
|
||||
}
|
||||
|
||||
image.subimages.push_back({
|
||||
.image = vkhelper::createImage(
|
||||
dld,
|
||||
device,
|
||||
imageExtent,
|
||||
format,
|
||||
isMipmapped ? 1 : image.signature.count,
|
||||
usage
|
||||
)
|
||||
});
|
||||
|
||||
if (!isMipmapped) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& subimage : image.subimages) {
|
||||
subimage.memory = device.getImageMemoryRequirements(*subimage.image, dld);
|
||||
|
||||
if (image.signature.flags & (ImageFlag::ExternalInput | ImageFlag::ExternalOutput))
|
||||
break;
|
||||
|
||||
alignment = std::max(alignment, subimage.memory.alignment);
|
||||
types &= subimage.memory.memoryTypeBits;
|
||||
}
|
||||
}
|
||||
|
||||
if (types == 0)
|
||||
throw std::runtime_error("No compatible memory type found for pipeline images");
|
||||
|
||||
LOG_DEBUG(" Created " << this->m_images.size() << " images with common alignment "
|
||||
<< alignment << " and memory type bits " << std::hex << types << std::dec)
|
||||
|
||||
LOG_DEBUG("Finished building pipeline")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,14 +4,33 @@
|
|||
|
||||
#include "library.hpp"
|
||||
#include "pipeline/signature.hpp"
|
||||
#include "pipeline/signature/image.hpp"
|
||||
#include "utility/vkhelper.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace lsfgvk::pipeline {
|
||||
|
||||
// TODO: Improve API design
|
||||
|
||||
/// Handle to an external image
|
||||
struct ExternalImage {
|
||||
/// Image Extent
|
||||
vk::Extent2D extent;
|
||||
/// Image Format
|
||||
vk::Format format;
|
||||
/// Amount of layers in image
|
||||
uint32_t layers;
|
||||
|
||||
/// Handle to the Vulkan image (not owned)
|
||||
vk::Image image;
|
||||
/// Handle to the Vulkan memory (not owned)
|
||||
vk::DeviceMemory memory;
|
||||
};
|
||||
|
||||
/// Struct for the uniform buffer
|
||||
struct UniformBuffer {
|
||||
float timestamp;
|
||||
|
|
@ -63,6 +82,20 @@ namespace lsfgvk::pipeline {
|
|||
bool hdr
|
||||
);
|
||||
|
||||
///
|
||||
/// Get all external input images
|
||||
///
|
||||
/// @return List of images
|
||||
///
|
||||
[[nodiscard]] auto& getExternalInputs() const {
|
||||
return this->m_externalInputs;
|
||||
}
|
||||
|
||||
/// Get all external output images
|
||||
[[nodiscard]] auto& getExternalOutputs() const {
|
||||
return this->m_externalOutputs;
|
||||
}
|
||||
|
||||
private:
|
||||
/// Vulkan descriptor set & pipeline layout
|
||||
struct Layout {
|
||||
|
|
@ -70,6 +103,26 @@ namespace lsfgvk::pipeline {
|
|||
vk::UniquePipelineLayout pipelineLayout;
|
||||
};
|
||||
Layout m_layout;
|
||||
|
||||
/// Sub-image of a Vulkan image
|
||||
struct SubImage {
|
||||
vk::UniqueImage image;
|
||||
vk::MemoryRequirements memory;
|
||||
vk::UniqueImageView view;
|
||||
};
|
||||
|
||||
/// Vulkan image created from an ImageSignature
|
||||
struct Image {
|
||||
ImageSignature signature;
|
||||
std::vector<SubImage> subimages;
|
||||
vk::DeviceSize size{};
|
||||
};
|
||||
std::vector<Image> m_images;
|
||||
|
||||
std::vector<ExternalImage> m_externalInputs;
|
||||
std::vector<ExternalImage> m_externalOutputs;
|
||||
|
||||
std::unordered_map<size_t, vk::UniqueDeviceMemory> m_externalAllocations;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,12 +3,14 @@
|
|||
#include "vkhelper.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <bitset>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <iomanip>
|
||||
#include <ios>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
|
|
@ -201,3 +203,102 @@ std::pair<vk::UniqueDescriptorSetLayout, vk::UniquePipelineLayout> vkhelper::cre
|
|||
|
||||
return { std::move(descriptorSetLayout), std::move(pipelineLayout) };
|
||||
}
|
||||
|
||||
/* Resources */
|
||||
|
||||
vk::UniqueImage vkhelper::createImage(
|
||||
const vk::detail::DispatchLoaderDynamic& dld,
|
||||
const vk::Device& device,
|
||||
vk::Extent2D extent,
|
||||
vk::Format format,
|
||||
uint32_t layers,
|
||||
vk::ImageUsageFlags usage
|
||||
) {
|
||||
const vk::ImageCreateInfo imageInfo{
|
||||
.imageType = vk::ImageType::e2D,
|
||||
.format = format,
|
||||
.extent = {
|
||||
.width = extent.width,
|
||||
.height = extent.height,
|
||||
.depth = 1
|
||||
},
|
||||
.mipLevels = 1,
|
||||
.arrayLayers = layers,
|
||||
.samples = vk::SampleCountFlagBits::e1,
|
||||
.usage = usage
|
||||
};
|
||||
return device.createImageUnique(imageInfo, nullptr, dld);
|
||||
}
|
||||
|
||||
/* External memory */
|
||||
|
||||
std::pair<vk::UniqueImage, vk::UniqueDeviceMemory> vkhelper::createExternalImage(
|
||||
const vk::detail::DispatchLoaderDynamic& dld,
|
||||
const vk::Device& device,
|
||||
const vk::PhysicalDevice& physdev,
|
||||
vk::Extent2D extent,
|
||||
vk::Format format,
|
||||
uint32_t layers,
|
||||
vk::ImageUsageFlags usage
|
||||
) {
|
||||
const vk::ExternalMemoryImageCreateInfo externalInfo{
|
||||
.handleTypes = vk::ExternalMemoryHandleTypeFlagBits::eOpaqueFd
|
||||
};
|
||||
const vk::ImageCreateInfo imageInfo{
|
||||
.pNext = &externalInfo,
|
||||
.imageType = vk::ImageType::e2D,
|
||||
.format = format,
|
||||
.extent = {
|
||||
.width = extent.width,
|
||||
.height = extent.height,
|
||||
.depth = 1
|
||||
},
|
||||
.mipLevels = 1,
|
||||
.arrayLayers = layers,
|
||||
.samples = vk::SampleCountFlagBits::e1,
|
||||
.usage = usage
|
||||
};
|
||||
auto image{device.createImageUnique(imageInfo, nullptr, dld)};
|
||||
|
||||
// Find a suitable memory type index
|
||||
const auto memProps{physdev.getMemoryProperties2(dld)};
|
||||
const auto requirements{device.getImageMemoryRequirements(*image, dld)};
|
||||
|
||||
std::optional<uint32_t> selectedTypeIdx{};
|
||||
for (uint32_t i = 0; i < memProps.memoryProperties.memoryTypeCount; i++) {
|
||||
if (!std::bitset<32>(requirements.memoryTypeBits).test(i))
|
||||
continue;
|
||||
const auto& memType{memProps.memoryProperties.memoryTypes.at(i)};
|
||||
|
||||
if (memType.propertyFlags & vk::MemoryPropertyFlagBits::eDeviceLocal) {
|
||||
selectedTypeIdx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!selectedTypeIdx)
|
||||
throw std::runtime_error("No suitable memory type found for allocation");
|
||||
|
||||
// Allocate memory
|
||||
const vk::MemoryDedicatedAllocateInfo dedicatedInfo{
|
||||
.image = *image,
|
||||
};
|
||||
const vk::ExportMemoryAllocateInfo exportInfo{
|
||||
.pNext = &dedicatedInfo,
|
||||
.handleTypes = vk::ExternalMemoryHandleTypeFlagBits::eOpaqueFd
|
||||
};
|
||||
const vk::MemoryAllocateInfo allocInfo{
|
||||
.pNext = &exportInfo,
|
||||
.allocationSize = requirements.size,
|
||||
.memoryTypeIndex = *selectedTypeIdx
|
||||
};
|
||||
auto memory{device.allocateMemoryUnique(allocInfo, nullptr, dld)};
|
||||
|
||||
// Bind memory
|
||||
device.bindImageMemory(*image, *memory, 0, dld);
|
||||
|
||||
return{
|
||||
std::move(image),
|
||||
std::move(memory)
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -135,4 +135,63 @@ namespace vkhelper {
|
|||
size_t pushConstantSize
|
||||
);
|
||||
|
||||
/* Resources */
|
||||
|
||||
///
|
||||
/// Create a (unallocated) Vulkan image for lsfg-vk
|
||||
///
|
||||
/// @param dld Dynamic dispatch loader
|
||||
/// @param device Vulkan device
|
||||
/// @param extent Image extent
|
||||
/// @param format Image format
|
||||
/// @param layers Amount of images
|
||||
/// @param usage Image usage flags
|
||||
/// @return RAII-wrapped Vulkan image
|
||||
/// @throws std::runtime_error on failure
|
||||
///
|
||||
vk::UniqueImage createImage(
|
||||
const vk::detail::DispatchLoaderDynamic& dld,
|
||||
const vk::Device& device,
|
||||
vk::Extent2D extent,
|
||||
vk::Format format,
|
||||
uint32_t layers,
|
||||
vk::ImageUsageFlags usage
|
||||
);
|
||||
|
||||
///
|
||||
/// Align a memory allocation
|
||||
///
|
||||
/// @param size Memory size
|
||||
/// @param align Alignment
|
||||
/// @return Aligned memory size
|
||||
///
|
||||
inline vk::DeviceSize align(vk::DeviceSize size, vk::DeviceSize align) noexcept {
|
||||
return (size + align - 1) & ~(align - 1);
|
||||
}
|
||||
|
||||
/* External memory */
|
||||
|
||||
///
|
||||
/// Create a Vulkan image with a fd-exportable dedicated allocation
|
||||
///
|
||||
/// @param dld Dynamic dispatch loader
|
||||
/// @param device Vulkan device
|
||||
/// @param physdev Physical device
|
||||
/// @param extent Image extent
|
||||
/// @param format Image format
|
||||
/// @param layers Amount of images
|
||||
/// @param usage Image usage flags
|
||||
/// @return RAII-wrapped Vulkan image
|
||||
/// @throws std::runtime_error on failure
|
||||
///
|
||||
std::pair<vk::UniqueImage, vk::UniqueDeviceMemory> createExternalImage(
|
||||
const vk::detail::DispatchLoaderDynamic& dld,
|
||||
const vk::Device& device,
|
||||
const vk::PhysicalDevice& physdev,
|
||||
vk::Extent2D extent,
|
||||
vk::Format format,
|
||||
uint32_t layers,
|
||||
vk::ImageUsageFlags usage
|
||||
);
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue