|
Главная
Статьи
Ссылки
Скачать
Скриншоты
Юмор
Почитать
Tools
Проекты
Обо мне
Гостевая
Форум
|
Стандартным способом передачи различных ресурсов (дескрипторов - буферов и текстур) в шейдеры
в Vulkan является использование descriptor set'ов.
При использовании данного способа мы должны сперва создать descriptor pool, потом
мы из него выделяем нужные нам descriptor set'ы через vkAllocateDescriptorSets, далее
мы записываем в них конкретные ресурсы через vkUpdateDesriptorSets и
потом передаем их в шейдер через vkCmdBindDescriptorSets.
Это довольно сложная и неудобная схема. При этом на самом деле каждый ресурс (дескриптор) это просто небольшой фрагмент памяти с данными, описывающими данный ресурс. А раз это просто блок памяти, то тогда возникает естественный вопрос - а почему нельзя передавать такие блоки памяти через буфера, для которых в Vulkan имеется простой и удобный API.
Именно такую возможность и дает расширение VK_EXT_descriptor_buffer. Оно вводит в Vulkan новый тип буферов - буфера дескрипторов (descriptor buffers). В буферах этого типа хранятся дескрипторы и через эти буфера они передаются в шейдеры, т.е. descriptor set'ы (а также их пулы) нам вообще больше не нужны.
С точки зрения данного расширения любой дескриптор это просто небольшой кусочек памяти, описывающий какой-то ресурс. При этом важно понимать, что размер этого кусочка и его выравнивание в памяти могут заметно отличаться у разных вендоров. Поэтому расширение предоставляет способы получения этой информации.
Для подключения данного расширения мы создаем экземпляр структуры VkPhysicalDeviceDescriptorBufferFeaturesEXT,
выставляем у нее поле descriptorBuffer в VK_TRUE и подключаем к списку требуемых возможностей (features),
передаваемому при создании логического устройства.
DevicePolicy policy;
// we require VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME device extension
policy.addDeviceExtension ( VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME );
VkPhysicalDeviceDescriptorBufferFeaturesEXT descriptorBufferFeatures = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_FEATURES_EXT };
VkPhysicalDeviceDescriptorBufferPropertiesEXT descriptorBufferProperties = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_PROPERTIES_EXT };
descriptorBufferFeatures.descriptorBuffer = VK_TRUE;
policy.addFeatures ( &descriptorBufferFeatures );
policy.addProperties ( &descriptorBufferProperties );
Далее нужно получить для всех дескрипторов это требуемое выравнивание в буфере.
Вводится новая структура VkPhysicalDeviceDescriptorBufferPropertiesEXT
(подключаемое через поле pNext к VkPhysicalDeviceProeprties2KHR),
содержащая информацию о поддержке буферов дескрипторов.
За выравнивание в этой структуре отвечает поле descriptorBufferOffsetAlignment
См. код выше).
Данное расширение различает два типа дескрипторов - дескрипторы буферов (UBO, SSBO) и
дескрипторы текстур (изображений, сэмплеров).
Соответственно эти дескрипторы должны размещаться в разных буферах.
Буфера дескрипторов создаются стандартным путем, только необходимо добавить обязательный
флаг VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT в поле usage.
Если буфер будет содержать дескрипторы текстур, то также необходимо добавить
еще и флаг VK_BUFFER_USAGE_SAMPLER_DESCRIPTOR_BUFFER_BIT_EXT.
Для непосредственного задания дескрипторов служит тип VkDescriptorDataEXT,
представляющий собой просто union из указателей на структуры для конкретных типов ресурсов.
void createDescriptorBuffer ( PersistentBuffer& buf, const DescSetLayout& descLayout, bool isBuffer, int sizeMultiplier,
uint32_t binding, VkDeviceSize& size, VkDeviceSize& offset )
{
// get size & offset
vkGetDescriptorSetLayoutSizeEXT ( device.getDevice (), descLayout.getHandle (), &size );
vkGetDescriptorSetLayoutBindingOffsetEXT ( device.getDevice (), descLayout.getHandle (), binding, &offset );
// force size alignment
VkDeviceSize allocSize = sizeMultiplier * alignedSize<VkDeviceSize> ( size, descriptorBufferProperties.descriptorBufferOffsetAlignment );
uint32_t usage;
if ( isBuffer )
usage = VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
else
usage = VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT | VK_BUFFER_USAGE_SAMPLER_DESCRIPTOR_BUFFER_BIT_EXT;
buf.create ( device, allocSize, usage, Buffer::hostWrite );
}
Для того, чтобы можно было поместить дескрипторы в буфер необходимо по layout узнать размер дескриптора и его смещение при помощи следующих функций:
void vkGetDescriptorSetLayoutSizeEXT (
VkDevice device,
VkDescriptorSetLayout layout,
VkDeviceSize* pLayoutSizeInBytes );
void vkGetDescriptorSetLayoutBindingOffsetEXT (
VkDevice device,
VkDescriptorSetLayout layout,
uint32_t binding,
VkDeviceSize* pOffset );
Таким образом мы можем получить информацию о том, где внутри буфера будет храниться
дескриптор с заданно точкой привязки (binding) и сколько байт он будет занимать.
Теперь нам остался еще один шаг - разобраться с тем, как именно поместить дескриптор,
соответствующий нужному ресурсу, в буфер.
Именно для этого и служит функция vkGetDescriptorEXT.
void vkGetDescriptorEXT (
VkDevice device,
const VkDescriptorGetInfoEXT* pDescriptorInfo,
size_t dataSize,
void* pDescriptor );
Здесь параметр pDescriptor это указатель на нужное место уже созданного буфера
дескрипторов, куда и будет производиться запись.
В dataSize передается размер дескриптора в байтах, а через
pDescriptorInfo передается указатель на VkDescriptorGetInfoEXT,
которая и задает параметры дескриптора.
typedef struct VkDescriptorGetInfoEXT
{
VkStructureType sType;
const void* pNext;
VkDescriptorType type;
VkDescriptorDataEXT data;
} VkDescriptorGetInfoEXT;
Поле type задает тип дескриптора и принимает стандартные значения для типа дескриптора за
исключением следующих значений - VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC и
VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK.
typedef union VkDescriptorDataEXT
{
const VkSampler* pSampler;
const VkDescriptorImageInfo* pCombinedImageSampler;
const VkDescriptorImageInfo* pInputAttachmentImage;
const VkDescriptorImageInfo* pSampledImage;
const VkDescriptorImageInfo* pStorageImage;
const VkDescriptorAddressInfoEXT* pUniformTexelBuffer;
const VkDescriptorAddressInfoEXT* pStorageTexelBuffer;
const VkDescriptorAddressInfoEXT* pUniformBuffer;
const VkDescriptorAddressInfoEXT* pStorageBuffer;
VkDeviceAddress accelerationStructure;
} VkDescriptorDataEXT;
Если параметр type задает буфер, то соответствующее поле в data должно быть указателем на
структуру VkDescriptorAddressInfoEXT, а если он задает текстуру/сэмплер, то соответствующее
поле в data указывает на структуру VkDescriptorImageInfo.
typedef struct VkDescriptorImageInfo
{
VkSampler sampler;
VkImageView imageView;
VkImageLayout imageLayout;
} VkDescriptorImageInfo;
typedef struct VkDescriptorAddressInfoEXT
{
VkStructureType sType;
void* pNext;
VkDeviceAddress address;
VkDeviceSize range;
VkFormat format;
} VkDescriptorAddressInfoEXT;
Если посмотреть на то, как задаются буфера (UBO, SSBO), то видно, что поскольку они задаются как область буфера, то мы можем в одном большом буфере разместить сразу много "виртуальный" буферов. При этом каждый такой "виртуальный" буфер будет иметь свое смещение и размер внутри основного буфера.
Также видно текстуру мы задаем набором (sampler, view, memoryLayout), а для задания буфера мы передаем адрес начала и длину участка в байтах.
Теперь нам нужно подключить буфер (или буфера) дескрипторов к командному буферу при помощи команды vkCmdBindDescriptorBuffersEXT:
void vkCmdBindDescriptorBuffersEXT (
VkCommandBuffer commandBuffer,
uint32_t bufferCount,
const VkDescriptorBufferBindingInfoEXT* pBindingInfos );
Мы одной командой можем подключить сразу много буферов, задав их все в массиве структур VkDescriptorBufferBindingInfoEXT.
Эта команда задает какие именно буфера дескрипторов мы собираемся использовать далее.
Но сами смещения для буфер мы будем задавать позже.
typedef struct VkDescriptorBufferBindingInfoEXT
{
VkStructureType sType;
void* pNext;
VkDeviceAddress address;
VkBufferUsageFlags usage;
} VkDescriptorBufferBindingInfoEXT;
Команда vkCmdBindDescriptorBuffersEXT просто задает набор буферов дескрипторов, но не их привязку к конкретным множествам дескрипторов.
Обратите внимание что данную команду не рекомендуется использовать часто.
Для того, чтобы задать как конкретное множество дескрипторов отображается внутрь буферов дескрипторов служит еще
однав команда vkCmdSetDescriptorBufferOffsetsEXT.
void vkCmdSetDescriptorBufferOffsetsEXT(
VkCommandBuffer commandBuffer,
VkPipelineBindPoint pipelineBindPoint,
VkPipelineLayout layout,
uint32_t firstSet,
uint32_t setCount,
const uint32_t* pBufferIndices,
const VkDeviceSize* pOffsets);
Эта команда задает как множества дескрипторов с номерами от firstSet до firstSet + setCount - 1
должны отображаться внутрь подключенных ранее буферов дескрипторов.
Для каждого множества дескрипторов firstSet + i в pBufferIndices[i]
храниться индекс буфера в массиве буферов переданных ранее через команду vkCmdBindDescriptorBuffersEXT.
В pOffsets [i] задается смещение в байтах от начала буфера.
Таким образом мы можем внутри одного буфера дескрипторов задать сразу много различных множеств дескрипторов
и потом для перехода между множествами дескрипторов просто задавать новые смещения.
for ( size_t i = 0; i < commandBuffers.size(); i++ )
{
commandBuffers [i]
.begin ()
.beginRenderPass ( RenderPassInfo ( renderPass ).framebuffer ( framebuffers [i] )
.extent ( swapChain.getExtent ().width, swapChain.getExtent ().height )
.clearColor (0,0,0,1).clearDepthStencil () )
.pipeline ( pipeline );
VkDeviceSize bufferOffset = 0;
uint32_t bufferIndexUbo = 0;
uint32_t bufferIndexImage = 1;
// set descriptor buffer bindings to our descriptor buffers
vkCmdBindDescriptorBuffersEXT ( commandBuffers [i].getHandle (), 2, bindings );
// camera ubo (set 0)
vkCmdSetDescriptorBufferOffsetsEXT ( commandBuffers [i].getHandle (), VK_PIPELINE_BIND_POINT_GRAPHICS,
pipeline.getLayout (), 0, 1, &bufferIndexUbo, &bufferOffset );
for ( int j = 0; j < 3; j++ )
{
// model ubo (set 1)
bufferOffset = (j + 1) * buffersDescriptorSize;
vkCmdSetDescriptorBufferOffsetsEXT ( commandBuffers [i].getHandle (), VK_PIPELINE_BIND_POINT_GRAPHICS,
pipeline.getLayout (), 1, 1, &bufferIndexUbo, &bufferOffset );
// texture (set 2)
bufferOffset = j * texturesDescriptorSize;
vkCmdSetDescriptorBufferOffsetsEXT ( commandBuffers [i].getHandle (), VK_PIPELINE_BIND_POINT_GRAPHICS,
pipeline.getLayout (), 2, 1, &bufferIndexImage, &bufferOffset );
// render mesh
commandBuffers [i].render ( mesh.get () );
}
commandBuffers [i].end ();
}
В качестве примера давайте рассмотрим случай, когда у нас выводится три одинаковых объекта (тора в нашем случае), причем каждый из них обладает своим UBO и своей текстурой. Кроме того, также есть еще и один глобальный UBO.
Тогда мы должны создать два буфера дескрипторов - один для UBO и один для текстур. В первый буфер мы поместим ссылки на UBO, при этом вначале будет идти ссылка на один глобальный UBO (камеры), а потом три ссылки на локальные UBO для каждого объекта. Во второй буфер дескрипторов мы поместим три ссылки на текстуры - по одной для каждого объекта.
Поскольку дескрипторы буферов и текстуре нельзя помещать в один буфер дескрипторов, то у нас будет два таких буфера - один для UBO, а другой - для текстур. При этом удобно вынести создание буфера дескрипторов в отдельный метод, обратите внимание, что мы передаем флаг, сообщающий буфер дескрипторов для хранения чего (буферов или текстур) мы создаем - от этого зависят передаваемые флаги
Также удобно вынести запись в буфер дескрипторов для буферов и текстур в отдельные методы:
// set entry index in buffers descriptor buffer to given buffer and offset in it
void uploadBuffer ( Buffer& buffer, uint32_t index, VkDeviceSize offset = 0 )
{
VkDescriptorGetInfoEXT descriptorInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_GET_INFO_EXT };
VkDescriptorAddressInfoEXT descriptorAddressInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_ADDRESS_INFO_EXT };
char* descriptorBufPtr = (char*)buffersDescriptorBuffer.getPtr ();
descriptorAddressInfo.address = buffer.getDeviceAddress () + offset;
descriptorAddressInfo.range = buffer.getSize (); //buffersDescriptorSize;
descriptorAddressInfo.format = VK_FORMAT_UNDEFINED;
descriptorInfo.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
descriptorInfo.data.pCombinedImageSampler = nullptr;
descriptorInfo.data.pUniformBuffer = &descriptorAddressInfo;
vkGetDescriptorEXT ( device.getDevice (), &descriptorInfo, descriptorBufferProperties.uniformBufferDescriptorSize,
descriptorBufPtr + index * buffersDescriptorSize + buffersDescriptorOffset );
}
void uploadImage ( Texture& texture, Sampler& sampler, uint32_t index,
VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL )
{
VkDescriptorGetInfoEXT descriptorInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_GET_INFO_EXT };
VkDescriptorImageInfo imageDescriptor = {};
char* descriptorBufPtr = (char*)texturesDescriptorBuffer.getPtr ();
imageDescriptor.sampler = sampler.getHandle ();
imageDescriptor.imageView = texture.getImageView ();
imageDescriptor.imageLayout = imageLayout;
descriptorInfo.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
descriptorInfo.data.pCombinedImageSampler = &imageDescriptor;
vkGetDescriptorEXT ( device.getDevice (), &descriptorInfo, descriptorBufferProperties.combinedImageSamplerDescriptorSize,
descriptorBufPtr + index * texturesDescriptorSize + texturesDescriptorOffset );
}
При создании объекта конвейера нам будет нужно descriptor set layout для всех трех множеств дескрипторов.
void createDescriptorSets ()
{
descSetLayoutBuffers .add ( 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT )
.create ( device.getDevice (), VK_DESCRIPTOR_SET_LAYOUT_CREATE_DESCRIPTOR_BUFFER_BIT_EXT );
descSetLayoutTextures.add ( 0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT )
.create ( device.getDevice (), VK_DESCRIPTOR_SET_LAYOUT_CREATE_DESCRIPTOR_BUFFER_BIT_EXT );
}
virtual void createPipelines () override
{
createUniformBuffers ();
createDefaultRenderPass ( renderPass );
pipeline.setDevice ( device )
.setVertexShader ( "shaders/shader-descriptor-buffer.vert.spv" )
.setFragmentShader ( "shaders/shader-descriptor-buffer.frag.spv" )
.setSize ( swapChain.getExtent () )
.addVertexBinding ( sizeof ( BasicVertex ) )
.addVertexAttributes <BasicVertex> ()
.addDescLayout ( 0, descSetLayoutBuffers )
.addDescLayout ( 1, descSetLayoutBuffers )
.addDescLayout ( 2, descSetLayoutTextures )
.setCullMode ( VK_CULL_MODE_NONE )
.setDepthTest ( true )
.setDepthWrite ( true )
.create ( renderPass, VK_PIPELINE_CREATE_DESCRIPTOR_BUFFER_BIT_EXT );
// create before command buffers
swapChain.createFramebuffers ( renderPass, depthTexture.getImageView () );
createCommandBuffers ( renderPass );
}
Ниже приводятся используемые в примере вершинный и фрагментный шейдеры.
#version 450
layout ( location = 0 ) in vec3 pos;
layout ( location = 1 ) in vec2 texCoord;
layout ( location = 2 ) in vec3 normal;
layout ( set = 0, binding = 0 ) uniform Camera
{
mat4 proj;
mat4 view;
} camera;
layout ( set = 1, binding = 0 ) uniform Model
{
mat4 model;
} model;
layout ( location = 0 ) out vec2 tex;
void main()
{
gl_Position = camera.proj * camera.view * model.model * vec4 ( pos, 1.0 );
tex = texCoord;
}
#version 450
layout ( set = 2, binding = 0 ) uniform sampler2D image;
layout ( location = 0 ) in vec2 tex;
layout ( location = 0 ) out vec4 color;
void main()
{
color = texture ( image, tex );
}
Ниже идет полный исходный код на С++ соответствующего примера.
#include <ctype.h>
#include <memory>
#include "VulkanWindow.h"
#include "Buffer.h"
#include "DescriptorSet.h"
#include "Mesh.h"
#include "Controller.h"
struct UboCamera
{
glm::mat4 proj;
glm::mat4 view;
};
struct UboModel
{
glm::mat4 model;
};
class ExampleWindow : public VulkanWindow
{
std::vector<CommandBuffer> commandBuffers;
GraphicsPipeline pipeline;
Renderpass renderPass;
std::vector<Texture> textures;
Sampler sampler;
std::unique_ptr<Mesh> mesh;
Uniform<UboCamera> cameraUbo; // camera uniform buffer
std::vector<Uniform<UboModel>> modelUbos; // models uniform buffers
DescSetLayout descSetLayoutBuffers; // descriptor set layout for descriptor buffer with buffers (Ubo)
DescSetLayout descSetLayoutTextures; // descriptor set layout for descriptor buffer with textures
PersistentBuffer buffersDescriptorBuffer; // descriptor buffer with buffers (Ubo)
VkDeviceSize buffersDescriptorOffset;
VkDeviceSize buffersDescriptorSize;
PersistentBuffer texturesDescriptorBuffer; // descriptor buffer with textures
VkDeviceSize texturesDescriptorOffset;
VkDeviceSize texturesDescriptorSize;
// we need properties for buffer alignment
VkPhysicalDeviceDescriptorBufferPropertiesEXT& descriptorBufferProperties;
// function pointers to new commands
PFN_vkGetDescriptorSetLayoutSizeEXT vkGetDescriptorSetLayoutSizeEXT = {};
PFN_vkGetDescriptorSetLayoutBindingOffsetEXT vkGetDescriptorSetLayoutBindingOffsetEXT = {};
PFN_vkCmdBindDescriptorBuffersEXT vkCmdBindDescriptorBuffersEXT = {};
PFN_vkCmdSetDescriptorBufferOffsetsEXT vkCmdSetDescriptorBufferOffsetsEXT = {};
PFN_vkGetDescriptorEXT vkGetDescriptorEXT = {};
PFN_vkCmdBindDescriptorBufferEmbeddedSamplersEXT vkCmdBindDescriptorBufferEmbeddedSamplersEXT = {};
public:
ExampleWindow ( int w, int h, const std::string& t, bool depth, DevicePolicy * p,
VkPhysicalDeviceDescriptorBufferPropertiesEXT& descBufferProps ) :
VulkanWindow ( w, h, t, depth, p ), descriptorBufferProperties ( descBufferProps )
{
loadExtensions ();
setController ( new RotateController ( this, glm::vec3(2.0f, 2.0f, 2.0f) ) );
mesh = std::unique_ptr<Mesh> ( createKnot ( device, 0.2f, 0.13f, 120, 30 ) );
createDescriptorSets (); // create descriptor sets for descriptor buffers
// create buffers
createDescriptorBuffer ( buffersDescriptorBuffer, descSetLayoutBuffers, true, 4, 0, buffersDescriptorSize, buffersDescriptorOffset );
createDescriptorBuffer ( texturesDescriptorBuffer, descSetLayoutTextures, false, 3, 0, texturesDescriptorSize, texturesDescriptorOffset );
sampler.create ( device ); // use default options
createTextures ();
createPipelines ();
uploadBuffer ( cameraUbo, 0, 0 );
for ( int i = 0; i < modelUbos.size (); i++ )
{
uploadBuffer ( modelUbos [i], i + 1, 0 );
uploadImage ( textures [i], sampler, i );
}
}
void createTextures ()
{
std::string texPath = "../../Textures/";
std::vector<const char *> texs = { "Fieldstone.dds", "16.jpg", "flower.png", "lena.png", "block.jpg",
"brick.tga", "oak.jpg", "rockwall.jpg" };
textures.resize ( texs.size () );
for ( size_t i = 0; i < texs.size (); i++ )
if ( !textures [i].load ( device, texPath + texs [i] ) )
fatal () << "Error loading texture " << texs [i] << std::endl;
}
void createDescriptorBuffer ( PersistentBuffer& buf, const DescSetLayout& descLayout, bool isBuffer,
int sizeMultiplier, uint32_t binding, VkDeviceSize& size, VkDeviceSize& offset )
{
// get size & offset
vkGetDescriptorSetLayoutSizeEXT ( device.getDevice (), descLayout.getHandle (), &size );
vkGetDescriptorSetLayoutBindingOffsetEXT ( device.getDevice (), descLayout.getHandle (), binding, &offset );
// force size alignment
VkDeviceSize allocSize = sizeMultiplier * alignedSize<VkDeviceSize> ( size,
descriptorBufferProperties.descriptorBufferOffsetAlignment );
uint32_t usage;
if ( isBuffer )
usage = VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
else
usage = VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT | VK_BUFFER_USAGE_SAMPLER_DESCRIPTOR_BUFFER_BIT_EXT;
buf.create ( device, allocSize, usage, Buffer::hostWrite );
}
// set entry index in buffers descriptor buffer to given buffer and offset in it
void uploadBuffer ( Buffer& buffer, uint32_t index, VkDeviceSize offset = 0 )
{
VkDescriptorGetInfoEXT descriptorInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_GET_INFO_EXT };
VkDescriptorAddressInfoEXT descriptorAddressInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_ADDRESS_INFO_EXT };
char* descriptorBufPtr = (char*)buffersDescriptorBuffer.getPtr ();
descriptorAddressInfo.address = buffer.getDeviceAddress () + offset;
descriptorAddressInfo.range = buffer.getSize (); //buffersDescriptorSize;
descriptorAddressInfo.format = VK_FORMAT_UNDEFINED;
descriptorInfo.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
descriptorInfo.data.pCombinedImageSampler = nullptr;
descriptorInfo.data.pUniformBuffer = &descriptorAddressInfo;
vkGetDescriptorEXT ( device.getDevice (), &descriptorInfo, descriptorBufferProperties.uniformBufferDescriptorSize,
descriptorBufPtr + index * buffersDescriptorSize + buffersDescriptorOffset );
}
void uploadImage ( Texture& texture, Sampler& sampler, uint32_t index,
VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL )
{
VkDescriptorGetInfoEXT descriptorInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_GET_INFO_EXT };
VkDescriptorImageInfo imageDescriptor = {};
char* descriptorBufPtr = (char*)texturesDescriptorBuffer.getPtr ();
imageDescriptor.sampler = sampler.getHandle ();
imageDescriptor.imageView = texture.getImageView ();
imageDescriptor.imageLayout = imageLayout;
descriptorInfo.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
descriptorInfo.data.pCombinedImageSampler = &imageDescriptor;
vkGetDescriptorEXT ( device.getDevice (), &descriptorInfo, descriptorBufferProperties.combinedImageSamplerDescriptorSize,
descriptorBufPtr + index * texturesDescriptorSize + texturesDescriptorOffset );
}
void createUniformBuffers ()
{
cameraUbo.create ( device );
modelUbos.resize ( 3 );
for ( size_t i = 0; i < 3; i++ )
modelUbos [i].create ( device );
}
void freeUniformBuffers ()
{
cameraUbo.clean ();
modelUbos.clear ();
}
void createDescriptorSets ()
{
descSetLayoutBuffers .add ( 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT )
.create ( device.getDevice (), VK_DESCRIPTOR_SET_LAYOUT_CREATE_DESCRIPTOR_BUFFER_BIT_EXT );
descSetLayoutTextures.add ( 0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT )
.create ( device.getDevice (), VK_DESCRIPTOR_SET_LAYOUT_CREATE_DESCRIPTOR_BUFFER_BIT_EXT );
}
virtual void createPipelines () override
{
createUniformBuffers ();
createDefaultRenderPass ( renderPass );
pipeline.setDevice ( device )
.setVertexShader ( "shaders/shader-descriptor-buffer.vert.spv" )
.setFragmentShader ( "shaders/shader-descriptor-buffer.frag.spv" )
.setSize ( swapChain.getExtent () )
.addVertexBinding ( sizeof ( BasicVertex ) )
.addVertexAttributes <BasicVertex> ()
.addDescLayout ( 0, descSetLayoutBuffers )
.addDescLayout ( 1, descSetLayoutBuffers )
.addDescLayout ( 2, descSetLayoutTextures )
.setCullMode ( VK_CULL_MODE_NONE )
.setDepthTest ( true )
.setDepthWrite ( true )
.create ( renderPass, VK_PIPELINE_CREATE_DESCRIPTOR_BUFFER_BIT_EXT );
// create before command buffers
swapChain.createFramebuffers ( renderPass, depthTexture.getImageView () );
createCommandBuffers ( renderPass );
}
virtual void freePipelines () override
{
commandBuffers.clear ();
pipeline.clean ();
renderPass.clean ();
freeUniformBuffers ();
descAllocator.clean ();
}
virtual void submit ( uint32_t imageIndex ) override
{
updateUniformBuffers ( imageIndex );
defaultSubmit ( commandBuffers [imageIndex] );
}
void createCommandBuffers ( Renderpass& renderPass )
{
auto framebuffers = swapChain.getFramebuffers ();
VkDescriptorBufferBindingInfoEXT bindings [2] {};
bindings [0].sType = VK_STRUCTURE_TYPE_DESCRIPTOR_BUFFER_BINDING_INFO_EXT;
bindings [0].address = buffersDescriptorBuffer.getDeviceAddress ();
bindings [0].usage = VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT;
bindings [1].sType = VK_STRUCTURE_TYPE_DESCRIPTOR_BUFFER_BINDING_INFO_EXT;
bindings [1].address = texturesDescriptorBuffer.getDeviceAddress ();
bindings [1].usage = VK_BUFFER_USAGE_SAMPLER_DESCRIPTOR_BUFFER_BIT_EXT | VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT;
commandBuffers = device.allocCommandBuffers ( (uint32_t)framebuffers.size ());
for ( size_t i = 0; i < commandBuffers.size(); i++ )
{
commandBuffers [i]
.begin ()
.beginRenderPass ( RenderPassInfo ( renderPass ).framebuffer ( framebuffers [i] )
.extent ( swapChain.getExtent ().width, swapChain.getExtent ().height )
.clearColor (0,0,0,1).clearDepthStencil () )
.pipeline ( pipeline );
VkDeviceSize bufferOffset = 0;
uint32_t bufferIndexUbo = 0;
uint32_t bufferIndexImage = 1;
// set descriptor buffer bindings to our descriptor buffers
vkCmdBindDescriptorBuffersEXT ( commandBuffers [i].getHandle (), 2, bindings );
// camera ubo (set 0)
vkCmdSetDescriptorBufferOffsetsEXT ( commandBuffers [i].getHandle (), VK_PIPELINE_BIND_POINT_GRAPHICS,
pipeline.getLayout (), 0, 1, &bufferIndexUbo, &bufferOffset );
for ( int j = 0; j < 3; j++ )
{
// model ubo (set 1)
bufferOffset = (j + 1) * buffersDescriptorSize;
vkCmdSetDescriptorBufferOffsetsEXT ( commandBuffers [i].getHandle (), VK_PIPELINE_BIND_POINT_GRAPHICS,
pipeline.getLayout (), 1, 1, &bufferIndexUbo, &bufferOffset );
// texture (set 2)
bufferOffset = j * texturesDescriptorSize;
vkCmdSetDescriptorBufferOffsetsEXT ( commandBuffers [i].getHandle (), VK_PIPELINE_BIND_POINT_GRAPHICS,
pipeline.getLayout (), 2, 1, &bufferIndexImage, &bufferOffset );
// render mesh
commandBuffers [i].render ( mesh.get () );
}
commandBuffers [i].end ();
}
}
void updateUniformBuffers ( uint32_t )
{
float t = float ( getTime () );
//t = 0;
cameraUbo->view = controller->getModelView ();
cameraUbo->proj = controller->getProjection ();
modelUbos [0]->model = glm::translate ( glm::mat4(1), glm::vec3 ( -1, -1, 0 ) ) * glm::rotate ( glm::mat4(1), t, glm::vec3 ( 1, 0, 0 ) );
modelUbos [1]->model = glm::translate ( glm::mat4(1), glm::vec3 ( 0, 0, 1 ) ) * glm::rotate ( glm::mat4(1), t, glm::vec3 ( 0, 1, 0 ) );
modelUbos [2]->model = glm::translate ( glm::mat4(1), glm::vec3 ( 1, 0, 0 ) ) * glm::rotate ( glm::mat4(1), t, glm::vec3 ( 0, 1, 1 ) );
}
void loadExtensions ()
{
vkGetDescriptorSetLayoutSizeEXT = reinterpret_cast<PFN_vkGetDescriptorSetLayoutSizeEXT>
(vkGetDeviceProcAddr(device.getDevice (), "vkGetDescriptorSetLayoutSizeEXT"));
vkGetDescriptorSetLayoutBindingOffsetEXT = reinterpret_cast<PFN_vkGetDescriptorSetLayoutBindingOffsetEXT>
(vkGetDeviceProcAddr(device.getDevice (), "vkGetDescriptorSetLayoutBindingOffsetEXT"));
vkCmdBindDescriptorBuffersEXT = reinterpret_cast<PFN_vkCmdBindDescriptorBuffersEXT>
(vkGetDeviceProcAddr(device.getDevice (), "vkCmdBindDescriptorBuffersEXT"));
vkGetDescriptorEXT = reinterpret_cast<PFN_vkGetDescriptorEXT>
(vkGetDeviceProcAddr(device.getDevice (), "vkGetDescriptorEXT"));
vkCmdBindDescriptorBufferEmbeddedSamplersEXT = reinterpret_cast<PFN_vkCmdBindDescriptorBufferEmbeddedSamplersEXT>
(vkGetDeviceProcAddr(device.getDevice (), "vkCmdBindDescriptorBufferEmbeddedSamplersEXT"));
vkCmdSetDescriptorBufferOffsetsEXT = reinterpret_cast<PFN_vkCmdSetDescriptorBufferOffsetsEXT>
(vkGetDeviceProcAddr(device.getDevice (), "vkCmdSetDescriptorBufferOffsetsEXT"));
}
};
int main ( int argc, const char * argv [] )
{
DevicePolicy policy;
// we require VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME device extension
policy.addDeviceExtension ( VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME );
VkPhysicalDeviceDescriptorBufferFeaturesEXT descriptorBufferFeatures = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_FEATURES_EXT };
VkPhysicalDeviceDescriptorBufferPropertiesEXT descriptorBufferProperties = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_PROPERTIES_EXT };
descriptorBufferFeatures.descriptorBuffer = VK_TRUE;
policy.addFeatures ( &descriptorBufferFeatures );
policy.addProperties ( &descriptorBufferProperties );
return ExampleWindow ( 800, 600, "Vulkan descriptor buffer", true, &policy, descriptorBufferProperties ).run ();
}
Весь исходный код уже доступен в репозитории на github.
Полезные ссылки -
Vulkan Documentation: Descriptor buffers