Qt GUI と Vulkan API: 三角形描画サンプルコードで学ぶ QVulkanWindow::currentCommandBuffer()


QVulkanWindow::currentCommandBuffer() は、Qt GUI 6.7.2以降で Vulkan API を用いるアプリケーション開発において、現在アクティブなコマンドバッファを取得するための関数です。コマンドバッファは、Vulkan API で GPU に送信する命令を記録するために使用されます。

機能

この関数は、現在のスワップチェーンイメージに対応するアクティブなコマンドバッファを返します。スワップチェーンイメージは、画面に描画される画像を表します。

使用方法

QVulkanWindow::currentCommandBuffer() は、Vulkan API で描画コマンドを記録する前に呼び出す必要があります。記録されたコマンドは、vkEndCommandBuffer() 関数を使用してコマンドバッファに完了させ、vkQueueSubmit() 関数を使用して GPU に送信されます。

void QVulkanWindowRenderer::handleRenderRequest()
{
    // ...

    VkCommandBuffer commandBuffer = window->currentCommandBuffer();

    // 描画コマンドを記録

    vkBeginCommandBuffer(commandBuffer, VK_COMMAND_BUFFER_BEGIN_INHERIT_BIT);

    // ...

    vkEndCommandBuffer(commandBuffer);

    // ...
}
  • コマンドバッファは、vkQueueSubmit() 関数を使用して GPU に送信されます。
  • 描画コマンドを記録した後は、vkEndCommandBuffer() 関数を使用してコマンドバッファを完了させる必要があります。
  • 描画コマンドを記録する前に、vkBeginCommandBuffer() 関数を呼び出す必要があります。
  • QVulkanWindow::currentFramebuffer() 関数は、現在のスワップチェーンイメージに対応するフレームバッファを取得します。


#include <QCoreApplication>
#include <QVulkanWindow>

class VulkanWindow : public QVulkanWindow
{
public:
    VulkanWindow(QWidget *parent = nullptr);

protected:
    void initialize();
    void render();

private:
    VkPhysicalDevice physicalDevice;
    VkDevice logicalDevice;
    VkQueue graphicsQueue;
    VkSwapchainKHR swapchain;
    std::vector<VkImage> swapchainImages;
    std::vector<VkImageView> swapchainImageViews;
    VkRenderPass renderPass;
    VkPipelineLayout pipelineLayout;
    VkPipeline graphicsPipeline;
    VkCommandPool commandPool;
    std::vector<VkCommandBuffer> commandBuffers;
    VkSemaphore signalSemaphore;
    VkSemaphore waitSemaphore;
};

VulkanWindow::VulkanWindow(QWidget *parent) :
    QVulkanWindow(parent)
{
}

void VulkanWindow::initialize()
{
    // ...

    // コマンドバッファを作成

    commandPool = vkCreateCommandPool(logicalDevice, &createInfo, nullptr);
    VkCommandBufferAllocateInfo commandBufferAllocateInfo = {};
    commandBufferAllocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
    commandBufferAllocateInfo.level = VK_COMMAND_LEVEL_PRIMARY;
    commandBufferAllocateInfo.commandPool = commandPool;
    commandBufferAllocateInfo.commandBufferCount = swapchainImages.size();
    vkAllocateCommandBuffers(logicalDevice, &commandBufferAllocateInfo, commandBuffers.data());

    // ...
}

void VulkanWindow::render()
{
    // ...

    // コマンドバッファを取得

    VkCommandBuffer commandBuffer = currentCommandBuffer();

    // 描画コマンドを記録

    vkBeginCommandBuffer(commandBuffer, VK_COMMAND_BUFFER_BEGIN_INHERIT_BIT);

    // ...

    vkEndCommandBuffer(commandBuffer);

    // ...
}

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);

    VulkanWindow window;
    window.show();

    return app.exec();
}

このコードでは、QVulkanWindow::currentCommandBuffer() 関数は render() 関数内で使用されています。この関数は、現在のスワップチェーンイメージに対応するアクティブなコマンドバッファを取得します。

コマンドバッファを取得したら、vkBeginCommandBuffer() 関数を使用してコマンドバッファを記録し始め、描画コマンドを記録します。描画コマンドを記録した後は、vkEndCommandBuffer() 関数を使用してコマンドバッファを完了させます。



以下に、QVulkanWindow::currentCommandBuffer() の代替方法として検討できるいくつかの方法をご紹介します。

vkGetDeviceQueue() を使用する

vkGetDeviceQueue() 関数は、特定のタイプのキューを取得するために使用できます。この場合、Graphics キューを取得して、現在アクティブなコマンドバッファに関連付けられているキューを取得することができます。

VkQueue graphicsQueue;
vkGetDeviceQueue(logicalDevice, VK_QUEUE_FAMILY_GRAPHICS, 0, &graphicsQueue);

// ...

VkCommandBuffer commandBuffer;
vkGetDeviceQueueFamilyExternalImageFormat(logicalDevice, graphicsQueue, format, supportedFormats, &commandBuffer);

// ...

vkAcquireNextImageKHR() を使用する

vkAcquireNextImageKHR() 関数は、スワップチェーンから次のイメージを取得するために使用できます。この関数は、イメージとそれに関連付けられたコマンドバッファを返します。

VkImage image;
uint32_t imageIndex;
VkSemaphore signalSemaphore;
VkFence fence;
VkResult result = vkAcquireNextImageKHR(logicalDevice, swapchain, UINT64_MAX, signalSemaphore, fence, &image, &imageIndex);

// ...

VkCommandBuffer commandBuffer;
vkGetImageSubresourceLayout(logicalDevice, image, VK_IMAGE_SUBRESOURCE_LAYOUT_TYPE_COLOR_ATTACHMENT, 0, 1, &subresourceLayout, &commandBuffer);

// ...

スレッドローカルストレージを使用する

スレッドローカルストレージを使用して、現在アクティブなコマンドバッファを追跡することができます。これは、マルチスレッドアプリケーションで特に役立ちます。

__thread VkCommandBuffer currentCommandBuffer;

void render()
{
    // ...

    currentCommandBuffer = window->currentCommandBuffer();

    // ...
}

それぞれの方法の利点と欠点

それぞれの方法には、利点と欠点があります。

  • スレッドローカルストレージを使用する: この方法はマルチスレッドアプリケーションで最も効率的ですが、スレッドローカルストレージを適切に管理する必要があります。
  • vkAcquireNextImageKHR() を使用する: この方法は、スワップチェーンイメージとコマンドバッファの関係を理解する必要がなく、マルチスレッドアプリケーションで特に役立ちます。ただし、この関数は同期が必要であるため、パフォーマンスに影響を与える可能性があります。
  • vkGetDeviceQueue() を使用する: この方法はシンプルですが、スワップチェーンイメージとコマンドバッファの関係を理解する必要があります。

QVulkanWindow::currentCommandBuffer() は、多くの場合、現在アクティブなコマンドバッファを取得するための便利な方法です。しかし、状況によっては、上記の代替方法の方が適している場合があります。