文档列表见: 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
初始化流程如下:
用 pso::DescriptorSetLayoutBinding 分别描述 Shader 声明的 Uniform 变量并组成数组, 比如 texture2D,sampler 和 UniformBlock 中的每个变量.
传递前面的 pso::DescriptorSetLayoutBinding 数组到 Device 创建 DescriptorSetLayout.
用 pso::DescriptorRangeDesc 汇总描述 Shader 声明的 Set 数量与所有 Uniform 变量并组成数组.
传递前面的 pso::DescriptorRangeDesc 数组到 Device 创建 DescriptorPool.
传递前面的 DescriptorSetLayout 到 DescriptorPool 创建 DescriptorSet, 此时 DescriptorSet 并无实际数据.
通过 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.
DescriptorSetLayoutBinding 定义
Structure specifying a descriptor set layout binding
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
初始化流程如下:
由前面创建的 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>)>;
PipelineLayout 相关数据结构定义
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.
GraphicsPipeline
初始化流程如下:
todo
来源: https://juejin.im/post/5c069bb3f265da617974998b