以OpenGL/ES视角介绍gfx-hal(Vulkan) Shader/Program接口使用

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

内容简介:文档列表见:【草稿】接下来的故事围绕

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

【草稿】

接下来的故事围绕 RawCommandBuffer 定义的两个核心方法展开:

bind_graphics_pipeline(&GraphicsPipeline)
bind_graphics_descriptor_sets(PipelineLayout, DescriptorSet)
复制代码

函数原型:

/// Bind a graphics pipeline.
///
/// # Errors
///
/// This function does not return an error. Invalid usage of this function
/// will result in an error on `finish`.
///
/// - Command buffer must be in recording state.
/// - Only queues with graphics capability support this function.
fn bind_graphics_pipeline(&mut self, pipeline: &B::GraphicsPipeline);

/// Takes an iterator of graphics `DescriptorSet`'s, and binds them to the command buffer.
/// `first_set` is the index that the first descriptor is mapped to in the command buffer.
fn bind_graphics_descriptor_sets<I, J>(
    &mut self,
    layout: &B::PipelineLayout,
    first_set: usize,
    sets: I,
    offsets: J,
) where
    I: IntoIterator,
    I::Item: Borrow<B::DescriptorSet>,
    J: IntoIterator,
    J::Item: Borrow<DescriptorSetOffset>;
复制代码

这两个方法涉及三个重要数据结构:GraphicsPipeline、PipelineLayout、DescriptorSet,它们的创建顺序是相反的,从后到前。下面逐一介绍。

DescriptorSet

初始化流程如下:

  1. 用pso::DescriptorSetLayoutBinding分别描述Shader声明的Uniform变量并组成数组,比如texture2D、sampler和UniformBlock中的每个变量。
  2. 传递前面的pso::DescriptorSetLayoutBinding数组到Device创建DescriptorSetLayout。
  3. 用pso::DescriptorRangeDesc 汇总描述 Shader声明的Set数量与所有Uniform变量并组成数组。
  4. 传递前面的pso::DescriptorRangeDesc数组到Device创建DescriptorPool。
  5. 传递前面的DescriptorSetLayout到DescriptorPool创建DescriptorSet, 此时DescriptorSet并无实际数据
  6. 通过Device写入实际数据到DescriptorSet

DescriptorSet初始化流程示例

假设Fragment Shader定义如下uniform变量:

layout(set = 0, binding = 0) uniform texture2D u_texture;
layout(set = 0, binding = 1) uniform sampler u_sampler;

layout(set = 0, binding = 2) uniform texture2D u_texture2;
layout(set = 0, binding = 3) uniform sampler u_sampler2;

layout(set = 0, binding = 4) uniform UBOCol {
    vec4 color;
} color_dat;
复制代码

那么,对应的DescriptorSetLayout和DescriptorSetLayoutBinding为:

let set_layout = device
    .create_descriptor_set_layout(
        &[
            pso::DescriptorSetLayoutBinding {
                binding: 0,
                ty: pso::DescriptorType::SampledImage,
                count: 1,
                stage_flags: ShaderStageFlags::FRAGMENT,
            },
            pso::DescriptorSetLayoutBinding {
                binding: 1,
                ty: pso::DescriptorType::Sampler,
                count: 1,
                stage_flags: ShaderStageFlags::FRAGMENT,
            },
            pso::DescriptorSetLayoutBinding {
                binding: 2,
                ty: pso::DescriptorType::SampledImage,
                count: 1,
                stage_flags: ShaderStageFlags::FRAGMENT,
            },
            pso::DescriptorSetLayoutBinding {
                binding: 3,
                ty: pso::DescriptorType::Sampler,
                count: 1,
                stage_flags: ShaderStageFlags::FRAGMENT,
            },
            pso::DescriptorSetLayoutBinding {
                binding: 4,
                ty: pso::DescriptorType::UniformBuffer,
                count: 1,
                stage_flags: ShaderStageFlags::FRAGMENT,
            },
        ],
        &[], // Ignore immutable_samplers
    )
    .expect("Can't create descriptor set layout");

let mut desc_pool = device
    .create_descriptor_pool(
        1, // sets
        &[
            pso::DescriptorRangeDesc {
                ty: pso::DescriptorType::SampledImage,
                count: 2,
            },
            pso::DescriptorRangeDesc {
                ty: pso::DescriptorType::Sampler,
                count: 2,
            },
            pso::DescriptorRangeDesc {
                ty: pso::DescriptorType::UniformBuffer,
                count: 1,
            },
        ],
    )
    .expect("Can't create descriptor pool");
// 分配资源
let desc_set/* B::DescriptorSet */ = desc_pool.allocate_set(&set_layout).unwrap();
// 写入实际数据
device.write_descriptor_sets(vec![
    pso::DescriptorSetWrite {
        set: &desc_set,
        binding: 0,
        array_offset: 0,
        descriptors: Some(pso::Descriptor::Image(&image_srv, image::Layout::Undefined)),
    },
    pso::DescriptorSetWrite {
        set: &desc_set,
        binding: 1,
        array_offset: 0,
        descriptors: Some(pso::Descriptor::Sampler(&sampler)),
    },
    pso::DescriptorSetWrite {
        set: &desc_set,
        binding: 2,
        array_offset: 0,
        descriptors: Some(pso::Descriptor::Image(&image_srv2, image::Layout::Undefined)),
    },
    pso::DescriptorSetWrite {
        set: &desc_set,
        binding: 3,
        array_offset: 0,
        descriptors: Some(pso::Descriptor::Sampler(&sampler2)),
    },    
    pso::DescriptorSetWrite {
        set: &desc_set,
        binding: 4,
        array_offset: 0,
        descriptors: Some(pso::Descriptor::Buffer(&uniform_buffer, Some(0)..Some(1))),
    },
]);
复制代码

相关操作的函数原型

/// Create a descriptor set layout.
///
/// A descriptor set layout object is defined by an array of zero or more descriptor bindings.
/// Each individual descriptor binding is specified by a descriptor type, a count (array size)
/// of the number of descriptors in the binding, a set of shader stages that **can** access the
/// binding, and (if using immutable samplers) an array of sampler descriptors.
fn create_descriptor_set_layout<I, J>(
    &self,
    bindings: I,
    immutable_samplers: J,
) -> Result<B::DescriptorSetLayout, OutOfMemory>
where
    I: IntoIterator,
    I::Item: Borrow<pso::DescriptorSetLayoutBinding>,
    J: IntoIterator,
    J::Item: Borrow<B::Sampler>;

/// Create a descriptor pool.
///
/// Descriptor pools allow allocation of descriptor sets.
/// The pool can't be modified directly, only through updating descriptor sets.
fn create_descriptor_pool<I>(&self, max_sets: usize, descriptor_ranges: I) -> Result<B::DescriptorPool, OutOfMemory>
where
    I: IntoIterator,
    I::Item: Borrow<pso::DescriptorRangeDesc>;

/// Allocate a descriptor set from the pool.
///
/// The descriptor set will be allocated from the pool according to the corresponding set layout. However,
/// specific descriptors must still be written to the set before use using a [`DescriptorSetWrite`] or
/// [`DescriptorSetCopy`].
/// 
/// Descriptors will become invalid once the pool is reset. Usage of invalidated descriptor sets results
/// in undefined behavior.
/// 
/// [`DescriptorSetWrite`]: struct.DescriptorSetWrite.html
/// [`DescriptorSetCopy`]: struct.DescriptorSetCopy.html
fn allocate_set(&mut self, layout: &B::DescriptorSetLayout) -> Result<B::DescriptorSet, AllocationError> {
    let mut sets = Vec::with_capacity(1);
    self.allocate_sets(Some(layout), &mut sets)
        .map(|_| sets.remove(0))
}

/// Allocate one or multiple descriptor sets from the pool.
///
/// The descriptor set will be allocated from the pool according to the corresponding set layout. However,
/// specific descriptors must still be written to the set before use using a [`DescriptorSetWrite`] or
/// [`DescriptorSetCopy`].
/// 
/// Each descriptor set will be allocated from the pool according to the corresponding set layout.
/// Descriptors will become invalid once the pool is reset. Usage of invalidated descriptor sets results
/// in undefined behavior.
/// 
/// [`DescriptorSetWrite`]: struct.DescriptorSetWrite.html
/// [`DescriptorSetCopy`]: struct.DescriptorSetCopy.html
fn allocate_sets<I>(&mut self, layouts: I, sets: &mut Vec<B::DescriptorSet>) -> Result<(), AllocationError>
where
    I: IntoIterator,
    I::Item: Borrow<B::DescriptorSetLayout>,
{
    let base = sets.len();
    for layout in layouts {
        match self.allocate_set(layout.borrow()) {
            Ok(set) => sets.push(set),
            Err(e) => {
                self.free_sets(sets.drain(base ..));
                return Err(e)
            }
        }
    }
    Ok(())
}

