2021. 8. 11. 15:01ㆍ그래픽스/vk
1. Command buffers
- Vulkan의 commands (drawing operations and memory transfers)
- 함수호출하여 직접적으로 사용하지 않음
- 수행하고자 하는 모든 operations을 command buffer에 record 해야함.
- 이후에는 mainloop에서 Vulkan에게 실행해주라고 명령만해주면됨.
장점
- 미리 drawing commands을 설정할 수 있다.
- multiple threads을 사용할 수 있다.
2. Command pools
- command buffers을 만들기전에 command pool을 생성해야한다.
- Command pools 는 buffers을 저장하는데 사용하는 메모리를 관리한다.
- command buffers은 command pools로 부터 할당된다.
- VkCommandPool 타입인 새 class member를 만들어 주자
VkCommandPool commandPool;
- 그다음 createCommandPool 함수를 만들고 initVulkan 에서 createFramebuffers 앞에 호출하자
void initVulkan() {
createInstance();
setupDebugMessenger();
createSurface();
pickPhysicalDevice();
createLogicalDevice();
createSwapChain();
createImageViews();
createRenderPass();
createGraphicsPipeline();
createFramebuffers();
createCommandPool();
}
...
void createCommandPool() {
}
2.1 Create info
- create info에서는 기본 두개(pNext, sType)를 제외하고, 두개의 파라미터를 더 설정해야함.
QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice);
VkCommandPoolCreateInfo poolInfo{};
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value();
poolInfo.flags = 0; // Optional
- Command buffers은 devices queue중 하나에 제출함으로써 실행된다.
(검색을 통해 얻은 graphics and presentation queues중 하나)
- 각각의 command pool은 오직 단일 타입 queue에 제출된 command buffers만 할당할 수 있다
(Each command pool can only allocate command buffers that are submitted on a single type of queue.)
- 여기서 graphicsFamily로 설정한 이유는, 오직 그리기 명령만 기록하려고 하기 때문이다.
- Flags은 두가지 종류가 있다.
- VK_COMMAND_POOL_CREATE_TRANSIENT_BIT: 명령 버퍼가 새 명령으로 매우 자주 재기록된다는 힌트(메모리 할당 동작이 변경될 수 있음)
- VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT: 명령 버퍼가 개별적으로 재기록되도록 허용. 이 플래그가 없으면 모두 함께 재설정해야함.
- 튜토리얼에선 프로그램시작 부분에서 the command buffers 를 기록한 다음
- 메인 루프에서 여러번 그것들을 실행할것임.
- 그러므로 이러한 flag를 무시해도됨.
2.2 Create & Destroy
if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) {
throw std::runtime_error("failed to create command pool!");
}
- screen에 무언가를 그리는 명령어들은 프로그램 전체에서 사용될것임.
- 따라서 destroy는 프로그램 끝에서 수행
void cleanup() {
vkDestroyCommandPool(device, commandPool, nullptr);
...
}
3. Command buffer allocation
- 이제 command buffers을 할당 받을 수 있다.
- 이제 여기에 drawing commands을 기록할 수 있다.
- drawing commands중 하나는 적절한 VkFramebuffer 과의 바인딩을 포함하고있기 때문에
- 실제로 모든 swapchain의 images에 대한 commandbuffer를 다시 기록해야함.
- 이를 위해, VkCommandBuffer객체의 list를 class member로 생성해야함.
- command buffers은 자동적으로 command pool이 destroy될 때 같이 destroy됨.
(따로 cleanup에 명시할 필요없음)
std::vector<VkCommandBuffer> commandBuffers;
void initVulkan() {
createInstance();
setupDebugMessenger();
createSurface();
pickPhysicalDevice();
createLogicalDevice();
createSwapChain();
createImageViews();
createRenderPass();
createGraphicsPipeline();
createFramebuffers();
createCommandPool();
createCommandBuffers();
}
...
void createCommandBuffers() {
commandBuffers.resize(swapChainFramebuffers.size());
}
3.1 createCommandBuffers
createCommandBuffers
- 각각의 swapchain image를 위한 commands를 할당하고 기록
command buffers
- vkAllocateCommandBuffers 함수로 할당됨
- - - - 이 함수는 할당할 버퍼, command pool의 수를 지정하는
- - - - VkCommandBufferAllocateInfo 구조체를 매개변수로 받음
VkCommandBufferAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocInfo.commandPool = commandPool;
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocInfo.commandBufferCount = (uint32_t) commandBuffers.size();
if (vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != VK_SUCCESS) {
throw std::runtime_error("failed to allocate command buffers!");
}
level : 할당된 command buffers 가 "primary" 인지 "secondary" 인지를 명시
- VK_COMMAND_BUFFER_LEVEL_PRIMARY: Can be submitted to a queue for execution, but cannot be called from other command buffers.
- VK_COMMAND_BUFFER_LEVEL_SECONDARY: Cannot be submitted directly, but can be called from primary command buffers.
- 여기선 secondary 기능을 사용하지 않지만,
- primary command buffer에서 일반적인 작업을 재사용할 수 있는것이 도움이 됨.?
4. Starting command buffer recording
- vkBeginCommandBuffer 함수를 호출하여 기록을 시작할 수 있음
// Provided by VK_VERSION_1_0
VkResult vkBeginCommandBuffer(
VkCommandBuffer commandBuffer,
const VkCommandBufferBeginInfo* pBeginInfo);
4.1 BeginInfo
- 파라미터인 VkCommandBufferBeginInfo 를 기술해야함.
for (size_t i = 0; i < commandBuffers.size(); i++) {
VkCommandBufferBeginInfo beginInfo{};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = 0; // Optional
beginInfo.pInheritanceInfo = nullptr; // Optional
if (vkBeginCommandBuffer(commandBuffers[i], &beginInfo) != VK_SUCCESS) {
throw std::runtime_error("failed to begin recording command buffer!");
}
}
- flags : 어떻게 명령 버퍼를 사용하는지를 나타냄
- VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT: The command buffer will be rerecorded right after executing it once.
- VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT: This is a secondary command buffer that will be entirely within a single render pass.
- VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT: The command buffer can be resubmitted while it is also already pending execution.
- 이 플래그들은 지금당장 사용하지 않음.
- pInheritanceInfo : secondary(보조) 명령 버퍼에만 적용됨.
- - - - 호출하는 primary command buffers에서 상속할 상태를 지정
- 만일 이 command buffer 가 이미 기록되어졌다면.
- vkBeginCommandBuffer 는 암시적으로 재설정함.
- 나중에 버퍼에 명령을 추가할 수 없음. (not possible append command)
5. Starting a render pass
- 드로잉는 vkCmdBeginRenderPass로 render pass를 시작함으로써 시작한다.
- render pass 는 VkRenderPassBeginInfo 안의 파라미터들을 사용하여 구성됨.
5.1 Begin info
VkRenderPassBeginInfo renderPassInfo{};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
renderPassInfo.renderPass = renderPass;
renderPassInfo.framebuffer = swapChainFramebuffers[i];
- renderPass : 인스턴스를 시작하기 위한 renderpass (framebuffer 의 renderpass와 호환이되는)
( and the attachments to bind )
- framebuffer : renderpass와 함께 사용되는 attachments을 포함하고 있는 framebuffer.
- - - - 각 swapchain image의 color attachment에 대한 framebuffer를 만든것으로 지정
renderPassInfo.renderArea.offset = {0, 0};
renderPassInfo.renderArea.extent = swapChainExtent;
- renderArea 의 size를 정의
- renderArea는 shader 에서 loads 와 store 이 발생하는 영역을 정의함
- 이 영역 밖의 픽셀은 정의되지 않음.
- attachment의 사이즈와 크기가 일치해야 좋은 성능을 보여줌.
VkClearValue clearColor = {{{0.0f, 0.0f, 0.0f, 1.0f}}};
renderPassInfo.clearValueCount = 1;
renderPassInfo.pClearValues = &clearColor;
- VK_ATTACHMENT_LOAD_OP_CLEAR 에서 사용할 값을 정의.
- 이 값은 color attachment 에 대한 로드 연산으로 사용됨.
- 명확한 색상을 위해 black + 100% opacity로 설정함
5.2 BeginRenderPass
vkCmdBeginRenderPass(commandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
- render pass 를 이제 시작할 수 있음.
- 명령어를 기록할 수 있는 모든 명령어(함수) 들은 전부 vkCmd 접두사가 붙음
- 그 함수들은 전부 void를 반환하는 함수임
- 그래서 기록을 하는동안에는 오류처리가 없을 것임.
- 파라미터 1 : 기록하려는 모든 명령어의 첫번째 파라미터는 항상 command buffer임.
- 파라미터 2 : 방금 생성한 render pass의 세부정보를 지정
- 파라미터 3 : 어떻게 commands을 컨트롤할 것인지에 대해, (render pass에서 제공될 범위안에서),
- - - - 다음 두가지 값을 가질 수 있음.
- VK_SUBPASS_CONTENTS_INLINE: The render pass commands will be embedded in the primary command buffer itself and no secondary command buffers will be executed.
- VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS: The render pass commands will be executed from secondary command buffers.
- 지금은 secondary command buffers을 사용하지 않으므로 첫번째 옵션을 설정한것.
6. Basic drawing commands
- 이제 graphics pipeline을 바인딩할 수 있음.
vkCmdBindPipeline(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline);
- 파라미터 2 : graphics or compute pipeline, 용도를 명시.
- 이제 Vulkan에게 그래픽 파이프 라인에서 수행할 작업과 fragment shader에서 사용할 attachment를 알려주었음.
- 이제 남은것은 삼각형을 그려달라고 말하는것만 남음
6.1 vkCmdDraw
- vkCmdDraw- anticlimactic
- 실제 이 함수는 약간 반항적이지만
- 미리 지정한 모든 정보 때문에 간단하다.
- 이것은 다음과 같은 파라미터를 가짐 (commandbuffer를 제외하고)
- vertexCount: Even though we don't have a vertex buffer, we technically still have 3 vertices to draw.
- instanceCount: Used for instanced rendering, use 1 if you're not doing that.
- firstVertex: Used as an offset into the vertex buffer, defines the lowest value of gl_VertexIndex.
- firstInstance: Used as an offset for instanced rendering, defines the lowest value of gl_InstanceIndex.
vkCmdDraw(commandBuffers[i], 3, 1, 0, 0);
7. Finishing up
- EndRenderPass 함수로 rendpass를 종료ㅅ킴.
vkCmdEndRenderPass(commandBuffers[i]);
- 그다음에, command buffer 기록을 종료한다.
if (vkEndCommandBuffer(commandBuffers[i]) != VK_SUCCESS) {
throw std::runtime_error("failed to record command buffer!");
}
- 다음 챕터는 image를 swap chain으로부터 얻어와 적절한 command buffer를 실행하고, swapchain 에 finished image를 반환하는 main loop를 작성할것임.
void createCommandBuffers() {
commandBuffers.resize(swapChainFramebuffers.size());
VkCommandBufferAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocInfo.commandPool = commandPool;
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocInfo.commandBufferCount = (uint32_t) commandBuffers.size();
if (vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != VK_SUCCESS) {
throw std::runtime_error("failed to allocate command buffers!");
}
for (size_t i = 0; i < commandBuffers.size(); i++) {
VkCommandBufferBeginInfo beginInfo{};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
if (vkBeginCommandBuffer(commandBuffers[i], &beginInfo) != VK_SUCCESS) {
throw std::runtime_error("failed to begin recording command buffer!");
}
VkRenderPassBeginInfo renderPassInfo{};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
renderPassInfo.renderPass = renderPass;
renderPassInfo.framebuffer = swapChainFramebuffers[i];
renderPassInfo.renderArea.offset = {0, 0};
renderPassInfo.renderArea.extent = swapChainExtent;
VkClearValue clearColor = {{{0.0f, 0.0f, 0.0f, 1.0f}}};
renderPassInfo.clearValueCount = 1;
renderPassInfo.pClearValues = &clearColor;
vkCmdBeginRenderPass(commandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
vkCmdBindPipeline(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline);
vkCmdDraw(commandBuffers[i], 3, 1, 0, 0);
vkCmdEndRenderPass(commandBuffers[i]);
if (vkEndCommandBuffer(commandBuffers[i]) != VK_SUCCESS) {
throw std::runtime_error("failed to record command buffer!");
}
}
C++ code / Vertex shader / Fragment shader
https://vulkan-tutorial.com/en/Drawing_a_triangle/Drawing/Command_buffers
'그래픽스 > vk' 카테고리의 다른 글
[vk] Drawing triangle - Drawing - Swap chain recreation (0) | 2021.08.11 |
---|---|
[vk] Drawing triangle - Drawing - Rendering and presentation (0) | 2021.08.11 |
[vk] Drawing triangle - Drawing - Framebuffers (0) | 2021.08.11 |
[vk] Drawing triangle - graphics pipeline basics - Conclusion (0) | 2021.08.09 |
[vk] Drawing triangle - graphics pipeline basics - Render passes (0) | 2021.08.09 |