Skip to main content

dunge/
layer.rs

1//! Layer types.
2
3use {
4    crate::{
5        color::Format,
6        shader::{ShaderData, SlotNumbers},
7        state::State,
8    },
9    std::marker::PhantomData,
10};
11
12/// [Layer's](Layer) blend mode.
13#[derive(Clone, Copy, Default)]
14pub enum Blend {
15    #[default]
16    None,
17    Replace,
18    Alpha,
19}
20
21impl Blend {
22    fn wgpu(self) -> Option<wgpu::BlendState> {
23        match self {
24            Self::None => None,
25            Self::Replace => Some(wgpu::BlendState::REPLACE),
26            Self::Alpha => Some(wgpu::BlendState::ALPHA_BLENDING),
27        }
28    }
29}
30
31/// [Layer's](Layer) primitive topology.
32#[derive(Clone, Copy, Default)]
33pub enum Topology {
34    PointList,
35    LineList,
36    LineStrip,
37    #[default]
38    TriangleList,
39    TriangleStrip,
40}
41
42impl Topology {
43    fn wgpu(self) -> wgpu::PrimitiveTopology {
44        match self {
45            Self::PointList => wgpu::PrimitiveTopology::PointList,
46            Self::LineList => wgpu::PrimitiveTopology::LineList,
47            Self::LineStrip => wgpu::PrimitiveTopology::LineStrip,
48            Self::TriangleList => wgpu::PrimitiveTopology::TriangleList,
49            Self::TriangleStrip => wgpu::PrimitiveTopology::TriangleStrip,
50        }
51    }
52}
53
54/// [Layer's](Layer) polygon mode.
55#[derive(Clone, Copy, Default)]
56pub enum Polygon {
57    #[default]
58    Fill,
59    #[cfg(not(target_family = "wasm"))]
60    Line,
61    #[cfg(not(target_family = "wasm"))]
62    Point,
63}
64
65impl Polygon {
66    fn wgpu(self) -> wgpu::PolygonMode {
67        match self {
68            Self::Fill => wgpu::PolygonMode::Fill,
69            #[cfg(not(target_family = "wasm"))]
70            Self::Line => wgpu::PolygonMode::Line,
71            #[cfg(not(target_family = "wasm"))]
72            Self::Point => wgpu::PolygonMode::Point,
73        }
74    }
75}
76
77/// [Layer's](Layer) depth mode.
78#[derive(Clone, Copy, Default)]
79pub enum Depth {
80    #[default]
81    None,
82    Noop,
83    Less,
84    Greater,
85}
86
87/// The [layer](Layer) configuration.
88///
89/// Used when creating a layer in the [`make_layer`](crate::Context::make_layer) method.
90#[derive(Clone, Copy, Default)]
91pub struct Config {
92    pub format: Format,
93    pub blend: Blend,
94    pub topology: Topology,
95    pub polygon: Polygon,
96    pub depth: Depth,
97}
98
99impl From<Format> for Config {
100    fn from(format: Format) -> Self {
101        Self {
102            format,
103            ..Default::default()
104        }
105    }
106}
107
108/// The render layer.
109///
110/// Can be created using the [`make_layer`](crate::Context::make_layer) method.
111pub struct Layer<I> {
112    slots: SlotNumbers,
113    depth: Depth,
114    format: Format,
115    render: wgpu::RenderPipeline,
116    inp: PhantomData<I>,
117}
118
119impl<I> Layer<I> {
120    pub(crate) fn new(state: &State, shader: &ShaderData, conf: Config) -> Self {
121        let Config {
122            format,
123            blend,
124            topology,
125            polygon,
126            depth,
127        } = conf;
128
129        let targets = [Some(wgpu::ColorTargetState {
130            format: format.wgpu(),
131            blend: blend.wgpu(),
132            write_mask: wgpu::ColorWrites::ALL,
133        })];
134
135        let module = shader.module();
136        let buffers = shader.vertex_buffers();
137        let topology = topology.wgpu();
138        let desc = wgpu::RenderPipelineDescriptor {
139            label: None,
140            layout: Some(shader.layout()),
141            vertex: wgpu::VertexState {
142                module,
143                entry_point: Some("vs"),
144                compilation_options: wgpu::PipelineCompilationOptions::default(),
145                buffers: &buffers,
146            },
147            primitive: wgpu::PrimitiveState {
148                topology,
149                strip_index_format: topology.is_strip().then_some(wgpu::IndexFormat::Uint32),
150                cull_mode: Some(wgpu::Face::Back),
151                polygon_mode: polygon.wgpu(),
152                ..Default::default()
153            },
154            depth_stencil: match depth {
155                Depth::None => None,
156                Depth::Noop => Some(wgpu::CompareFunction::Always),
157                Depth::Less => Some(wgpu::CompareFunction::Less),
158                Depth::Greater => Some(wgpu::CompareFunction::Greater),
159            }
160            .map(|depth_compare| wgpu::DepthStencilState {
161                format: Format::Depth.wgpu(),
162                depth_write_enabled: Some(true),
163                depth_compare: Some(depth_compare),
164                stencil: wgpu::StencilState::default(),
165                bias: wgpu::DepthBiasState::default(),
166            }),
167            multisample: wgpu::MultisampleState::default(),
168            fragment: Some(wgpu::FragmentState {
169                module,
170                entry_point: Some("fs"),
171                compilation_options: wgpu::PipelineCompilationOptions::default(),
172                targets: &targets,
173            }),
174            multiview_mask: None,
175            cache: None,
176        };
177
178        let render = state.device().create_render_pipeline(&desc);
179
180        Self {
181            slots: shader.slots(),
182            depth,
183            format,
184            render,
185            inp: PhantomData,
186        }
187    }
188
189    pub(crate) fn slots(&self) -> SlotNumbers {
190        self.slots
191    }
192
193    /// Whether the layer uses depth.
194    pub fn depth(&self) -> bool {
195        !matches!(self.depth, Depth::None)
196    }
197
198    /// Returns the color [format](Format) used by the layer.
199    pub fn format(&self) -> Format {
200        self.format
201    }
202
203    pub(crate) fn render(&self) -> &wgpu::RenderPipeline {
204        &self.render
205    }
206}