操作系統(tǒng):Windows8.1
顯卡:Nivida GTX965M
開發(fā)工具:Visual Studio 2017
Introduction
描述符布局描述了前一章節(jié)討論過的可以綁定的描述符的類型。在本章節(jié),我們創(chuàng)建描述符集,它將實際指定一個VkBuffer來綁定到一個uniform buffer描述符。
Descriptor pool
描述符集合不能集合創(chuàng)建,它們必須像命令緩沖區(qū)一樣,從對象池中分配使用。對于描述符集合相當于調(diào)用描述符對象池。我們將寫一個新的函數(shù)createDescriptorPool來配置。
void initVulkan() { ... createUniformBuffer(); createDescriptorPool(); ... } ...void createDescriptorPool() { }
我們首先需要明確我們使用的描述符集合包含哪些描述符類型,并且有多少,這里使用VkDescriptorPoolSize結(jié)構(gòu)體集合。
VkDescriptorPoolSize poolSize = {}; poolSize.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; poolSize.descriptorCount = 1;
現(xiàn)在我們只有一個uniform buffer類型的單描述符。對象池大小將被VkDescriptorPoolCreateInfo結(jié)構(gòu)體引用:
VkDescriptorPoolCreateInfo poolInfo = {}; poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; poolInfo.poolSizeCount = 1; poolInfo.pPoolSizes = &poolSize;
我們也需要指定最大的描述符集合的分配數(shù)量:
poolInfo.maxSets = 1;
該結(jié)構(gòu)體與命令對象池類似,有一些可選項用于決定每個描述符集合是否可以獨立管理生命周期:VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT。創(chuàng)建完畢后我們不會進一步使用它,所以我們不需要該flag。在這里設置flags默認值為0。
VkDescriptorPool descriptorPool; ...if (vkCreateDescriptorPool(device, &poolInfo, nullptr, &descriptorPool) != VK_SUCCESS) { throw std::runtime_error("failed to create descriptor pool!"); }
添加新的類成員對象保存描述符對象池的句柄,通過調(diào)用vkCreateDescriptorPool創(chuàng)建它。描述符對象池應該僅在程序退出的時候銷毀,很想其他的繪制資源:
void cleanup() { cleanupSwapChain(); vkDestroyDescriptorPool(device, descriptorPool, nullptr); ... }
Descriptor set
為了分配描述符集合從對象池中,我們需要添加一個createDescriptorSet函數(shù):
void initVulkan() { ... createDescriptorPool(); createDescriptorSet(); ... } ...void createDescriptorSet() { }
描述符集合通過VkDescriptorSetAllocateInfo結(jié)構(gòu)體描述具體的分配。需要指定用于分配的描述符對象池,分配的描述符集合數(shù)量,以及基于此的描述符布局:
VkDescriptorSetLayout layouts[] = {descriptorSetLayout}; VkDescriptorSetAllocateInfo allocInfo = {}; allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; allocInfo.descriptorPool = descriptorPool; allocInfo.descriptorSetCount = 1; allocInfo.pSetLayouts = layouts;
添加類成員存儲描述符集合的句柄,并使用vkAllocateDescriptorSets分配:
VkDescriptorPool descriptorPool; VkDescriptorSet descriptorSet; ...if (vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet) != VK_SUCCESS) { throw std::runtime_error("failed to allocate descriptor set!"); }
我們不需要明確清理描述符集合,因為它們會在描述符對象池銷毀的時候自動清理。調(diào)用vkAllocateDescriptorSets會分配一個具有uniform buffer描述符的描述符集合。
描述符集合已經(jīng)分配了,但是內(nèi)部的描述符需要配置。描述符需要引用緩沖區(qū),就像uniform buffer描述符,使用VkDescriptorBufferInfo結(jié)構(gòu)體進行配置。該結(jié)構(gòu)體指定緩沖區(qū)和描述符內(nèi)部包含的數(shù)據(jù)的區(qū)域:
VkDescriptorBufferInfo bufferInfo = {}; bufferInfo.buffer = uniformBuffer; bufferInfo.offset = 0; bufferInfo.range = sizeof(UniformBufferObject);
描述符的配置更新使用vkUpdateDescriptorSets函數(shù),它需要VkWriteDescriptorSet結(jié)構(gòu)體的數(shù)組作為參數(shù)。
VkWriteDescriptorSet descriptorWrite = {}; descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; descriptorWrite.dstSet = descriptorSet; descriptorWrite.dstBinding = 0; descriptorWrite.dstArrayElement = 0;
前兩個字段指定描述符集合更新和綁定的設置。我們?yōu)閡niform buffer 綁定的索引設定為0。描述符可以是數(shù)組,所以我們需要指定要更新的數(shù)組索引。在這里沒有使用數(shù)組,所以簡單的設置為0。
descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; descriptorWrite.descriptorCount = 1;
我們在這里再一次指定描述符類型??梢酝ㄟ^數(shù)組一次性更新多個描述符,使用dstArrayElement起始索引。descriptorCount字段描述多少描述符需要被更新。
descriptorWrite.pBufferInfo = &bufferInfo; descriptorWrite.pImageInfo = nullptr; // OptionaldescriptorWrite.pTexelBufferView = nullptr; // Optional
最后的字段引用descriptorCount結(jié)構(gòu)體的數(shù)組,它配置了實際的描述符。它的類型根據(jù)實際需要的三個描述符類型來設定。pBufferInfo字段用于指定描述符引用的緩沖區(qū)數(shù)據(jù),pImageInfo字段用于指定描述符引用的圖像數(shù)據(jù),描述符使用pTexelBufferView引用緩沖區(qū)視圖。我們的描述符是基于緩沖區(qū)的,所以我們使用pBufferInfo。
vkUpdateDescriptorSets(device, 1, &descriptorWrite, 0, nullptr);
使用vkUpdateDescriptorSets應用實際的更新。它接受兩種數(shù)組的參數(shù):一個數(shù)組是VkWriteDescriptorSet,另一個是VkCopyDescriptorSet。后一個數(shù)組可以用于兩個描述符之間進行拷貝操作。
Using a descriptor set
我們現(xiàn)在需要更新createCommandBuffers函數(shù),使用cmdBindDescriptorSets將描述符集合綁定到實際的著色器的描述符中:
vkCmdBindDescriptorSets(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, nullptr);
與頂點和索引緩沖區(qū)不同,描述符集合不是圖形管線唯一的。因此,我們需要指定是否要將描述符集綁定到圖形或者計算管線。下一個參數(shù)是描述符所基于的布局。接下來的三個參數(shù)指定首個描述符的索引,要綁定的集合的數(shù)量以及要綁定的集合的數(shù)組。我們稍后回來。最后兩個參數(shù)指定用于動態(tài)描述符的偏移數(shù)組。我們在后續(xù)的章節(jié)中會看到這些。
如果此時運行程序,會看不到任何內(nèi)容在屏幕上。問題在于,由于我們在投影矩陣中進行了Y-flip操作,所以頂點現(xiàn)在以順時針順序而不是逆時針順序繪制。這導致背面剔除以防止任何背面的集合體被繪制。來到createGraphicsPipeline函數(shù),修改VkPipelineRasterizationStateCreateInfo結(jié)構(gòu)體的frontFace如下:
rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
運行程序如下:
矩形已經(jīng)變?yōu)檎叫?,因為投影矩陣現(xiàn)在修正了寬高比。updateUniformData需要考慮屏幕的尺寸大小變化,所以我們不需要重新創(chuàng)建描述符集合在recreateSwapChain中。
Multiple descriptor sets
正如某些結(jié)構(gòu)體和函數(shù)調(diào)用時候的提示所示,實際上可以綁定多個描述符集合。你需要在管線創(chuàng)建布局的時候為每個描述符集合指定描述符布局。著色器可以引用具體的描述符集合如下:
layout(set = 0, binding = 0) uniform UniformBufferObject { ... }
我們可以使用此功能將每個對象和發(fā)生變化的描述符分配到單獨的描述符集合中,在這種情況下,可以避免重新綁定大部分描述符,而這些描述符可能會更有效率。
http://www.cnblogs.com/heitao/p/Vulkan.html