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