vulkanscenegraph显示倾斜模型(5.6)-vsg::RenderGraph的创建

前言

上一章深入分析了vsg::CommandGraph的创建过程及其通过子场景遍历实现Vulkan命令录制的机制。本章将在该基础上,进一步探讨Vulkan命令录制中的核心封装------vsg::RenderGraph。作为渲染流程的关键组件,RenderGraph封装了vkCmdBeginRenderPass和vkCmdEndRenderPass的核心功能,RecordTraversal会在这两个关键调用之间遍历并处理其子节点。同时本章内容将涵盖vsg::FrameBuffer(封装VkFramebuffer)、vsg::RenderPass(封装VkRenderPass)和vsg::ImageView(封装VkImageView)等重要概念。


目录

  • 1 vsg::RenderPass
  • 2 vsg::FrameBuffer
  • 3 vsg::RenderGraph

1 vsg::RenderPass

vsg::RenderPass 是 VulkanSceneGraph (VSG) 框架中的核心类,它封装了 Vulkan 的渲染通道(Render Pass),用于定义渲染过程中帧缓冲区附件的结构和组织方式。

1.1 VkRenderPass的创建

颜色附件描述:

cpp 复制代码
    VkAttachmentDescription colorAttachment
    {
        0, VK_FORMAT_B8G8R8A8_SRGB,
        VK_SAMPLE_COUNT_1_BIT,
        VK_ATTACHMENT_LOAD_OP_CLEAR,       // Load/Store
        VK_ATTACHMENT_STORE_OP_STORE,
        VK_ATTACHMENT_LOAD_OP_DONT_CARE,   // stencilLoad/Store
        VK_ATTACHMENT_STORE_OP_DONT_CARE,
        VK_IMAGE_LAYOUT_UNDEFINED,         // initialLayout
        VK_IMAGE_LAYOUT_PRESENT_SRC_KHR    // finalLayout
    };

    VkAttachmentReference colorAttachmentRef
    {
        0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
    };

子通道描述:

cpp 复制代码
    VkSubpassDescription subpass
    {
        0, VK_PIPELINE_BIND_POINT_GRAPHICS,
        0, nullptr,              // pInputAttachments
        1, &colorAttachmentRef,  // pColorAttachments
        nullptr,                 // pResolveAttachments
        nullptr,                 // pDepthStencilAttachment
        0, nullptr,              // pPreserveAttachments
    };

渲染通道创建信息:

cpp 复制代码
    VkRenderPassCreateInfo renderPassInfo
    {
        VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
        nullptr, 0,
        1, &colorAttachment,  // pAttachments
        1, &subpass,          // pSubpasses
        0, nullptr            // pDependencies
    };

创建渲染通道:

cpp 复制代码
    VkRenderPass renderPass = nullptr;
    VkResult result = vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass);
    if (result != VK_SUCCESS)
    {
        printf("Failed to create render-pass.\n");
        return 1;
    }

1.2 vsg::RenderPass的创建

在5.1章(vulkanscenegraph显示倾斜模型(5.1)-窗口创建-CSDN博客)中深入探讨了窗口的创建过程,vsg中窗口相关的基类为vsg::Window,RenderPass的创建与窗口密切相关,如下为vsg::Window中初始化vsg::RenderPass相关代码:

cpp 复制代码
void Window::_initRenderPass()
{
    if (!_device) _initDevice();

    bool requiresDepthRead = (_traits->depthImageUsage & VK_IMAGE_USAGE_TRANSFER_SRC_BIT) != 0;

    if (_framebufferSamples == VK_SAMPLE_COUNT_1_BIT)
    {
        _renderPass = vsg::createRenderPass(_device, _imageFormat.format, _depthFormat, requiresDepthRead);
    }
    else
    {
        _renderPass = vsg::createMultisampledRenderPass(_device, _imageFormat.format, _depthFormat, _framebufferSamples, requiresDepthRead);
    }
}

在场景创建的过程中,vsg::RenderGraph 在初始化时会获取或创建 vsg::RenderPass 对象,vsg::RenderPass有三个来源,示例代码如下:

