gfx-hal 逐帧渲染流程介绍

栏目: 后端 · 发布时间: 5年前

内容简介:文档列表见:可见,OpenGL / ES的接口屏蔽了绝大部分细节,

文档列表见: Rust 移动端跨平台复杂图形渲染项目开发系列总结(目录)

gfx-hal 接口以1:1模仿Vulkan,下面改用Vulkan接口作说明。由于Vulkan接口粒度过细,比OpenGL / ES难学数倍。根据个人经验,对于移动端图形开发者,照着OpenGL ES的接口讲解Vulkan可降低学习难度。

OpenGL / ES 逐帧渲染流程示例

// 准备渲染目标环境
glBindFramebuffer();
glFramebufferTexture2D(); glCheckFramebufferStatus(); // 假如渲染到纹理
glViewport(x, y, width, height);
// 准备渲染目标环境
glUseProgram(x);
glBindBuffer(i)
loop i in 0..VertexVarCount {
    glEnableVertexAttribArray(i);
    glVertexAttribPointer(i, ...); 
}
loop i in 0..UniformVarCount {
    switch UniformType {
        case NoTexture: glUniformX(i, data); break;
        case Texture: {
            glActiveTexture(j);
            glBindTexture(type, texture_name);
            glUniform1i(location, j);
            break;
        }
        default:ERROR();
    }
}
// 配置其他Fragment操作,比如glBlend, glStencil
glDrawArrays/Elements/ArraysInstanced...
// 到此完成Draw Call,视情况调用EGL函数交换前后帧缓冲区,非GL函数,
// 渲染到纹理则无此操作。
// 为了不干扰后续绘制,恢复刚才设置的Fragment操作为默认值。
eglSwapbuffers()/[EAGLContext presentRenderbuffer]; 
复制代码

可见,OpenGL / ES的接口屏蔽了绝大部分细节, 整体代码量显得很少,但初学时也不好理解 ,用久了就成套路,觉得就该这样,以致于第一次接触Vulkan发现很多细节之前完全不了解,有点懵。

gfx-hal逐帧渲染到视图的调用流程介绍

gfx-hal(Vulkan)逐帧渲染到视图的核心调用流程如下所示:

EventSource ->[CommandPool -> ComanndBuffer
                -> Submit -> Submission
                -> QueueGroup -> CommandQueue]
-> GraphicsHardware
复制代码

说明:

  • EventSource:表示信号源,比如相机回调一帧图像、屏幕的vsync信号、用户输入等。
  • CommandQueue:用于执行不同类型任务的队列,比如渲染任务、计算任务。
  • QueueGroup:CommandQueue集合
  • GraphicsHardware:图形硬件

具体流程代码:

  • 重置Fence,给后面提交Submission到队列使用。

    device.reset_fence(&frame_fence);
    复制代码
  • 重置CommandPool

    command_pool.reset();
    复制代码
  • 从SwapChain获取Image索引

    let frame = swap_chain.acquire_image(!0, FrameSync::Semaphore(&mut frame_semaphore));
    复制代码
  • 通过CommandPool创建、配置CommandBuffer,然后得到Submit对象

    let mut cmd_buffer = command_pool.acquire_command_buffer(false);
    // 一系列类似OpenGL / ES的Fragment操作、绑定数据到Program的配置
    // 两个值得注意的Pipeline操作
    cmd_buffer.bind_graphics_pipeline(&pipeline);
    cmd_buffer.bind_graphics_descriptor_sets(&pipeline_layout, 0, Some(&desc_set), &[]);
    // 联合RenderPass的操作
    let mut encoder = cmd_buffer.begin_render_pass_inline(&render_pass,...);
    let submit = cmd_buffer.finish()
    复制代码
  • 通过Submit创建Submission

    let submission = Submission::new()
        .wait_on(&[(&frame_semaphore, PipelineStage::BOTTOM_OF_PIPE)])
        .submit(Some(submit));
    复制代码
  • 提交Submission到队列

    queue.submit(submission, Some(&mut frame_fence));
    复制代码
  • 等待CPU编码完成

    device.wait_for_fence(&frame_fence, !0);
    复制代码
  • 交换前后帧缓冲区

    swap_chain.present(&mut queue_group.queues[0], frame, &[])
    复制代码

    配置CommandBuffer的进一步介绍

OpenGL / ES 2/3.x没 CommandPoolCommandBuffer 数据结构,除了最新的OpenGL小版本才加入了SPIR-V和Command,但OpenGL ES还没更新。Metal的 CommandBuffer 接口定义不同于Vulkan。Metal创建 MTLCommandBuffer ,由Buffer与 RenderPassDescriptor 一起创建出 Enconder ,然后打包本次渲染相关的资源,最后提交Buffer到队列让GPU执行。Vulkan基本把Metal的Encoder操作放到CommandBuffer,只留了很薄的Encoder操作。

总体流程:

  • 由Command Pool分配可用Command Buffer
  • 配置viewport等信息
  • 设置输出目标
  • 设置绘制方式, draw / draw_indexed / draw_indirect 等等
  • 结束配置

代码示例如下:

let submit = {
    // 从缓冲区中取出一个实际为RawCommandBuffer的实例,加上线程安全对象,组装成CommandBuffer实例,这是线程安全的
    let mut cmd_buffer = command_pool.acquire_command_buffer(false);

    cmd_buffer.set_viewports(0, &[viewport.clone()]);
    cmd_buffer.set_scissors(0, &[viewport.rect]);
    cmd_buffer.bind_graphics_pipeline(&pipeline.as_ref().unwrap());
    cmd_buffer.bind_vertex_buffers(0, pso::VertexBufferSet(vec![(&vertex_buffer, 0)]));
    cmd_buffer.bind_graphics_descriptor_sets(&pipeline_layout, 0, Some(&desc_set)); //TODO

    {
        let mut encoder = cmd_buffer.begin_render_pass_inline(
            &render_pass,
            &framebuffers[frame.id()],
            viewport.rect,
            &[command::ClearValue::Color(command::ClearColor::Float([0.8, 0.8, 0.8, 1.0]))],
        );
        encoder.draw(0..6, 0..1);
    }

    cmd_buffer.finish()
};
复制代码

前面代码显示了CommandBuffer两个很关键的操作: bind_graphics_pipeline(GraphicsPipeline)bind_graphics_descriptor_sets(PipelineLayout, DescriptorSet) 。GraphicsPipeline相当于OpenGL / ES的Program, PipelineLayoutDescriptorSet 描述了Shader的Uniform变量如何读取Buffer的数据。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Algorithms Illuminated (Part 2)

Algorithms Illuminated (Part 2)

Tim Roughgarden / Soundlikeyourself Publishing, LLC / 2018-8-5 / USD 17.99

Algorithms are the heart and soul of computer science. Their applications range from network routing and computational genomics to public-key cryptography and machine learning. Studying algorithms can......一起来看看 《Algorithms Illuminated (Part 2)》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换