/// Specifying the parameters of a descriptor set write operation
fn write_descriptor_sets<'a, I, J>(&self, write_iter: I)
where
    I: IntoIterator<Item = pso::DescriptorSetWrite<'a, B, J>>,
    J: IntoIterator,
    J::Item: Borrow<pso::Descriptor<'a, B>>;
复制代码

DescriptorSet相关数据结构定义

DescriptorSetLayout定义

A descriptor set layout object is defined by an array of zero or more descriptor bindings. Each individual descriptor binding is specified by a descriptor type, a count (array size) of the number of descriptors in the binding, a set of shader stages that can access the binding, and (if using immutable samplers) an array of sampler descriptors.

www.khronos.org/registry/vu…

DescriptorSetLayoutBinding定义

Structure specifying a descriptor set layout binding

www.khronos.org/registry/vu…

Immutable Samplers定义

todo

DescriptorSetWrite

/// Writes the actual descriptors to be bound into a descriptor set. Should be provided
/// to the `write_descriptor_sets` method of a `Device`.
#[allow(missing_docs)]
pub struct DescriptorSetWrite<'a, B: Backend, WI>
    where WI: IntoIterator,
          WI::Item: Borrow<Descriptor<'a, B>>
{
    pub set: &'a B::DescriptorSet,
    /// *Note*: when there is more descriptors provided than
    /// array elements left in the specified binding starting
    /// at specified, offset, the updates are spilled onto
    /// the next binding (starting with offset 0), and so on.
    pub binding: DescriptorBinding,
    pub array_offset: DescriptorArrayIndex,
    pub descriptors: WI,
}
复制代码

PipelineLayout

初始化流程如下:

  1. 由前面创建的DescriptorSetLayout + pso::ShaderStageFlags向Device申请创建PipelineLayout实例。

PipelineLayout初始化流程示例

let pipeline_layout = device
    .create_pipeline_layout(
        std::iter::once(&set_layout),
        &[(pso::ShaderStageFlags::VERTEX, 0..8)],
    )
    .expect("Can't create pipeline layout");
复制代码

相关操作的函数原型

/// Create a new pipeline layout object.
///
/// # Arguments
///
/// * `set_layouts` - Descriptor set layouts
/// * `push_constants` - Ranges of push constants. A shader stage may only contain one push
///     constant block. The length of the range indicates the number of u32 constants occupied
///     by the push constant block.
///
/// # PipelineLayout
///
/// Access to descriptor sets from a pipeline is accomplished through a *pipeline layout*.
/// Zero or more descriptor set layouts and zero or more push constant ranges are combined to
/// form a pipeline layout object which describes the complete set of resources that **can** be
/// accessed by a pipeline. The pipeline layout represents a sequence of descriptor sets with
/// each having a specific layout. This sequence of layouts is used to determine the interface
/// between shader stages and shader resources. Each pipeline is created using a pipeline layout.
fn create_pipeline_layout<IS, IR>(
    &self,
    set_layouts: IS,
    push_constant: IR,
) -> Result<B::PipelineLayout, OutOfMemory>
where
    IS: IntoIterator,
    IS::Item: Borrow<B::DescriptorSetLayout>,
    IR: IntoIterator,
    IR::Item: Borrow<(pso::ShaderStageFlags, Range<u32>)>;
复制代码

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

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

Understanding Machine Learning

Understanding Machine Learning

Shai Shalev-Shwartz、Shai Ben-David / Cambridge University Press / 2014 / USD 48.51

Machine learning is one of the fastest growing areas of computer science, with far-reaching applications. The aim of this textbook is to introduce machine learning, and the algorithmic paradigms it of......一起来看看 《Understanding Machine Learning》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

SHA 加密
SHA 加密

SHA 加密工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具