DescriptorSetVk.h#
The DescriptorSet class, representing a “directory” of shader-accessible objects like Buffers and Images.
Includes#
PVRVk/BufferVk.h
PVRVk/DeviceVk.h
PVRVk/PipelineConfigVk.h
Included By#
Namespaces#
Classes#
Variables#
Source Code#
#pragma once
#include "PVRVk/DeviceVk.h"
#include "PVRVk/PipelineConfigVk.h"
#include "PVRVk/BufferVk.h"
namespace pvrvk {
const uint32_t descriptorTypeSize = 14; // Number elements in enum class DescriptorType
struct DescriptorSetLayoutCreateInfo
{
public:
struct DescriptorSetLayoutBinding
{
uint16_t binding;
pvrvk::DescriptorType descriptorType;
uint16_t descriptorCount;
pvrvk::ShaderStageFlags stageFlags;
Sampler immutableSampler;
DescriptorSetLayoutBinding() : descriptorCount(1), stageFlags(pvrvk::ShaderStageFlags::e_ALL), descriptorType(pvrvk::DescriptorType::e_MAX_ENUM) {}
DescriptorSetLayoutBinding(uint16_t bindIndex, pvrvk::DescriptorType descType, uint16_t descriptorCount = 1,
pvrvk::ShaderStageFlags stageFlags = pvrvk::ShaderStageFlags::e_ALL, const Sampler& immutableSampler = Sampler())
: binding(bindIndex), descriptorType(descType), descriptorCount(descriptorCount), stageFlags(stageFlags), immutableSampler(immutableSampler)
{}
bool operator==(const DescriptorSetLayoutBinding& rhs) const
{
return binding == rhs.binding && descriptorType == rhs.descriptorType && descriptorCount == rhs.descriptorCount && stageFlags == rhs.stageFlags;
}
bool operator!=(const DescriptorSetLayoutBinding& rhs) const { return !(*this == rhs); }
};
private:
std::vector<DescriptorSetLayoutBinding> descLayoutInfo;
public:
DescriptorSetLayoutCreateInfo() = default;
DescriptorSetLayoutCreateInfo(std::initializer_list<DescriptorSetLayoutBinding> bindingList)
{
for (auto& it : bindingList) { setBinding(it); }
}
DescriptorSetLayoutCreateInfo& setBinding(const DescriptorSetLayoutBinding& layoutBinding)
{
auto it =
std::find_if(descLayoutInfo.begin(), descLayoutInfo.end(), [&layoutBinding](const DescriptorSetLayoutBinding& info) { return info.binding == layoutBinding.binding; });
if (it != descLayoutInfo.end()) { (*it) = layoutBinding; }
else
{
descLayoutInfo.emplace_back(layoutBinding);
}
return *this;
}
DescriptorSetLayoutCreateInfo& setBinding(uint16_t binding, pvrvk::DescriptorType descriptorType, uint16_t descriptorCount = 1,
pvrvk::ShaderStageFlags stageFlags = pvrvk::ShaderStageFlags::e_ALL, Sampler immutableSampler = Sampler())
{
return setBinding(DescriptorSetLayoutBinding(binding, descriptorType, descriptorCount, stageFlags, immutableSampler));
}
DescriptorSetLayoutCreateInfo& clear()
{
descLayoutInfo.clear();
return *this;
}
uint16_t getNumBindings() const { return static_cast<uint16_t>(descLayoutInfo.size()); }
bool operator==(const DescriptorSetLayoutCreateInfo& rhs) const
{
if (getNumBindings() != rhs.getNumBindings()) { return false; }
for (uint32_t i = 0; i < getNumBindings(); ++i)
{
if (descLayoutInfo[i] != rhs.descLayoutInfo[i]) { return false; }
}
return true;
}
const DescriptorSetLayoutBinding* getBinding(uint16_t bindingId) const
{
auto it = std::find_if(descLayoutInfo.begin(), descLayoutInfo.end(), [&](const DescriptorSetLayoutBinding& info) { return info.binding == bindingId; });
if (it != descLayoutInfo.end()) { return &(*it); }
return nullptr;
}
const DescriptorSetLayoutBinding* getAllBindings() const { return descLayoutInfo.data(); }
private:
friend class ::pvrvk::impl::DescriptorSetLayout_;
friend struct ::pvrvk::WriteDescriptorSet;
};
namespace impl {
class DescriptorSetLayout_ : public PVRVkDeviceObjectBase<VkDescriptorSetLayout, ObjectType::e_DESCRIPTOR_SET_LAYOUT>, public DeviceObjectDebugUtils<DescriptorSetLayout_>
{
private:
friend class Device_;
class make_shared_enabler
{
protected:
make_shared_enabler() = default;
friend class DescriptorSetLayout_;
};
static DescriptorSetLayout constructShared(const DeviceWeakPtr& device, const DescriptorSetLayoutCreateInfo& createInfo)
{
return std::make_shared<DescriptorSetLayout_>(make_shared_enabler{}, device, createInfo);
}
DescriptorSetLayoutCreateInfo _createInfo;
public:
DECLARE_NO_COPY_SEMANTICS(DescriptorSetLayout_)
~DescriptorSetLayout_();
DescriptorSetLayout_(make_shared_enabler, const DeviceWeakPtr& device, const DescriptorSetLayoutCreateInfo& createInfo);
const DescriptorSetLayoutCreateInfo& getCreateInfo() const { return _createInfo; }
void clearCreateInfo() { _createInfo.clear(); }
};
template<typename T, uint32_t ArraySize>
class DescriptorStore
{
public:
DescriptorStore() : _ptr(_tArray), _numItems(0) {}
// Copy Constructor
DescriptorStore(const DescriptorStore& descStore) : _numItems(descStore._numItems), _tVector(descStore._tVector)
{
// Copy _tArray elements
std::copy(std::begin(descStore._tArray), std::end(descStore._tArray), _tArray);
// Point towards _tVector.data() if _tVector is being used else point towards _tArray
_ptr = !_tVector.empty() ? _tVector.data() : _tArray;
}
// Destructor
~DescriptorStore() = default;
// Add swap functionality
friend void swap(DescriptorStore& first, DescriptorStore& second)
{
using std::swap;
// IF second._ptr is pointing to the array then point to MY array
// If second._ptr is pointing to the vector then point to seconds vector
T* tempPtr1 = second._ptr == second._tArray ? first._tArray : second._tVector.data();
first._ptr = tempPtr1;
// IF first._ptr is pointing to the array then point to MY array
// If first._ptr is pointing to the vector then point to firsts vector
T* tempPtr2 = first._ptr == first._tArray ? second._tArray : first._tVector.data();
second._ptr = tempPtr2;
swap(first._numItems, second._numItems);
swap(first._tVector, second._tVector);
swap(first._tArray, second._tArray);
}
// Assignment Operator
DescriptorStore& operator=(DescriptorStore other)
{
swap(*this, other);
return *this;
}
// Move Assignment Operator
DescriptorStore& operator=(DescriptorStore&& other)
{
swap(other);
return *this;
}
// Move constructor
DescriptorStore(DescriptorStore&& other) noexcept : DescriptorStore() { swap(*this, other); }
void clear()
{
// Reset each of the items
std::fill(std::begin(_tArray), std::end(_tArray), T());
_tVector.clear();
_ptr = _tArray;
_numItems = 0;
}
void set(uint32_t index, const T& obj)
{
// Move to overflow when the number of items reaches the limit of the limited storage array
if (_tVector.empty() && index >= ArraySize)
{
// Move the limited storage array into the vector
moveToOverFlow();
}
// Determine whether we should grow the container
if (!_tVector.empty() && index >= _tVector.size())
{
// Grow by ArraySize to reduce the number of times we call resize
_tVector.resize(index + ArraySize);
_ptr = _tVector.data();
}
_numItems = std::max(_numItems, index + 1);
_ptr[index] = obj;
// Ensure _ptr is pointing at either _tArray or _tVector.data() depending on the index currently being set
assert(index < ArraySize ? _ptr == _tArray : true && "Pointer must be pointing at _tArray");
assert(index >= ArraySize ? _ptr == _tVector.data() : true && "Pointer must be pointing at _tVector.data()");
}
uint32_t size() const { return _numItems; }
const T* begin() const { return _ptr; }
const T* end() const { return _ptr + _numItems; }
const T& operator[](uint32_t index) const { return get(index); }
const T& get(uint32_t index) const { return _ptr[index]; }
private:
void moveToOverFlow()
{
_tVector.reserve(ArraySize * 2);
_tVector.assign(std::begin(_tArray), std::end(_tArray));
// The pointer now points to the head of the vector
_ptr = _tVector.data();
}
T _tArray[ArraySize];
std::vector<T> _tVector;
T* _ptr;
uint32_t _numItems;
};
} // namespace impl
struct DescriptorPoolCreateInfo
{
private:
std::array<pvrvk::DescriptorPoolSize, descriptorTypeSize> _descriptorPoolSizes;
uint16_t _numDescriptors;
uint16_t _maxSets;
public:
DescriptorPoolCreateInfo() : _numDescriptors(0), _maxSets(200) {}
explicit DescriptorPoolCreateInfo(uint16_t maxSets, uint16_t combinedImageSamplers = 32, uint16_t inputAttachments = 0, uint16_t staticUbos = 32, uint16_t dynamicUbos = 32,
uint16_t staticSsbos = 0, uint16_t dynamicSsbos = 0)
: _numDescriptors(0), _maxSets(maxSets)
{
if (combinedImageSamplers != 0) { addDescriptorInfo(pvrvk::DescriptorType::e_COMBINED_IMAGE_SAMPLER, combinedImageSamplers); }
if (inputAttachments != 0) { addDescriptorInfo(pvrvk::DescriptorType::e_INPUT_ATTACHMENT, inputAttachments); }
if (staticUbos != 0) { addDescriptorInfo(pvrvk::DescriptorType::e_UNIFORM_BUFFER, staticUbos); }
if (dynamicUbos != 0) { addDescriptorInfo(pvrvk::DescriptorType::e_UNIFORM_BUFFER_DYNAMIC, dynamicUbos); }
if (staticSsbos != 0) { addDescriptorInfo(pvrvk::DescriptorType::e_STORAGE_BUFFER, staticSsbos); }
if (dynamicSsbos != 0) { addDescriptorInfo(pvrvk::DescriptorType::e_STORAGE_BUFFER_DYNAMIC, dynamicSsbos); }
}
explicit DescriptorPoolCreateInfo(std::initializer_list<pvrvk::DescriptorPoolSize> descriptorPoolSizes, uint16_t maxSets = 200) : _numDescriptors(0), _maxSets(maxSets)
{
for (auto& it : descriptorPoolSizes) { addDescriptorInfo(it); }
}
DescriptorPoolCreateInfo& addDescriptorInfo(const pvrvk::DescriptorPoolSize& descriptorPoolSize)
{
_descriptorPoolSizes[_numDescriptors] = descriptorPoolSize;
_numDescriptors++;
return *this;
}
DescriptorPoolCreateInfo& addDescriptorInfo(pvrvk::DescriptorType descType, uint16_t count) { return addDescriptorInfo(pvrvk::DescriptorPoolSize(descType, count)); }
DescriptorPoolCreateInfo& setMaxDescriptorSets(uint16_t maxSets)
{
this->_maxSets = maxSets;
return *this;
}
uint32_t getNumPoolSizes() const { return _numDescriptors; }
const pvrvk::DescriptorPoolSize& getPoolSize(uint32_t index) const { return _descriptorPoolSizes[index]; }
uint32_t getMaxDescriptorSets() const { return _maxSets; }
};
struct DescriptorImageInfo
{
Sampler sampler;
ImageView imageView;
pvrvk::ImageLayout imageLayout;
DescriptorImageInfo() : imageLayout(pvrvk::ImageLayout::e_UNDEFINED) {}
explicit DescriptorImageInfo(const Sampler& sampler) : sampler(sampler), imageLayout(pvrvk::ImageLayout::e_UNDEFINED) {}
DescriptorImageInfo(const ImageView& imageView, const Sampler& sampler, pvrvk::ImageLayout imageLayout = pvrvk::ImageLayout::e_SHADER_READ_ONLY_OPTIMAL)
: sampler(sampler), imageView(imageView), imageLayout(imageLayout)
{}
DescriptorImageInfo(const ImageView& imageView, pvrvk::ImageLayout imageLayout = pvrvk::ImageLayout::e_SHADER_READ_ONLY_OPTIMAL)
: imageView(imageView), imageLayout(imageLayout)
{}
};
struct DescriptorBufferInfo
{
Buffer buffer;
VkDeviceSize offset;
VkDeviceSize range;
DescriptorBufferInfo() : offset(0), range(0) {}
DescriptorBufferInfo(const Buffer& buffer, VkDeviceSize offset, VkDeviceSize range) : buffer(buffer), offset(offset), range(range) {}
};
struct WriteDescriptorSet
{
WriteDescriptorSet() : _infos() {}
WriteDescriptorSet(pvrvk::DescriptorType descType, DescriptorSet descSet, uint32_t dstBinding = 0, uint32_t dstArrayElement = 0)
: _descType(descType), _descSet(descSet), _dstBinding(dstBinding), _dstArrayElement(dstArrayElement), _infos()
{
set(descType, descSet, dstBinding, dstArrayElement);
}
WriteDescriptorSet& setDescriptorType(pvrvk::DescriptorType descType)
{
_descType = descType;
if ((_descType >= pvrvk::DescriptorType::e_SAMPLER && _descType <= pvrvk::DescriptorType::e_STORAGE_IMAGE) || _descType == pvrvk::DescriptorType::e_INPUT_ATTACHMENT)
{ _infoType = InfoType::ImageInfo; }
else if (_descType >= pvrvk::DescriptorType::e_UNIFORM_BUFFER && _descType <= pvrvk::DescriptorType::e_STORAGE_BUFFER_DYNAMIC)
{
_infoType = InfoType::BufferInfo;
}
else if (_descType == pvrvk::DescriptorType::e_UNIFORM_TEXEL_BUFFER || _descType == pvrvk::DescriptorType::e_STORAGE_TEXEL_BUFFER)
{
_infoType = InfoType::TexelBufferView;
}
else if (_descType == pvrvk::DescriptorType::e_ACCELERATION_STRUCTURE_KHR)
{
_infoType = InfoType::AccelerationStructureInfo;
}
else
{
assert(false && "Cannot resolve Info type from descriptor type");
}
return *this;
}
WriteDescriptorSet& setDescriptorSet(DescriptorSet& descriptorSet)
{
_descSet = descriptorSet;
return *this;
}
WriteDescriptorSet& setDestBinding(uint32_t binding)
{
_dstBinding = binding;
return *this;
}
WriteDescriptorSet& setDestArrayElement(uint32_t arrayElement)
{
_dstArrayElement = arrayElement;
return *this;
}
WriteDescriptorSet& set(pvrvk::DescriptorType newDescType, const DescriptorSet& descSet, uint32_t dstBinding = 0, uint32_t dstArrayElement = 0)
{
setDescriptorType(newDescType);
_descSet = descSet;
_dstBinding = dstBinding;
_dstArrayElement = dstArrayElement;
_infos.clear();
return *this;
}
WriteDescriptorSet& setImageInfo(uint32_t arrayIndex, const DescriptorImageInfo& imageInfo)
{
#ifdef DEBUG
// VALIDATE DESCRIPTOR TYPE
assert(((_descType >= pvrvk::DescriptorType::e_SAMPLER) && (_descType <= pvrvk::DescriptorType::e_STORAGE_IMAGE)) || (_descType == pvrvk::DescriptorType::e_INPUT_ATTACHMENT));
if (_descType == pvrvk::DescriptorType::e_COMBINED_IMAGE_SAMPLER) { assert(imageInfo.sampler && imageInfo.imageView && "Sampler and ImageView must be valid"); }
#endif
DescriptorInfos info;
info.imageInfo = imageInfo;
_infos.set(arrayIndex, info);
return *this;
}
WriteDescriptorSet& setBufferInfo(uint32_t arrayIndex, const DescriptorBufferInfo& bufferInfo)
{
#ifdef DEBUG
assert(_descType >= pvrvk::DescriptorType::e_UNIFORM_BUFFER && _descType <= pvrvk::DescriptorType::e_STORAGE_BUFFER_DYNAMIC);
assert(bufferInfo.buffer && "Buffer must be valid");
#endif
DescriptorInfos info;
info.bufferInfo = bufferInfo;
_infos.set(arrayIndex, info);
return *this;
}
WriteDescriptorSet& setAccelerationStructureInfo(uint32_t arrayIndex, const AccelerationStructure& accelerationStructure)
{
#ifdef DEBUG
assert(_descType == pvrvk::DescriptorType::e_ACCELERATION_STRUCTURE_KHR);
assert(accelerationStructure && "Acceleration Structure must be valid");
#endif
DescriptorInfos info;
info.accelerationStructure = accelerationStructure;
_infos.set(arrayIndex, info);
return *this;
}
WriteDescriptorSet& setTexelBufferInfo(uint32_t arrayIndex, const BufferView& bufferView)
{
#ifdef DEBUG
assert(_descType >= pvrvk::DescriptorType::e_UNIFORM_TEXEL_BUFFER && _descType <= pvrvk::DescriptorType::e_STORAGE_TEXEL_BUFFER);
assert(bufferView && "Texel BufferView must be valid");
#endif
DescriptorInfos info;
info.texelBuffer = bufferView;
_infos.set(arrayIndex, info);
return *this;
}
WriteDescriptorSet& clearAllInfos()
{
_infos.clear();
return *this;
}
uint32_t getNumDescriptors() const { return _infos.size(); }
pvrvk::DescriptorType getDescriptorType() const { return _descType; }
DescriptorSet getDescriptorSet() { return _descSet; }
const DescriptorSet& getDescriptorSet() const { return _descSet; }
uint32_t getDestArrayElement() const { return _dstArrayElement; }
uint32_t getDestBinding() const { return _dstBinding; }
private:
friend class ::pvrvk::impl::Device_;
pvrvk::DescriptorType _descType;
DescriptorSet _descSet;
uint32_t _dstBinding;
uint32_t _dstArrayElement;
struct DescriptorInfos
{
DescriptorImageInfo imageInfo;
DescriptorBufferInfo bufferInfo;
BufferView texelBuffer;
AccelerationStructure accelerationStructure;
DescriptorInfos() = default;
bool isValid() const { return imageInfo.imageView || imageInfo.sampler || bufferInfo.buffer || texelBuffer || accelerationStructure; }
};
impl::DescriptorStore<DescriptorInfos, 4> _infos;
enum InfoType
{
ImageInfo,
BufferInfo,
TexelBufferView,
AccelerationStructureInfo
};
InfoType _infoType;
// CALL THIS ONE FROM THE DEVICE - CPU SIDE KEEPING ALIVE OF THE DESCRIPTORS IN THIS SET
void updateKeepAliveIntoDestinationDescriptorSet() const;
};
struct CopyDescriptorSet
{
DescriptorSet srcSet;
uint32_t srcBinding;
uint32_t srcArrayElement;
DescriptorSet dstSet;
uint32_t dstBinding;
uint32_t dstArrayElement;
uint32_t descriptorCount;
};
namespace impl {
class DescriptorPool_ : public PVRVkDeviceObjectBase<VkDescriptorPool, ObjectType::e_DESCRIPTOR_POOL>,
public DeviceObjectDebugUtils<DescriptorPool_>,
public std::enable_shared_from_this<DescriptorPool_>
{
private:
friend class Device_;
class make_shared_enabler
{
protected:
make_shared_enabler() = default;
friend class DescriptorPool_;
};
static DescriptorPool constructShared(const DeviceWeakPtr& device, const DescriptorPoolCreateInfo& createInfo)
{
return std::make_shared<DescriptorPool_>(make_shared_enabler{}, device, createInfo);
}
DescriptorPoolCreateInfo _createInfo;
public:
DECLARE_NO_COPY_SEMANTICS(DescriptorPool_)
~DescriptorPool_();
DescriptorPool_(make_shared_enabler, const DeviceWeakPtr& device, const DescriptorPoolCreateInfo& createInfo);
DescriptorSet allocateDescriptorSet(const DescriptorSetLayout& layout);
const DescriptorPoolCreateInfo& getCreateInfo() const { return _createInfo; }
};
class DescriptorSet_ : public PVRVkDeviceObjectBase<VkDescriptorSet, ObjectType::e_DESCRIPTOR_SET>, public DeviceObjectDebugUtils<DescriptorSet_>
{
private:
friend struct ::pvrvk::WriteDescriptorSet;
friend class DescriptorPool_;
class make_shared_enabler
{
protected:
make_shared_enabler() = default;
friend class DescriptorSet_;
};
static DescriptorSet constructShared(const DescriptorSetLayout& descSetLayout, DescriptorPool& pool)
{
return std::make_shared<DescriptorSet_>(make_shared_enabler{}, descSetLayout, pool);
}
mutable std::vector<std::vector<std::shared_ptr<void> > > _keepAlive;
DescriptorSetLayout _descSetLayout;
DescriptorPool _descPool;
public:
DECLARE_NO_COPY_SEMANTICS(DescriptorSet_)
DescriptorSet_(make_shared_enabler, const DescriptorSetLayout& descSetLayout, DescriptorPool& pool)
: PVRVkDeviceObjectBase(pool->getDevice()), DeviceObjectDebugUtils(), _descSetLayout(descSetLayout), _descPool(pool)
{
// Create the Vulkan VkDescriptorSetAllocateInfo structure
VkDescriptorSetAllocateInfo allocInfo = {};
allocInfo.sType = static_cast<VkStructureType>(pvrvk::StructureType::e_DESCRIPTOR_SET_ALLOCATE_INFO);
allocInfo.pSetLayouts = &getDescriptorSetLayout()->getVkHandle();
allocInfo.descriptorSetCount = 1;
allocInfo.descriptorPool = getDescriptorPool()->getVkHandle();
// For appropriate smart reference counting we need to keep alive the bindings
const auto& allBindings = _descSetLayout->getCreateInfo().getAllBindings();
uint16_t maxBinding = 0;
uint32_t i = 0, size = _descSetLayout->getCreateInfo().getNumBindings();
// Loop through the descriptor set bindings and determine the maximum binding
for (; i < size; ++i) { maxBinding = std::max(allBindings[i].binding, maxBinding); }
// Use the maximum binding + 1 to resize the keepAlive array
_keepAlive.resize(maxBinding + 1u);
// Now use the descriptor count for each descriptor binding to determine the total number of entries
for (i = 0; i < size; ++i)
{
auto& entry = allBindings[i];
auto& aliveEntry = _keepAlive[entry.binding];
aliveEntry.resize(entry.descriptorCount);
}
vkThrowIfFailed(getDevice()->getVkBindings().vkAllocateDescriptorSets(_descSetLayout->getDevice()->getVkHandle(), &allocInfo, &_vkHandle), "Allocate Descriptor Set failed");
}
~DescriptorSet_()
{
_keepAlive.clear();
if (getVkHandle() != VK_NULL_HANDLE)
{
if (getDescriptorPool()->getDevice())
{
getDevice()->getVkBindings().vkFreeDescriptorSets(getDescriptorPool()->getDevice()->getVkHandle(), getDescriptorPool()->getVkHandle(), 1, &getVkHandle());
_vkHandle = VK_NULL_HANDLE;
}
else
{
reportDestroyedAfterDevice();
}
_descSetLayout.reset();
}
}
const DescriptorSetLayout& getDescriptorSetLayout() const { return _descSetLayout; }
const DescriptorPool& getDescriptorPool() const { return _descPool; }
DescriptorPool& getDescriptorPool() { return _descPool; }
};
} // namespace impl
// For smart pointer reference counting we need to keep alive the descriptor set binding entries to do this we place them in an array kept alive by the DescriptorSet itself
// This means that the caller application can let resources go out scope and the Descriptor set "keepAlive" array will keep them alive as long as needed
inline void WriteDescriptorSet::updateKeepAliveIntoDestinationDescriptorSet() const
{
// Get the keep alive entry for the current binding
auto& keepAlive = getDescriptorSet()->_keepAlive[this->_dstBinding];
// Handle BufferInfo entries
if (_infoType == InfoType::BufferInfo)
{
for (uint32_t i = 0; i < _infos.size(); ++i)
{
// Ensure the into entry is valid
if (_infos[i].isValid()) { keepAlive[i] = _infos[i].bufferInfo.buffer; }
}
}
// Handle ImageInfo entries
else if (_infoType == InfoType::ImageInfo)
{
for (uint32_t i = 0; i < _infos.size(); ++i)
{
// Ensure the into entry is valid
if (_infos[i].isValid())
{
auto newpair = std::make_shared<std::pair<Sampler, ImageView> >();
newpair->first = _infos[i].imageInfo.sampler;
newpair->second = _infos[i].imageInfo.imageView;
keepAlive[i] = newpair;
}
}
}
// Handle TexelBufferView entries
else if (_infoType == InfoType::TexelBufferView)
{
for (uint32_t i = 0; i < _infos.size(); ++i)
{
// Ensure the into entry is valid
if (_infos[i].isValid()) { keepAlive[i] = _infos[i].texelBuffer; }
}
}
}
} // namespace pvrvk