Skip to main content

alpha2_render/
quad.rs

1use crate::{RenderError, RenderSize, RenderedTexture, Renderer, pipeline::CachedPipeline};
2use alpha2_shaders::FULLSCREEN_TEXTURE_QUAD_WGSL;
3
4impl Renderer {
5    /// Draws one premultiplied RGBA texel over an opaque clear target.
6    ///
7    /// # Errors
8    /// Returns [`RenderError::Gpu`] when the GPU rejected the draw.
9    #[allow(clippy::too_many_lines)]
10    pub fn render_quad(
11        &self,
12        size: RenderSize,
13        source: [u8; 4],
14    ) -> Result<RenderedTexture, RenderError> {
15        let format = self.intermediate_format();
16        let mut encoder = self.command_encoder("alpha2 quad encoder");
17        let target = self
18            .textures
19            .acquire(self.gpu.device(), size, format, "alpha2 quad target");
20        let input = self.gpu.device().create_texture(&wgpu::TextureDescriptor {
21            label: None,
22            size: wgpu::Extent3d {
23                width: 1,
24                height: 1,
25                depth_or_array_layers: 1,
26            },
27            mip_level_count: 1,
28            sample_count: 1,
29            dimension: wgpu::TextureDimension::D2,
30            format: wgpu::TextureFormat::Rgba8Unorm,
31            usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
32            view_formats: &[],
33        });
34        self.gpu.queue().write_texture(
35            wgpu::TexelCopyTextureInfo {
36                texture: &input,
37                mip_level: 0,
38                origin: wgpu::Origin3d::ZERO,
39                aspect: wgpu::TextureAspect::All,
40            },
41            &source,
42            wgpu::TexelCopyBufferLayout {
43                offset: 0,
44                bytes_per_row: Some(4),
45                rows_per_image: Some(1),
46            },
47            wgpu::Extent3d {
48                width: 1,
49                height: 1,
50                depth_or_array_layers: 1,
51            },
52        );
53        let cached = self.quad_pipeline();
54        let input_view = crate::pool::default_view(&input);
55        let group = self
56            .gpu
57            .device()
58            .create_bind_group(&wgpu::BindGroupDescriptor {
59                label: None,
60                layout: &cached.bind_group_layout,
61                entries: &[wgpu::BindGroupEntry {
62                    binding: 0,
63                    resource: wgpu::BindingResource::TextureView(&input_view),
64                }],
65            });
66        {
67            let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
68                label: None,
69                color_attachments: &[Some(wgpu::RenderPassColorAttachment {
70                    view: target.view(),
71                    depth_slice: None,
72                    resolve_target: None,
73                    ops: wgpu::Operations {
74                        load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
75                        store: wgpu::StoreOp::Store,
76                    },
77                })],
78                depth_stencil_attachment: None,
79                timestamp_writes: None,
80                occlusion_query_set: None,
81                multiview_mask: None,
82            });
83            pass.set_pipeline(&cached.pipeline);
84            pass.set_bind_group(0, &group, &[]);
85            pass.draw(0..3, 0..1);
86        }
87        self.submit([encoder.finish()])?;
88        Ok(RenderedTexture::pooled(size, format, target))
89    }
90
91    pub(crate) fn quad_pipeline(&self) -> &CachedPipeline {
92        self.pipelines.quad.get_or_init(|| {
93            let bind_group_layout =
94                self.gpu
95                    .device()
96                    .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
97                        label: Some("alpha2 quad layout"),
98                        entries: &[wgpu::BindGroupLayoutEntry {
99                            binding: 0,
100                            visibility: wgpu::ShaderStages::FRAGMENT,
101                            ty: wgpu::BindingType::Texture {
102                                sample_type: wgpu::TextureSampleType::Float { filterable: false },
103                                view_dimension: wgpu::TextureViewDimension::D2,
104                                multisampled: false,
105                            },
106                            count: None,
107                        }],
108                    });
109            let shader = self
110                .gpu
111                .device()
112                .create_shader_module(wgpu::ShaderModuleDescriptor {
113                    label: Some("alpha2 quad shader"),
114                    source: wgpu::ShaderSource::Wgsl(FULLSCREEN_TEXTURE_QUAD_WGSL.into()),
115                });
116            let pipeline_layout =
117                self.gpu
118                    .device()
119                    .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
120                        label: Some("alpha2 quad pipeline layout"),
121                        bind_group_layouts: &[Some(&bind_group_layout)],
122                        immediate_size: 0,
123                    });
124            let pipeline =
125                self.gpu
126                    .device()
127                    .create_render_pipeline(&wgpu::RenderPipelineDescriptor {
128                        label: Some("alpha2 quad pipeline"),
129                        layout: Some(&pipeline_layout),
130                        vertex: wgpu::VertexState {
131                            module: &shader,
132                            entry_point: Some("vs"),
133                            buffers: &[],
134                            compilation_options: wgpu::PipelineCompilationOptions::default(),
135                        },
136                        fragment: Some(wgpu::FragmentState {
137                            module: &shader,
138                            entry_point: Some("fs"),
139                            targets: &[Some(wgpu::ColorTargetState {
140                                format: self.intermediate_format(),
141                                blend: Some(wgpu::BlendState::PREMULTIPLIED_ALPHA_BLENDING),
142                                write_mask: wgpu::ColorWrites::ALL,
143                            })],
144                            compilation_options: wgpu::PipelineCompilationOptions::default(),
145                        }),
146                        primitive: wgpu::PrimitiveState::default(),
147                        depth_stencil: None,
148                        multisample: wgpu::MultisampleState::default(),
149                        multiview_mask: None,
150                        cache: None,
151                    });
152            CachedPipeline {
153                bind_group_layout,
154                pipeline,
155            }
156        })
157    }
158}