1use futures_lite::future::block_on;
9use std::f32::consts::FRAC_PI_2;
10use wgpu::util::DeviceExt;
11
12use super::*;
13use crate::DrawShadedImpl;
14use crate::Options;
15use kas::cast::traits::*;
16use kas::config::RasterConfig;
17use kas::draw::color::Rgba;
18use kas::draw::*;
19use kas::geom::{Quad, Size, Vec2};
20use kas::runner::{GraphicsFeatures, RunError};
21use kas::text::{Effect, TextDisplay};
22
23impl<C: CustomPipe> DrawPipe<C> {
24 pub fn new<CB: CustomPipeBuilder<Pipe = C>>(
26 instance: &wgpu::Instance,
27 custom: &mut CB,
28 options: &Options,
29 surface: Option<&wgpu::Surface>,
30 features: GraphicsFeatures,
31 ) -> Result<Self, RunError> {
32 let mut adapter_options = options.adapter_options();
33 adapter_options.compatible_surface = surface;
34 let req = instance.request_adapter(&adapter_options);
35 let adapter = match block_on(req) {
36 Ok(a) => a,
37 Err(e) => return Err(RunError::Graphics(Box::new(e))),
38 };
39 log::info!("Using graphics adapter: {}", adapter.get_info().name);
40
41 let mut desc = CB::device_descriptor(&adapter);
43 if features.subpixel_rendering
44 && adapter
45 .features()
46 .contains(wgpu::Features::DUAL_SOURCE_BLENDING)
47 {
48 desc.required_features |= wgpu::Features::DUAL_SOURCE_BLENDING;
49 }
50 desc.required_limits = desc.required_limits.using_resolution(adapter.limits());
51
52 let req = adapter.request_device(&desc);
53 let (device, queue) = block_on(req).map_err(|e| RunError::Graphics(Box::new(e)))?;
54
55 let shaders = ShaderManager::new(&device);
56
57 let staging_belt = wgpu::util::StagingBelt::new(device.clone(), 1024);
59
60 let bgl_common = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
61 label: Some("common bind group layout"),
62 entries: &[
63 wgpu::BindGroupLayoutEntry {
64 binding: 0,
65 visibility: wgpu::ShaderStages::VERTEX,
66 ty: wgpu::BindingType::Buffer {
67 ty: wgpu::BufferBindingType::Uniform,
68 has_dynamic_offset: false,
69 min_binding_size: wgpu::BufferSize::new(16),
70 },
71 count: None,
72 },
73 wgpu::BindGroupLayoutEntry {
74 binding: 1,
75 visibility: wgpu::ShaderStages::FRAGMENT,
76 ty: wgpu::BindingType::Buffer {
77 ty: wgpu::BufferBindingType::Uniform,
78 has_dynamic_offset: false,
79 min_binding_size: wgpu::BufferSize::new(16),
80 },
81 count: None,
82 },
83 ],
84 });
85
86 let dir: (f32, f32) = (0.3, 0.4);
90 assert!(0.0 <= dir.0 && dir.0 < FRAC_PI_2);
91 let a = (dir.0.sin(), dir.0.cos());
92 let f = a.0 / a.1;
94 let light_norm = [dir.1.sin() * f, -dir.1.cos() * f, 1.0, 0.0];
95
96 let light_norm_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
97 label: Some("light_norm_buf"),
98 contents: bytemuck::cast_slice(&light_norm),
99 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
100 });
101
102 let images = images::Images::new(&device, &shaders, &bgl_common);
103 let shaded_square = shaded_square::Pipeline::new(&device, &shaders, &bgl_common);
104 let shaded_round = shaded_round::Pipeline::new(&device, &shaders, &bgl_common);
105 let flat_round = flat_round::Pipeline::new(&device, &shaders, &bgl_common);
106 let round_2col = round_2col::Pipeline::new(&device, &shaders, &bgl_common);
107 let custom = custom.build(&device, &bgl_common, RENDER_TEX_FORMAT);
108
109 Ok(DrawPipe {
110 adapter,
111 device,
112 queue,
113 staging_belt,
114 bgl_common,
115 light_norm_buf,
116 bg_common: vec![],
117 images,
118 text: kas::text::raster::State::default(),
119 shaded_square,
120 shaded_round,
121 flat_round,
122 round_2col,
123 custom,
124 })
125 }
126
127 pub fn resize(&self, window: &mut DrawWindow<C::Window>, size: Size) {
129 window.clip_regions[0].rect.size = size;
130
131 let vsize = Vec2::conv(size);
132 let off = vsize * -0.5;
133 let scale = Vec2::splat(2.0) / vsize;
134 window.scale = [off.0, off.1, scale.0, -scale.1];
135
136 self.custom
137 .resize(&mut window.custom, &self.device, &self.queue, size);
138
139 self.queue.submit(std::iter::empty());
140 }
141
142 pub fn render(
144 &mut self,
145 window: &mut DrawWindow<C::Window>,
146 frame_view: &wgpu::TextureView,
147 clear_color: wgpu::Color,
148 ) {
149 let mut scale = window.scale;
153 let base_offset = (scale[0], scale[1]);
154 for (region, bg) in window.clip_regions.iter().zip(self.bg_common.iter()) {
155 let offset = Vec2::conv(region.offset);
156 scale[0] = base_offset.0 - offset.0;
157 scale[1] = base_offset.1 - offset.1;
158 self.queue
159 .write_buffer(&bg.0, 0, bytemuck::cast_slice(&scale));
160 }
161 let device = &self.device;
162 let bg_len = self.bg_common.len();
163 if window.clip_regions.len() > bg_len {
164 let (bgl_common, light_norm_buf) = (&self.bgl_common, &self.light_norm_buf);
165 self.bg_common
166 .extend(window.clip_regions[bg_len..].iter().map(|region| {
167 let offset = Vec2::conv(region.offset);
168 scale[0] = base_offset.0 - offset.0;
169 scale[1] = base_offset.1 - offset.1;
170 let scale_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
171 label: Some("scale_buf"),
172 contents: bytemuck::cast_slice(&scale),
173 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
174 });
175 let bg_common = device.create_bind_group(&wgpu::BindGroupDescriptor {
176 label: Some("common bind group"),
177 layout: bgl_common,
178 entries: &[
179 wgpu::BindGroupEntry {
180 binding: 0,
181 resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
182 buffer: &scale_buf,
183 offset: 0,
184 size: None,
185 }),
186 },
187 wgpu::BindGroupEntry {
188 binding: 1,
189 resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
190 buffer: light_norm_buf,
191 offset: 0,
192 size: None,
193 }),
194 },
195 ],
196 });
197 (scale_buf, bg_common)
198 }));
199 }
200 self.queue.submit(std::iter::empty());
201
202 let mut encoder = self
203 .device
204 .create_command_encoder(&wgpu::CommandEncoderDescriptor {
205 label: Some("render"),
206 });
207
208 self.images.prepare(
209 &mut window.images,
210 &self.device,
211 &self.queue,
212 &mut self.staging_belt,
213 &mut encoder,
214 &mut self.text,
215 );
216 window
217 .shaded_square
218 .write_buffers(&self.device, &mut self.staging_belt, &mut encoder);
219 window
220 .shaded_round
221 .write_buffers(&self.device, &mut self.staging_belt, &mut encoder);
222 window
223 .flat_round
224 .write_buffers(&self.device, &mut self.staging_belt, &mut encoder);
225 window
226 .round_2col
227 .write_buffers(&self.device, &mut self.staging_belt, &mut encoder);
228 self.custom.prepare(
229 &mut window.custom,
230 &self.device,
231 &mut self.staging_belt,
232 &mut encoder,
233 );
234
235 let mut color_attachments = [Some(wgpu::RenderPassColorAttachment {
236 view: frame_view,
237 depth_slice: None,
238 resolve_target: None,
239 ops: wgpu::Operations {
240 load: wgpu::LoadOp::Clear(clear_color),
241 store: wgpu::StoreOp::Store,
242 },
243 })];
244
245 let mut passes: Vec<_> = window
247 .clip_regions
248 .iter()
249 .map(|pass| pass.order)
250 .enumerate()
251 .collect();
252 passes.sort_by_key(|pass| pass.1);
254
255 for (pass, _) in passes.drain(..) {
257 let rect = window.clip_regions[pass].rect;
258 if rect.size.0 == 0 || rect.size.1 == 0 {
259 continue;
260 }
261 let bg_common = &self.bg_common[pass].1;
262
263 {
264 let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
265 label: Some("kas-wgpu render pass"),
266 color_attachments: &color_attachments,
267 depth_stencil_attachment: None,
268 timestamp_writes: None,
269 occlusion_query_set: None,
270 multiview_mask: None,
271 });
272 rpass.set_scissor_rect(
273 rect.pos.0.cast(),
274 rect.pos.1.cast(),
275 rect.size.0.cast(),
276 rect.size.1.cast(),
277 );
278
279 self.round_2col
280 .render(&window.round_2col, pass, &mut rpass, bg_common);
281 self.shaded_square
282 .render(&window.shaded_square, pass, &mut rpass, bg_common);
283 self.images
284 .render(&window.images, pass, &mut rpass, bg_common);
285 self.shaded_round
286 .render(&window.shaded_round, pass, &mut rpass, bg_common);
287 self.flat_round
288 .render(&window.flat_round, pass, &mut rpass, bg_common);
289 self.custom.render_pass(
290 &mut window.custom,
291 &self.device,
292 pass,
293 &mut rpass,
294 bg_common,
295 );
296 }
297
298 color_attachments[0].as_mut().unwrap().ops.load = wgpu::LoadOp::Load;
299 }
300
301 let size = window.clip_regions[0].rect.size;
302
303 self.custom.render_final(
304 &mut window.custom,
305 &self.device,
306 &mut encoder,
307 frame_view,
308 size,
309 );
310
311 window.clip_regions.truncate(1);
313
314 self.staging_belt.finish();
315 self.queue.submit(std::iter::once(encoder.finish()));
316
317 self.staging_belt.recall();
318 }
319}
320
321impl<C: CustomPipe> DrawSharedImpl for DrawPipe<C> {
322 type Draw = DrawWindow<C::Window>;
323
324 fn max_texture_dimension_2d(&self) -> u32 {
325 self.device.limits().max_texture_dimension_2d
326 }
327
328 fn set_raster_config(&mut self, config: &RasterConfig) {
329 self.text.set_raster_config(config);
330 }
331
332 #[inline]
333 fn image_alloc(&mut self, format: ImageFormat, size: Size) -> Result<ImageId, AllocError> {
334 self.images.alloc(format, size)
335 }
336
337 #[inline]
338 fn image_upload(&mut self, id: ImageId, data: &[u8]) -> Result<(), UploadError> {
339 self.images.upload(&self.device, &self.queue, id, data)
340 }
341
342 #[inline]
343 fn image_free(&mut self, id: ImageId) {
344 self.images.free(id);
345 }
346
347 #[inline]
348 fn image_size(&self, id: ImageId) -> Option<Size> {
349 self.images.image_size(id)
350 }
351
352 #[inline]
353 fn draw_image(&self, draw: &mut Self::Draw, pass: PassId, id: ImageId, rect: Quad) {
354 if let Some((atlas, tex)) = self.images.get_im_atlas_coords(id) {
355 draw.images.rect(pass, atlas, tex, rect);
356 };
357 }
358
359 #[inline]
360 fn draw_text(
361 &mut self,
362 draw: &mut Self::Draw,
363 pass: PassId,
364 pos: Vec2,
365 bb: Quad,
366 text: &TextDisplay,
367 col: Rgba,
368 ) {
369 let time = std::time::Instant::now();
370 self.text
371 .text(&mut self.images, &mut draw.images, pass, pos, bb, text, col);
372 draw.common.report_dur_text(time.elapsed());
373 }
374
375 fn draw_text_effects(
376 &mut self,
377 draw: &mut Self::Draw,
378 pass: PassId,
379 pos: Vec2,
380 bb: Quad,
381 text: &TextDisplay,
382 colors: &[Rgba],
383 effects: &[Effect],
384 ) {
385 let time = std::time::Instant::now();
386 self.text.text_effects(
387 &mut self.images,
388 &mut draw.images,
389 pass,
390 pos,
391 bb,
392 text,
393 colors,
394 effects,
395 |quad, col| {
396 draw.shaded_square.rect(pass, quad, col);
397 },
398 );
399 draw.common.report_dur_text(time.elapsed());
400 }
401}
402
403impl<CW: CustomWindow> DrawImpl for DrawWindow<CW> {
404 fn common_mut(&mut self) -> &mut WindowCommon {
405 &mut self.common
406 }
407
408 fn new_pass(
409 &mut self,
410 parent_pass: PassId,
411 rect: Rect,
412 offset: Offset,
413 class: PassType,
414 ) -> PassId {
415 let parent = match class {
416 PassType::Clip => &self.clip_regions[parent_pass.pass()],
417 PassType::Overlay => {
418 &self.clip_regions[0]
421 }
422 };
423 let order = match class {
424 PassType::Clip => (parent.order << 4) + 1,
425 PassType::Overlay => (parent.order << 16) + 1,
426 };
427 let rect = rect - parent.offset;
428 let offset = offset + parent.offset;
429 let rect = rect.intersection(&parent.rect).unwrap_or(Rect::ZERO);
430 let pass = self.clip_regions.len().cast();
431 self.clip_regions.push(ClipRegion {
432 rect,
433 offset,
434 order,
435 });
436 PassId::new(pass)
437 }
438
439 #[inline]
440 fn get_clip_rect(&self, pass: PassId) -> Rect {
441 let region = &self.clip_regions[pass.pass()];
442 region.rect + region.offset
443 }
444
445 #[inline]
446 fn rect(&mut self, pass: PassId, rect: Quad, col: Rgba) {
447 self.shaded_square.rect(pass, rect, col);
448 }
449
450 #[inline]
451 fn frame(&mut self, pass: PassId, outer: Quad, inner: Quad, col: Rgba) {
452 self.shaded_square.frame(pass, outer, inner, col);
453 }
454
455 #[inline]
456 fn line(&mut self, pass: PassId, p1: Vec2, p2: Vec2, width: f32, col: Rgba) {
457 self.flat_round.line(pass, p1, p2, 0.5 * width, col);
458 }
459}
460
461impl<CW: CustomWindow> DrawRoundedImpl for DrawWindow<CW> {
462 #[inline]
463 fn rounded_line(&mut self, pass: PassId, p1: Vec2, p2: Vec2, radius: f32, col: Rgba) {
464 self.flat_round.line(pass, p1, p2, radius, col);
465 }
466
467 #[inline]
468 fn circle(&mut self, pass: PassId, rect: Quad, inner_radius: f32, col: Rgba) {
469 self.flat_round.circle(pass, rect, inner_radius, col);
470 }
471
472 #[inline]
473 fn circle_2col(&mut self, pass: PassId, rect: Quad, col1: Rgba, col2: Rgba) {
474 self.round_2col.circle(pass, rect, col1, col2);
475 }
476
477 #[inline]
478 fn rounded_frame(&mut self, pass: PassId, outer: Quad, inner: Quad, r1: f32, col: Rgba) {
479 self.flat_round.rounded_frame(pass, outer, inner, r1, col);
480 }
481
482 #[inline]
483 fn rounded_frame_2col(&mut self, pass: PassId, outer: Quad, inner: Quad, c1: Rgba, c2: Rgba) {
484 self.round_2col.frame(pass, outer, inner, c1, c2);
485 }
486}
487
488impl<CW: CustomWindow> DrawShadedImpl for DrawWindow<CW> {
489 #[inline]
490 fn shaded_square(&mut self, pass: PassId, rect: Quad, norm: (f32, f32), col: Rgba) {
491 self.shaded_square
492 .shaded_rect(pass, rect, Vec2::from(norm), col);
493 }
494
495 #[inline]
496 fn shaded_circle(&mut self, pass: PassId, rect: Quad, norm: (f32, f32), col: Rgba) {
497 self.shaded_round.circle(pass, rect, Vec2::from(norm), col);
498 }
499
500 #[inline]
501 fn shaded_square_frame(
502 &mut self,
503 pass: PassId,
504 outer: Quad,
505 inner: Quad,
506 norm: (f32, f32),
507 outer_col: Rgba,
508 inner_col: Rgba,
509 ) {
510 self.shaded_square
511 .shaded_frame(pass, outer, inner, Vec2::from(norm), outer_col, inner_col);
512 }
513
514 #[inline]
515 fn shaded_round_frame(
516 &mut self,
517 pass: PassId,
518 outer: Quad,
519 inner: Quad,
520 norm: (f32, f32),
521 col: Rgba,
522 ) {
523 self.shaded_round
524 .shaded_frame(pass, outer, inner, Vec2::from(norm), col);
525 }
526}