操作系統(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來配置。

移動開發(fā)培訓,Android培訓,安卓培訓,手機開發(fā)培訓,手機維修培訓,手機軟件培訓

void initVulkan() {
    ...
    createUniformBuffer();
    createDescriptorPool();
    ...
}

...void createDescriptorPool() {

}

移動開發(fā)培訓,Android培訓,安卓培訓,手機開發(fā)培訓,手機維修培訓,手機軟件培訓

我們首先需要明確我們使用的描述符集合包含哪些描述符類型,并且有多少,這里使用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。

移動開發(fā)培訓,Android培訓,安卓培訓,手機開發(fā)培訓,手機維修培訓,手機軟件培訓

VkDescriptorPool descriptorPool;

...if (vkCreateDescriptorPool(device, &poolInfo, nullptr, &descriptorPool) != VK_SUCCESS) {    throw std::runtime_error("failed to create descriptor pool!");
}

移動開發(fā)培訓,Android培訓,安卓培訓,手機開發(fā)培訓,手機維修培訓,手機軟件培訓

添加新的類成員對象保存描述符對象池的句柄,通過調(diào)用vkCreateDescriptorPool創(chuàng)建它。描述符對象池應該僅在程序退出的時候銷毀,很想其他的繪制資源:

移動開發(fā)培訓,Android培訓,安卓培訓,手機開發(fā)培訓,手機維修培訓,手機軟件培訓

void cleanup() {
    cleanupSwapChain();

    vkDestroyDescriptorPool(device, descriptorPool, nullptr);

    ...
}

移動開發(fā)培訓,Android培訓,安卓培訓,手機開發(fā)培訓,手機維修培訓,手機軟件培訓

Descriptor set


為了分配描述符集合從對象池中,我們需要添加一個createDescriptorSet函數(shù):

移動開發(fā)培訓,Android培訓,安卓培訓,手機開發(fā)培訓,手機維修培訓,手機軟件培訓

void initVulkan() {
    ...
    createDescriptorPool();
    createDescriptorSet();
    ...
}

...void createDescriptorSet() {

}

移動開發(fā)培訓,Android培訓,安卓培訓,手機開發(fā)培訓,手機維修培訓,手機軟件培訓

描述符集合通過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分配:

移動開發(fā)培訓,Android培訓,安卓培訓,手機開發(fā)培訓,手機維修培訓,手機軟件培訓

VkDescriptorPool descriptorPool;
VkDescriptorSet descriptorSet;

...if (vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet) != VK_SUCCESS) {    throw std::runtime_error("failed to allocate descriptor set!");
}

移動開發(fā)培訓,Android培訓,安卓培訓,手機開發(fā)培訓,手機維修培訓,手機軟件培訓

我們不需要明確清理描述符集合,因為它們會在描述符對象池銷毀的時候自動清理。調(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;

運行程序如下:

移動開發(fā)培訓,Android培訓,安卓培訓,手機開發(fā)培訓,手機維修培訓,手機軟件培訓

矩形已經(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