lf_gfx/
fragment_only.rs

1use core::num::NonZeroU32;
2use std::{borrow::Cow, collections::HashMap};
3
4use wgpu::{
5    util::{DeviceExt, RenderEncoder},
6    PipelineCompilationOptions,
7};
8
9#[repr(C)]
10#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
11pub struct FullscreenVertex {
12    pub position: [f32; 4],
13    pub uv: [f32; 2],
14}
15
16pub struct FragmentOnlyRenderPipelineDescriptor<'a> {
17    pub label: wgpu::Label<'a>,
18    pub layout: Option<&'a wgpu::PipelineLayout>,
19    pub multisample: wgpu::MultisampleState,
20    pub fragment: wgpu::FragmentState<'a>,
21    pub multiview: Option<NonZeroU32>,
22    pub cache: Option<&'a wgpu::PipelineCache>,
23}
24
25pub struct FragmentOnlyRenderPipeline {
26    pipeline: wgpu::RenderPipeline,
27    vertex_buffer: wgpu::Buffer,
28}
29
30impl AsRef<wgpu::RenderPipeline> for FragmentOnlyRenderPipeline {
31    fn as_ref(&self) -> &wgpu::RenderPipeline {
32        &self.pipeline
33    }
34}
35
36impl FragmentOnlyRenderPipeline {
37    pub(crate) fn new(device: &wgpu::Device, desc: &FragmentOnlyRenderPipelineDescriptor) -> Self {
38        let fullscreen_vertex_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
39            label: Some("fullscreen triangle vertex shader"),
40            source: wgpu::ShaderSource::Wgsl(Cow::from(include_str!("shaders/fullscreen.wgsl"))),
41        });
42
43        let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
44            label: desc.label.as_deref(),
45            layout: desc.layout.clone(),
46            vertex: wgpu::VertexState {
47                module: &fullscreen_vertex_shader,
48                entry_point: Some("main"),
49                buffers: &[wgpu::VertexBufferLayout {
50                    array_stride: std::mem::size_of::<FullscreenVertex>() as wgpu::BufferAddress,
51                    step_mode: wgpu::VertexStepMode::Vertex,
52                    attributes: &[
53                        wgpu::VertexAttribute {
54                            offset: 0,
55                            shader_location: 0,
56                            format: wgpu::VertexFormat::Float32x4,
57                        },
58                        wgpu::VertexAttribute {
59                            offset: std::mem::size_of::<[f32; 4]>() as wgpu::BufferAddress,
60                            shader_location: 1,
61                            format: wgpu::VertexFormat::Float32x2,
62                        },
63                    ],
64                }],
65                compilation_options: PipelineCompilationOptions {
66                    constants: &HashMap::new(),
67                    zero_initialize_workgroup_memory: false,
68                },
69            },
70            primitive: wgpu::PrimitiveState::default(),
71            depth_stencil: None,
72            multisample: desc.multisample.clone(),
73            fragment: Some(desc.fragment.clone()),
74            multiview: desc.multiview.clone(),
75            cache: desc.cache.clone(),
76        });
77
78        let fullscreen_vertices = [
79            FullscreenVertex {
80                position: [-2.0, -1.0, 0.5, 1.0],
81                uv: [-2.0, -1.0],
82            },
83            FullscreenVertex {
84                position: [2.0, -1.0, 0.5, 1.0],
85                uv: [2.0, -1.0],
86            },
87            FullscreenVertex {
88                position: [0.0, 3.0, 0.5, 1.0],
89                uv: [0.0, 3.0],
90            },
91        ];
92        let fullscreen_vertices_bytes: &[u8] = bytemuck::cast_slice(&fullscreen_vertices);
93        let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
94            label: Some("fullscreen triangle"),
95            contents: fullscreen_vertices_bytes,
96            usage: wgpu::BufferUsages::VERTEX,
97        });
98
99        Self {
100            pipeline,
101            vertex_buffer,
102        }
103    }
104
105    pub fn get_bind_group_layout(&self, index: u32) -> wgpu::BindGroupLayout {
106        self.pipeline.get_bind_group_layout(index)
107    }
108}
109
110fn set_pipeline<'a>(
111    encoder: &mut impl RenderEncoder<'a>,
112    pipeline: &'a FragmentOnlyRenderPipeline,
113) {
114    encoder.set_pipeline(&pipeline.pipeline);
115    encoder.set_vertex_buffer(0, pipeline.vertex_buffer.slice(..));
116}
117
118fn draw<'a>(encoder: &mut impl RenderEncoder<'a>) {
119    encoder.draw(0..3, 0..1)
120}
121
122fn set_push_constants<'a>(encoder: &mut impl RenderEncoder<'a>, offset: u32, data: &[u8]) {
123    encoder.set_push_constants(wgpu::ShaderStages::FRAGMENT, offset, data)
124}
125
126pub struct FragmentOnlyRenderBundleEncoderDescriptor<'a> {
127    pub label: wgpu::Label<'a>,
128    pub color_formats: &'a [Option<wgpu::TextureFormat>],
129    pub sample_count: u32,
130    pub multiview: Option<NonZeroU32>,
131}
132
133pub struct FragmentOnlyRenderBundleEncoder<'a> {
134    encoder: wgpu::RenderBundleEncoder<'a>,
135}
136
137impl<'a> FragmentOnlyRenderBundleEncoder<'a> {
138    pub(crate) fn new(
139        device: &'a wgpu::Device,
140        desc: &FragmentOnlyRenderBundleEncoderDescriptor,
141    ) -> Self {
142        let encoder = device.create_render_bundle_encoder(&wgpu::RenderBundleEncoderDescriptor {
143            label: desc.label.as_deref(),
144            color_formats: desc.color_formats,
145            depth_stencil: None,
146            sample_count: desc.sample_count,
147            multiview: desc.multiview,
148        });
149
150        Self { encoder }
151    }
152
153    // Mostly pass-through
154    pub fn finish(self, desc: &wgpu::RenderBundleDescriptor<'_>) -> FragmentOnlyRenderBundle {
155        let render_bundle = self.encoder.finish(desc);
156
157        FragmentOnlyRenderBundle { render_bundle }
158    }
159    pub fn set_pipeline(&mut self, pipeline: &'a FragmentOnlyRenderPipeline) {
160        set_pipeline(&mut self.encoder, pipeline)
161    }
162    pub fn draw(&mut self) {
163        draw(&mut self.encoder)
164    }
165    pub fn set_push_constants(&mut self, offset: u32, data: &[u8]) {
166        set_push_constants(&mut self.encoder, offset, data)
167    }
168}
169
170pub struct FragmentOnlyRenderBundle {
171    render_bundle: wgpu::RenderBundle,
172}
173
174pub struct FragmentOnlyRenderPassStencilAttachment<'tex> {
175    pub view: &'tex wgpu::TextureView,
176    pub stencil_ops: Option<wgpu::Operations<u32>>,
177}
178
179pub struct FragmentOnlyRenderPassDescriptor<'tex, 'desc> {
180    pub label: wgpu::Label<'desc>,
181    pub color_attachments: &'desc [Option<wgpu::RenderPassColorAttachment<'tex>>],
182    pub stencil_attachment: Option<FragmentOnlyRenderPassStencilAttachment<'tex>>,
183    pub timestamp_writes: Option<wgpu::RenderPassTimestampWrites<'desc>>,
184}
185
186#[derive(Debug)]
187pub struct FragmentOnlyRenderPass<'a> {
188    renderpass: wgpu::RenderPass<'a>,
189}
190
191impl<'a> FragmentOnlyRenderPass<'a> {
192    pub(crate) fn new(
193        command_encoder: &'a mut wgpu::CommandEncoder,
194        desc: &FragmentOnlyRenderPassDescriptor<'a, '_>,
195    ) -> Self {
196        let desc = wgpu::RenderPassDescriptor {
197            label: desc.label,
198            color_attachments: desc.color_attachments,
199            depth_stencil_attachment: desc.stencil_attachment.as_ref().map(|attachment| {
200                wgpu::RenderPassDepthStencilAttachment {
201                    view: attachment.view,
202                    depth_ops: None,
203                    stencil_ops: attachment.stencil_ops,
204                }
205            }),
206            timestamp_writes: desc.timestamp_writes.clone(),
207            occlusion_query_set: None, // Occlusion queries don't make sense when we're just doing fragment invocations
208        };
209
210        let renderpass = command_encoder.begin_render_pass(&desc);
211
212        Self { renderpass }
213    }
214
215    // These methods have changed
216
217    pub fn set_pipeline(&mut self, pipeline: &'a FragmentOnlyRenderPipeline) {
218        set_pipeline(&mut self.renderpass, pipeline)
219    }
220    pub fn draw(&mut self) {
221        draw(&mut self.renderpass)
222    }
223    pub fn set_push_constants(&mut self, offset: u32, data: &[u8]) {
224        set_push_constants(&mut self.renderpass, offset, data)
225    }
226    pub fn execute_bundles<I: IntoIterator<Item = &'a FragmentOnlyRenderBundle>>(
227        &mut self,
228        render_bundles: I,
229    ) {
230        let render_bundles: Vec<&wgpu::RenderBundle> = render_bundles
231            .into_iter()
232            .map(|bundle| &bundle.render_bundle)
233            .collect();
234
235        self.renderpass.execute_bundles(render_bundles)
236    }
237
238    // We just pass most things through
239
240    /// See wgpu::RenderPass
241    pub fn set_bind_group(
242        &mut self,
243        index: u32,
244        bind_group: &'a wgpu::BindGroup,
245        offsets: &[wgpu::DynamicOffset],
246    ) {
247        self.renderpass.set_bind_group(index, bind_group, offsets)
248    }
249    pub fn set_blend_constant(&mut self, color: wgpu::Color) {
250        self.renderpass.set_blend_constant(color)
251    }
252    pub fn set_scissor_rect(&mut self, x: u32, y: u32, width: u32, height: u32) {
253        self.renderpass.set_scissor_rect(x, y, width, height)
254    }
255    pub fn set_stencil_reference(&mut self, reference: u32) {
256        self.renderpass.set_stencil_reference(reference)
257    }
258    pub fn insert_debug_marker(&mut self, label: &str) {
259        self.renderpass.insert_debug_marker(label)
260    }
261    pub fn push_debug_group(&mut self, label: &str) {
262        self.renderpass.push_debug_group(label)
263    }
264    pub fn pop_debug_group(&mut self) {
265        self.renderpass.pop_debug_group()
266    }
267    pub fn write_timestamp(&mut self, query_set: &wgpu::QuerySet, query_index: u32) {
268        self.renderpass.write_timestamp(query_set, query_index)
269    }
270    pub fn begin_pipeline_statistics_query(
271        &mut self,
272        query_set: &wgpu::QuerySet,
273        query_index: u32,
274    ) {
275        self.renderpass
276            .begin_pipeline_statistics_query(query_set, query_index)
277    }
278    pub fn end_pipeline_statistics_query(&mut self) {
279        self.renderpass.end_pipeline_statistics_query()
280    }
281}