mirror of
https://github.com/PancakeTAS/lsfg-vk.git
synced 2026-05-10 19:21:42 +00:00
feat(bindless): Build descriptor set & compute pipelines for pipelines
This commit is contained in:
parent
f3d7f1fcea
commit
8ba32ddca6
4 changed files with 490 additions and 0 deletions
|
|
@ -13,8 +13,10 @@
|
|||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <ios>
|
||||
#include <memory>
|
||||
#include <numeric>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
|
@ -352,5 +354,183 @@ Pipeline::Pipeline(
|
|||
<< allocation.segments.size() << " segments")
|
||||
}
|
||||
|
||||
// Create image views
|
||||
for (auto& image : this->m_images) {
|
||||
const bool hasHdrVariant{image.signature.flags & ImageFlag::HdrVariant};
|
||||
const bool isLayered{image.subimages.size() == 1 && image.signature.count > 1};
|
||||
|
||||
for (auto& subimage : image.subimages) {
|
||||
subimage.view = vkhelper::createImageView(
|
||||
dld,
|
||||
device,
|
||||
*subimage.image,
|
||||
static_cast<vk::Format>((hasHdrVariant && hdr)
|
||||
? image.signature.hdrFormat : image.signature.format),
|
||||
isLayered ? image.signature.count : 1
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Create the descriptor set & required resources
|
||||
auto [pool, set] = vkhelper::createDescriptorSet(
|
||||
dld,
|
||||
device,
|
||||
*this->m_layout.layout,
|
||||
3, 1, sampledImageCount, storageImageCount
|
||||
);
|
||||
this->m_descriptorSet.pool = std::move(pool);
|
||||
this->m_descriptorSet.set = set;
|
||||
|
||||
const UniformBuffer buf{
|
||||
.advancedColorKind = hdr ? 2U : 0U,
|
||||
.hdrSupport = hdr ? 1U : 0U,
|
||||
.resolutionInvScale = 1.0F / flow,
|
||||
.uiThreshold = 0.5F
|
||||
};
|
||||
this->m_descriptorSet.buffer = vkhelper::createBuffer(
|
||||
dld,
|
||||
device,
|
||||
physdev,
|
||||
buf
|
||||
);
|
||||
auto* mapped{static_cast<UniformBuffer*>(
|
||||
device.mapMemory(
|
||||
*this->m_descriptorSet.buffer.second,
|
||||
0,
|
||||
VK_WHOLE_SIZE,
|
||||
{},
|
||||
dld
|
||||
)
|
||||
)};
|
||||
this->m_descriptorSet.mappedBuffer = std::shared_ptr<UniformBuffer*>(
|
||||
new UniformBuffer*{mapped},
|
||||
[device, memory = *this->m_descriptorSet.buffer.second, dld](auto* ptr) {
|
||||
device.unmapMemory(memory, dld);
|
||||
delete ptr; // NOLINT (manual memory management)
|
||||
}
|
||||
);
|
||||
this->m_descriptorSet.samplers.at(0) = vkhelper::createSampler(
|
||||
dld,
|
||||
device,
|
||||
vk::SamplerAddressMode::eClampToBorder,
|
||||
vk::CompareOp::eNever,
|
||||
false
|
||||
);
|
||||
this->m_descriptorSet.samplers.at(1) = vkhelper::createSampler(
|
||||
dld,
|
||||
device,
|
||||
vk::SamplerAddressMode::eClampToBorder,
|
||||
vk::CompareOp::eNever,
|
||||
true
|
||||
);
|
||||
this->m_descriptorSet.samplers.at(2) = vkhelper::createSampler(
|
||||
dld,
|
||||
device,
|
||||
vk::SamplerAddressMode::eClampToEdge,
|
||||
vk::CompareOp::eAlways,
|
||||
false
|
||||
);
|
||||
|
||||
// Update descriptor set bindings
|
||||
std::vector<vk::WriteDescriptorSet> writeInfos(4 + signature.descriptors.size());
|
||||
bindingIdx = 0;
|
||||
|
||||
std::array<vk::DescriptorBufferInfo, 1> bufferInfos;
|
||||
bufferInfos.at(0) = {
|
||||
.buffer = *this->m_descriptorSet.buffer.first,
|
||||
.range = VK_WHOLE_SIZE
|
||||
};
|
||||
writeInfos.at(0) = {
|
||||
.dstSet = this->m_descriptorSet.set,
|
||||
.dstBinding = bindingIdx++,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = vk::DescriptorType::eUniformBuffer,
|
||||
.pBufferInfo = bufferInfos.data()
|
||||
};
|
||||
|
||||
std::array<vk::DescriptorImageInfo, 3> samplerInfos;
|
||||
for (uint32_t i = 0; i < 3; i++) {
|
||||
auto& writeInfo{writeInfos.at(bindingIdx)};
|
||||
|
||||
samplerInfos.at(i) = {
|
||||
.sampler = *this->m_descriptorSet.samplers.at(i)
|
||||
};
|
||||
writeInfo = {
|
||||
.dstSet = this->m_descriptorSet.set,
|
||||
.dstBinding = bindingIdx++,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = vk::DescriptorType::eSampler,
|
||||
.pImageInfo = &samplerInfos.at(i)
|
||||
};
|
||||
}
|
||||
|
||||
std::vector<std::vector<vk::DescriptorImageInfo>> imageInfos2D(signature.descriptors.size());
|
||||
for (const auto& binding : signature.descriptors) {
|
||||
auto& writeInfo{writeInfos.at(bindingIdx)};
|
||||
|
||||
auto& imageInfos{imageInfos2D.at(bindingIdx - 4)};
|
||||
imageInfos.reserve(binding.resources.size());
|
||||
|
||||
for (const auto& resourceIdx : binding.resources) {
|
||||
const auto& image{this->m_images.at(resourceIdx)};
|
||||
|
||||
for (const auto& subimage : image.subimages) {
|
||||
imageInfos.push_back({
|
||||
.imageView = *subimage.view,
|
||||
.imageLayout = vk::ImageLayout::eGeneral
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
writeInfo = {
|
||||
.dstSet = this->m_descriptorSet.set,
|
||||
.dstBinding = bindingIdx++,
|
||||
.descriptorCount = static_cast<uint32_t>(imageInfos.size()),
|
||||
.descriptorType = binding.type == BindingType::StorageImage ?
|
||||
vk::DescriptorType::eStorageImage : vk::DescriptorType::eSampledImage,
|
||||
.pImageInfo = imageInfos.data()
|
||||
};
|
||||
}
|
||||
|
||||
device.updateDescriptorSets(writeInfos, {}, dld);
|
||||
|
||||
LOG_DEBUG(" Updated descriptor set with " << writeInfos.size() << " bindings")
|
||||
|
||||
// Build all shader pipelines
|
||||
std::vector<vk::ComputePipelineCreateInfo> pipelineCreateInfos;
|
||||
for (const auto& [name, variant] : signature.shaders) {
|
||||
std::string name2{name};
|
||||
if (variant) name2 += hdr ? "_16bit" : "_8bit";
|
||||
|
||||
const auto& module{library.shader(name2, perf)};
|
||||
|
||||
pipelineCreateInfos.push_back({
|
||||
.stage = {
|
||||
.stage = vk::ShaderStageFlagBits::eCompute,
|
||||
.module = *module,
|
||||
.pName = "main"
|
||||
},
|
||||
.layout = *this->m_layout.pipelineLayout
|
||||
});
|
||||
}
|
||||
|
||||
this->m_cache = vkhelper::createPipelineCache(dld, device);
|
||||
std::vector<vk::UniquePipeline> pipelines{
|
||||
device.createComputePipelinesUnique(
|
||||
*this->m_cache,
|
||||
pipelineCreateInfos,
|
||||
nullptr,
|
||||
dld
|
||||
).value
|
||||
};
|
||||
|
||||
this->m_pipelines.reserve(signature.shaders.size());
|
||||
for (size_t i = 0; i < signature.shaders.size(); i++) {
|
||||
const auto& name{signature.shaders.at(i).first};
|
||||
this->m_pipelines.emplace(name, std::move(pipelines.at(i)));
|
||||
}
|
||||
|
||||
LOG_DEBUG(" Created " << this->m_pipelines.size() << " pipelines")
|
||||
|
||||
LOG_DEBUG("Finished building pipeline")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,10 @@
|
|||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace lsfgvk::pipeline {
|
||||
|
|
@ -97,6 +100,15 @@ namespace lsfgvk::pipeline {
|
|||
return this->m_externalOutputs;
|
||||
}
|
||||
|
||||
///
|
||||
/// Get the mapped uniform buffer
|
||||
///
|
||||
/// @return Mapped uniform buffer
|
||||
///
|
||||
[[nodiscard]] auto* getMappedBuffer() const {
|
||||
return *this->m_descriptorSet.mappedBuffer.get();
|
||||
}
|
||||
|
||||
private:
|
||||
/// Vulkan descriptor set & pipeline layout
|
||||
struct Layout {
|
||||
|
|
@ -145,6 +157,19 @@ namespace lsfgvk::pipeline {
|
|||
};
|
||||
std::array<AllocationInfo, 2> m_allocations;
|
||||
std::unordered_map<size_t, vk::UniqueDeviceMemory> m_externalAllocations;
|
||||
|
||||
/// Vulkan descriptor set
|
||||
struct DescriptorSet {
|
||||
vk::UniqueDescriptorPool pool;
|
||||
vk::DescriptorSet set; // Can not be freed
|
||||
std::pair<vk::UniqueBuffer, vk::UniqueDeviceMemory> buffer;
|
||||
std::shared_ptr<UniformBuffer*> mappedBuffer;
|
||||
std::array<vk::UniqueSampler, 3> samplers;
|
||||
};
|
||||
DescriptorSet m_descriptorSet;
|
||||
|
||||
vk::UniquePipelineCache m_cache;
|
||||
std::unordered_map<std::string_view, vk::UniquePipeline> m_pipelines;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,11 +2,14 @@
|
|||
|
||||
#include "vkhelper.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <bitset>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <ios>
|
||||
#include <iostream>
|
||||
|
|
@ -176,6 +179,45 @@ vk::UniqueShaderModule vkhelper::createShaderModule(
|
|||
return device.createShaderModuleUnique(shaderInfo, nullptr, dld);
|
||||
}
|
||||
|
||||
vk::UniquePipelineCache vkhelper::createPipelineCache(
|
||||
const vk::detail::DispatchLoaderDynamic& dld,
|
||||
const vk::Device& device
|
||||
) {
|
||||
// Find the cache file path
|
||||
std::filesystem::path path{"/tmp/lsfg-vk_pipeline_cache.bin"};
|
||||
|
||||
const char* xdgCacheHome{std::getenv("XDG_CACHE_HOME")};
|
||||
if (xdgCacheHome && *xdgCacheHome != '\0')
|
||||
path = std::filesystem::path(xdgCacheHome) / "lsfg-vk_pipeline_cache.bin";
|
||||
|
||||
const char* home{std::getenv("HOME")};
|
||||
if (home && *home != '\0')
|
||||
path = std::filesystem::path(home) / ".cache" / "lsfg-vk_pipeline_cache.bin";
|
||||
|
||||
// Read cache data (if any)
|
||||
std::vector<uint8_t> cacheData{};
|
||||
|
||||
if (std::filesystem::exists(path)) {
|
||||
std::ifstream file(path, std::ios::binary | std::ios::ate);
|
||||
if (!file.is_open())
|
||||
throw std::runtime_error("Unable to open pipeline cache file for reading");
|
||||
|
||||
const std::streamsize size{static_cast<std::streamsize>(file.tellg())};
|
||||
cacheData = std::vector<uint8_t>(static_cast<size_t>(size));
|
||||
|
||||
file.seekg(0, std::ios::beg);
|
||||
if (!file.read(reinterpret_cast<char*>(cacheData.data()), size)) // NOLINT (unsafe cast)
|
||||
throw std::runtime_error("Unable to read pipeline cache file");
|
||||
}
|
||||
|
||||
// Build pipeline cache
|
||||
const vk::PipelineCacheCreateInfo pipelineCacheInfo{
|
||||
.initialDataSize = cacheData.size(),
|
||||
.pInitialData = cacheData.data()
|
||||
};
|
||||
return device.createPipelineCacheUnique(pipelineCacheInfo, nullptr, dld);
|
||||
}
|
||||
|
||||
std::pair<vk::UniqueDescriptorSetLayout, vk::UniquePipelineLayout> vkhelper::createLayout(
|
||||
const vk::detail::DispatchLoaderDynamic& dld,
|
||||
const vk::Device& device,
|
||||
|
|
@ -230,6 +272,76 @@ vk::UniqueImage vkhelper::createImage(
|
|||
return device.createImageUnique(imageInfo, nullptr, dld);
|
||||
}
|
||||
|
||||
vk::UniqueSampler vkhelper::createSampler(
|
||||
const vk::detail::DispatchLoaderDynamic& dld,
|
||||
const vk::Device& device,
|
||||
vk::SamplerAddressMode mode,
|
||||
vk::CompareOp compare,
|
||||
bool white
|
||||
) {
|
||||
const vk::SamplerCreateInfo samplerInfo{
|
||||
.magFilter = vk::Filter::eLinear,
|
||||
.minFilter = vk::Filter::eLinear,
|
||||
.mipmapMode = vk::SamplerMipmapMode::eLinear,
|
||||
.addressModeU = mode,
|
||||
.addressModeV = mode,
|
||||
.addressModeW = mode,
|
||||
.compareOp = compare,
|
||||
.maxLod = vk::LodClampNone,
|
||||
.borderColor = white ?
|
||||
vk::BorderColor::eFloatOpaqueWhite : vk::BorderColor::eFloatTransparentBlack
|
||||
};
|
||||
return device.createSamplerUnique(samplerInfo, nullptr, dld);
|
||||
}
|
||||
|
||||
std::pair<vk::UniqueBuffer, vk::UniqueDeviceMemory> vkhelper::createBuffer(
|
||||
const vk::detail::DispatchLoaderDynamic& dld,
|
||||
const vk::Device& device,
|
||||
const vk::PhysicalDevice& physdev,
|
||||
vk::BufferUsageFlags usage,
|
||||
const void* data,
|
||||
size_t size
|
||||
) {
|
||||
// Create buffer
|
||||
const vk::BufferCreateInfo bufferInfo{
|
||||
.size = size,
|
||||
.usage = usage,
|
||||
.sharingMode = vk::SharingMode::eExclusive
|
||||
};
|
||||
auto buffer{device.createBufferUnique(bufferInfo, nullptr, dld)};
|
||||
|
||||
// Allocate memory
|
||||
const auto requirements{device.getBufferMemoryRequirements(*buffer, dld)};
|
||||
|
||||
auto memory{vkhelper::allocateMemory(
|
||||
dld,
|
||||
device,
|
||||
physdev,
|
||||
requirements.size,
|
||||
requirements.memoryTypeBits,
|
||||
true
|
||||
)};
|
||||
|
||||
// Bind memory
|
||||
device.bindBufferMemory(*buffer, *memory, 0, dld);
|
||||
|
||||
// Copy data
|
||||
if (data) {
|
||||
void* mapped{device.mapMemory(*memory, 0, size, {}, dld)};
|
||||
std::copy_n(
|
||||
reinterpret_cast<const uint8_t*>(data), // NOLINT (unsafe cast)
|
||||
size,
|
||||
reinterpret_cast<uint8_t*>(mapped) // NOLINT (unsafe cast)
|
||||
);
|
||||
device.unmapMemory(*memory, dld);
|
||||
}
|
||||
|
||||
return {
|
||||
std::move(buffer),
|
||||
std::move(memory)
|
||||
};
|
||||
}
|
||||
|
||||
/* Memory allocations */
|
||||
|
||||
vk::UniqueDeviceMemory vkhelper::allocateMemory(
|
||||
|
|
@ -275,6 +387,64 @@ vk::UniqueDeviceMemory vkhelper::allocateMemory(
|
|||
return device.allocateMemoryUnique(allocInfo, nullptr, dld);
|
||||
}
|
||||
|
||||
/* Descriptors */
|
||||
|
||||
std::pair<vk::UniqueDescriptorPool, vk::DescriptorSet> vkhelper::createDescriptorSet(
|
||||
const vk::detail::DispatchLoaderDynamic& dld,
|
||||
const vk::Device& device,
|
||||
const vk::DescriptorSetLayout& layout,
|
||||
uint32_t samplers, uint32_t buffers,
|
||||
uint32_t sampledImages, uint32_t storageImages
|
||||
) {
|
||||
const std::array<vk::DescriptorPoolSize, 4> poolSizes{{
|
||||
{ .type = vk::DescriptorType::eSampler,
|
||||
.descriptorCount = samplers },
|
||||
{ .type = vk::DescriptorType::eSampledImage,
|
||||
.descriptorCount = sampledImages },
|
||||
{ .type = vk::DescriptorType::eStorageImage,
|
||||
.descriptorCount = storageImages },
|
||||
{ .type = vk::DescriptorType::eUniformBuffer,
|
||||
.descriptorCount = buffers }
|
||||
}};
|
||||
auto pool{device.createDescriptorPoolUnique({
|
||||
.flags = vk::DescriptorPoolCreateFlagBits::eUpdateAfterBind,
|
||||
.maxSets = 1,
|
||||
.poolSizeCount = static_cast<uint32_t>(poolSizes.size()),
|
||||
.pPoolSizes = poolSizes.data()
|
||||
}, nullptr, dld)};
|
||||
|
||||
auto set{device.allocateDescriptorSets({
|
||||
.descriptorPool = *pool,
|
||||
.descriptorSetCount = 1,
|
||||
.pSetLayouts = &layout
|
||||
}, dld).at(0)};
|
||||
|
||||
return{
|
||||
std::move(pool),
|
||||
set
|
||||
};
|
||||
}
|
||||
|
||||
vk::UniqueImageView vkhelper::createImageView(
|
||||
const vk::detail::DispatchLoaderDynamic& dld,
|
||||
const vk::Device& device,
|
||||
const vk::Image& image,
|
||||
vk::Format format,
|
||||
uint32_t layers
|
||||
) {
|
||||
const vk::ImageViewCreateInfo viewInfo{
|
||||
.image = image,
|
||||
.viewType = layers == 1 ? vk::ImageViewType::e2D : vk::ImageViewType::e2DArray,
|
||||
.format = format,
|
||||
.subresourceRange = {
|
||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||
.levelCount = 1,
|
||||
.layerCount = layers
|
||||
}
|
||||
};
|
||||
return device.createImageViewUnique(viewInfo, nullptr, dld);
|
||||
}
|
||||
|
||||
/* External memory */
|
||||
|
||||
std::pair<vk::UniqueImage, vk::UniqueDeviceMemory> vkhelper::createExternalImage(
|
||||
|
|
|
|||
|
|
@ -119,6 +119,21 @@ namespace vkhelper {
|
|||
const std::span<const uint32_t>& code
|
||||
);
|
||||
|
||||
///
|
||||
/// Create and maintain the Vulkan pipeline cache for lsfg-vk
|
||||
///
|
||||
/// @param dld Dynamic dispatch loader
|
||||
/// @param device Vulkan device
|
||||
/// @return RAII-wrapped Vulkan pipeline cache
|
||||
/// @throws std::runtime_error on failure
|
||||
///
|
||||
vk::UniquePipelineCache createPipelineCache(
|
||||
const vk::detail::DispatchLoaderDynamic& dld,
|
||||
const vk::Device& device
|
||||
);
|
||||
|
||||
// TODO: Persist pipeline cache
|
||||
|
||||
///
|
||||
/// Create a Vulkan descriptor set layout
|
||||
///
|
||||
|
|
@ -159,6 +174,62 @@ namespace vkhelper {
|
|||
vk::ImageUsageFlags usage
|
||||
);
|
||||
|
||||
///
|
||||
/// Create a Vulkan sampler for lsfg-vk
|
||||
///
|
||||
/// @param dld Dynamic dispatch loader
|
||||
/// @param device Vulkan device
|
||||
/// @param mode Address mode
|
||||
/// @param compare Comparison mode
|
||||
/// @param white Black/White border color
|
||||
/// @return RAII-wrapped Vulkan sampler
|
||||
/// @throws std::runtime_error on failure
|
||||
///
|
||||
vk::UniqueSampler createSampler(
|
||||
const vk::detail::DispatchLoaderDynamic& dld,
|
||||
const vk::Device& device,
|
||||
vk::SamplerAddressMode mode,
|
||||
vk::CompareOp compare,
|
||||
bool white
|
||||
);
|
||||
|
||||
// (forward decl)
|
||||
std::pair<vk::UniqueBuffer, vk::UniqueDeviceMemory> createBuffer(
|
||||
const vk::detail::DispatchLoaderDynamic& dld,
|
||||
const vk::Device& device,
|
||||
const vk::PhysicalDevice& physdev,
|
||||
vk::BufferUsageFlags usage,
|
||||
const void* data,
|
||||
size_t size
|
||||
);
|
||||
|
||||
///
|
||||
/// Create a Vulkan buffer for lsfg-vk
|
||||
///
|
||||
/// @param dld Dynamic dispatch loader
|
||||
/// @param device Vulkan device
|
||||
/// @param physdev Physical device
|
||||
/// @param data Buffer contained data
|
||||
/// @return RAII-wrapped Vulkan uniform buffer & device memory
|
||||
/// @throws std::runtime_error on failure
|
||||
///
|
||||
template<typename T>
|
||||
std::pair<vk::UniqueBuffer, vk::UniqueDeviceMemory> createBuffer(
|
||||
const vk::detail::DispatchLoaderDynamic& dld,
|
||||
const vk::Device& device,
|
||||
const vk::PhysicalDevice& physdev,
|
||||
const T& data
|
||||
) {
|
||||
return createBuffer(
|
||||
dld,
|
||||
device,
|
||||
physdev,
|
||||
vk::BufferUsageFlagBits::eUniformBuffer | vk::BufferUsageFlagBits::eTransferDst,
|
||||
static_cast<const void*>(&data),
|
||||
sizeof(T)
|
||||
);
|
||||
}
|
||||
|
||||
/* Memory allocations */
|
||||
|
||||
///
|
||||
|
|
@ -193,6 +264,50 @@ namespace vkhelper {
|
|||
return (size + align - 1) & ~(align - 1);
|
||||
}
|
||||
|
||||
/* Descriptors */
|
||||
|
||||
///
|
||||
/// Create a Vulkan descriptor set for lsfg-vk
|
||||
///
|
||||
/// @param dld Dynamic dispatch loader
|
||||
/// @param device Vulkan device
|
||||
/// @param layout Descriptor set layout
|
||||
/// @param samplers Amount of samplers
|
||||
/// @param buffers Amount of buffers
|
||||
/// @param sampledImages Amount of sampled images
|
||||
/// @param storageImages Amount of storage images
|
||||
/// @return Vulkan descriptor pool & set
|
||||
/// @throws std::runtime_error on failure
|
||||
///
|
||||
std::pair<vk::UniqueDescriptorPool, vk::DescriptorSet> createDescriptorSet(
|
||||
const vk::detail::DispatchLoaderDynamic& dld,
|
||||
const vk::Device& device,
|
||||
const vk::DescriptorSetLayout& layout,
|
||||
uint32_t samplers, uint32_t buffers,
|
||||
uint32_t sampledImages, uint32_t storageImages
|
||||
);
|
||||
|
||||
///
|
||||
/// Create an image view
|
||||
///
|
||||
/// @param dld Dynamic dispatch loader
|
||||
/// @param device Vulkan device
|
||||
/// @param image Vulkan image
|
||||
/// @param format Image format
|
||||
/// @param layers Amount of layers in image
|
||||
/// @return RAII-wrapped Vulkan image view
|
||||
/// @throws std::runtime_error on failure
|
||||
///
|
||||
vk::UniqueImageView createImageView(
|
||||
const vk::detail::DispatchLoaderDynamic& dld,
|
||||
const vk::Device& device,
|
||||
const vk::Image& image,
|
||||
vk::Format format,
|
||||
uint32_t layers
|
||||
);
|
||||
|
||||
/* External memory */
|
||||
|
||||
///
|
||||
/// Create a Vulkan image with a fd-exportable dedicated allocation
|
||||
///
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue