1use bytemuck::{Pod, Zeroable};
7use glam::Mat4;
8use wgpu::util::DeviceExt;
9
10use crate::application::Application;
11use crate::color::Color;
12use crate::texture::Texture;
13
14#[repr(C)]
15#[derive(Clone, Copy, Pod, Zeroable)]
16struct QuadUniforms {
17 model: [[f32; 4]; 4],
18 tint: [f32; 4],
19}
20
21pub(crate) struct QuadPipeline {
22 pipeline: wgpu::RenderPipeline,
23 uniforms_layout: wgpu::BindGroupLayout,
24 texture_layout: wgpu::BindGroupLayout,
25}
26
27impl QuadPipeline {
28 pub(crate) fn new(app: &Application, output_format: wgpu::TextureFormat) -> Self {
29 let device = app.device();
30 let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
31 label: Some("wisp::quad"),
32 source: wgpu::ShaderSource::Wgsl(include_str!("../../shaders/quad.wgsl").into()),
33 });
34
35 let uniforms_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
36 label: Some("wisp::quad uniforms layout"),
37 entries: &[wgpu::BindGroupLayoutEntry {
38 binding: 0,
39 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
40 ty: wgpu::BindingType::Buffer {
41 ty: wgpu::BufferBindingType::Uniform,
42 has_dynamic_offset: false,
43 min_binding_size: None,
44 },
45 count: None,
46 }],
47 });
48
49 let texture_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
50 label: Some("wisp::quad texture layout"),
51 entries: &[
52 wgpu::BindGroupLayoutEntry {
53 binding: 0,
54 visibility: wgpu::ShaderStages::FRAGMENT,
55 ty: wgpu::BindingType::Texture {
56 sample_type: wgpu::TextureSampleType::Float { filterable: true },
57 view_dimension: wgpu::TextureViewDimension::D2,
58 multisampled: false,
59 },
60 count: None,
61 },
62 wgpu::BindGroupLayoutEntry {
63 binding: 1,
64 visibility: wgpu::ShaderStages::FRAGMENT,
65 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
66 count: None,
67 },
68 ],
69 });
70
71 let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
72 label: Some("wisp::quad pipeline layout"),
73 bind_group_layouts: &[&uniforms_layout, &texture_layout],
74 push_constant_ranges: &[],
75 });
76
77 let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
78 label: Some("wisp::quad pipeline"),
79 layout: Some(&pipeline_layout),
80 vertex: wgpu::VertexState {
81 module: &shader,
82 entry_point: Some("main_vs"),
83 buffers: &[],
84 compilation_options: wgpu::PipelineCompilationOptions::default(),
85 },
86 fragment: Some(wgpu::FragmentState {
87 module: &shader,
88 entry_point: Some("main_fs"),
89 targets: &[Some(wgpu::ColorTargetState {
90 format: output_format,
91 blend: Some(wgpu::BlendState::ALPHA_BLENDING),
92 write_mask: wgpu::ColorWrites::ALL,
93 })],
94 compilation_options: wgpu::PipelineCompilationOptions::default(),
95 }),
96 primitive: wgpu::PrimitiveState::default(),
97 depth_stencil: None,
98 multisample: wgpu::MultisampleState::default(),
99 multiview: None,
100 cache: None,
101 });
102
103 Self {
104 pipeline,
105 uniforms_layout,
106 texture_layout,
107 }
108 }
109
110 pub(crate) fn draw(
113 &self,
114 app: &Application,
115 pass: &mut wgpu::RenderPass<'_>,
116 texture: &Texture,
117 model: Mat4,
118 tint: Color,
119 ) {
120 let device = app.device();
121 let uniforms = QuadUniforms {
122 model: model.to_cols_array_2d(),
123 tint: [tint.r, tint.g, tint.b, tint.a],
124 };
125 let buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
126 label: Some("wisp::quad uniforms"),
127 contents: bytemuck::bytes_of(&uniforms),
128 usage: wgpu::BufferUsages::UNIFORM,
129 });
130
131 let uniforms_bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
132 label: Some("wisp::quad uniforms bg"),
133 layout: &self.uniforms_layout,
134 entries: &[wgpu::BindGroupEntry {
135 binding: 0,
136 resource: buffer.as_entire_binding(),
137 }],
138 });
139
140 let texture_bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
141 label: Some("wisp::quad texture bg"),
142 layout: &self.texture_layout,
143 entries: &[
144 wgpu::BindGroupEntry {
145 binding: 0,
146 resource: wgpu::BindingResource::TextureView(texture.view()),
147 },
148 wgpu::BindGroupEntry {
149 binding: 1,
150 resource: wgpu::BindingResource::Sampler(texture.sampler()),
151 },
152 ],
153 });
154
155 pass.set_pipeline(&self.pipeline);
156 pass.set_bind_group(0, &uniforms_bg, &[]);
157 pass.set_bind_group(1, &texture_bg, &[]);
158 pass.draw(0..6, 0..1);
159 }
160}