Vulkan,metal和DX12的Device,Queue,CommandBuffer
Vulkan、Metal和DirectX 12都包含了 Device、Queue 和 CommandBuffer 这三个概念,它们之间有相似之处,但也存在一些差异:
Device:
在 Vulkan、Metal 和 DirectX 12 中,Device 是对 GPU 的抽象。开发人员需向 Device 发送命令来执行渲染任务,并向其提供数据和资源。在各个 API 中,Device 对象的创建和初始化方法略有不同,而且支持的特性也各不相同。
Vulkan:
vkCreateDevice(
VkPhysicalDevice physicalDevice,
const VkDeviceCreateInfo* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkDevice* pDevice)
Metal:
id<MTLDevice> device = MTLCreateSystemDefaultDevice();
DX12:
D3D12CreateDevice(
_In_opt_ IUnknown* pAdapter,
D3D_FEATURE_LEVEL MinimumFeatureLevel,
_In_ REFIID riid, // Expected: ID3D12Device
_COM_Outptr_opt_ void** ppDevice )
Queue:
Queue 是将多个命令按照一定的顺序排列起来执行的抽象概念。所有渲染任务都需要通过 CommandBuffer 提交到 Queue 中进行处理。在 Vulkan、Metal 和 DirectX 12 中,Queue 对象均可以处理多个任务,并支持异步执行。虽然各个 API 在使用时有所不同,但它们的基本概念都是类似的。
Vulkan:
vkGetDeviceQueue(
VkDevice device,
uint32_t queueFamilyIndex,
uint32_t queueIndex,
VkQueue* pQueue);
Metal:
id<MTLCommandQueue> commandQueue = [device newCommandQueue];
DX12:
CreateCommandQueue(
_In_ const D3D12_COMMAND_QUEUE_DESC *pDesc,
REFIID riid,
_COM_Outptr_ void **ppCommandQueue)
device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&commandQueue));
CommandBuffer:
在 Vulkan、Metal 和 DirectX 12 中,CommandBuffer 是开发人员设置渲染任务的主要对象。通过填充 CommandBuffer 对象,开发人员可以定义整个渲染过程中所需的所有命令,例如绑定顶点缓存和着色器、设置渲染状态、执行绘制命令等。在这三个 API 中,CommandBuffer 也都能够存储多个命令,并可以在提交到 GPU 之前进行重新排序和修改。
Vulkan创建和使用CommandBuffer:
std::vector<VkCommandBuffer> commandBuffers(images.size());
VkCommandBufferAllocateInfo allocateInfo{};
allocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocateInfo.commandPool = commandPool;
allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocateInfo.commandBufferCount = (uint32_t)commandBuffers.size();
if (vkAllocateCommandBuffers(device, &allocateInfo, 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 = framebuffers[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);
VkBuffer vertexBuffers[] = { vertexBuffer };
VkDeviceSize offsets[] = { 0 };
vkCmdBindVertexBuffers(commandBuffers[i], 0, 1, vertexBuffers, offsets);
vkCmdBindIndexBuffer(commandBuffers[i], indexBuffer, 0, VK_INDEX_TYPE_UINT16);
vkCmdBindDescriptorSets(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, nullptr);
vkCmdDrawIndexed(commandBuffers[i], static_cast<uint32_t>(indices.size()), 1, 0, 0, 0);
vkCmdEndRenderPass(commandBuffers[i]);
if (vkEndCommandBuffer(commandBuffers[i]) != VK_SUCCESS) {
throw std::runtime_error("Failed to record command buffer!");
// 创建信号量用于同步
VkSemaphore imageAvailableSemaphore;
VkSemaphore renderFinishedSemaphore;
VkSemaphoreCreateInfo semaphoreCreateInfo{};
semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
if (vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &imageAvailableSemaphore) != VK_SUCCESS ||
vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &renderFinishedSemaphore) != VK_SUCCESS) {
throw std::runtime_error("Failed to create semaphores!");
// 定义需要在呈现完成后提交的信息
VkSubmitInfo submitInfo{};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
VkSemaphore waitSemaphores[] = { imageAvailableSemaphore };
VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
submitInfo.waitSemaphoreCount = 1;
submitInfo.pWaitSemaphores = waitSemaphores;
submitInfo.pWaitDstStageMask = waitStages;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &commandBuffers[imageIndex];
VkSemaphore signalSemaphores[] = { renderFinishedSemaphore };
submitInfo.signalSemaphoreCount = 1;
submitInfo.pSignalSemaphores = signalSemaphores;
// 将命令缓冲提交到图形队列族
if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE) != VK_SUCCESS) {
throw std::runtime_error("Failed to submit draw command buffer!");
// 将呈现完成的图像返回到交换链中
VkPresentInfoKHR presentInfo{};
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
presentInfo.waitSemaphoreCount = 1;
presentInfo.pWaitSemaphores = signalSemaphores;
VkSwapchainKHR swapChains[] = { swapchain };
presentInfo.swapchainCount = 1;
presentInfo.pSwapchains = swapChains;
presentInfo.pImageIndices = &imageIndex;
auto result = vkQueuePresentKHR(presentQueue, &presentInfo);
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) {
swapchainRecreateRequested = true;
else if (result != VK_SUCCESS) {
throw std::runtime_error("Failed to present swap chain image!");
Metal创建和使用CommandBuffer示例:
id<MTLDevice> device = MTLCreateSystemDefaultDevice();
id<MTLCommandQueue> commandQueue = [device newCommandQueue];
id<MTLCommandBuffer> commandBuffer = [commandQueue commandBuffer];
id<MTLRenderCommandEncoder> renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:descriptor];
[renderEncoder setRenderPipelineState:pipelineState];
[renderEncoder setVertexBuffer:vertexBuffer offset:0 atIndex:0]
[renderEncoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:3]
[renderEncoder endEncoding];
[commandBuffer presentDrawable:drawable];
[commandBuffer commit];
DX12创建和使用CommandBuffer示例:
ID3D12GraphicsCommandList* commandList = NULL;
hr = device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, commandAllocator, pso, IID_PPV_ARGS(&commandList));
commandList->SetGraphicsRootSignature(rootSignature);
D3D12_VERTEX_BUFFER_VIEW vbView = {};
vbView.BufferLocation = vertexBuffer->GetGPUVirtualAddress();
vbView.StrideInBytes = sizeof(Vertex);
vbView.SizeInBytes = sizeof(vertices);
commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
commandList->IASetVertexBuffers(0, 1, &vbView);
commandList->RSSetViewports(1, &vp);
commandList->RSSetScissorRects(1, &scissorRect);
commandList->OMSetRenderTargets(1, &rtvHandle, FALSE, NULL);
commandList->ClearRenderTargetView(rtvHandle, Colors::CornflowerBlue, 0, NULL);
commandList->DrawInstanced(3, 1, 0, 0);
hr = commandList->Close();