添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

Vulkan,metal和DX12的Device,Queue,CommandBuffer

12 天前

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();