Skip to main content

wasdk_wgpu/convert/
render.rs

1//! Render state: primitive, depth-stencil, blend, multisample, vertex layouts and load/store ops.
2
3use wasdk::webgpu::{
4    GpuBlendComponent, GpuBlendFactor, GpuBlendOperation, GpuBlendState, GpuColorTargetState,
5    GpuCompareFunction, GpuCullMode, GpuDepthStencilState, GpuFrontFace, GpuLoadOp,
6    GpuMultisampleState, GpuPrimitiveState, GpuPrimitiveTopology, GpuStencilFaceState,
7    GpuStencilOperation, GpuStoreOp, GpuVertexAttribute, GpuVertexBufferLayout, GpuVertexStepMode,
8};
9use wgpu::{
10    BlendComponent, BlendFactor, BlendOperation, BlendState, ColorTargetState, CompareFunction,
11    DepthStencilState, Face, Features, FrontFace, LoadOp, MultisampleState, Operations,
12    PolygonMode, PrimitiveState, PrimitiveTopology, StencilFaceState, StencilOperation, StoreOp,
13    VertexAttribute, VertexBufferLayout, VertexStepMode,
14};
15
16use super::{
17    color_writes_to_wasi, index_format_to_wasi, texture_format_to_wasi, vertex_format_to_wasi,
18};
19use crate::gap::feature_gap;
20
21pub fn compare_function_to_wasi(function: CompareFunction) -> GpuCompareFunction {
22    match function {
23        CompareFunction::Never => GpuCompareFunction::Never,
24        CompareFunction::Less => GpuCompareFunction::Less,
25        CompareFunction::Equal => GpuCompareFunction::Equal,
26        CompareFunction::LessEqual => GpuCompareFunction::LessEqual,
27        CompareFunction::Greater => GpuCompareFunction::Greater,
28        CompareFunction::NotEqual => GpuCompareFunction::NotEqual,
29        CompareFunction::GreaterEqual => GpuCompareFunction::GreaterEqual,
30        CompareFunction::Always => GpuCompareFunction::Always,
31    }
32}
33
34pub fn stencil_operation_to_wasi(operation: StencilOperation) -> GpuStencilOperation {
35    match operation {
36        StencilOperation::Keep => GpuStencilOperation::Keep,
37        StencilOperation::Zero => GpuStencilOperation::Zero,
38        StencilOperation::Replace => GpuStencilOperation::Replace,
39        StencilOperation::Invert => GpuStencilOperation::Invert,
40        StencilOperation::IncrementClamp => GpuStencilOperation::IncrementClamp,
41        StencilOperation::DecrementClamp => GpuStencilOperation::DecrementClamp,
42        StencilOperation::IncrementWrap => GpuStencilOperation::IncrementWrap,
43        StencilOperation::DecrementWrap => GpuStencilOperation::DecrementWrap,
44    }
45}
46
47pub fn stencil_face_to_wasi(face: &StencilFaceState) -> GpuStencilFaceState {
48    GpuStencilFaceState {
49        compare: Some(compare_function_to_wasi(face.compare)),
50        fail_op: Some(stencil_operation_to_wasi(face.fail_op)),
51        depth_fail_op: Some(stencil_operation_to_wasi(face.depth_fail_op)),
52        pass_op: Some(stencil_operation_to_wasi(face.pass_op)),
53    }
54}
55
56pub fn depth_stencil_state_to_wasi(state: &DepthStencilState) -> GpuDepthStencilState {
57    GpuDepthStencilState {
58        format: texture_format_to_wasi(state.format),
59        depth_write_enabled: state.depth_write_enabled,
60        depth_compare: state.depth_compare.map(compare_function_to_wasi),
61        stencil_front: Some(stencil_face_to_wasi(&state.stencil.front)),
62        stencil_back: Some(stencil_face_to_wasi(&state.stencil.back)),
63        stencil_read_mask: Some(state.stencil.read_mask),
64        stencil_write_mask: Some(state.stencil.write_mask),
65        depth_bias: Some(state.bias.constant),
66        depth_bias_slope_scale: Some(state.bias.slope_scale),
67        depth_bias_clamp: Some(state.bias.clamp),
68    }
69}
70
71pub fn blend_factor_to_wasi(factor: BlendFactor) -> GpuBlendFactor {
72    match factor {
73        BlendFactor::Zero => GpuBlendFactor::Zero,
74        BlendFactor::One => GpuBlendFactor::One,
75        BlendFactor::Src => GpuBlendFactor::Src,
76        BlendFactor::OneMinusSrc => GpuBlendFactor::OneMinusSrc,
77        BlendFactor::SrcAlpha => GpuBlendFactor::SrcAlpha,
78        BlendFactor::OneMinusSrcAlpha => GpuBlendFactor::OneMinusSrcAlpha,
79        BlendFactor::Dst => GpuBlendFactor::Dst,
80        BlendFactor::OneMinusDst => GpuBlendFactor::OneMinusDst,
81        BlendFactor::DstAlpha => GpuBlendFactor::DstAlpha,
82        BlendFactor::OneMinusDstAlpha => GpuBlendFactor::OneMinusDstAlpha,
83        BlendFactor::SrcAlphaSaturated => GpuBlendFactor::SrcAlphaSaturated,
84        BlendFactor::Constant => GpuBlendFactor::Constant,
85        BlendFactor::OneMinusConstant => GpuBlendFactor::OneMinusConstant,
86        BlendFactor::Src1 => GpuBlendFactor::Src1,
87        BlendFactor::OneMinusSrc1 => GpuBlendFactor::OneMinusSrc1,
88        BlendFactor::Src1Alpha => GpuBlendFactor::Src1Alpha,
89        BlendFactor::OneMinusSrc1Alpha => GpuBlendFactor::OneMinusSrc1Alpha,
90    }
91}
92
93pub fn blend_operation_to_wasi(operation: BlendOperation) -> GpuBlendOperation {
94    match operation {
95        BlendOperation::Add => GpuBlendOperation::Add,
96        BlendOperation::Subtract => GpuBlendOperation::Subtract,
97        BlendOperation::ReverseSubtract => GpuBlendOperation::ReverseSubtract,
98        BlendOperation::Min => GpuBlendOperation::Min,
99        BlendOperation::Max => GpuBlendOperation::Max,
100    }
101}
102
103pub fn blend_component_to_wasi(component: &BlendComponent) -> GpuBlendComponent {
104    GpuBlendComponent {
105        operation: Some(blend_operation_to_wasi(component.operation)),
106        src_factor: Some(blend_factor_to_wasi(component.src_factor)),
107        dst_factor: Some(blend_factor_to_wasi(component.dst_factor)),
108    }
109}
110
111pub fn blend_state_to_wasi(state: &BlendState) -> GpuBlendState {
112    GpuBlendState {
113        color: blend_component_to_wasi(&state.color),
114        alpha: blend_component_to_wasi(&state.alpha),
115    }
116}
117
118pub fn color_target_to_wasi(target: &ColorTargetState) -> GpuColorTargetState {
119    GpuColorTargetState {
120        format: texture_format_to_wasi(target.format),
121        blend: target.blend.as_ref().map(blend_state_to_wasi),
122        write_mask: Some(color_writes_to_wasi(target.write_mask)),
123    }
124}
125
126pub fn primitive_topology_to_wasi(topology: PrimitiveTopology) -> GpuPrimitiveTopology {
127    match topology {
128        PrimitiveTopology::PointList => GpuPrimitiveTopology::PointList,
129        PrimitiveTopology::LineList => GpuPrimitiveTopology::LineList,
130        PrimitiveTopology::LineStrip => GpuPrimitiveTopology::LineStrip,
131        PrimitiveTopology::TriangleList => GpuPrimitiveTopology::TriangleList,
132        PrimitiveTopology::TriangleStrip => GpuPrimitiveTopology::TriangleStrip,
133    }
134}
135
136pub fn front_face_to_wasi(front_face: FrontFace) -> GpuFrontFace {
137    match front_face {
138        FrontFace::Ccw => GpuFrontFace::Ccw,
139        FrontFace::Cw => GpuFrontFace::Cw,
140    }
141}
142
143pub fn cull_mode_to_wasi(cull_mode: Option<Face>) -> GpuCullMode {
144    match cull_mode {
145        None => GpuCullMode::None,
146        Some(Face::Front) => GpuCullMode::Front,
147        Some(Face::Back) => GpuCullMode::Back,
148    }
149}
150
151pub fn primitive_state_to_wasi(state: &PrimitiveState) -> GpuPrimitiveState {
152    match state.polygon_mode {
153        PolygonMode::Fill => {}
154        PolygonMode::Line => feature_gap(Features::POLYGON_MODE_LINE, "PolygonMode::Line"),
155        PolygonMode::Point => feature_gap(Features::POLYGON_MODE_POINT, "PolygonMode::Point"),
156    }
157    if state.conservative {
158        feature_gap(
159            Features::CONSERVATIVE_RASTERIZATION,
160            "PrimitiveState::conservative",
161        );
162    }
163    GpuPrimitiveState {
164        topology: Some(primitive_topology_to_wasi(state.topology)),
165        strip_index_format: state.strip_index_format.map(index_format_to_wasi),
166        front_face: Some(front_face_to_wasi(state.front_face)),
167        cull_mode: Some(cull_mode_to_wasi(state.cull_mode)),
168        unclipped_depth: Some(state.unclipped_depth),
169    }
170}
171
172pub fn multisample_state_to_wasi(state: &MultisampleState) -> GpuMultisampleState {
173    GpuMultisampleState {
174        count: Some(state.count),
175        mask: Some(sample_mask_to_wasi(state.mask)),
176        alpha_to_coverage_enabled: Some(state.alpha_to_coverage_enabled),
177    }
178}
179
180/// WebGPU's sample mask is 32 bits wide, and no sample count reaches 32: the bits wgpu's
181/// 64-bit mask has above them select no sample and are dropped.
182fn sample_mask_to_wasi(mask: u64) -> u32 {
183    mask as u32
184}
185
186pub fn vertex_step_mode_to_wasi(mode: VertexStepMode) -> GpuVertexStepMode {
187    match mode {
188        VertexStepMode::Vertex => GpuVertexStepMode::Vertex,
189        VertexStepMode::Instance => GpuVertexStepMode::Instance,
190    }
191}
192
193pub fn vertex_attribute_to_wasi(attribute: &VertexAttribute) -> GpuVertexAttribute {
194    GpuVertexAttribute {
195        format: vertex_format_to_wasi(attribute.format),
196        offset: attribute.offset,
197        shader_location: attribute.shader_location,
198    }
199}
200
201pub fn vertex_buffer_layout_to_wasi(layout: &VertexBufferLayout<'_>) -> GpuVertexBufferLayout {
202    GpuVertexBufferLayout {
203        array_stride: layout.array_stride,
204        step_mode: Some(vertex_step_mode_to_wasi(layout.step_mode)),
205        attributes: layout
206            .attributes
207            .iter()
208            .map(vertex_attribute_to_wasi)
209            .collect(),
210    }
211}
212
213pub fn store_op_to_wasi(op: StoreOp) -> GpuStoreOp {
214    match op {
215        StoreOp::Store => GpuStoreOp::Store,
216        StoreOp::Discard => GpuStoreOp::Discard,
217    }
218}
219
220/// An attachment's operations as WebGPU states them: a load op plus the value a clear uses.
221#[derive(Debug, Clone, Copy)]
222pub struct AttachmentOps<V> {
223    pub load: GpuLoadOp,
224    pub clear_value: Option<V>,
225    pub store: GpuStoreOp,
226}
227
228/// `LoadOp::DontCare` leaves the contents undefined, which WebGPU cannot say; it clears to
229/// `undefined_value`, as wgpu's web backend does.
230pub fn operations_to_wasi<V: Clone>(ops: &Operations<V>, undefined_value: V) -> AttachmentOps<V> {
231    let (load, clear_value) = match &ops.load {
232        LoadOp::Clear(value) => (GpuLoadOp::Clear, Some(value.clone())),
233        LoadOp::Load => (GpuLoadOp::Load, None),
234        LoadOp::DontCare(_) => (GpuLoadOp::Clear, Some(undefined_value)),
235    };
236    AttachmentOps {
237        load,
238        clear_value,
239        store: store_op_to_wasi(ops.store),
240    }
241}