1use std::collections::HashMap;
2use std::ops::{Deref, DerefMut};
3
4use zerocopy::AsBytes;
5
6use wgpu::*;
7
8use crate::draw::{Command as DrawCommand, DrawList, Update, Vertex};
9use crate::layout::Rectangle;
10use crate::style::Style;
11use crate::Component;
12use std::num::NonZeroU32;
13use wgpu::util::DeviceExt;
14
15pub struct Ui<C: 'static + Component> {
18 inner: crate::Ui<C>,
19 pipeline: RenderPipeline,
20 bind_group_layout: BindGroupLayout,
21 sampler: Sampler,
22 textures: HashMap<usize, TextureEntry>,
23 vertex_buffer: Option<Buffer>,
24 draw_commands: Vec<DrawCommand>,
25}
26
27struct TextureEntry {
28 texture: Texture,
29 bind_group: BindGroup,
30}
31
32impl<C: Component> Ui<C> {
33 pub fn new<S, E>(
35 root_component: C,
36 viewport: Rectangle,
37 hidpi_scale: f32,
38 style: S,
39 format: wgpu::TextureFormat,
40 device: &Device,
41 ) -> anyhow::Result<Self>
42 where
43 S: TryInto<Style, Error = E>,
44 anyhow::Error: From<E>,
45 {
46 Ok(Self::new_inner(
47 crate::Ui::new(root_component, viewport, hidpi_scale, style)?,
48 format,
49 device,
50 ))
51 }
52
53 fn new_inner(inner: crate::Ui<C>, format: wgpu::TextureFormat, device: &Device) -> Self {
54 let shader_module = device.create_shader_module(&ShaderModuleDescriptor {
55 label: Some("wgpu.wgsl"),
56 source: wgpu::ShaderSource::Wgsl(include_str!("wgpu.wgsl").into()),
57 });
58 let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
59 label: None,
60 entries: &[
61 wgpu::BindGroupLayoutEntry {
62 binding: 0,
63 visibility: wgpu::ShaderStages::FRAGMENT,
64 ty: wgpu::BindingType::Texture {
65 sample_type: wgpu::TextureSampleType::Float { filterable: true },
66 multisampled: false,
67 view_dimension: wgpu::TextureViewDimension::D2,
68 },
69 count: None,
70 },
71 wgpu::BindGroupLayoutEntry {
72 binding: 1,
73 visibility: wgpu::ShaderStages::FRAGMENT,
74 ty: wgpu::BindingType::Sampler {
75 filtering: true,
76 comparison: false,
77 },
78 count: None,
79 },
80 ],
81 });
82 let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
83 label: None,
84 bind_group_layouts: &[&bind_group_layout],
85 push_constant_ranges: &[],
86 });
87 let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
88 label: None,
89 layout: Some(&pipeline_layout),
90 vertex: wgpu::VertexState {
91 module: &shader_module,
92 entry_point: "vs_main",
93 buffers: &[wgpu::VertexBufferLayout {
94 array_stride: std::mem::size_of::<Vertex>() as u64,
95 step_mode: wgpu::VertexStepMode::Vertex,
96 attributes: &[
97 wgpu::VertexAttribute {
98 format: VertexFormat::Float32x2,
99 offset: 0,
100 shader_location: 0,
101 },
102 wgpu::VertexAttribute {
103 format: VertexFormat::Float32x2,
104 offset: 8,
105 shader_location: 1,
106 },
107 wgpu::VertexAttribute {
108 format: VertexFormat::Float32x4,
109 offset: 16,
110 shader_location: 2,
111 },
112 wgpu::VertexAttribute {
113 format: VertexFormat::Float32,
114 offset: 32,
115 shader_location: 3,
116 },
117 ],
118 }],
119 },
120 primitive: wgpu::PrimitiveState {
121 topology: PrimitiveTopology::TriangleList,
122 ..wgpu::PrimitiveState::default()
123 },
124 depth_stencil: None,
125 multisample: Default::default(),
126 fragment: Some(wgpu::FragmentState {
127 module: &shader_module,
128 entry_point: "fs_main",
129 targets: &[wgpu::ColorTargetState {
130 format,
131 blend: Some(wgpu::BlendState::ALPHA_BLENDING),
132 write_mask: wgpu::ColorWrites::ALL,
133 }],
134 }),
135 });
136
137 let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
138 label: None,
139 address_mode_u: wgpu::AddressMode::ClampToEdge,
140 address_mode_v: wgpu::AddressMode::ClampToEdge,
141 address_mode_w: wgpu::AddressMode::ClampToEdge,
142 mag_filter: wgpu::FilterMode::Nearest,
143 min_filter: wgpu::FilterMode::Linear,
144 mipmap_filter: wgpu::FilterMode::Linear,
145 lod_min_clamp: 0.0,
146 lod_max_clamp: 0.0,
147 compare: None,
148 anisotropy_clamp: None,
149 border_color: None,
150 });
151
152 Self {
153 inner,
154 pipeline,
155 bind_group_layout,
156 sampler,
157 textures: HashMap::new(),
158 vertex_buffer: None,
159 draw_commands: Vec::new(),
160 }
161 }
162
163 pub fn draw<'a>(&'a mut self, device: &Device, queue: &Queue, render_pass: &mut RenderPass<'a>) {
167 if self.inner.needs_redraw() {
168 let DrawList {
169 updates,
170 vertices,
171 commands,
172 } = self.inner.draw();
173
174 self.vertex_buffer.take();
175 self.draw_commands = commands;
176
177 if !updates.is_empty() {
178 let cmd = device.create_command_encoder(&CommandEncoderDescriptor { label: None });
179 queue.submit(Some(
180 updates
181 .into_iter()
182 .fold(cmd, |mut cmd, update| {
183 match update {
184 Update::Texture {
185 id,
186 size,
187 data,
188 atlas: _,
189 } => {
190 let texture_desc = wgpu::TextureDescriptor {
191 label: None,
192 size: wgpu::Extent3d {
193 width: size[0],
194 height: size[1],
195 depth_or_array_layers: 1,
196 },
197 mip_level_count: 1,
198 sample_count: 1,
199 dimension: wgpu::TextureDimension::D2,
200 format: wgpu::TextureFormat::Rgba8Unorm,
201 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
202 };
203 let texture = if data.is_empty() {
204 device.create_texture(&texture_desc)
205 } else {
206 device.create_texture_with_data(queue, &texture_desc, data.as_slice())
207 };
208
209 let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
210
211 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
212 layout: &self.bind_group_layout,
213 entries: &[
214 wgpu::BindGroupEntry {
215 binding: 0,
216 resource: wgpu::BindingResource::TextureView(&view),
217 },
218 wgpu::BindGroupEntry {
219 binding: 1,
220 resource: wgpu::BindingResource::Sampler(&self.sampler),
221 },
222 ],
223 label: None,
224 });
225
226 self.textures.insert(id, TextureEntry { bind_group, texture });
227 }
228 Update::TextureSubresource { id, offset, size, data } => {
229 let texture = self
230 .textures
231 .get(&id)
232 .map(|val| &val.texture)
233 .expect("non existing texture is updated");
234
235 let padding = 256 - (size[0] * 4) % 256;
236 let data = if padding > 0 {
237 data.chunks(size[0] as usize * 4).fold(Vec::new(), |mut data, row| {
238 data.extend_from_slice(row);
239 data.extend(std::iter::repeat(0).take(padding as _));
240 data
241 })
242 } else {
243 data
244 };
245 let staging = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
246 label: None,
247 contents: data.as_slice(),
248 usage: wgpu::BufferUsages::COPY_SRC,
249 });
250 cmd.copy_buffer_to_texture(
251 wgpu::ImageCopyBuffer {
252 buffer: &staging,
253 layout: wgpu::ImageDataLayout {
254 offset: 0,
255 bytes_per_row: NonZeroU32::new(size[0] * 4 + padding),
256 rows_per_image: None,
257 },
258 },
259 wgpu::ImageCopyTexture {
260 texture,
261 mip_level: 0,
262 origin: wgpu::Origin3d {
263 x: offset[0],
264 y: offset[1],
265 z: 0,
266 },
267 aspect: wgpu::TextureAspect::All,
268 },
269 wgpu::Extent3d {
270 width: size[0],
271 height: size[1],
272 depth_or_array_layers: 1,
273 },
274 );
275 }
276 }
277 cmd
278 })
279 .finish(),
280 ));
281 }
282
283 if !vertices.is_empty() {
284 self.vertex_buffer
285 .replace(device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
286 label: None,
287 contents: vertices.as_bytes(),
288 usage: wgpu::BufferUsages::VERTEX,
289 }));
290 }
291 }
292
293 if let Some(vertex_buffer) = self.vertex_buffer.as_ref() {
294 render_pass.set_pipeline(&self.pipeline);
295 render_pass.set_bind_group(0, &self.textures.values().next().unwrap().bind_group, &[]);
296 render_pass.set_vertex_buffer(0, vertex_buffer.slice(..));
297 }
298
299 for command in self.draw_commands.iter() {
300 match command {
301 DrawCommand::Clip { scissor } => {
302 render_pass.set_scissor_rect(
303 scissor.left as u32,
304 scissor.top as u32,
305 scissor.width() as u32,
306 scissor.height() as u32,
307 );
308 }
309 &DrawCommand::Colored { offset, count } => {
310 render_pass.draw(offset as u32..(offset + count) as u32, 0..1);
311 }
312 &DrawCommand::Textured { texture, offset, count } => {
313 render_pass.set_bind_group(0, &self.textures.get(&texture).unwrap().bind_group, &[]);
314 render_pass.draw(offset as u32..(offset + count) as u32, 0..1);
315 }
316 DrawCommand::Nop => (),
317 }
318 }
319 }
320}
321
322impl<C: Component> Deref for Ui<C> {
323 type Target = crate::Ui<C>;
324
325 fn deref(&self) -> &Self::Target {
326 &self.inner
327 }
328}
329
330impl<C: Component> DerefMut for Ui<C> {
331 fn deref_mut(&mut self) -> &mut Self::Target {
332 &mut self.inner
333 }
334}