cpp 复制代码
RenderPass* RenderGraph::getRenderPass()
{
    if (renderPass)
    {
        return renderPass;
    }
    else if (framebuffer)
    {
        return framebuffer->getRenderPass();
    }
    else if (window)
    {
        return window->getOrCreateRenderPass();
    }
    return nullptr;
}

2 vsg::FrameBuffer

vsg::FrameBuffer​封装了VkFramebuffer,用作与窗口关联的渲染目标或用于渲染到纹理。

2.1 VkFrameBuffer的创建

VkFrameBuffer的创建分为VkImageView的创建与VkFrameBuffer的创建两个过程。

获取所有图像:从VkSwapChain中获取VkImage分两步调用vkGetSwapchainImagesKHR函数:首先传入nullptr作为第四个参数以获取交换链中的图像数量,然后再次调用该函数来实际获取所有VkImage对象。

cpp 复制代码
    uint32_t imageCount = 0;
    vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr);

    std::vector<VkImage> swapChainImages(imageCount);
    vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data());

创建图像视图:在vulkan中,图像视图提供了对图像的访问接口,图像视图的创建示例代码如下:

cpp 复制代码
    VkImageViewCreateInfo createInfo
    {
        VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
        nullptr, 0,
        nullptr, VK_IMAGE_VIEW_TYPE_2D,
        VK_FORMAT_B8G8R8A8_SRGB,
        { VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
          VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY },
        { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }
    };

    std::vector<VkImageView> swapchainImageViews(swapChainImages.size());
    for (size_t i = 0; i < swapchainImageViews.size(); ++i)
    {
        createInfo.image = swapChainImages[i];
        VkResult result = vkCreateImageView(
            app.getDevice(), &createInfo, nullptr, &swapchainImageViews[i]);
        if (result != VK_SUCCESS)
        {
            printf("Failed to create image view: %zd.\n", i);
            return 1;
        }
    }

创建VkFramebuffer:下述代码表示为交换链(Swapchain)的每个图像视图(VkImageView)创建一个帧缓冲(VkFramebuffer),并建立它们与渲染流程(VkRenderPass)的关联,其中1024和768分别表示对应影像的宽高。VkRenderPass 定义了渲染流程的附件结构和子流程(Subpass),但不绑定具体图像,VkFramebuffer 将 VkRenderPass 的抽象附件绑定到具体的 VkImageView,使渲染操作可以实际写入图像。

cpp 复制代码
    std::vector<VkFramebuffer> framebuffers(swapchainImageViews.size());
    for (size_t i = 0; i < framebuffers.size(); ++i)
    {
        VkImageView attachments[] = { swapchainImageViews[i] };
        VkFramebufferCreateInfo framebufferInfo
        {
            VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
            nullptr, 0,
            renderPass,
            1, attachments,
            1024, 768, 1
        };
        vkCreateFramebuffer(device, &framebufferInfo, nullptr, &framebuffers[i]);
    }

2.2 vsg::FrameBuffer的封装

在5.2章(vulkanscenegraph显示倾斜模型(5.2)-交换链-CSDN博客)中深入探讨了交换链的创建过程,与交换链创建紧密相关的有VkImageView与VkFramebuffer的创建。

vsg中Window.cpp文件中372-339行代码如下,为Window::buildSwapchain()函数中,创建vsg::ImageView与vsg::Framebuffer的部分。

cpp 复制代码
auto& imageViews = _swapchain->getImageViews();

_availableSemaphore = vsg::Semaphore::create(_device, _traits->imageAvailableSemaphoreWaitFlag);

size_t initial_indexValue = imageViews.size();
for (size_t i = 0; i < imageViews.size(); ++i)
{
    vsg::ImageViews attachments;
    if (_multisampleImageView)
    {
        attachments.push_back(_multisampleImageView);
    }
    attachments.push_back(imageViews[i]);

    if (_multisampleDepthImageView)
    {
        attachments.push_back(_multisampleDepthImageView);
    }
    attachments.push_back(_depthImageView);

    ref_ptr<Framebuffer> fb = Framebuffer::create(_renderPass, attachments, _extent2D.width, _extent2D.height, 1);

    ref_ptr<Semaphore> ias = vsg::Semaphore::create(_device, _traits->imageAvailableSemaphoreWaitFlag);

    //_frames.push_back({multisampling ? _multisampleImageView : imageViews[i], fb, ias});
    _frames.push_back({imageViews[i], fb, ias});
    _indices.push_back(initial_indexValue);
}

