본문 바로가기

Vulkan

Descriptor pool and sets

이전 글 : Descriptor layout and buffer



Descriptor pool and sets


Introduction


이전 장의 descriptor 레이아웃은 바인딩 할 수 있는 descriptor 유형을 기술 합니다. 이 장에서는 디스크립터 세트를 생성하는데, 실제로는 uniform 버퍼 디스크립터에 바인드 할 VkBuffer 리소스를 지정한다.


Descriptor pool


디스크립터 세트는 직접 생성 할 수 없으며 명령 버퍼와 같은 풀에서 할당해야 합니다. 디스크립터 집합에 을 우리는 디스크립터 풀이라 부릅니다. 우리는 새로운 함수 createDescriptorPool을 작성하여 이를 설정합니다.


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

...

void createDescriptorPool() {

}


먼저 VkDescriptorPoolSize 구조체를 사용하여 디스크립터 세트에 포함 할 디스크립터 유형과 그 중 몇 개를 기술해야 합니다.


VkDescriptorPoolSize poolSize = {};
poolSize.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
poolSize.descriptorCount = 1;


우리는 현재 uniform 버퍼 유형을 가진 하나의 디스크립터 만 가지고있다. 이 풀 크기 구조는 기본 VkDescriptorPoolCreateInfo 에 의해 참조됩니다.


VkDescriptorPoolCreateInfo poolInfo = {};
poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
poolInfo.poolSizeCount = 1;
poolInfo.pPoolSizes = &poolSize;


또한 할당 할 디스크립터 세트의 최대 수를 지정해야 합니다.


poolInfo.maxSets = 1;


구조체에는 개별 설명자 세트를 해제 할 수 있는지 여부를 결정하는 명령 풀과 유사한 선택적 플래그가 있습니다. VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT. 디스크립터 세트를 만든 후에는 디스크립터 세트를 건드리지 않을 것이므로 이 플래그는 필요 없습니다. 플래그를 기본값 0 으로 둘 수 있습니다.


VkDescriptorPool descriptorPool;

...

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


새로운 클래스 멤버를 추가하여 설명자 풀의 핸들을 저장하고 vkCreateDescriptorPool 을 호출하여이 클래스를 만듭니다. 설명자 풀은 다른 도면 관련 자원과 마찬가지로 프로그램 끝에서만 제거해야합니다.


void cleanup() {
   cleanupSwapChain();

   vkDestroyDescriptorPool(device, descriptorPool, nullptr);

   ...
}



Descriptor Set

이제 우리는 디스크립터 세트 자체를 할당 할 수 있습니다. 그 목적을 위해 createDescriptorSet 함수를 추가하십시오.


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

...

void createDescriptorSet() {

}


설명자 세트 할당은 VkDescriptorSetAllocateInfo 구조체로 설명됩니다. 할당 할 디스크립터 풀, 할당 할 디스크립터 세트의 수 및 이들을 기반으로하는 디스크립터 레이아웃을 지정해야 합니다.


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!");
}


설명자 풀이 소멸 될 때 자동으로 해제되므로 설명자 세트를 명시적으로 정리할 필요가 없습니다. vkAllocateDescriptorSets 에 대한 호출은 하나의 유니폼 버퍼 디스크립터와 함께 하나의 디스크립터 세트를 할당 할 것이다.


설명자 세트가 지금 할당되었지만 그 안에있는 디스크립터가 여전히 구성되어야합니다. 유니폼 버퍼 디스크립터와 같이 버퍼를 참조하는 디스크립터는 VkDescriptorBufferInfo 구조체로 구성됩니다. 이 구조체는 디스크립터에 대한 데이터를 포함하고있는 버퍼와 그 내부의 영역을 지정합니다.


VkDescriptorBufferInfo bufferInfo = {};
bufferInfo.buffer = uniformBuffer;
bufferInfo.offset = 0;
bufferInfo.range = sizeof(UniformBufferObject);




설명자의 구성은 VkWriteDescriptorSet 구조체의 배열을 매개 변수로 취하는 vkUpdateDescriptorSets 함수를 사용하여 업데이트됩니다.


VkWriteDescriptorSet descriptorWrite = {};
descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrite.dstSet = descriptorSet;
descriptorWrite.dstBinding = 0;
descriptorWrite.dstArrayElement = 0;


처음 두 필드는 업데이트 할 디스크립터 집합과 바인딩을 지정합니다. 디스크립터는 배열이 될 수 있으므로 업데이트 할 배열의 첫 번째 인덱스를 지정해야합니다. 우리는 배열을 사용하지 않으므로 인덱스는 0입니다.


descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
descriptorWrite.descriptorCount = 1;


설명자의 유형을 다시 지정해야합니다. 인덱스 dstArrayElement에서 시작하여 배열에서 여러 설명자를 한 번에 업데이트 할 수 있습니다. descriptorCount 필드는 업데이트 할 배열 요소 수를 지정합니다.


descriptorWrite.pBufferInfo = &bufferInfo;
descriptorWrite.pImageInfo = nullptr; // Optional
descriptorWrite.pTexelBufferView = nullptr; // Optional


마지막 필드는 실제로 설명자를 구성하는 descriptorCount 구조체가 있는 배열을 참조합니다. 그것은 당신이 실제로 사용해야하는 세 가지 디스크립터의 유형에 달려 있습니다. pBufferInfo 필드는 버퍼 데이터를 참조하는 디스크립터, pImageInfo는 이미지 데이터를 참조하는 디스크립터, pTexelBufferView는 버퍼 뷰를 참조하는 디스크립터로 사용됩니다. 우리의 설명자는 버퍼를 기반으로 하므로 우리는 pBufferInfo를 사용하고 있습니다.


vkUpdateDescriptorSets(device, 1, &descriptorWrite, 0, nullptr);


업데이트는 vkUpdateDescriptorSets 를 사용하여 적용됩니다. VkWriteDescriptorSet 의 배열과 VkCopyDescriptorSet 의 배열의 두 가지 배열을 매개 변수로 사용합니다. 후자는 이름에서 알 수 있듯이 설명자를 서로 복사하는 데 사용할 수 있습니다.



Using a descriptor set


이제 createCommandBuffers 함수를 업데이트하여 셰이더의 설명자 세트를 cmdBindDescriptorSets로 실제로 바인딩합니다.


vkCmdBindDescriptorSets(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, nullptr);


버텍스 및 인덱스 버퍼와 달리 설명자 세트는 그래픽 파이프 라인에 유일하지 않습니다. 따라서 설명자 세트를 그래픽에 바인딩하거나 파이프 라인을 계산할지 여부를 지정해야합니다. 다음 매개 변수는 설명자의 기반이되는 레이아웃입니다. 다음 세 매개 변수는 첫 번째 설명자 집합의 인덱스, 바인딩 할 집합의 수 및 바인딩 할 집합의 배열을 지정합니다. 우리는 잠시 후에 이것에 대해 다시 생각해 볼 것입니다. 마지막 두 매개 변수는 동적 디스크립터에 사용되는 오프셋 배열을 지정합니다. 우리는 장래의 장에서 이것들을 볼 것입니다.


지금 프로그램을 실행하면 불행히도 아무 것도 볼 수 없습니다. 문제는 투영 행렬에서 수행 한 Y 플립 때문에 정점이 이제 반 시계 방향이 아닌 시계 방향으로 그려지는 것입니다. 이로 인해 배면 컬링이 시작되어 지오메트리가 그려지지 않습니다. createGraphicsPipeline 함수로 이동하여 VkPipelineRasterizationStateCreateInfo 에서 frontFace를 수정하여 다음을 수정합니다.


rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;


프로그램을 다시 실행하면 다음이 표시됩니다.


투영 행렬이 종횡비를 수정하기 때문에 직사각형이 정사각형으로 변경되었습니다. updateUniformBuffer 는 화면 크기 조정을 담당하므로 recreateSwapChain에 설정된 디스크립터를 다시 만들 필요가 없습니다.



Multiple descriptor sets


일부 구조 및 함수 호출에서 암시 된 것처럼 실제로 여러 디스크립터 세트를 바인드 할 수 있습니다. 파이프 라인 레이아웃을 만들 때 각 설명자 세트에 대한 설명자 레이아웃을 지정해야합니다. 셰이더는 다음과 같이 특정 설명자 세트를 참조 할 수 있습니다.


layout(set = 0, binding = 0) uniform UniformBufferObject { ... }


이 기능을 사용하여 개체별로 다른 설명자를 공유하고 공유되는 설명자를 별도의 설명자 집합으로 지정할 수 있습니다. 이 경우 잠재적으로 더 효율적 인 그리기 호출에서 대부분의 설명자를 리바인드하지 않아야합니다.


C++ code / Vertex shader / Fragment shader


이전 글 : Descriptor layout and buffer


'Vulkan' 카테고리의 다른 글

Descriptor layout and buffer  (0) 2018.02.05
행렬  (0) 2018.02.05
Index buffer  (0) 2018.01.25
Staging buffer  (0) 2018.01.25
Vertex buffer creation  (0) 2018.01.25