1176: Rewrite RenderPipelineDescriptor according to aspects r=grovesNL,cwfitzgerald a=kvark

**Connections**
Fixes #1166

**Description**
Totally re-imagines the structure tree for describing the rendering pipeline, based on the aspects instead of logical steps.

**Testing**
Just local testing. I don't think there is any concern that these changes might not work, they just need the clients to adapt.

Co-authored-by: Dzmitry Malyshau <kvarkus@gmail.com>
This commit is contained in:
bors[bot] 2021-01-28 06:21:57 +00:00 committed by GitHub
commit 63b0d36074
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 387 additions and 226 deletions

View File

@ -1,5 +1,26 @@
# Change Log
## v0.7 (TBD)
- Major API changes:
- `RenderPipelineDescriptor`
- `BindingType`
- Features:
- (beta) WGSL support, including the ability to bypass SPIR-V entirely
- (beta) implicit bind group layout support
- timestamp and pipeline statistics queries
- ETC2 and ASTC compressed textures
- (beta) targeting WASM with WebGL backend
- reduced dependencies
- Native-only:
- clamp-to-border addressing
- polygon fill modes
- query a format for extra capabilities
- `f64` support in shaders
- Validation:
- shader interface
- render pipeline descriptor
- vertex buffers
## v0.6 (2020-08-17)
- Crates:
- C API is moved to [another repository](https://github.com/gfx-rs/wgpu-native)

View File

@ -18,7 +18,7 @@
CreateComputePipeline(Id(0, 1, Empty), (
label: None,
layout: Some(Id(0, 1, Empty)),
compute_stage: (
stage: (
module: Id(0, 1, Empty),
entry_point: "main",
),

View File

@ -56,42 +56,24 @@
CreateRenderPipeline(Id(0, 1, Empty), (
label: None,
layout: Some(Id(0, 1, Empty)),
vertex_stage: (
module: Id(0, 1, Empty),
entry_point: "vs_main",
),
fragment_stage: Some((
module: Id(0, 1, Empty),
entry_point: "fs_main",
)),
rasterization_state: None,
primitive_topology: TriangleList,
color_states: [
(
format: Rgba8Unorm,
alpha_blend: (
src_factor: One,
dst_factor: Zero,
operation: Add,
),
color_blend: (
src_factor: One,
dst_factor: Zero,
operation: Add,
),
write_mask: (
bits: 15,
),
vertex: (
stage: (
module: Id(0, 1, Empty),
entry_point: "vs_main",
),
],
depth_stencil_state: None,
vertex_state: (
index_format: None,
vertex_buffers: [],
buffers: [],
),
sample_count: 1,
sample_mask: 4294967295,
alpha_to_coverage_enabled: false,
fragment: Some((
stage: (
module: Id(0, 1, Empty),
entry_point: "fs_main",
),
targets: [
(
format: Rgba8Unorm,
),
],
)),
)),
Submit(1, [
RunRenderPass(

View File

@ -233,7 +233,7 @@ impl RenderBundleEncoder {
pipeline_layout_id = Some(pipeline.layout_id.value);
state.set_pipeline(
pipeline.index_format,
pipeline.strip_index_format,
&pipeline.vertex_strides,
&layout.bind_group_layout_ids,
&layout.push_constant_ranges,

View File

@ -1248,7 +1248,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
}
}
state.index.pipeline_format = pipeline.index_format;
state.index.pipeline_format = pipeline.strip_index_format;
let vertex_strides_len = pipeline.vertex_strides.len();
state.vertex.buffers_required = vertex_strides_len as u32;

View File

@ -177,14 +177,14 @@ pub fn map_primitive_topology(primitive_topology: wgt::PrimitiveTopology) -> hal
}
}
pub fn map_color_state_descriptor(desc: &wgt::ColorStateDescriptor) -> hal::pso::ColorBlendDesc {
pub fn map_color_target_state(desc: &wgt::ColorTargetState) -> hal::pso::ColorBlendDesc {
let color_mask = desc.write_mask;
let blend_state = if desc.color_blend != wgt::BlendDescriptor::REPLACE
|| desc.alpha_blend != wgt::BlendDescriptor::REPLACE
let blend_state = if desc.color_blend != wgt::BlendState::REPLACE
|| desc.alpha_blend != wgt::BlendState::REPLACE
{
Some(hal::pso::BlendState {
color: map_blend_descriptor(&desc.color_blend),
alpha: map_blend_descriptor(&desc.alpha_blend),
color: map_blend_state(&desc.color_blend),
alpha: map_blend_state(&desc.alpha_blend),
})
} else {
None
@ -215,7 +215,7 @@ fn map_color_write_flags(flags: wgt::ColorWrite) -> hal::pso::ColorMask {
value
}
fn map_blend_descriptor(blend_desc: &wgt::BlendDescriptor) -> hal::pso::BlendOp {
fn map_blend_state(blend_desc: &wgt::BlendState) -> hal::pso::BlendOp {
use hal::pso::BlendOp as H;
use wgt::BlendOperation as Bo;
match blend_desc.operation {
@ -256,9 +256,7 @@ fn map_blend_factor(blend_factor: wgt::BlendFactor) -> hal::pso::Factor {
}
}
pub fn map_depth_stencil_state_descriptor(
desc: &wgt::DepthStencilStateDescriptor,
) -> hal::pso::DepthStencilDesc {
pub fn map_depth_stencil_state(desc: &wgt::DepthStencilState) -> hal::pso::DepthStencilDesc {
hal::pso::DepthStencilDesc {
depth: if desc.is_depth_enabled() {
Some(hal::pso::DepthTest {
@ -290,9 +288,7 @@ pub fn map_depth_stencil_state_descriptor(
}
}
fn map_stencil_face(
stencil_state_face_desc: &wgt::StencilStateFaceDescriptor,
) -> hal::pso::StencilFace {
fn map_stencil_face(stencil_state_face_desc: &wgt::StencilStateFace) -> hal::pso::StencilFace {
hal::pso::StencilFace {
fun: map_compare_function(stencil_state_face_desc.compare),
op_fail: map_stencil_operation(stencil_state_face_desc.fail_op),
@ -774,12 +770,38 @@ pub fn map_wrap(address: wgt::AddressMode) -> hal::image::WrapMode {
}
}
pub fn map_rasterization_state_descriptor(
desc: &wgt::RasterizationStateDescriptor,
pub fn map_primitive_state_to_input_assembler(
desc: &wgt::PrimitiveState,
) -> hal::pso::InputAssemblerDesc {
hal::pso::InputAssemblerDesc {
primitive: map_primitive_topology(desc.topology),
with_adjacency: false,
restart_index: desc.strip_index_format.map(map_index_format),
}
}
pub fn map_primitive_state_to_rasterizer(
desc: &wgt::PrimitiveState,
depth_stencil: Option<&wgt::DepthStencilState>,
) -> hal::pso::Rasterizer {
use hal::pso;
let (depth_clamping, depth_bias) = match depth_stencil {
Some(dsd) => {
let bias = if dsd.has_depth_bias() {
Some(pso::State::Static(pso::DepthBias {
const_factor: dsd.depth_bias as f32,
slope_factor: dsd.depth_bias_slope_scale,
clamp: dsd.depth_bias_clamp,
}))
} else {
None
};
(dsd.clamp_depth, bias)
}
None => (false, None),
};
pso::Rasterizer {
depth_clamping: desc.clamp_depth,
depth_clamping,
polygon_mode: match desc.polygon_mode {
wgt::PolygonMode::Fill => pso::PolygonMode::Fill,
wgt::PolygonMode::Line => pso::PolygonMode::Line,
@ -794,23 +816,22 @@ pub fn map_rasterization_state_descriptor(
wgt::FrontFace::Ccw => pso::FrontFace::CounterClockwise,
wgt::FrontFace::Cw => pso::FrontFace::Clockwise,
},
depth_bias: if desc.depth_bias != 0
|| desc.depth_bias_slope_scale != 0.0
|| desc.depth_bias_clamp != 0.0
{
Some(pso::State::Static(pso::DepthBias {
const_factor: desc.depth_bias as f32,
slope_factor: desc.depth_bias_slope_scale,
clamp: desc.depth_bias_clamp,
}))
} else {
None
},
depth_bias,
conservative: false,
line_width: pso::State::Static(1.0),
}
}
pub fn map_multisample_state(desc: &wgt::MultisampleState) -> hal::pso::Multisampling {
hal::pso::Multisampling {
rasterization_samples: desc.count as _,
sample_shading: None,
sample_mask: desc.mask,
alpha_coverage: desc.alpha_to_coverage_enabled,
alpha_to_one: false,
}
}
pub fn map_index_format(index_format: wgt::IndexFormat) -> hal::IndexType {
match index_format {
wgt::IndexFormat::Uint16 => hal::IndexType::U16,

View File

@ -1778,15 +1778,12 @@ impl<B: GfxBackend> Device<B> {
ArrayVec::<[binding_model::BindEntryMap; MAX_BIND_GROUPS]>::new();
let io = validation::StageIo::default();
let pipeline_stage = &desc.compute_stage;
let (shader_module_guard, _) = hub.shader_modules.read(&mut token);
let entry_point_name = &pipeline_stage.entry_point;
let shader_module = shader_module_guard
.get(pipeline_stage.module)
.map_err(|_| {
pipeline::CreateComputePipelineError::Stage(validation::StageError::InvalidModule)
})?;
let entry_point_name = &desc.stage.entry_point;
let shader_module = shader_module_guard.get(desc.stage.module).map_err(|_| {
pipeline::CreateComputePipelineError::Stage(validation::StageError::InvalidModule)
})?;
let flag = wgt::ShaderStage::COMPUTE;
if let Some(ref interface) = shader_module.interface {
@ -1898,47 +1895,37 @@ impl<B: GfxBackend> Device<B> {
let mut derived_group_layouts =
ArrayVec::<[binding_model::BindEntryMap; MAX_BIND_GROUPS]>::new();
let samples = {
let sc = desc.sample_count;
if sc == 0 || sc > 32 || !conv::is_power_of_two(sc) {
return Err(pipeline::CreateRenderPipelineError::InvalidSampleCount(sc));
}
sc as u8
};
let color_states = &desc.color_states;
let depth_stencil_state = desc.depth_stencil_state.as_ref();
let rasterization_state = desc
.rasterization_state
let color_states = desc
.fragment
.as_ref()
.cloned()
.unwrap_or_default();
let rasterizer = conv::map_rasterization_state_descriptor(&rasterization_state);
.map_or(&[][..], |fragment| &fragment.targets);
let depth_stencil_state = desc.depth_stencil.as_ref();
let rasterizer =
conv::map_primitive_state_to_rasterizer(&desc.primitive, depth_stencil_state);
let mut io = validation::StageIo::default();
let mut validated_stages = wgt::ShaderStage::empty();
let desc_vbs = &desc.vertex_state.vertex_buffers;
let desc_vbs = &desc.vertex.buffers;
let mut vertex_strides = Vec::with_capacity(desc_vbs.len());
let mut vertex_buffers = Vec::with_capacity(desc_vbs.len());
let mut attributes = Vec::new();
for (i, vb_state) in desc_vbs.iter().enumerate() {
vertex_strides
.alloc()
.init((vb_state.stride, vb_state.step_mode));
.init((vb_state.array_stride, vb_state.step_mode));
if vb_state.attributes.is_empty() {
continue;
}
if vb_state.stride % wgt::VERTEX_STRIDE_ALIGNMENT != 0 {
if vb_state.array_stride % wgt::VERTEX_STRIDE_ALIGNMENT != 0 {
return Err(pipeline::CreateRenderPipelineError::UnalignedVertexStride {
index: i as u32,
stride: vb_state.stride,
stride: vb_state.array_stride,
});
}
vertex_buffers.alloc().init(hal::pso::VertexBufferDesc {
binding: i as u32,
stride: vb_state.stride as u32,
stride: vb_state.array_stride as u32,
rate: match vb_state.step_mode {
InputStepMode::Vertex => hal::pso::VertexInputRate::Vertex,
InputStepMode::Instance => hal::pso::VertexInputRate::Instance(1),
@ -1985,33 +1972,25 @@ impl<B: GfxBackend> Device<B> {
}
}
let input_assembler = hal::pso::InputAssemblerDesc {
primitive: conv::map_primitive_topology(desc.primitive_topology),
with_adjacency: false,
restart_index: None, //TODO
};
let input_assembler = conv::map_primitive_state_to_input_assembler(&desc.primitive);
let blender = hal::pso::BlendDesc {
logic_op: None, // TODO
targets: color_states
.iter()
.map(conv::map_color_state_descriptor)
.map(conv::map_color_target_state)
.collect(),
};
let depth_stencil = depth_stencil_state
.map(conv::map_depth_stencil_state_descriptor)
.unwrap_or_default();
let multisampling: Option<hal::pso::Multisampling> = if samples == 1 {
None
} else {
Some(hal::pso::Multisampling {
rasterization_samples: samples,
sample_shading: None,
sample_mask: desc.sample_mask as u64,
alpha_coverage: desc.alpha_to_coverage_enabled,
alpha_to_one: false,
})
let depth_stencil = match depth_stencil_state {
Some(dsd) => {
if dsd.clamp_depth && !self.features.contains(wgt::Features::DEPTH_CLAMPING) {
return Err(pipeline::CreateRenderPipelineError::MissingFeature(
wgt::Features::DEPTH_CLAMPING,
));
}
conv::map_depth_stencil_state(dsd)
}
None => hal::pso::DepthStencilDesc::default(),
};
// TODO
@ -2022,13 +2001,7 @@ impl<B: GfxBackend> Device<B> {
depth_bounds: None,
};
if rasterization_state.clamp_depth && !self.features.contains(wgt::Features::DEPTH_CLAMPING)
{
return Err(pipeline::CreateRenderPipelineError::MissingFeature(
wgt::Features::DEPTH_CLAMPING,
));
}
if rasterization_state.polygon_mode != wgt::PolygonMode::Fill
if desc.primitive.polygon_mode != wgt::PolygonMode::Fill
&& !self.features.contains(wgt::Features::NON_FILL_POLYGON_MODE)
{
return Err(pipeline::CreateRenderPipelineError::MissingFeature(
@ -2042,7 +2015,18 @@ impl<B: GfxBackend> Device<B> {
}
}
let (shader_module_guard, _) = hub.shader_modules.read(&mut token);
let samples = {
let sc = desc.multisample.count;
if sc == 0 || sc > 32 || !conv::is_power_of_two(sc) {
return Err(pipeline::CreateRenderPipelineError::InvalidSampleCount(sc));
}
sc as u8
};
let multisampling = if samples == 1 {
None
} else {
Some(conv::map_multisample_state(&desc.multisample))
};
let rp_key = RenderPassKey {
colors: color_states
@ -2081,17 +2065,18 @@ impl<B: GfxBackend> Device<B> {
}),
};
let (shader_module_guard, _) = hub.shader_modules.read(&mut token);
let vertex = {
let entry_point_name = &desc.vertex_stage.entry_point;
let stage = &desc.vertex.stage;
let flag = wgt::ShaderStage::VERTEX;
let shader_module =
shader_module_guard
.get(desc.vertex_stage.module)
.map_err(|_| pipeline::CreateRenderPipelineError::Stage {
flag,
error: validation::StageError::InvalidModule,
})?;
let shader_module = shader_module_guard.get(stage.module).map_err(|_| {
pipeline::CreateRenderPipelineError::Stage {
flag,
error: validation::StageError::InvalidModule,
}
})?;
if let Some(ref interface) = shader_module.interface {
let provided_layouts = match desc.layout {
@ -2108,7 +2093,7 @@ impl<B: GfxBackend> Device<B> {
.check_stage(
provided_layouts.as_ref().map(|p| p.as_slice()),
&mut derived_group_layouts,
&entry_point_name,
&stage.entry_point,
flag,
io,
)
@ -2117,23 +2102,24 @@ impl<B: GfxBackend> Device<B> {
}
hal::pso::EntryPoint::<B> {
entry: &entry_point_name, // TODO
entry: &stage.entry_point,
module: &shader_module.raw,
specialization: hal::pso::Specialization::EMPTY,
}
};
let fragment = match &desc.fragment_stage {
Some(stage) => {
let entry_point_name = &stage.entry_point;
let fragment = match &desc.fragment {
Some(fragment) => {
let entry_point_name = &fragment.stage.entry_point;
let flag = wgt::ShaderStage::FRAGMENT;
let shader_module = shader_module_guard.get(stage.module).map_err(|_| {
pipeline::CreateRenderPipelineError::Stage {
flag,
error: validation::StageError::InvalidModule,
}
})?;
let shader_module =
shader_module_guard
.get(fragment.stage.module)
.map_err(|_| pipeline::CreateRenderPipelineError::Stage {
flag,
error: validation::StageError::InvalidModule,
})?;
let provided_layouts = match desc.layout {
Some(pipeline_layout_id) => Some(Device::get_introspection_bind_group_layouts(
@ -2199,7 +2185,7 @@ impl<B: GfxBackend> Device<B> {
}
}
}
let last_stage = match desc.fragment_stage {
let last_stage = match desc.fragment {
Some(_) => wgt::ShaderStage::FRAGMENT,
None => wgt::ShaderStage::VERTEX,
};
@ -2310,7 +2296,7 @@ impl<B: GfxBackend> Device<B> {
},
pass_context,
flags,
index_format: desc.vertex_state.index_format,
strip_index_format: desc.primitive.strip_index_format,
vertex_strides,
life_guard: LifeGuard::new(desc.label.borrow_or_default()),
};

View File

@ -11,7 +11,6 @@ use crate::{
};
use std::borrow::Cow;
use thiserror::Error;
use wgt::{BufferAddress, IndexFormat, InputStepMode};
#[derive(Debug)]
pub enum ShaderModuleSource<'a> {
@ -99,7 +98,7 @@ pub struct ComputePipelineDescriptor<'a> {
/// The layout of bind groups for this pipeline.
pub layout: Option<PipelineLayoutId>,
/// The compiled compute stage and its entry point.
pub compute_stage: ProgrammableStageDescriptor<'a>,
pub stage: ProgrammableStageDescriptor<'a>,
}
#[derive(Clone, Debug, Error)]
@ -134,24 +133,35 @@ impl<B: hal::Backend> Resource for ComputePipeline<B> {
#[derive(Clone, Debug)]
#[cfg_attr(feature = "trace", derive(serde::Serialize))]
#[cfg_attr(feature = "replay", derive(serde::Deserialize))]
pub struct VertexBufferDescriptor<'a> {
pub struct VertexBufferLayout<'a> {
/// The stride, in bytes, between elements of this buffer.
pub stride: BufferAddress,
pub array_stride: wgt::BufferAddress,
/// How often this vertex buffer is "stepped" forward.
pub step_mode: InputStepMode,
pub step_mode: wgt::InputStepMode,
/// The list of attributes which comprise a single vertex.
pub attributes: Cow<'a, [wgt::VertexAttributeDescriptor]>,
pub attributes: Cow<'a, [wgt::VertexAttribute]>,
}
/// Describes vertex input state for a render pipeline.
/// Describes the vertex process in a render pipeline.
#[derive(Clone, Debug)]
#[cfg_attr(feature = "trace", derive(serde::Serialize))]
#[cfg_attr(feature = "replay", derive(serde::Deserialize))]
pub struct VertexStateDescriptor<'a> {
/// The format of any index buffers used with this pipeline.
pub index_format: Option<IndexFormat>,
pub struct VertexState<'a> {
/// The compiled vertex stage and its entry point.
pub stage: ProgrammableStageDescriptor<'a>,
/// The format of any vertex buffers used with this pipeline.
pub vertex_buffers: Cow<'a, [VertexBufferDescriptor<'a>]>,
pub buffers: Cow<'a, [VertexBufferLayout<'a>]>,
}
/// Describes fragment processing in a render pipeline.
#[derive(Clone, Debug)]
#[cfg_attr(feature = "trace", derive(serde::Serialize))]
#[cfg_attr(feature = "replay", derive(serde::Deserialize))]
pub struct FragmentState<'a> {
/// The compiled fragment stage and its entry point.
pub stage: ProgrammableStageDescriptor<'a>,
/// The effect of draw calls on the color aspect of the output target.
pub targets: Cow<'a, [wgt::ColorTargetState]>,
}
/// Describes a render (graphics) pipeline.
@ -162,33 +172,19 @@ pub struct RenderPipelineDescriptor<'a> {
pub label: Label<'a>,
/// The layout of bind groups for this pipeline.
pub layout: Option<PipelineLayoutId>,
/// The compiled vertex stage and its entry point.
pub vertex_stage: ProgrammableStageDescriptor<'a>,
/// The compiled fragment stage and its entry point, if any.
pub fragment_stage: Option<ProgrammableStageDescriptor<'a>>,
/// The rasterization process for this pipeline.
pub rasterization_state: Option<wgt::RasterizationStateDescriptor>,
/// The primitive topology used to interpret vertices.
pub primitive_topology: wgt::PrimitiveTopology,
/// The effect of draw calls on the color aspect of the output target.
pub color_states: Cow<'a, [wgt::ColorStateDescriptor]>,
/// The vertex processing state for this pipeline.
pub vertex: VertexState<'a>,
/// The properties of the pipeline at the primitive assembly and rasterization level.
#[cfg_attr(any(feature = "replay", feature = "trace"), serde(default))]
pub primitive: wgt::PrimitiveState,
/// The effect of draw calls on the depth and stencil aspects of the output target, if any.
pub depth_stencil_state: Option<wgt::DepthStencilStateDescriptor>,
/// The vertex input state for this pipeline.
pub vertex_state: VertexStateDescriptor<'a>,
/// The number of samples calculated per pixel (for MSAA). For non-multisampled textures,
/// this should be `1`
pub sample_count: u32,
/// Bitmask that restricts the samples of a pixel modified by this pipeline. All samples
/// can be enabled using the value `!0`
pub sample_mask: u32,
/// When enabled, produces another sample mask per pixel based on the alpha output value, that
/// is ANDed with the sample_mask and the primitive coverage to restrict the set of samples
/// affected by a primitive.
///
/// The implicit mask produced for alpha of zero is guaranteed to be zero, and for alpha of one
/// is guaranteed to be all 1-s.
pub alpha_to_coverage_enabled: bool,
#[cfg_attr(any(feature = "replay", feature = "trace"), serde(default))]
pub depth_stencil: Option<wgt::DepthStencilState>,
/// The multi-sampling properties of the pipeline.
#[cfg_attr(any(feature = "replay", feature = "trace"), serde(default))]
pub multisample: wgt::MultisampleState,
/// The fragment processing state for this pipeline.
pub fragment: Option<FragmentState<'a>>,
}
#[derive(Clone, Debug, Error)]
@ -206,11 +202,14 @@ pub enum CreateRenderPipelineError {
#[error("invalid sample count {0}")]
InvalidSampleCount(u32),
#[error("vertex buffer {index} stride {stride} does not respect `VERTEX_STRIDE_ALIGNMENT`")]
UnalignedVertexStride { index: u32, stride: BufferAddress },
UnalignedVertexStride {
index: u32,
stride: wgt::BufferAddress,
},
#[error("vertex attribute at location {location} has invalid offset {offset}")]
InvalidVertexAttributeOffset {
location: wgt::ShaderLocation,
offset: BufferAddress,
offset: wgt::BufferAddress,
},
#[error("missing required device features {0:?}")]
MissingFeature(wgt::Features),
@ -238,8 +237,8 @@ pub struct RenderPipeline<B: hal::Backend> {
pub(crate) device_id: Stored<DeviceId>,
pub(crate) pass_context: RenderPassContext,
pub(crate) flags: PipelineFlags,
pub(crate) index_format: Option<IndexFormat>,
pub(crate) vertex_strides: Vec<(BufferAddress, InputStepMode)>,
pub(crate) strip_index_format: Option<wgt::IndexFormat>,
pub(crate) vertex_strides: Vec<(wgt::BufferAddress, wgt::InputStepMode)>,
pub(crate) life_guard: LifeGuard,
}

View File

@ -2,11 +2,16 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*! This library describes the API surface of WebGPU that is agnostic of the backend.
* This API is used for targeting both Web and Native.
*/
// The intra doc links to the wgpu crate in this crate actually succesfully link to the types in the wgpu crate, when built from the wgpu crate.
// However when building from both the wgpu crate or this crate cargo doc will claim all the links cannot be resolved
// despite the fact that it works fine when it needs to.
// So we just disable those warnings.
#![allow(broken_intra_doc_links)]
#![warn(missing_docs)]
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
@ -46,12 +51,19 @@ pub const QUERY_SIZE: u32 = 8;
#[cfg_attr(feature = "trace", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum Backend {
/// Dummy backend, used for testing.
Empty = 0,
/// Vulkan API
Vulkan = 1,
/// Metal API (Apple platforms)
Metal = 2,
/// Direct3D-12 (Windows)
Dx12 = 3,
/// Direct3D-11 (Windows)
Dx11 = 4,
/// OpenGL ES-3 (Linux, Android)
Gl = 5,
/// WebGPU in the browser
BrowserWebGpu = 6,
}
@ -529,6 +541,7 @@ pub struct DeviceDescriptor<L> {
}
impl<L> DeviceDescriptor<L> {
///
pub fn map_label<K>(&self, fun: impl FnOnce(&L) -> K) -> DeviceDescriptor<K> {
DeviceDescriptor {
label: fun(&self.label),
@ -559,6 +572,9 @@ bitflags::bitflags! {
}
bitflags::bitflags! {
/// Flags controlling the shader processing.
///
/// Note: These flags are internal tweaks, they don't affect the API.
#[repr(transparent)]
#[derive(Default)]
#[cfg_attr(feature = "trace", derive(serde::Serialize))]
@ -620,18 +636,31 @@ impl TextureViewDimension {
#[cfg_attr(feature = "trace", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum BlendFactor {
/// 0.0
Zero = 0,
/// 1.0
One = 1,
/// S.color
SrcColor = 2,
/// 1.0 - S.color
OneMinusSrcColor = 3,
/// S.alpha
SrcAlpha = 4,
/// 1.0 - S.alpha
OneMinusSrcAlpha = 5,
/// D.color
DstColor = 6,
/// 1.0 - D.color
OneMinusDstColor = 7,
/// D.alpha
DstAlpha = 8,
/// 1.0 - D.alpha
OneMinusDstAlpha = 9,
/// min(S.alpha, 1.0 - D.alpha)
SrcAlphaSaturated = 10,
/// Constant
BlendColor = 11,
/// 1.0 - Constant
OneMinusBlendColor = 12,
}
@ -643,10 +672,15 @@ pub enum BlendFactor {
#[cfg_attr(feature = "trace", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum BlendOperation {
/// Src + Dst
Add = 0,
/// Src - Dst
Subtract = 1,
/// Dst - Src
ReverseSubtract = 2,
/// min(Src, Dst)
Min = 3,
/// max(Src, Dst)
Max = 4,
}
@ -663,19 +697,26 @@ impl Default for BlendOperation {
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "trace", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct BlendDescriptor {
pub struct BlendState {
/// Multiplier for the source, which is produced by the fragment shader.
pub src_factor: BlendFactor,
/// Multiplier for the destination, which is stored in the target.
pub dst_factor: BlendFactor,
/// The binary operation applied to the source and destination,
/// multiplied by their respective factors.
pub operation: BlendOperation,
}
impl BlendDescriptor {
pub const REPLACE: Self = BlendDescriptor {
impl BlendState {
/// Default blending state that replaces destination with the source.
pub const REPLACE: Self = BlendState {
src_factor: BlendFactor::One,
dst_factor: BlendFactor::Zero,
operation: BlendOperation::Add,
};
/// Returns true if the state relies on the constant color, which is
/// set independently on a render command encoder.
pub fn uses_color(&self) -> bool {
match (self.src_factor, self.dst_factor) {
(BlendFactor::BlendColor, _)
@ -687,7 +728,7 @@ impl BlendDescriptor {
}
}
impl Default for BlendDescriptor {
impl Default for BlendState {
fn default() -> Self {
Self::REPLACE
}
@ -698,24 +739,27 @@ impl Default for BlendDescriptor {
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "trace", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct ColorStateDescriptor {
pub struct ColorTargetState {
/// The [`TextureFormat`] of the image that this pipeline will render to. Must match the the format
/// of the corresponding color attachment in [`CommandEncoder::begin_render_pass`].
pub format: TextureFormat,
/// The alpha blending that is used for this pipeline.
pub alpha_blend: BlendDescriptor,
#[cfg_attr(any(feature = "trace", feature = "replay"), serde(default))]
pub alpha_blend: BlendState,
/// The color blending that is used for this pipeline.
pub color_blend: BlendDescriptor,
#[cfg_attr(any(feature = "trace", feature = "replay"), serde(default))]
pub color_blend: BlendState,
/// Mask which enables/disables writes to different color/alpha channel.
#[cfg_attr(any(feature = "trace", feature = "replay"), serde(default))]
pub write_mask: ColorWrite,
}
impl From<TextureFormat> for ColorStateDescriptor {
impl From<TextureFormat> for ColorTargetState {
fn from(format: TextureFormat) -> Self {
Self {
format,
alpha_blend: BlendDescriptor::REPLACE,
color_blend: BlendDescriptor::REPLACE,
alpha_blend: BlendState::REPLACE,
color_blend: BlendState::REPLACE,
write_mask: ColorWrite::ALL,
}
}
@ -747,6 +791,12 @@ pub enum PrimitiveTopology {
TriangleStrip = 4,
}
impl Default for PrimitiveTopology {
fn default() -> Self {
PrimitiveTopology::TriangleList
}
}
/// Winding order which classifies the "front" face.
#[repr(C)]
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
@ -809,25 +859,59 @@ impl Default for PolygonMode {
}
}
/// Describes the state of the rasterizer in a render pipeline.
/// Describes the state of primitive assembly and rasterization in a render pipeline.
#[repr(C)]
#[derive(Clone, Debug, Default, PartialEq)]
#[cfg_attr(feature = "trace", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct RasterizationStateDescriptor {
pub struct PrimitiveState {
/// The primitive topology used to interpret vertices.
pub topology: PrimitiveTopology,
/// The format of index buffers for strip topologies. Should be left `None` for non-strip.
#[cfg_attr(any(feature = "trace", feature = "replay"), serde(default))]
pub strip_index_format: Option<IndexFormat>,
/// The face to consider the front for the purpose of culling and stencil operations.
#[cfg_attr(any(feature = "trace", feature = "replay"), serde(default))]
pub front_face: FrontFace,
/// The fact culling mode.
#[cfg_attr(any(feature = "trace", feature = "replay"), serde(default))]
pub cull_mode: CullMode,
/// Controls the way each polygon is rasterized. Can be either `Fill` (default), `Line` or `Point`
///
/// Setting this to something other than `Fill` requires `Features::NON_FILL_POLYGON_MODE` to be enabled.
#[cfg_attr(any(feature = "trace", feature = "replay"), serde(default))]
pub polygon_mode: PolygonMode,
/// If enabled polygon depth is clamped to 0-1 range instead of being clipped.
}
/// Describes the multi-sampling state of a render pipeline.
#[repr(C)]
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "trace", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct MultisampleState {
/// The number of samples calculated per pixel (for MSAA). For non-multisampled textures,
/// this should be `1`
pub count: u32,
/// Bitmask that restricts the samples of a pixel modified by this pipeline. All samples
/// can be enabled using the value `!0`
pub mask: u64,
/// When enabled, produces another sample mask per pixel based on the alpha output value, that
/// is ANDed with the sample_mask and the primitive coverage to restrict the set of samples
/// affected by a primitive.
///
/// Requires `Features::DEPTH_CLAMPING` enabled.
pub clamp_depth: bool,
pub depth_bias: i32,
pub depth_bias_slope_scale: f32,
pub depth_bias_clamp: f32,
/// The implicit mask produced for alpha of zero is guaranteed to be zero, and for alpha of one
/// is guaranteed to be all 1-s.
pub alpha_to_coverage_enabled: bool,
}
impl Default for MultisampleState {
fn default() -> Self {
MultisampleState {
count: 1,
mask: !0,
alpha_to_coverage_enabled: false,
}
}
}
bitflags::bitflags! {
@ -848,7 +932,9 @@ bitflags::bitflags! {
/// Features are defined by WebGPU specification unless `Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES` is enabled.
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub struct TextureFormatFeatures {
/// Valid bits for `TextureDescriptor::Usage` provided for format creation.
pub allowed_usages: TextureUsage,
/// Additional property flags for the format.
pub flags: TextureFormatFeatureFlags,
}
@ -1437,30 +1523,33 @@ impl Default for ColorWrite {
}
}
/// State of the stencil operation (fixed-pipeline stage).
#[repr(C)]
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "trace", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct StencilStateDescriptor {
pub struct StencilState {
/// Front face mode.
pub front: StencilStateFaceDescriptor,
pub front: StencilStateFace,
/// Back face mode.
pub back: StencilStateFaceDescriptor,
pub back: StencilStateFace,
/// Stencil values are AND'd with this mask when reading and writing from the stencil buffer. Only low 8 bits are used.
pub read_mask: u32,
/// Stencil values are AND'd with this mask when writing to the stencil buffer. Only low 8 bits are used.
pub write_mask: u32,
}
impl StencilStateDescriptor {
impl StencilState {
/// Returns true if the stencil test is enabled.
pub fn is_enabled(&self) -> bool {
(self.front != StencilStateFaceDescriptor::IGNORE
|| self.back != StencilStateFaceDescriptor::IGNORE)
(self.front != StencilStateFace::IGNORE || self.back != StencilStateFace::IGNORE)
&& (self.read_mask != 0 || self.write_mask != 0)
}
/// Returns true if the state doesn't mutate the target values.
pub fn is_read_only(&self) -> bool {
self.write_mask == 0
}
/// Returns true if the stencil state uses the reference value for testing.
pub fn needs_ref_value(&self) -> bool {
self.front.compare.needs_ref_value() || self.back.compare.needs_ref_value()
}
@ -1468,10 +1557,10 @@ impl StencilStateDescriptor {
/// Describes the depth/stencil state in a render pipeline.
#[repr(C)]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "trace", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct DepthStencilStateDescriptor {
pub struct DepthStencilState {
/// Format of the depth/stencil buffer, must be special depth format. Must match the the format
/// of the depth/stencil attachment in [`CommandEncoder::begin_render_pass`].
pub format: TextureFormat,
@ -1479,16 +1568,40 @@ pub struct DepthStencilStateDescriptor {
pub depth_write_enabled: bool,
/// Comparison function used to compare depth values in the depth test.
pub depth_compare: CompareFunction,
pub stencil: StencilStateDescriptor,
/// Stencil state.
#[cfg_attr(any(feature = "trace", feature = "replay"), serde(default))]
pub stencil: StencilState,
/// Constant depth biasing factor, in basic units of the depth format.
#[cfg_attr(any(feature = "trace", feature = "replay"), serde(default))]
pub depth_bias: i32,
/// Slope depth biasing factor.
#[cfg_attr(any(feature = "trace", feature = "replay"), serde(default))]
pub depth_bias_slope_scale: f32,
/// Depth bias clamp value (absolute).
#[cfg_attr(any(feature = "trace", feature = "replay"), serde(default))]
pub depth_bias_clamp: f32,
/// If enabled polygon depth is clamped to 0-1 range instead of being clipped.
///
/// Requires `Features::DEPTH_CLAMPING` enabled.
#[cfg_attr(any(feature = "trace", feature = "replay"), serde(default))]
pub clamp_depth: bool,
}
impl DepthStencilStateDescriptor {
impl DepthStencilState {
/// Returns true if the depth testing is enabled.
pub fn is_depth_enabled(&self) -> bool {
self.depth_compare != CompareFunction::Always || self.depth_write_enabled
}
/// Returns true if the state doesn't mutate either depth or stencil of the target.
pub fn is_read_only(&self) -> bool {
!self.depth_write_enabled && self.stencil.is_read_only()
}
/// Returns true if the depth bias is applied.
pub fn has_depth_bias(&self) -> bool {
self.depth_bias != 0 || self.depth_bias_slope_scale != 0.0
}
}
/// Format of indices used with pipeline.
@ -1540,12 +1653,12 @@ impl Default for StencilOperation {
/// Describes stencil state in a render pipeline.
///
/// If you are not using stencil state, set this to [`StencilStateFaceDescriptor::IGNORE`].
/// If you are not using stencil state, set this to [`StencilStateFace::IGNORE`].
#[repr(C)]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "trace", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct StencilStateFaceDescriptor {
pub struct StencilStateFace {
/// Comparison function that determines if the fail_op or pass_op is used on the stencil buffer.
pub compare: CompareFunction,
/// Operation that is preformed when stencil test fails.
@ -1556,8 +1669,9 @@ pub struct StencilStateFaceDescriptor {
pub pass_op: StencilOperation,
}
impl StencilStateFaceDescriptor {
pub const IGNORE: Self = StencilStateFaceDescriptor {
impl StencilStateFace {
/// Ignore the stencil state for the face.
pub const IGNORE: Self = StencilStateFace {
compare: CompareFunction::Always,
fail_op: StencilOperation::Keep,
depth_fail_op: StencilOperation::Keep,
@ -1565,7 +1679,7 @@ impl StencilStateFaceDescriptor {
};
}
impl Default for StencilStateFaceDescriptor {
impl Default for StencilStateFace {
fn default() -> Self {
Self::IGNORE
}
@ -1596,6 +1710,7 @@ pub enum CompareFunction {
}
impl CompareFunction {
/// Returns true if the comparison depends on the reference value.
pub fn needs_ref_value(self) -> bool {
match self {
Self::Never | Self::Always => false,
@ -1616,6 +1731,12 @@ pub enum InputStepMode {
Instance = 1,
}
impl Default for InputStepMode {
fn default() -> Self {
InputStepMode::Vertex
}
}
/// Vertex inputs (attributes) to shaders.
///
/// Arrays of these can be made with the [`vertex_attr_array`] macro. Vertex attributes are assumed to be tightly packed.
@ -1623,11 +1744,11 @@ pub enum InputStepMode {
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "trace", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct VertexAttributeDescriptor {
/// Byte offset of the start of the input
pub offset: BufferAddress,
pub struct VertexAttribute {
/// Format of the input
pub format: VertexFormat,
/// Byte offset of the start of the input
pub offset: BufferAddress,
/// Location for this input. Must match the location in the shader.
pub shader_location: ShaderLocation,
}
@ -1709,6 +1830,7 @@ pub enum VertexFormat {
}
impl VertexFormat {
/// Returns the byte size of the format.
pub const fn size(&self) -> u64 {
match self {
Self::Uchar2 | Self::Char2 | Self::Uchar2Norm | Self::Char2Norm => 2,
@ -1800,6 +1922,7 @@ pub struct BufferDescriptor<L> {
}
impl<L> BufferDescriptor<L> {
///
pub fn map_label<K>(&self, fun: impl FnOnce(&L) -> K) -> BufferDescriptor<K> {
BufferDescriptor {
label: fun(&self.label),
@ -1821,6 +1944,7 @@ pub struct CommandEncoderDescriptor<L> {
}
impl<L> CommandEncoderDescriptor<L> {
///
pub fn map_label<K>(&self, fun: impl FnOnce(&L) -> K) -> CommandEncoderDescriptor<K> {
CommandEncoderDescriptor {
label: fun(&self.label),
@ -1904,10 +2028,16 @@ pub struct SwapChainDescriptor {
#[repr(C)]
#[derive(Debug)]
pub enum SwapChainStatus {
/// No issues.
Good,
/// The swap chain is operational, but it does no longer perfectly
/// match the surface. A re-configuration is needed.
Suboptimal,
/// Unable to get the next frame, timed out.
Timeout,
/// The surface under the swap chain has changed.
Outdated,
/// The surface under the swap chain is lost.
Lost,
}
@ -1918,12 +2048,17 @@ pub enum SwapChainStatus {
#[derive(Clone, Copy, Debug, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Color {
///
pub r: f64,
///
pub g: f64,
///
pub b: f64,
///
pub a: f64,
}
#[allow(missing_docs)]
impl Color {
pub const TRANSPARENT: Self = Self {
r: 0.0,
@ -1983,12 +2118,16 @@ pub enum TextureDimension {
#[cfg_attr(feature = "trace", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct Origin3d {
///
pub x: u32,
///
pub y: u32,
///
pub z: u32,
}
impl Origin3d {
/// Zero origin.
pub const ZERO: Self = Self { x: 0, y: 0, z: 0 };
}
@ -2004,8 +2143,11 @@ impl Default for Origin3d {
#[cfg_attr(feature = "trace", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct Extent3d {
///
pub width: u32,
///
pub height: u32,
///
pub depth: u32,
}
@ -2137,6 +2279,7 @@ pub struct TextureDescriptor<L> {
}
impl<L> TextureDescriptor<L> {
///
pub fn map_label<K>(&self, fun: impl FnOnce(&L) -> K) -> TextureDescriptor<K> {
TextureDescriptor {
label: fun(&self.label),
@ -2246,10 +2389,12 @@ pub struct PushConstantRange {
#[cfg_attr(feature = "trace", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct CommandBufferDescriptor<L> {
/// Debug label of this command buffer.
pub label: L,
}
impl<L> CommandBufferDescriptor<L> {
///
pub fn map_label<K>(&self, fun: impl FnOnce(&L) -> K) -> CommandBufferDescriptor<K> {
CommandBufferDescriptor {
label: fun(&self.label),
@ -2268,6 +2413,7 @@ pub struct RenderBundleDescriptor<L> {
}
impl<L> RenderBundleDescriptor<L> {
///
pub fn map_label<K>(&self, fun: impl FnOnce(&L) -> K) -> RenderBundleDescriptor<K> {
RenderBundleDescriptor {
label: fun(&self.label),
@ -2360,15 +2506,16 @@ impl Default for BufferBindingType {
pub enum TextureSampleType {
/// Sampling returns floats.
///
/// If `filterable` is false, the texture can't be sampled with
/// a filtering sampler.
///
/// Example GLSL syntax:
/// ```cpp,ignore
/// layout(binding = 0)
/// uniform texture2D t;
/// ```
Float { filterable: bool },
Float {
/// If `filterable` is false, the texture can't be sampled with
/// a filtering sampler.
filterable: bool,
},
/// Sampling does the depth reference comparison.
///
/// Example GLSL syntax:
@ -2445,16 +2592,17 @@ pub enum StorageTextureAccess {
pub enum BindingType {
/// A buffer binding.
Buffer {
/// Sub-type of the buffer binding.
ty: BufferBindingType,
/// Indicates that the binding has a dynamic offset.
/// One offset must be passed to [`RenderPass::set_bind_group`] for each dynamic binding in increasing order of binding number.
#[cfg_attr(any(feature = "replay", feature = "trace"), serde(default))]
#[cfg_attr(any(feature = "trace", feature = "replay"), serde(default))]
has_dynamic_offset: bool,
/// Minimum size of the corresponding `BufferBinding` required to match this entry.
/// When pipeline is created, the size has to cover at least the corresponding structure in the shader
/// plus one element of the unbound array, which can only be last in the structure.
/// If `None`, the check is performed at draw call time instead of pipeline and bind group creation.
#[cfg_attr(any(feature = "replay", feature = "trace"), serde(default))]
#[cfg_attr(any(feature = "trace", feature = "replay"), serde(default))]
min_binding_size: Option<BufferSize>,
},
/// A sampler that can be used to sample a texture.
@ -2510,6 +2658,7 @@ pub enum BindingType {
}
impl BindingType {
/// Returns true for buffer bindings with dynamic offset enabled.
pub fn has_dynamic_offset(&self) -> bool {
match *self {
Self::Buffer {
@ -2537,7 +2686,7 @@ pub struct BindGroupLayoutEntry {
/// If this value is Some and `ty` is `BindingType::Texture`, [`Features::SAMPLED_TEXTURE_BINDING_ARRAY`] must be supported.
///
/// If this value is Some and `ty` is any other variant, bind group creation will fail.
#[cfg_attr(any(feature = "replay", feature = "trace"), serde(default))]
#[cfg_attr(any(feature = "trace", feature = "replay"), serde(default))]
pub count: Option<NonZeroU32>,
}
@ -2564,7 +2713,7 @@ pub struct TextureCopyView<T> {
/// The target mip level of the texture.
pub mip_level: u32,
/// The base texel of the texture in the selected `mip_level`.
#[cfg_attr(any(feature = "replay", feature = "trace"), serde(default))]
#[cfg_attr(any(feature = "trace", feature = "replay"), serde(default))]
pub origin: Origin3d,
}
@ -2574,8 +2723,11 @@ pub struct TextureCopyView<T> {
#[cfg_attr(feature = "trace", derive(serde::Serialize))]
#[cfg_attr(feature = "replay", derive(serde::Deserialize))]
pub enum SamplerBorderColor {
/// [0, 0, 0, 0]
TransparentBlack,
/// [0, 0, 0, 1]
OpaqueBlack,
/// [1, 1, 1, 1]
OpaqueWhite,
}