如下代码为vsg中Framebuffer的构造函数,在构造函数中实现了VkFramebuffer的创建。

cpp 复制代码
Framebuffer::Framebuffer(ref_ptr<RenderPass> renderPass, const ImageViews& attachments, uint32_t width, uint32_t height, uint32_t layers) :
    _device(renderPass->device),
    _renderPass(renderPass),
    _attachments(attachments),
    _width(width),
    _height(height),
    _layers(layers)
{
    auto deviceID = _device->deviceID;

    std::vector<VkImageView> vk_attachments;
    for (auto& attachment : attachments)
    {
        vk_attachments.push_back(attachment->vk(deviceID));
    }

    VkFramebufferCreateInfo framebufferInfo = {};
    framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
    framebufferInfo.flags = 0;
    framebufferInfo.renderPass = *_renderPass;
    framebufferInfo.attachmentCount = static_cast<uint32_t>(vk_attachments.size());
    framebufferInfo.pAttachments = vk_attachments.data();
    framebufferInfo.width = width;
    framebufferInfo.height = height;
    framebufferInfo.layers = layers;

    if (VkResult result = vkCreateFramebuffer(*_device, &framebufferInfo, nullptr, &_framebuffer); result != VK_SUCCESS)
    {
        throw Exception{"Error: vsg::Framebuffer::create(...) Failed to create VkFramebuffer.", result};
    }
}

3 vsg::RenderGraph

本章在上章(vulkanscenegraph显示倾斜模型(5.5)-CommandGraph的创建-CSDN博客)场景图构建基础上,继续深入vsg::RenderGraph的创建:

vsg::RenderGraph中通过访问者模式(accept函数)实现场景的遍历,进而实现命令的录制,其核心过程如下(RenderGraph.cpp中146-151行代码):

cpp 复制代码
    vkCmdBeginRenderPass(vk_commandBuffer, &renderPassInfo, contents);

    // traverse the subgraph to place commands into the command buffer.
    traverse(recordTraversal);

    vkCmdEndRenderPass(vk_commandBuffer);

文末:本章在上一篇文章的基础上,继续深入vsg场景图中另一关键的节点vsg::RenderGraph,RecordTraversal遍历场景图过程中,RenderGraph的子节点会在vkCmdBeginRenderPass和vkCmdEndRenderPass之间被遍历访问。同时本章介绍了vsg::FrameBuffer(封装VkFramebuffer)、vsg::RenderPass(封装VkRenderPass)和vsg::ImageView(封装VkImageView)三个关键概念,vsg::RenderPass定义渲染流程,vsg::ImageView提供图像资源访问接口,vsg::FrameBuffer将二者绑定为具体渲染目标。下章将介绍vulkan图形渲染过程中另一关键对象VkPipeline,以及vsg对其的封装vsg::GraphicsPipeline。

相关推荐
拉不动的猪2 分钟前
vue自定义指令的几个注意点
前端·javascript·vue.js
yanyu-yaya3 分钟前
react redux的学习,单个reducer
前端·javascript·react.js
加瓦点灯31 分钟前
观察者模式:解耦对象间的依赖关系
开发语言·javascript·观察者模式
愚润求学34 分钟前
Linux开发工具——apt
linux·服务器·开发语言
程序员小赵同学35 分钟前
AI Agent设计模式二:Parallelization
开发语言·python·设计模式
时光话41 分钟前
Lua:第1-4部分 语言基础
开发语言·lua
欧宸雅1 小时前
Clojure语言的持续集成
开发语言·后端·golang
胡斌附体1 小时前
qt tcpsocket编程遇到的并发问题
开发语言·网络·qt·并发编程·tcpsocket
z_mazin1 小时前
Chrome开发者工具实战:调试三剑客
前端·javascript·chrome·网络爬虫
学c真好玩2 小时前
4.3python操作ppt
开发语言·python·powerpoint