以OpenGL/ES視角介紹gfx-hal(Vulkan) Shader/Program介面使用
文件列表見: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