[Vulkan教程]绘制一个三角形/绘制/渲染和呈现(Rendering and presentation)

设置

所有内容都汇集到这一章节了。我们编写一个drawFrame函数来将三角形绘制到屏幕上。创建该函数并在mainLoop中调用:

void mainLoop() {
    while (!glfwWindowShouldClose(window)) {
        glfwPollEvents();
        drawFrame();
    }
}

...

void drawFrame() {

}

同步

drawFrame函数将执行如下操作:

  • 从交换链中获取图像
  • 将图像作为帧缓冲附件并执行命令缓冲区
  • 将图像返还到交换链进行显示

这些操作都由一个函数调用来执行,但它们是异步执行的。函数会在操作完成先返回,操作执行顺序是未定义的。但每个操作都依赖前一步操作,这可不行。

有两种同步交换链时间的方法:栅栏和信号量。它们都是可用于协调操作的对象,操作等待栅栏或信号量变为有信号状态,然后继续执行。

栅栏和信号量的区别是栅栏可以通过vkWariForFences从程序访问,而信号量不行。栅栏主要设计用来同步应用程序本身和渲染操作,而信号量用于同步命令队列内或命令队列间的操作。我们希望同步绘制命令和呈现操作,这里使用信号量最合适。

信号量(Semaphores)

我们需要一个信号量来表示图像已经被获取并准备好渲染,另一个信号量来表示渲染已经完成并且可以进行展示。创建两个类成员保存信号量对象:

VkSemaphore imageAvailableSemaphore;
VkSemaphore renderFinishedSemaphore;

创建函数createSemaphores创建信号量:

void initVulkan() {
    ...
    createCommandBuffers();
    createSemaphores();
}

...

void createSemaphores() {

}

创建信号量的结构体VkSemaphoreCreateInfo现在还没有任何参数:

void createSemaphores() {
    VkSemaphoreCreateInfo semaphoreInfo{};
    semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
}

未来该机构提可能会添加flagspHext参数来扩展功能。创建信号量遵循Vulkan创建模式:

if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphore) != VK_SUCCESS ||
    vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphore) != VK_SUCCESS) {

    throw std::runtime_error("failed to create semaphores!");
}

在程序最后销毁信号量:

void cleanup() {
    vkDestroySemaphore(device, renderFinishedSemaphore, nullptr);
    vkDestroySemaphore(device, imageAvailableSemaphore, nullptr);

从交换链获取图像

drawFrame函数要做的第一件事就是从交换链获取一个图像。交换链是一个扩展特性,所以我们需要使用具有vk*KHR命名约定的函数:

void drawFrame() {
    uint32_t imageIndex;
    vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphore, VK_NULL_HANDLE, &imageIndex);
}

vkAcquireNextImageKHR的前两个参数是逻辑设备和交换链。第三个参数为超时时间,UINT64_MAX表示禁用超时。

接着两个参数表示呈现引擎使用过图像时发出的信号对象。有该信号就表示我们可以绘制该图像了。这里可以指定信号量和栅栏。我们使用前面创建的imageAvailableSemaphore

最后一个参数输出可用的交换链图像的索引。该索引指向我们swapChainImages数组中的VkImage。我们使用该索引选择正确的命令缓冲区。

提交命令缓冲区

队列提交和同步通过VkSubmitInfo来配置。

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;

前三个参数指定要等待的信号量和要等待的管道阶段。我们要写入颜色到图像,所以我们指定等待图形管线的颜色缓冲附件阶段。不等附件意味着理论上可以在图像不可用的时候执行顶点着色器。waitStages数组对应的每个条目对应于pWaitSemaphores中相同索引的信号量(?)。

submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &commandBuffers[imageIndex];

接下来两个参数指定要提交的命令缓冲区,选择交换链图像对应的那个。

VkSemaphore signalSemaphores[] = {renderFinishedSemaphore};
submitInfo.signalSemaphoreCount = 1;
submitInfo.pSignalSemaphores = signalSemaphores;

signalSemaphoreCountpSignalSemaphores参数指定命令缓冲区完成后要发出的信号量。我们这里使用renderFinishedSemaphore

if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE) != VK_SUCCESS) {
    throw std::runtime_error("failed to submit draw command buffer!");
}

现在,我们可以使用vkQueueSubmit来将命令缓冲区提交到图形队列。该函数接受VkSubmitInfo数组,以提高效率。最后一个参数可以指定一个栅栏,来表明命令缓冲区执行完毕。我们这边用信号量来做同步,所以这里设置为VK_NULL_HANDLE

子通道依赖

我们要记得,渲染通道中的子通道会自动处理图像布局转换。这些转换被子通道依赖控制,子通道依赖指定了子通道间的内存和执行依赖关系。我们现在只有一个子通道,但其前后的操作也算作隐式“子通道”。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值