1use std::sync::Arc;
2
3use crate::allocator::{RenderAllocator, TexKey};
6use crate::pipeline::{
8 BackgroundRenderer, BasicSolidRenderer, Blitter, BlurRenderer, Compositor,
9 OverlaySolidRenderer, ScrimSolidRenderer, ScrimStencilMaskRenderer, ScrimStencilRenderer,
10 ShadowCompositeRenderer, SmaaRenderer, TextRenderer,
11};
12use crate::scene::{BoxShadowSpec, RoundedRadii, RoundedRect};
13use crate::upload::GpuScene;
14
15fn apply_transform_to_point(point: [f32; 2], transform: crate::Transform2D) -> [f32; 2] {
17 let [a, b, c, d, e, f] = transform.m;
18 let x = point[0];
19 let y = point[1];
20 [a * x + c * y + e, b * x + d * y + f]
21}
22
23fn u16_unorm_to_u8(v: u16) -> u8 {
24 ((u32::from(v) * 255 + 32767) / 65535) as u8
25}
26
27fn glyph_mask_for_atlas(
28 mask: &crate::text::GlyphMask,
29 force_grayscale: bool,
30) -> (u32, u32, std::borrow::Cow<'_, [u8]>) {
31 match mask {
32 crate::text::GlyphMask::Color(m) => {
33 (m.width, m.height, std::borrow::Cow::Borrowed(&m.data))
34 }
35 crate::text::GlyphMask::Subpixel(m) => match m.format {
36 crate::text::MaskFormat::Rgba8 => {
37 if !force_grayscale {
38 return (m.width, m.height, std::borrow::Cow::Borrowed(&m.data));
39 }
40
41 let mut out = Vec::with_capacity((m.width as usize) * (m.height as usize) * 4);
42 for px in m.data.chunks_exact(4) {
43 let gray = ((u16::from(px[0]) + u16::from(px[1]) + u16::from(px[2])) / 3) as u8;
44 out.extend_from_slice(&[gray, gray, gray, 0]);
45 }
46 (m.width, m.height, std::borrow::Cow::Owned(out))
47 }
48 crate::text::MaskFormat::Rgba16 => {
49 let mut out = Vec::with_capacity((m.width as usize) * (m.height as usize) * 4);
51 for px in m.data.chunks_exact(8) {
52 let r = u16::from_le_bytes([px[0], px[1]]);
53 let g = u16::from_le_bytes([px[2], px[3]]);
54 let b = u16::from_le_bytes([px[4], px[5]]);
55 let (r8, g8, b8) = if force_grayscale {
56 let gray16 = ((u32::from(r) + u32::from(g) + u32::from(b)) / 3) as u16;
57 let gray8 = u16_unorm_to_u8(gray16);
58 (gray8, gray8, gray8)
59 } else {
60 (u16_unorm_to_u8(r), u16_unorm_to_u8(g), u16_unorm_to_u8(b))
61 };
62 out.extend_from_slice(&[r8, g8, b8, 0]);
63 }
64 (m.width, m.height, std::borrow::Cow::Owned(out))
65 }
66 },
67 }
68}
69
70pub struct PassTargets {
71 pub color: crate::OwnedTexture,
72}
73
74pub enum Background {
75 Solid(crate::scene::ColorLinPremul),
76 LinearGradient {
77 start_uv: [f32; 2],
78 end_uv: [f32; 2],
79 stop0: (f32, crate::scene::ColorLinPremul),
80 stop1: (f32, crate::scene::ColorLinPremul),
81 },
82}
83
84pub struct PassManager {
85 device: Arc<wgpu::Device>,
86 pub solid_offscreen: BasicSolidRenderer,
87 pub solid_direct: BasicSolidRenderer,
88 pub transparent_solid_offscreen: BasicSolidRenderer,
89 pub transparent_solid_direct: BasicSolidRenderer,
90 pub solid_direct_no_msaa: BasicSolidRenderer,
91 overlay_solid: OverlaySolidRenderer,
92 scrim_solid: ScrimSolidRenderer,
93 pub compositor: Compositor,
94 pub blitter: Blitter,
95 pub smaa: SmaaRenderer,
96 scrim_mask: ScrimStencilMaskRenderer,
97 scrim_stencil: ScrimStencilRenderer,
98 pub mask_renderer: BasicSolidRenderer,
100 pub blur_r8: BlurRenderer,
101 pub shadow_comp: ShadowCompositeRenderer,
102 pub text: TextRenderer,
103 pub text_offscreen: TextRenderer,
104 pub image: crate::pipeline::ImageRenderer,
105 pub image_offscreen: crate::pipeline::ImageRenderer,
106 pub svg_cache: crate::svg::SvgRasterCache,
107 pub image_cache: crate::image_cache::ImageCache,
108 offscreen_format: wgpu::TextureFormat,
109 surface_format: wgpu::TextureFormat,
110 vp_buffer: wgpu::Buffer,
111 scroll_offset: [f32; 2],
115 z_index_buffer: wgpu::Buffer,
117 bg: BackgroundRenderer,
118 bg_param_buffer: wgpu::Buffer,
119 bg_stops_buffer: wgpu::Buffer,
120 scale_factor: f32,
122 ui_scale: f32,
124 logical_pixels: bool,
126 pub intermediate_texture: Option<crate::OwnedTexture>,
128 smaa_edges: Option<crate::OwnedTexture>,
129 smaa_weights: Option<crate::OwnedTexture>,
130 depth_texture: Option<crate::OwnedTexture>,
132 scrim_stencil_tex: Option<crate::OwnedTexture>,
134 text_mask_atlas: wgpu::Texture,
136 #[allow(dead_code)]
138 text_mask_atlas_view: wgpu::TextureView,
139 text_bind_group: wgpu::BindGroup,
140 prev_atlas_max_x: u32,
142 prev_atlas_max_y: u32,
143 smaa_param_buffer: wgpu::Buffer,
144 external_textures:
146 std::collections::HashMap<crate::display_list::ExternalTextureId, wgpu::TextureView>,
147}
148
149#[repr(C)]
151#[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
152struct TextQuadVtx {
153 pos: [f32; 2],
154 uv: [f32; 2],
155 color: [f32; 4],
156}
157
158#[repr(C)]
159#[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
160struct ImageQuadVtx {
161 pos: [f32; 2],
162 uv: [f32; 2],
163}
164
165impl PassManager {
166 fn choose_offscreen_format(
172 device: &wgpu::Device,
173 target_format: wgpu::TextureFormat,
174 ) -> wgpu::TextureFormat {
175 let prefer_srgb = target_format.is_srgb();
177 let preferred = if prefer_srgb {
178 wgpu::TextureFormat::Rgba8UnormSrgb
179 } else {
180 wgpu::TextureFormat::Rgba8Unorm
181 };
182
183 let device_features = device.features();
186 let supports = |format: wgpu::TextureFormat| {
187 format
188 .guaranteed_format_features(device_features)
189 .allowed_usages
190 .contains(wgpu::TextureUsages::RENDER_ATTACHMENT)
191 };
192
193 if supports(preferred) {
194 preferred
195 } else {
196 let fallback = if prefer_srgb {
197 wgpu::TextureFormat::Rgba8Unorm
198 } else {
199 wgpu::TextureFormat::Rgba8UnormSrgb
200 };
201
202 if supports(fallback) {
203 fallback
204 } else {
205 target_format
207 }
208 }
209 }
210
211 pub fn new(device: Arc<wgpu::Device>, target_format: wgpu::TextureFormat) -> Self {
212 let offscreen_format = Self::choose_offscreen_format(&device, target_format);
214 let msaa_count = 1;
218 let solid_offscreen = BasicSolidRenderer::new(device.clone(), offscreen_format, msaa_count);
219 let solid_direct = BasicSolidRenderer::new(device.clone(), target_format, msaa_count);
220 let transparent_solid_offscreen = BasicSolidRenderer::new_with_depth_state(
221 device.clone(),
222 offscreen_format,
223 msaa_count,
224 false,
225 wgpu::CompareFunction::LessEqual,
226 );
227 let transparent_solid_direct = BasicSolidRenderer::new_with_depth_state(
228 device.clone(),
229 target_format,
230 msaa_count,
231 false,
232 wgpu::CompareFunction::LessEqual,
233 );
234 let solid_direct_no_msaa = BasicSolidRenderer::new(device.clone(), target_format, 1);
235 let overlay_solid = OverlaySolidRenderer::new(device.clone(), target_format);
236 let scrim_solid = ScrimSolidRenderer::new(device.clone(), target_format);
237 let compositor = Compositor::new(device.clone(), target_format);
238 let blitter = Blitter::new(device.clone(), target_format);
239 let smaa = SmaaRenderer::new(device.clone(), target_format);
240 let scrim_mask = ScrimStencilMaskRenderer::new(device.clone(), target_format);
241 let scrim_stencil = ScrimStencilRenderer::new(device.clone(), target_format);
242 let mask_renderer =
244 BasicSolidRenderer::new(device.clone(), wgpu::TextureFormat::R8Unorm, 1);
245 let blur_r8 = BlurRenderer::new(device.clone(), wgpu::TextureFormat::R8Unorm);
246 let shadow_comp = ShadowCompositeRenderer::new(device.clone(), target_format);
247 let text = TextRenderer::new(device.clone(), target_format);
248 let text_offscreen = TextRenderer::new(device.clone(), offscreen_format);
249 let image = crate::pipeline::ImageRenderer::new(device.clone(), target_format);
250 let image_offscreen = crate::pipeline::ImageRenderer::new(device.clone(), offscreen_format);
251 let svg_cache = crate::svg::SvgRasterCache::new(device.clone());
252 let image_cache = crate::image_cache::ImageCache::new(device.clone());
253 let bg = BackgroundRenderer::new(device.clone(), target_format);
254 let vp_buffer = device.create_buffer(&wgpu::BufferDescriptor {
255 label: Some("viewport-uniform"),
256 size: 32, usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
258 mapped_at_creation: false,
259 });
260 let z_index_buffer = device.create_buffer(&wgpu::BufferDescriptor {
262 label: Some("z-index-uniform"),
263 size: 4, usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
265 mapped_at_creation: false,
266 });
267 let bg_param_buffer = device.create_buffer(&wgpu::BufferDescriptor {
268 label: Some("background-params"),
269 size: 64,
270 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
271 mapped_at_creation: false,
272 });
273 let bg_stops_buffer = device.create_buffer(&wgpu::BufferDescriptor {
274 label: Some("background-stops"),
275 size: 256, usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
277 mapped_at_creation: false,
278 });
279 let smaa_param_buffer = device.create_buffer(&wgpu::BufferDescriptor {
280 label: Some("smaa-params"),
281 size: 16,
282 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
283 mapped_at_creation: false,
284 });
285 let text_mask_atlas = device.create_texture(&wgpu::TextureDescriptor {
287 label: Some("text-mask-atlas"),
288 size: wgpu::Extent3d {
289 width: 4096,
290 height: 4096,
291 depth_or_array_layers: 1,
292 },
293 mip_level_count: 1,
294 sample_count: 1,
295 dimension: wgpu::TextureDimension::D2,
296 format: wgpu::TextureFormat::Rgba8Unorm,
298 usage: wgpu::TextureUsages::TEXTURE_BINDING
299 | wgpu::TextureUsages::COPY_DST
300 | wgpu::TextureUsages::RENDER_ATTACHMENT,
301 view_formats: &[],
302 });
303 let text_mask_atlas_view =
304 text_mask_atlas.create_view(&wgpu::TextureViewDescriptor::default());
305 let text_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
306 label: Some("text-mask-bgl"),
307 layout: &text.tex_bgl,
308 entries: &[
309 wgpu::BindGroupEntry {
310 binding: 0,
311 resource: wgpu::BindingResource::TextureView(&text_mask_atlas_view),
312 },
313 wgpu::BindGroupEntry {
314 binding: 1,
315 resource: wgpu::BindingResource::Sampler(&text.sampler),
316 },
317 ],
318 });
319 let logical_default = true;
321 let ui_scale = 1.0;
322 Self {
323 device,
324 solid_offscreen,
325 solid_direct,
326 transparent_solid_offscreen,
327 transparent_solid_direct,
328 solid_direct_no_msaa,
329 overlay_solid,
330 scrim_solid,
331 compositor,
332 blitter,
333 smaa,
334 scrim_mask,
335 scrim_stencil,
336 mask_renderer,
337 blur_r8,
338 shadow_comp,
339 text,
340 text_offscreen,
341 image,
342 image_offscreen,
343 svg_cache,
344 image_cache,
345 offscreen_format,
346 surface_format: target_format,
347 vp_buffer,
348 scroll_offset: [0.0, 0.0],
349 z_index_buffer,
350 bg,
351 bg_param_buffer,
352 bg_stops_buffer,
353 scale_factor: 1.0,
354 ui_scale,
355 logical_pixels: logical_default,
356 intermediate_texture: None,
357 smaa_edges: None,
358 smaa_weights: None,
359 depth_texture: None,
360 text_mask_atlas,
361 text_mask_atlas_view,
362 text_bind_group,
363 prev_atlas_max_x: 0,
364 prev_atlas_max_y: 0,
365 smaa_param_buffer,
366 scrim_stencil_tex: None,
367 external_textures: std::collections::HashMap::new(),
368 }
369 }
370
371 pub fn device(&self) -> Arc<wgpu::Device> {
373 self.device.clone()
374 }
375
376 pub fn register_external_texture(
378 &mut self,
379 id: crate::display_list::ExternalTextureId,
380 view: wgpu::TextureView,
381 ) {
382 self.external_textures.insert(id, view);
383 }
384
385 pub fn clear_external_textures(&mut self) {
387 self.external_textures.clear();
388 }
389
390 pub fn create_z_bind_group(&self, z_index: f32, queue: &wgpu::Queue) -> wgpu::BindGroup {
393 queue.write_buffer(&self.z_index_buffer, 0, bytemuck::bytes_of(&z_index));
394 self.device.create_bind_group(&wgpu::BindGroupDescriptor {
395 label: Some("z-index-bg"),
396 layout: self.solid_direct.z_index_bgl(),
397 entries: &[wgpu::BindGroupEntry {
398 binding: 0,
399 resource: self.z_index_buffer.as_entire_binding(),
400 }],
401 })
402 }
403
404 fn create_group_z_bind_group(
408 &self,
409 z_index: f32,
410 queue: &wgpu::Queue,
411 ) -> (wgpu::BindGroup, wgpu::Buffer) {
412 let z_buf = self.device.create_buffer(&wgpu::BufferDescriptor {
413 label: Some("z-index-group-buffer"),
414 size: std::mem::size_of::<f32>() as u64,
415 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
416 mapped_at_creation: false,
417 });
418 queue.write_buffer(&z_buf, 0, bytemuck::bytes_of(&z_index));
419 let bg = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
420 label: Some("z-index-bg-group"),
421 layout: self.solid_direct.z_index_bgl(),
422 entries: &[wgpu::BindGroupEntry {
423 binding: 0,
424 resource: z_buf.as_entire_binding(),
425 }],
426 });
427 (bg, z_buf)
428 }
429
430 pub fn draw_image_quad(
433 &self,
434 encoder: &mut wgpu::CommandEncoder,
435 target_view: &wgpu::TextureView,
436 origin: [f32; 2],
437 size: [f32; 2],
438 tex_view: &wgpu::TextureView,
439 queue: &wgpu::Queue,
440 width: u32,
441 height: u32,
442 ) {
443 let logical =
445 crate::dpi::logical_multiplier(self.logical_pixels, self.scale_factor, self.ui_scale);
446 let scale = [
447 (2.0f32 / (width.max(1) as f32)) * logical,
448 (-2.0f32 / (height.max(1) as f32)) * logical,
449 ];
450 let translate = [-1.0f32, 1.0f32];
451 let vp_data: [f32; 8] = [
452 scale[0],
453 scale[1],
454 translate[0],
455 translate[1],
456 0.0,
457 0.0,
458 0.0,
459 0.0,
460 ];
461 queue.write_buffer(&self.vp_buffer, 0, bytemuck::bytes_of(&vp_data));
463
464 #[repr(C)]
465 #[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
466 struct QuadVtx {
467 pos: [f32; 2],
468 uv: [f32; 2],
469 }
470 let x = origin[0];
471 let y = origin[1];
472 let w = size[0].max(0.0);
473 let h = size[1].max(0.0);
474 let verts = [
475 QuadVtx {
476 pos: [x, y],
477 uv: [0.0, 0.0],
478 },
479 QuadVtx {
480 pos: [x + w, y],
481 uv: [1.0, 0.0],
482 },
483 QuadVtx {
484 pos: [x + w, y + h],
485 uv: [1.0, 1.0],
486 },
487 QuadVtx {
488 pos: [x, y + h],
489 uv: [0.0, 1.0],
490 },
491 ];
492 let idx: [u16; 6] = [0, 1, 2, 0, 2, 3];
493 let vsize = (verts.len() * std::mem::size_of::<QuadVtx>()) as u64;
494 let isize = (idx.len() * std::mem::size_of::<u16>()) as u64;
495 let vbuf = self.device.create_buffer(&wgpu::BufferDescriptor {
496 label: Some("image-vbuf"),
497 size: vsize.max(4),
498 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
499 mapped_at_creation: false,
500 });
501 let ibuf = self.device.create_buffer(&wgpu::BufferDescriptor {
502 label: Some("image-ibuf"),
503 size: isize.max(4),
504 usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
505 mapped_at_creation: false,
506 });
507 if vsize > 0 {
508 queue.write_buffer(&vbuf, 0, bytemuck::cast_slice(&verts));
509 }
510 if isize > 0 {
511 queue.write_buffer(&ibuf, 0, bytemuck::cast_slice(&idx));
512 }
513
514 let vp_bg = self.image.vp_bind_group(&self.device, &self.vp_buffer);
515 let z_bg = self.create_z_bind_group(0.0, queue);
516 let tex_bg = self.image.tex_bind_group(&self.device, tex_view);
517 let (params_bg, _params_buf) = self.image.params_bind_group(&self.device, 1.0, false);
518
519 let depth_tex = self.device.create_texture(&wgpu::TextureDescriptor {
521 label: Some("image-depth"),
522 size: wgpu::Extent3d {
523 width,
524 height,
525 depth_or_array_layers: 1,
526 },
527 mip_level_count: 1,
528 sample_count: 1,
529 dimension: wgpu::TextureDimension::D2,
530 format: wgpu::TextureFormat::Depth32Float,
531 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
532 view_formats: &[],
533 });
534 let depth_view = depth_tex.create_view(&wgpu::TextureViewDescriptor::default());
535
536 let depth_attachment = Some(wgpu::RenderPassDepthStencilAttachment {
537 view: &depth_view,
538 depth_ops: Some(wgpu::Operations {
539 load: wgpu::LoadOp::Load, store: wgpu::StoreOp::Store,
541 }),
542 stencil_ops: None,
543 });
544
545 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
546 label: Some("image-pass"),
547 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
548 view: target_view,
549 resolve_target: None,
550 ops: wgpu::Operations {
551 load: wgpu::LoadOp::Load,
552 store: wgpu::StoreOp::Store,
553 },
554 })],
555 depth_stencil_attachment: depth_attachment,
556 occlusion_query_set: None,
557 timestamp_writes: None,
558 });
559 self.image.record(
560 &mut pass,
561 &vp_bg,
562 &z_bg,
563 &tex_bg,
564 ¶ms_bg,
565 &vbuf,
566 &ibuf,
567 idx.len() as u32,
568 );
569 }
570
571 pub fn rasterize_svg_to_view(
575 &mut self,
576 path: &std::path::Path,
577 scale: f32,
578 style: Option<crate::svg::SvgStyle>,
579 queue: &wgpu::Queue,
580 ) -> Option<(wgpu::TextureView, u32, u32)> {
581 let svg_style = style.unwrap_or_default();
582 let (tex, w, h) = self
583 .svg_cache
584 .get_or_rasterize(path, scale, svg_style, queue)?;
585 let view = tex.create_view(&wgpu::TextureViewDescriptor::default());
586 Some((view, w, h))
587 }
588
589 pub fn load_image_to_view(
592 &mut self,
593 path: &std::path::Path,
594 queue: &wgpu::Queue,
595 ) -> Option<(wgpu::TextureView, u32, u32)> {
596 let (tex, w, h) = self.image_cache.get_or_load(path, queue)?;
597 let view = tex.create_view(&wgpu::TextureViewDescriptor::default());
598 Some((view, w, h))
599 }
600
601 pub fn try_get_image_view(
603 &mut self,
604 path: &std::path::Path,
605 ) -> Option<(wgpu::TextureView, u32, u32)> {
606 let (tex, w, h) = self.image_cache.get(path)?;
607 let view = tex.create_view(&wgpu::TextureViewDescriptor::default());
608 Some((view, w, h))
609 }
610
611 pub fn request_image_load(&mut self, path: &std::path::Path) {
613 self.image_cache.start_load(path);
614 }
615
616 pub fn is_image_ready(&self, path: &std::path::Path) -> bool {
618 self.image_cache.is_ready(path)
619 }
620
621 pub fn store_loaded_image(
623 &mut self,
624 path: &std::path::Path,
625 tex: Arc<wgpu::Texture>,
626 width: u32,
627 height: u32,
628 ) {
629 self.image_cache.store_ready(path, tex, width, height);
630 }
631
632 pub fn get_cached_texture(
635 &mut self,
636 path: &std::path::Path,
637 ) -> Option<(Arc<wgpu::Texture>, u32, u32)> {
638 self.image_cache.get(path)
639 }
640
641 pub fn set_scale_factor(&mut self, sf: f32) {
644 if sf.is_finite() && sf > 0.0 {
645 self.scale_factor = sf;
646 } else {
647 self.scale_factor = 1.0;
648 }
649 }
650
651 pub fn set_ui_scale(&mut self, s: f32) {
653 let s = if s.is_finite() { s } else { 1.0 };
654 self.ui_scale = s.clamp(0.25, 4.0);
655 }
656
657 pub fn set_scroll_offset(&mut self, offset: [f32; 2]) {
661 self.scroll_offset = offset;
662 }
663
664 pub fn scroll_offset(&self) -> [f32; 2] {
666 self.scroll_offset
667 }
668
669 pub fn set_logical_pixels(&mut self, on: bool) {
671 self.logical_pixels = on;
672 }
673
674 pub fn alloc_targets(
675 &self,
676 allocator: &mut RenderAllocator,
677 width: u32,
678 height: u32,
679 ) -> PassTargets {
680 let color = allocator.allocate_texture(TexKey {
681 width,
682 height,
683 format: self.offscreen_format,
684 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
685 });
686 PassTargets { color }
687 }
688
689 pub fn ensure_intermediate_texture(
695 &mut self,
696 allocator: &mut RenderAllocator,
697 width: u32,
698 height: u32,
699 ) {
700 let needs_realloc = match &self.intermediate_texture {
701 Some(tex) => {
702 tex.key.width != width || tex.key.height != height
705 }
706 None => true,
707 };
708
709 if needs_realloc {
710 if let Some(old_tex) = self.intermediate_texture.take() {
712 allocator.release_texture(old_tex);
713 }
714
715 let tex = allocator.allocate_texture(TexKey {
717 width,
718 height,
719 format: self.surface_format,
720 usage: wgpu::TextureUsages::RENDER_ATTACHMENT
721 | wgpu::TextureUsages::TEXTURE_BINDING
722 | wgpu::TextureUsages::COPY_SRC
723 | wgpu::TextureUsages::COPY_DST,
724 });
725 self.intermediate_texture = Some(tex);
726 }
727 }
728
729 pub fn clear_intermediate_texture(
732 &self,
733 encoder: &mut wgpu::CommandEncoder,
734 clear_color: wgpu::Color,
735 ) {
736 let intermediate = self
737 .intermediate_texture
738 .as_ref()
739 .expect("intermediate texture must be allocated before clearing");
740
741 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
742 label: Some("clear-intermediate"),
743 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
744 view: &intermediate.view,
745 resolve_target: None,
746 ops: wgpu::Operations {
747 load: wgpu::LoadOp::Clear(clear_color),
748 store: wgpu::StoreOp::Store,
749 },
750 })],
751 depth_stencil_attachment: None,
752 occlusion_query_set: None,
753 timestamp_writes: None,
754 });
755 }
756
757 pub fn blit_to_surface(
760 &self,
761 encoder: &mut wgpu::CommandEncoder,
762 surface_view: &wgpu::TextureView,
763 ) {
764 let intermediate = self
765 .intermediate_texture
766 .as_ref()
767 .expect("intermediate texture must be allocated before blitting");
768
769 let bg = self.blitter.bind_group(&self.device, &intermediate.view);
770 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
771 label: Some("blit-pass"),
772 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
773 view: surface_view,
774 resolve_target: None,
775 ops: wgpu::Operations {
776 load: wgpu::LoadOp::Load,
777 store: wgpu::StoreOp::Store,
778 },
779 })],
780 depth_stencil_attachment: None,
781 occlusion_query_set: None,
782 timestamp_writes: None,
783 });
784 self.blitter.record(&mut pass, &bg);
785 }
786
787 pub fn ensure_depth_texture(
790 &mut self,
791 allocator: &mut RenderAllocator,
792 width: u32,
793 height: u32,
794 ) {
795 let needs_realloc = match &self.depth_texture {
796 Some(tex) => tex.key.width != width || tex.key.height != height,
797 None => true,
798 };
799
800 if needs_realloc {
801 if let Some(old_tex) = self.depth_texture.take() {
803 allocator.release_texture(old_tex);
804 }
805
806 let tex = allocator.allocate_texture(TexKey {
808 width,
809 height,
810 format: wgpu::TextureFormat::Depth32Float,
811 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
812 });
813 self.depth_texture = Some(tex);
814 }
815 }
816
817 pub fn depth_view(&self) -> &wgpu::TextureView {
820 &self
821 .depth_texture
822 .as_ref()
823 .expect("depth texture must be allocated before use")
824 .view
825 }
826
827 fn ensure_scrim_stencil_texture(
828 &mut self,
829 allocator: &mut RenderAllocator,
830 width: u32,
831 height: u32,
832 ) {
833 let needs_realloc = match &self.scrim_stencil_tex {
834 Some(tex) => tex.key.width != width || tex.key.height != height,
835 None => true,
836 };
837
838 if needs_realloc {
839 if let Some(old) = self.scrim_stencil_tex.take() {
840 allocator.release_texture(old);
841 }
842 let tex = allocator.allocate_texture(TexKey {
843 width,
844 height,
845 format: wgpu::TextureFormat::Depth24PlusStencil8,
846 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
847 });
848 self.scrim_stencil_tex = Some(tex);
849 }
850 }
851
852 fn ensure_smaa_textures(&mut self, allocator: &mut RenderAllocator, width: u32, height: u32) {
853 let key = TexKey {
854 width,
855 height,
856 format: wgpu::TextureFormat::Rgba8Unorm,
857 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
858 };
859
860 if self.smaa_edges.as_ref().map_or(true, |tex| tex.key != key) {
861 if let Some(old) = self.smaa_edges.take() {
862 allocator.release_texture(old);
863 }
864 self.smaa_edges = Some(allocator.allocate_texture(key));
865 }
866
867 if self
868 .smaa_weights
869 .as_ref()
870 .map_or(true, |tex| tex.key != key)
871 {
872 if let Some(old) = self.smaa_weights.take() {
873 allocator.release_texture(old);
874 }
875 self.smaa_weights = Some(allocator.allocate_texture(key));
876 }
877 }
878
879 pub fn apply_smaa(
880 &mut self,
881 encoder: &mut wgpu::CommandEncoder,
882 allocator: &mut RenderAllocator,
883 src_view: &wgpu::TextureView,
884 dst_view: &wgpu::TextureView,
885 width: u32,
886 height: u32,
887 queue: &wgpu::Queue,
888 ) {
889 if width == 0 || height == 0 {
890 return;
891 }
892
893 self.ensure_smaa_textures(allocator, width, height);
894 let texel_size = [
895 1.0f32 / width.max(1) as f32,
896 1.0f32 / height.max(1) as f32,
897 0.0,
898 0.0,
899 ];
900 queue.write_buffer(&self.smaa_param_buffer, 0, bytemuck::bytes_of(&texel_size));
901
902 let edges = self
903 .smaa_edges
904 .as_ref()
905 .expect("SMAA edges texture must exist");
906 let weights = self
907 .smaa_weights
908 .as_ref()
909 .expect("SMAA weights texture must exist");
910
911 let edge_bg = self
912 .smaa
913 .edge_bind_group(&self.device, src_view, &self.smaa_param_buffer);
914 let blend_bg =
915 self.smaa
916 .blend_bind_group(&self.device, &edges.view, &self.smaa_param_buffer);
917 let resolve_bg = self.smaa.resolve_bind_group(
918 &self.device,
919 src_view,
920 &weights.view,
921 &self.smaa_param_buffer,
922 );
923
924 {
926 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
927 label: Some("smaa-edge-pass"),
928 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
929 view: &edges.view,
930 resolve_target: None,
931 ops: wgpu::Operations {
932 load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
933 store: wgpu::StoreOp::Store,
934 },
935 })],
936 depth_stencil_attachment: None,
937 occlusion_query_set: None,
938 timestamp_writes: None,
939 });
940 self.smaa.record_edges(&mut pass, &edge_bg);
941 }
942
943 {
945 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
946 label: Some("smaa-blend-pass"),
947 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
948 view: &weights.view,
949 resolve_target: None,
950 ops: wgpu::Operations {
951 load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
952 store: wgpu::StoreOp::Store,
953 },
954 })],
955 depth_stencil_attachment: None,
956 occlusion_query_set: None,
957 timestamp_writes: None,
958 });
959 self.smaa.record_blend(&mut pass, &blend_bg);
960 }
961
962 {
964 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
965 label: Some("smaa-resolve-pass"),
966 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
967 view: dst_view,
968 resolve_target: None,
969 ops: wgpu::Operations {
970 load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
971 store: wgpu::StoreOp::Store,
972 },
973 })],
974 depth_stencil_attachment: None,
975 occlusion_query_set: None,
976 timestamp_writes: None,
977 });
978 self.smaa.record_resolve(&mut pass, &resolve_bg);
979 }
980 }
981
982 pub fn draw_box_shadow(
985 &self,
986 encoder: &mut wgpu::CommandEncoder,
987 target_view: &wgpu::TextureView,
988 width: u32,
989 height: u32,
990 rrect: RoundedRect,
991 spec: BoxShadowSpec,
992 queue: &wgpu::Queue,
993 ) {
994 let blur = spec.blur_radius.max(0.0);
998 let sigma = if blur > 0.0 { blur } else { 0.5 };
999 let spread = spec.spread.max(0.0);
1000 let create_tex = |label: &str| -> wgpu::Texture {
1001 self.device.create_texture(&wgpu::TextureDescriptor {
1002 label: Some(label),
1003 size: wgpu::Extent3d {
1004 width: width.max(1),
1005 height: height.max(1),
1006 depth_or_array_layers: 1,
1007 },
1008 mip_level_count: 1,
1009 sample_count: 1,
1010 dimension: wgpu::TextureDimension::D2,
1011 format: wgpu::TextureFormat::R8Unorm,
1012 usage: wgpu::TextureUsages::RENDER_ATTACHMENT
1013 | wgpu::TextureUsages::TEXTURE_BINDING,
1014 view_formats: &[],
1015 })
1016 };
1017 let mask_tex = create_tex("shadow-mask");
1018 let ping_tex = create_tex("shadow-ping");
1019 let mask_view = mask_tex.create_view(&wgpu::TextureViewDescriptor::default());
1020 let ping_view = ping_tex.create_view(&wgpu::TextureViewDescriptor::default());
1021
1022 let logical =
1024 crate::dpi::logical_multiplier(self.logical_pixels, self.scale_factor, self.ui_scale);
1025 let scale = [
1026 (2.0f32 / (width.max(1) as f32)) * logical,
1027 (-2.0f32 / (height.max(1) as f32)) * logical,
1028 ];
1029 let translate = [-1.0f32, 1.0f32];
1030 let vp_data: [f32; 8] = [
1031 scale[0],
1032 scale[1],
1033 translate[0],
1034 translate[1],
1035 0.0,
1036 0.0,
1037 0.0,
1038 0.0,
1039 ];
1040 queue.write_buffer(&self.vp_buffer, 0, bytemuck::bytes_of(&vp_data));
1042
1043 let shadow_radii = RoundedRadii {
1044 tl: (rrect.radii.tl + spread).max(0.0),
1045 tr: (rrect.radii.tr + spread).max(0.0),
1046 br: (rrect.radii.br + spread).max(0.0),
1047 bl: (rrect.radii.bl + spread).max(0.0),
1048 };
1049 let expand = spread + 1.8 * sigma + 1.0;
1052 let mut rect = rrect.rect;
1053 rect.x = rect.x + spec.offset[0] - expand;
1054 rect.y = rect.y + spec.offset[1] - expand;
1055 rect.w = (rect.w + 2.0 * expand).max(0.0);
1056 rect.h = (rect.h + 2.0 * expand).max(0.0);
1057 let expanded = RoundedRect {
1058 rect,
1059 radii: shadow_radii,
1060 };
1061 #[repr(C)]
1064 #[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
1065 struct Vtx {
1066 pos: [f32; 2],
1067 color: [f32; 4],
1068 }
1069 let mut vertices: Vec<Vtx> = Vec::new();
1070 let mut indices: Vec<u16> = Vec::new();
1071 let rect = expanded.rect;
1072 let tl = expanded.radii.tl.min(rect.w * 0.5).min(rect.h * 0.5);
1073 let tr = expanded.radii.tr.min(rect.w * 0.5).min(rect.h * 0.5);
1074 let br = expanded.radii.br.min(rect.w * 0.5).min(rect.h * 0.5);
1075 let bl = expanded.radii.bl.min(rect.w * 0.5).min(rect.h * 0.5);
1076 let segs = 64u32;
1078 let mut ring: Vec<[f32; 2]> = Vec::new();
1079 fn arc_append(
1080 ring: &mut Vec<[f32; 2]>,
1081 c: [f32; 2],
1082 r: f32,
1083 start: f32,
1084 end: f32,
1085 segs: u32,
1086 include_start: bool,
1087 ) {
1088 if r <= 0.0 {
1089 return;
1090 }
1091 for i in 0..=segs {
1092 if i == 0 && !include_start {
1093 continue;
1094 }
1095 let t = (i as f32) / (segs as f32);
1096 let ang = start + t * (end - start);
1097 let p = [c[0] + r * ang.cos(), c[1] - r * ang.sin()];
1098 ring.push(p);
1099 }
1100 }
1101 if tl > 0.0 {
1102 arc_append(
1103 &mut ring,
1104 [rect.x + tl, rect.y + tl],
1105 tl,
1106 std::f32::consts::FRAC_PI_2,
1107 std::f32::consts::PI,
1108 segs,
1109 true,
1110 );
1111 } else {
1112 ring.push([rect.x + 0.0, rect.y + 0.0]);
1113 }
1114 if bl > 0.0 {
1115 arc_append(
1116 &mut ring,
1117 [rect.x + bl, rect.y + rect.h - bl],
1118 bl,
1119 std::f32::consts::PI,
1120 std::f32::consts::FRAC_PI_2 * 3.0,
1121 segs,
1122 true,
1123 );
1124 } else {
1125 ring.push([rect.x + 0.0, rect.y + rect.h]);
1126 }
1127 if br > 0.0 {
1128 arc_append(
1129 &mut ring,
1130 [rect.x + rect.w - br, rect.y + rect.h - br],
1131 br,
1132 std::f32::consts::FRAC_PI_2 * 3.0,
1133 std::f32::consts::TAU,
1134 segs,
1135 true,
1136 );
1137 } else {
1138 ring.push([rect.x + rect.w, rect.y + rect.h]);
1139 }
1140 if tr > 0.0 {
1141 arc_append(
1142 &mut ring,
1143 [rect.x + rect.w - tr, rect.y + tr],
1144 tr,
1145 0.0,
1146 std::f32::consts::FRAC_PI_2,
1147 segs,
1148 true,
1149 );
1150 } else {
1151 ring.push([rect.x + rect.w, rect.y + 0.0]);
1152 }
1153 let center = [rect.x + rect.w * 0.5, rect.y + rect.h * 0.5];
1154 let white = [1.0, 1.0, 1.0, 1.0];
1155 let base = vertices.len() as u16;
1156 vertices.push(Vtx {
1157 pos: center,
1158 color: white,
1159 });
1160 for p in ring.iter() {
1161 vertices.push(Vtx {
1162 pos: *p,
1163 color: white,
1164 });
1165 }
1166 let ring_len = (vertices.len() as u16) - base - 1;
1167 for i in 0..ring_len {
1168 let i0 = base;
1169 let i1 = base + 1 + i;
1170 let i2 = base + 1 + ((i + 1) % ring_len);
1171 indices.extend_from_slice(&[i0, i1, i2]);
1172 }
1173 let vsize = (vertices.len() * std::mem::size_of::<Vtx>()) as u64;
1175 let isize = (indices.len() * std::mem::size_of::<u16>()) as u64;
1176 let vbuf = self.device.create_buffer(&wgpu::BufferDescriptor {
1177 label: Some("shadow-mask-vbuf"),
1178 size: vsize.max(4),
1179 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
1180 mapped_at_creation: false,
1181 });
1182 let ibuf = self.device.create_buffer(&wgpu::BufferDescriptor {
1183 label: Some("shadow-mask-ibuf"),
1184 size: isize.max(4),
1185 usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
1186 mapped_at_creation: false,
1187 });
1188 if vsize > 0 {
1189 queue.write_buffer(&vbuf, 0, bytemuck::cast_slice(&vertices));
1190 }
1191 if isize > 0 {
1192 queue.write_buffer(&ibuf, 0, bytemuck::cast_slice(&indices));
1193 }
1194 let gpu = crate::upload::GpuScene {
1195 vertex: crate::allocator::OwnedBuffer {
1196 buffer: vbuf,
1197 key: crate::allocator::BufKey {
1198 size: vsize.max(4),
1199 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
1200 },
1201 },
1202 index: crate::allocator::OwnedBuffer {
1203 buffer: ibuf,
1204 key: crate::allocator::BufKey {
1205 size: isize.max(4),
1206 usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
1207 },
1208 },
1209 vertices: vertices.len() as u32,
1210 indices: indices.len() as u32,
1211 };
1212
1213 let vp_bg_mask = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
1215 label: Some("vp-bg-mask"),
1216 layout: self.mask_renderer.viewport_bgl(),
1217 entries: &[wgpu::BindGroupEntry {
1218 binding: 0,
1219 resource: self.vp_buffer.as_entire_binding(),
1220 }],
1221 });
1222 let _z_bg = self.create_z_bind_group(0.0, queue);
1226 {
1227 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1228 label: Some("shadow-mask-pass"),
1229 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1230 view: &mask_view,
1231 resolve_target: None,
1232 ops: wgpu::Operations {
1233 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
1234 store: wgpu::StoreOp::Store,
1235 },
1236 })],
1237 depth_stencil_attachment: None,
1238 occlusion_query_set: None,
1239 timestamp_writes: None,
1240 });
1241 self.mask_renderer.record(&mut pass, &vp_bg_mask, &gpu);
1242 }
1243
1244 #[repr(C)]
1246 #[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
1247 struct BlurParams {
1248 dir: [f32; 2],
1249 texel: [f32; 2],
1250 sigma: f32,
1251 _pad: f32,
1252 }
1253 let texel = [
1254 1.0f32 / (width.max(1) as f32),
1255 1.0f32 / (height.max(1) as f32),
1256 ];
1257 let bp_h = BlurParams {
1258 dir: [1.0, 0.0],
1259 texel,
1260 sigma,
1261 _pad: 0.0,
1262 };
1263 queue.write_buffer(&self.blur_r8.param_buffer, 0, bytemuck::bytes_of(&bp_h));
1264 let bg_h = self.blur_r8.bind_group(&self.device, &mask_view);
1265 {
1266 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1267 label: Some("shadow-blur-h"),
1268 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1269 view: &ping_view,
1270 resolve_target: None,
1271 ops: wgpu::Operations {
1272 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
1273 store: wgpu::StoreOp::Store,
1274 },
1275 })],
1276 depth_stencil_attachment: None,
1277 occlusion_query_set: None,
1278 timestamp_writes: None,
1279 });
1280 self.blur_r8.record(&mut pass, &bg_h);
1281 }
1282
1283 let bp_v = BlurParams {
1285 dir: [0.0, 1.0],
1286 texel,
1287 sigma,
1288 _pad: 0.0,
1289 };
1290 queue.write_buffer(&self.blur_r8.param_buffer, 0, bytemuck::bytes_of(&bp_v));
1291 let bg_v = self.blur_r8.bind_group(&self.device, &ping_view);
1292 {
1293 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1294 label: Some("shadow-blur-v"),
1295 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1296 view: &mask_view,
1297 resolve_target: None,
1298 ops: wgpu::Operations {
1299 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
1300 store: wgpu::StoreOp::Store,
1301 },
1302 })],
1303 depth_stencil_attachment: None,
1304 occlusion_query_set: None,
1305 timestamp_writes: None,
1306 });
1307 self.blur_r8.record(&mut pass, &bg_v);
1308 }
1309
1310 {
1313 let mut cutout_vertices: Vec<Vtx> = Vec::new();
1314 let mut cutout_indices: Vec<u16> = Vec::new();
1315 let rect = rrect.rect;
1317 let tl = rrect.radii.tl.min(rect.w * 0.5).min(rect.h * 0.5);
1318 let tr = rrect.radii.tr.min(rect.w * 0.5).min(rect.h * 0.5);
1319 let br = rrect.radii.br.min(rect.w * 0.5).min(rect.h * 0.5);
1320 let bl = rrect.radii.bl.min(rect.w * 0.5).min(rect.h * 0.5);
1321 let mut ring: Vec<[f32; 2]> = Vec::new();
1322 if tl > 0.0 {
1323 arc_append(
1324 &mut ring,
1325 [rect.x + tl, rect.y + tl],
1326 tl,
1327 std::f32::consts::FRAC_PI_2,
1328 std::f32::consts::PI,
1329 segs,
1330 true,
1331 );
1332 } else {
1333 ring.push([rect.x, rect.y]);
1334 }
1335 if bl > 0.0 {
1336 arc_append(
1337 &mut ring,
1338 [rect.x + bl, rect.y + rect.h - bl],
1339 bl,
1340 std::f32::consts::PI,
1341 std::f32::consts::FRAC_PI_2 * 3.0,
1342 segs,
1343 true,
1344 );
1345 } else {
1346 ring.push([rect.x, rect.y + rect.h]);
1347 }
1348 if br > 0.0 {
1349 arc_append(
1350 &mut ring,
1351 [rect.x + rect.w - br, rect.y + rect.h - br],
1352 br,
1353 std::f32::consts::FRAC_PI_2 * 3.0,
1354 std::f32::consts::TAU,
1355 segs,
1356 true,
1357 );
1358 } else {
1359 ring.push([rect.x + rect.w, rect.y + rect.h]);
1360 }
1361 if tr > 0.0 {
1362 arc_append(
1363 &mut ring,
1364 [rect.x + rect.w - tr, rect.y + tr],
1365 tr,
1366 0.0,
1367 std::f32::consts::FRAC_PI_2,
1368 segs,
1369 true,
1370 );
1371 } else {
1372 ring.push([rect.x + rect.w, rect.y]);
1373 }
1374 let center = [rect.x + rect.w * 0.5, rect.y + rect.h * 0.5];
1375 let clear_color = [0.0, 0.0, 0.0, 1.0];
1380 let base = cutout_vertices.len() as u16;
1381 cutout_vertices.push(Vtx {
1382 pos: center,
1383 color: clear_color,
1384 });
1385 for p in ring.iter() {
1386 cutout_vertices.push(Vtx {
1387 pos: *p,
1388 color: clear_color,
1389 });
1390 }
1391 let ring_len = (cutout_vertices.len() as u16) - base - 1;
1392 for i in 0..ring_len {
1393 let i0 = base;
1394 let i1 = base + 1 + i;
1395 let i2 = base + 1 + ((i + 1) % ring_len);
1396 cutout_indices.extend_from_slice(&[i0, i1, i2]);
1397 }
1398
1399 let vsize = (cutout_vertices.len() * std::mem::size_of::<Vtx>()) as u64;
1400 let isize = (cutout_indices.len() * std::mem::size_of::<u16>()) as u64;
1401 let vbuf = self.device.create_buffer(&wgpu::BufferDescriptor {
1402 label: Some("shadow-cutout-vbuf"),
1403 size: vsize.max(4),
1404 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
1405 mapped_at_creation: false,
1406 });
1407 let ibuf = self.device.create_buffer(&wgpu::BufferDescriptor {
1408 label: Some("shadow-cutout-ibuf"),
1409 size: isize.max(4),
1410 usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
1411 mapped_at_creation: false,
1412 });
1413 if vsize > 0 {
1414 queue.write_buffer(&vbuf, 0, bytemuck::cast_slice(&cutout_vertices));
1415 }
1416 if isize > 0 {
1417 queue.write_buffer(&ibuf, 0, bytemuck::cast_slice(&cutout_indices));
1418 }
1419 let cutout_gpu = crate::upload::GpuScene {
1420 vertex: crate::allocator::OwnedBuffer {
1421 buffer: vbuf,
1422 key: crate::allocator::BufKey {
1423 size: vsize.max(4),
1424 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
1425 },
1426 },
1427 index: crate::allocator::OwnedBuffer {
1428 buffer: ibuf,
1429 key: crate::allocator::BufKey {
1430 size: isize.max(4),
1431 usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
1432 },
1433 },
1434 vertices: cutout_vertices.len() as u32,
1435 indices: cutout_indices.len() as u32,
1436 };
1437
1438 let _z_bg_cutout = self.create_z_bind_group(0.0, queue);
1439 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1440 label: Some("shadow-cutout"),
1441 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1442 view: &mask_view,
1443 resolve_target: None,
1444 ops: wgpu::Operations {
1445 load: wgpu::LoadOp::Load,
1446 store: wgpu::StoreOp::Store,
1447 },
1448 })],
1449 depth_stencil_attachment: None,
1450 occlusion_query_set: None,
1451 timestamp_writes: None,
1452 });
1453 self.mask_renderer
1454 .record(&mut pass, &vp_bg_mask, &cutout_gpu);
1455 }
1456
1457 #[repr(C)]
1459 #[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
1460 struct ShadowColor {
1461 color: [f32; 4],
1462 }
1463 let c = spec.color;
1464 let scol = ShadowColor {
1465 color: [c.r, c.g, c.b, c.a],
1466 };
1467 queue.write_buffer(&self.shadow_comp.color_buffer, 0, bytemuck::bytes_of(&scol));
1468 let bg = self.shadow_comp.bind_group(&self.device, &mask_view);
1469 {
1470 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1471 label: Some("shadow-composite"),
1472 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1473 view: target_view,
1474 resolve_target: None,
1475 ops: wgpu::Operations {
1476 load: wgpu::LoadOp::Load,
1477 store: wgpu::StoreOp::Store,
1478 },
1479 })],
1480 depth_stencil_attachment: None,
1481 occlusion_query_set: None,
1482 timestamp_writes: None,
1483 });
1484 self.shadow_comp.record(&mut pass, &bg);
1485 }
1486
1487 }
1489
1490 pub fn draw_overlay_rect(
1494 &self,
1495 encoder: &mut wgpu::CommandEncoder,
1496 target_view: &wgpu::TextureView,
1497 width: u32,
1498 height: u32,
1499 rect: crate::scene::Rect,
1500 color: crate::scene::ColorLinPremul,
1501 queue: &wgpu::Queue,
1502 ) {
1503 let logical =
1505 crate::dpi::logical_multiplier(self.logical_pixels, self.scale_factor, self.ui_scale);
1506 let scale = [
1507 (2.0f32 / (width.max(1) as f32)) * logical,
1508 (-2.0f32 / (height.max(1) as f32)) * logical,
1509 ];
1510 let translate = [-1.0f32, 1.0f32];
1511 let vp_data: [f32; 8] = [
1512 scale[0],
1513 scale[1],
1514 translate[0],
1515 translate[1],
1516 0.0,
1517 0.0,
1518 0.0,
1519 0.0,
1520 ];
1521 queue.write_buffer(&self.vp_buffer, 0, bytemuck::bytes_of(&vp_data));
1522
1523 #[repr(C)]
1524 #[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
1525 struct OverlayVtx {
1526 pos: [f32; 2],
1527 color: [f32; 4],
1528 z_index: f32,
1529 }
1530
1531 let overlay_color = [color.r, color.g, color.b, color.a];
1532 let z_index = 0.0f32;
1533 let x = rect.x;
1534 let y = rect.y;
1535 let w = rect.w.max(0.0);
1536 let h = rect.h.max(0.0);
1537
1538 if w <= 0.0 || h <= 0.0 {
1540 return;
1541 }
1542
1543 let verts = [
1544 OverlayVtx {
1545 pos: [x, y],
1546 color: overlay_color,
1547 z_index,
1548 },
1549 OverlayVtx {
1550 pos: [x + w, y],
1551 color: overlay_color,
1552 z_index,
1553 },
1554 OverlayVtx {
1555 pos: [x + w, y + h],
1556 color: overlay_color,
1557 z_index,
1558 },
1559 OverlayVtx {
1560 pos: [x, y + h],
1561 color: overlay_color,
1562 z_index,
1563 },
1564 ];
1565 let idx: [u16; 6] = [0, 1, 2, 0, 2, 3];
1566
1567 let vsize = (verts.len() * std::mem::size_of::<OverlayVtx>()) as u64;
1568 let isize = (idx.len() * std::mem::size_of::<u16>()) as u64;
1569 let vbuf = self.device.create_buffer(&wgpu::BufferDescriptor {
1570 label: Some("overlay-rect-vbuf"),
1571 size: vsize.max(4),
1572 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
1573 mapped_at_creation: false,
1574 });
1575 let ibuf = self.device.create_buffer(&wgpu::BufferDescriptor {
1576 label: Some("overlay-rect-ibuf"),
1577 size: isize.max(4),
1578 usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
1579 mapped_at_creation: false,
1580 });
1581 if vsize > 0 {
1582 queue.write_buffer(&vbuf, 0, bytemuck::cast_slice(&verts));
1583 }
1584 if isize > 0 {
1585 queue.write_buffer(&ibuf, 0, bytemuck::cast_slice(&idx));
1586 }
1587
1588 let vp_bg = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
1589 label: Some("overlay-vp-bg"),
1590 layout: self.overlay_solid.viewport_bgl(),
1591 entries: &[wgpu::BindGroupEntry {
1592 binding: 0,
1593 resource: self.vp_buffer.as_entire_binding(),
1594 }],
1595 });
1596
1597 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1599 label: Some("overlay-rect-pass"),
1600 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1601 view: target_view,
1602 resolve_target: None,
1603 ops: wgpu::Operations {
1604 load: wgpu::LoadOp::Load,
1605 store: wgpu::StoreOp::Store,
1606 },
1607 })],
1608 depth_stencil_attachment: None,
1609 occlusion_query_set: None,
1610 timestamp_writes: None,
1611 });
1612 self.overlay_solid
1613 .record(&mut pass, &vp_bg, &vbuf, &ibuf, idx.len() as u32);
1614 }
1615
1616 pub fn draw_scrim_rect(
1626 &self,
1627 encoder: &mut wgpu::CommandEncoder,
1628 target_view: &wgpu::TextureView,
1629 width: u32,
1630 height: u32,
1631 rect: crate::scene::Rect,
1632 color: crate::scene::ColorLinPremul,
1633 queue: &wgpu::Queue,
1634 ) {
1635 let logical =
1637 crate::dpi::logical_multiplier(self.logical_pixels, self.scale_factor, self.ui_scale);
1638 let scale = [
1639 (2.0f32 / (width.max(1) as f32)) * logical,
1640 (-2.0f32 / (height.max(1) as f32)) * logical,
1641 ];
1642 let translate = [-1.0f32, 1.0f32];
1643 let vp_data: [f32; 8] = [
1644 scale[0],
1645 scale[1],
1646 translate[0],
1647 translate[1],
1648 0.0,
1649 0.0,
1650 0.0,
1651 0.0,
1652 ];
1653 queue.write_buffer(&self.vp_buffer, 0, bytemuck::bytes_of(&vp_data));
1654
1655 #[repr(C)]
1656 #[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
1657 struct ScrimVtx {
1658 pos: [f32; 2],
1659 color: [f32; 4],
1660 z_index: f32,
1661 }
1662
1663 let scrim_color = [color.r, color.g, color.b, color.a];
1664 let z_index = 0.5f32;
1666 let x = rect.x;
1667 let y = rect.y;
1668 let w = rect.w.max(0.0);
1669 let h = rect.h.max(0.0);
1670
1671 if w <= 0.0 || h <= 0.0 {
1673 return;
1674 }
1675
1676 let verts = [
1677 ScrimVtx {
1678 pos: [x, y],
1679 color: scrim_color,
1680 z_index,
1681 },
1682 ScrimVtx {
1683 pos: [x + w, y],
1684 color: scrim_color,
1685 z_index,
1686 },
1687 ScrimVtx {
1688 pos: [x + w, y + h],
1689 color: scrim_color,
1690 z_index,
1691 },
1692 ScrimVtx {
1693 pos: [x, y + h],
1694 color: scrim_color,
1695 z_index,
1696 },
1697 ];
1698 let idx: [u16; 6] = [0, 1, 2, 0, 2, 3];
1699
1700 let vsize = (verts.len() * std::mem::size_of::<ScrimVtx>()) as u64;
1701 let isize = (idx.len() * std::mem::size_of::<u16>()) as u64;
1702 let vbuf = self.device.create_buffer(&wgpu::BufferDescriptor {
1703 label: Some("scrim-rect-vbuf"),
1704 size: vsize.max(4),
1705 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
1706 mapped_at_creation: false,
1707 });
1708 let ibuf = self.device.create_buffer(&wgpu::BufferDescriptor {
1709 label: Some("scrim-rect-ibuf"),
1710 size: isize.max(4),
1711 usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
1712 mapped_at_creation: false,
1713 });
1714 if vsize > 0 {
1715 queue.write_buffer(&vbuf, 0, bytemuck::cast_slice(&verts));
1716 }
1717 if isize > 0 {
1718 queue.write_buffer(&ibuf, 0, bytemuck::cast_slice(&idx));
1719 }
1720
1721 let vp_bg = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
1722 label: Some("scrim-vp-bg"),
1723 layout: self.scrim_solid.viewport_bgl(),
1724 entries: &[wgpu::BindGroupEntry {
1725 binding: 0,
1726 resource: self.vp_buffer.as_entire_binding(),
1727 }],
1728 });
1729
1730 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1734 label: Some("scrim-rect-pass"),
1735 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1736 view: target_view,
1737 resolve_target: None,
1738 ops: wgpu::Operations {
1739 load: wgpu::LoadOp::Load,
1740 store: wgpu::StoreOp::Store,
1741 },
1742 })],
1743 depth_stencil_attachment: None,
1744 occlusion_query_set: None,
1745 timestamp_writes: None,
1746 });
1747 self.scrim_solid
1748 .record(&mut pass, &vp_bg, &vbuf, &ibuf, idx.len() as u32);
1749 }
1750
1751 pub fn draw_scrim_with_cutout(
1753 &mut self,
1754 encoder: &mut wgpu::CommandEncoder,
1755 allocator: &mut RenderAllocator,
1756 target_view: &wgpu::TextureView,
1757 width: u32,
1758 height: u32,
1759 hole: RoundedRect,
1760 color: crate::scene::ColorLinPremul,
1761 queue: &wgpu::Queue,
1762 ) {
1763 self.ensure_scrim_stencil_texture(allocator, width, height);
1764 let stencil_tex = self
1765 .scrim_stencil_tex
1766 .as_ref()
1767 .expect("stencil texture must exist");
1768
1769 let logical =
1771 crate::dpi::logical_multiplier(self.logical_pixels, self.scale_factor, self.ui_scale);
1772 let scale = [
1773 (2.0f32 / (width.max(1) as f32)) * logical,
1774 (-2.0f32 / (height.max(1) as f32)) * logical,
1775 ];
1776 let translate = [-1.0f32, 1.0f32];
1777 let vp_data: [f32; 8] = [
1778 scale[0],
1779 scale[1],
1780 translate[0],
1781 translate[1],
1782 0.0,
1783 0.0,
1784 0.0,
1785 0.0,
1786 ];
1787 queue.write_buffer(&self.vp_buffer, 0, bytemuck::bytes_of(&vp_data));
1788
1789 #[repr(C)]
1790 #[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
1791 struct Vtx {
1792 pos: [f32; 2],
1793 color: [f32; 4],
1794 z: f32,
1795 }
1796
1797 let mut vertices: Vec<Vtx> = Vec::new();
1799 let mut indices: Vec<u16> = Vec::new();
1800 let rect = hole.rect;
1801 let tl = hole.radii.tl.min(rect.w * 0.5).min(rect.h * 0.5);
1802 let tr = hole.radii.tr.min(rect.w * 0.5).min(rect.h * 0.5);
1803 let br = hole.radii.br.min(rect.w * 0.5).min(rect.h * 0.5);
1804 let bl = hole.radii.bl.min(rect.w * 0.5).min(rect.h * 0.5);
1805 let segs = 32u32;
1806 let mut ring: Vec<[f32; 2]> = Vec::new();
1807 fn arc_append(
1808 ring: &mut Vec<[f32; 2]>,
1809 c: [f32; 2],
1810 r: f32,
1811 start: f32,
1812 end: f32,
1813 segs: u32,
1814 include_start: bool,
1815 ) {
1816 if r <= 0.0 {
1817 return;
1818 }
1819 for i in 0..=segs {
1820 if i == 0 && !include_start {
1821 continue;
1822 }
1823 let t = (i as f32) / (segs as f32);
1824 let ang = start + t * (end - start);
1825 let p = [c[0] + r * ang.cos(), c[1] - r * ang.sin()];
1826 ring.push(p);
1827 }
1828 }
1829 if tl > 0.0 {
1830 arc_append(
1831 &mut ring,
1832 [rect.x + tl, rect.y + tl],
1833 tl,
1834 std::f32::consts::FRAC_PI_2,
1835 std::f32::consts::PI,
1836 segs,
1837 true,
1838 );
1839 } else {
1840 ring.push([rect.x + 0.0, rect.y + 0.0]);
1841 }
1842 if bl > 0.0 {
1843 arc_append(
1844 &mut ring,
1845 [rect.x + bl, rect.y + rect.h - bl],
1846 bl,
1847 std::f32::consts::PI,
1848 std::f32::consts::FRAC_PI_2 * 3.0,
1849 segs,
1850 true,
1851 );
1852 } else {
1853 ring.push([rect.x + 0.0, rect.y + rect.h]);
1854 }
1855 if br > 0.0 {
1856 arc_append(
1857 &mut ring,
1858 [rect.x + rect.w - br, rect.y + rect.h - br],
1859 br,
1860 std::f32::consts::FRAC_PI_2 * 3.0,
1861 std::f32::consts::TAU,
1862 segs,
1863 true,
1864 );
1865 } else {
1866 ring.push([rect.x + rect.w, rect.y + rect.h]);
1867 }
1868 if tr > 0.0 {
1869 arc_append(
1870 &mut ring,
1871 [rect.x + rect.w - tr, rect.y + tr],
1872 tr,
1873 0.0,
1874 std::f32::consts::FRAC_PI_2,
1875 segs,
1876 true,
1877 );
1878 } else {
1879 ring.push([rect.x + rect.w, rect.y + 0.0]);
1880 }
1881
1882 let center = [rect.x + rect.w * 0.5, rect.y + rect.h * 0.5];
1884 vertices.push(Vtx {
1885 pos: center,
1886 color: [color.r, color.g, color.b, color.a],
1887 z: 0.5,
1888 });
1889 for p in ring.iter() {
1890 vertices.push(Vtx {
1891 pos: *p,
1892 color: [color.r, color.g, color.b, color.a],
1893 z: 0.5,
1894 });
1895 }
1896 for i in 1..(vertices.len() - 1) {
1898 indices.extend_from_slice(&[0, i as u16, (i as u16) + 1]);
1899 }
1900 if vertices.len() > 2 {
1901 indices.extend_from_slice(&[0, (vertices.len() - 1) as u16, 1]);
1902 }
1903
1904 if indices.len() % 2 != 0 {
1906 indices.push(*indices.last().unwrap_or(&0));
1907 }
1908
1909 let vsize = (vertices.len() * std::mem::size_of::<Vtx>()) as u64;
1910 let isize = (indices.len() * std::mem::size_of::<u16>()) as u64;
1911 let vbuf = self.device.create_buffer(&wgpu::BufferDescriptor {
1912 label: Some("scrim-hole-vbuf"),
1913 size: vsize.max(4),
1914 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
1915 mapped_at_creation: false,
1916 });
1917 let ibuf = self.device.create_buffer(&wgpu::BufferDescriptor {
1918 label: Some("scrim-hole-ibuf"),
1919 size: isize.max(4),
1920 usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
1921 mapped_at_creation: false,
1922 });
1923 if vsize > 0 {
1924 queue.write_buffer(&vbuf, 0, bytemuck::cast_slice(&vertices));
1925 }
1926 if isize > 0 {
1927 queue.write_buffer(&ibuf, 0, bytemuck::cast_slice(&indices));
1928 }
1929
1930 let vp_bg = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
1931 label: Some("scrim-stencil-vp-bg"),
1932 layout: self.scrim_mask.viewport_bgl(),
1933 entries: &[wgpu::BindGroupEntry {
1934 binding: 0,
1935 resource: self.vp_buffer.as_entire_binding(),
1936 }],
1937 });
1938
1939 {
1941 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1942 label: Some("scrim-stencil-mask-pass"),
1943 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1944 view: target_view,
1945 resolve_target: None,
1946 ops: wgpu::Operations {
1947 load: wgpu::LoadOp::Load,
1948 store: wgpu::StoreOp::Store,
1949 },
1950 })],
1951 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
1952 view: &stencil_tex.view,
1953 depth_ops: None,
1954 stencil_ops: Some(wgpu::Operations {
1955 load: wgpu::LoadOp::Clear(0),
1956 store: wgpu::StoreOp::Store,
1957 }),
1958 }),
1959 occlusion_query_set: None,
1960 timestamp_writes: None,
1961 });
1962 pass.set_stencil_reference(1);
1963 self.scrim_mask
1964 .record(&mut pass, &vp_bg, &vbuf, &ibuf, indices.len() as u32);
1965 }
1966
1967 let quad = [
1969 Vtx {
1970 pos: [0.0, 0.0],
1971 color: [color.r, color.g, color.b, color.a],
1972 z: 0.5,
1973 },
1974 Vtx {
1975 pos: [width as f32, 0.0],
1976 color: [color.r, color.g, color.b, color.a],
1977 z: 0.5,
1978 },
1979 Vtx {
1980 pos: [width as f32, height as f32],
1981 color: [color.r, color.g, color.b, color.a],
1982 z: 0.5,
1983 },
1984 Vtx {
1985 pos: [0.0, height as f32],
1986 color: [color.r, color.g, color.b, color.a],
1987 z: 0.5,
1988 },
1989 ];
1990 let quad_idx: [u16; 6] = [0, 1, 2, 0, 2, 3];
1991 let qvbuf = self.device.create_buffer(&wgpu::BufferDescriptor {
1992 label: Some("scrim-fullscreen-vbuf"),
1993 size: (quad.len() * std::mem::size_of::<Vtx>()) as u64,
1994 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
1995 mapped_at_creation: false,
1996 });
1997 let qibuf = self.device.create_buffer(&wgpu::BufferDescriptor {
1998 label: Some("scrim-fullscreen-ibuf"),
1999 size: (quad_idx.len() * std::mem::size_of::<u16>()) as u64,
2000 usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
2001 mapped_at_creation: false,
2002 });
2003 queue.write_buffer(&qvbuf, 0, bytemuck::cast_slice(&quad));
2004 queue.write_buffer(&qibuf, 0, bytemuck::cast_slice(&quad_idx));
2005
2006 let vp_bg_scrim = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
2007 label: Some("scrim-stencil-vp-bg-scrim"),
2008 layout: self.scrim_stencil.viewport_bgl(),
2009 entries: &[wgpu::BindGroupEntry {
2010 binding: 0,
2011 resource: self.vp_buffer.as_entire_binding(),
2012 }],
2013 });
2014
2015 {
2017 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2018 label: Some("scrim-stencil-pass"),
2019 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2020 view: target_view,
2021 resolve_target: None,
2022 ops: wgpu::Operations {
2023 load: wgpu::LoadOp::Load,
2024 store: wgpu::StoreOp::Store,
2025 },
2026 })],
2027 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
2028 view: &stencil_tex.view,
2029 depth_ops: None,
2030 stencil_ops: Some(wgpu::Operations {
2031 load: wgpu::LoadOp::Load,
2032 store: wgpu::StoreOp::Store,
2033 }),
2034 }),
2035 occlusion_query_set: None,
2036 timestamp_writes: None,
2037 });
2038 pass.set_stencil_reference(0);
2039 self.scrim_stencil.record(
2040 &mut pass,
2041 &vp_bg_scrim,
2042 &qvbuf,
2043 &qibuf,
2044 quad_idx.len() as u32,
2045 );
2046 }
2047 }
2048
2049 pub fn draw_filled_rounded_rect(
2052 &self,
2053 encoder: &mut wgpu::CommandEncoder,
2054 target_view: &wgpu::TextureView,
2055 width: u32,
2056 height: u32,
2057 rrect: RoundedRect,
2058 color: crate::scene::ColorLinPremul,
2059 queue: &wgpu::Queue,
2060 ) {
2061 let logical =
2063 crate::dpi::logical_multiplier(self.logical_pixels, self.scale_factor, self.ui_scale);
2064 let scale = [
2065 (2.0f32 / (width.max(1) as f32)) * logical,
2066 (-2.0f32 / (height.max(1) as f32)) * logical,
2067 ];
2068 let translate = [-1.0f32, 1.0f32];
2069 let vp_data: [f32; 8] = [
2070 scale[0],
2071 scale[1],
2072 translate[0],
2073 translate[1],
2074 0.0,
2075 0.0,
2076 0.0,
2077 0.0,
2078 ];
2079 queue.write_buffer(&self.vp_buffer, 0, bytemuck::bytes_of(&vp_data));
2081
2082 #[repr(C)]
2084 #[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
2085 struct Vtx {
2086 pos: [f32; 2],
2087 color: [f32; 4],
2088 }
2089 let mut vertices: Vec<Vtx> = Vec::new();
2090 let mut indices: Vec<u16> = Vec::new();
2091 let rect = rrect.rect;
2092 let tl = rrect.radii.tl.min(rect.w * 0.5).min(rect.h * 0.5);
2093 let tr = rrect.radii.tr.min(rect.w * 0.5).min(rect.h * 0.5);
2094 let br = rrect.radii.br.min(rect.w * 0.5).min(rect.h * 0.5);
2095 let bl = rrect.radii.bl.min(rect.w * 0.5).min(rect.h * 0.5);
2096 let segs = 64u32;
2097 let mut ring: Vec<[f32; 2]> = Vec::new();
2098 fn arc_append(
2099 ring: &mut Vec<[f32; 2]>,
2100 c: [f32; 2],
2101 r: f32,
2102 start: f32,
2103 end: f32,
2104 segs: u32,
2105 include_start: bool,
2106 ) {
2107 if r <= 0.0 {
2108 return;
2109 }
2110 for i in 0..=segs {
2111 if i == 0 && !include_start {
2112 continue;
2113 }
2114 let t = (i as f32) / (segs as f32);
2115 let ang = start + t * (end - start);
2116 let p = [c[0] + r * ang.cos(), c[1] - r * ang.sin()];
2117 ring.push(p);
2118 }
2119 }
2120 if tl > 0.0 {
2121 arc_append(
2122 &mut ring,
2123 [rect.x + tl, rect.y + tl],
2124 tl,
2125 std::f32::consts::FRAC_PI_2,
2126 std::f32::consts::PI,
2127 segs,
2128 true,
2129 );
2130 } else {
2131 ring.push([rect.x + 0.0, rect.y + 0.0]);
2132 }
2133 if bl > 0.0 {
2134 arc_append(
2135 &mut ring,
2136 [rect.x + bl, rect.y + rect.h - bl],
2137 bl,
2138 std::f32::consts::PI,
2139 std::f32::consts::FRAC_PI_2 * 3.0,
2140 segs,
2141 true,
2142 );
2143 } else {
2144 ring.push([rect.x + 0.0, rect.y + rect.h]);
2145 }
2146 if br > 0.0 {
2147 arc_append(
2148 &mut ring,
2149 [rect.x + rect.w - br, rect.y + rect.h - br],
2150 br,
2151 std::f32::consts::FRAC_PI_2 * 3.0,
2152 std::f32::consts::TAU,
2153 segs,
2154 true,
2155 );
2156 } else {
2157 ring.push([rect.x + rect.w, rect.y + rect.h]);
2158 }
2159 if tr > 0.0 {
2160 arc_append(
2161 &mut ring,
2162 [rect.x + rect.w - tr, rect.y + tr],
2163 tr,
2164 0.0,
2165 std::f32::consts::FRAC_PI_2,
2166 segs,
2167 true,
2168 );
2169 } else {
2170 ring.push([rect.x + rect.w, rect.y + 0.0]);
2171 }
2172 let center = [rect.x + rect.w * 0.5, rect.y + rect.h * 0.5];
2173 let col = [color.r, color.g, color.b, color.a];
2174 let base = vertices.len() as u16;
2175 vertices.push(Vtx {
2176 pos: center,
2177 color: col,
2178 });
2179 for p in ring.iter() {
2180 vertices.push(Vtx {
2181 pos: *p,
2182 color: col,
2183 });
2184 }
2185 let ring_len = (vertices.len() as u16) - base - 1;
2186 for i in 0..ring_len {
2187 let i0 = base;
2188 let i1 = base + 1 + i;
2189 let i2 = base + 1 + ((i + 1) % ring_len);
2190 indices.extend_from_slice(&[i0, i1, i2]);
2191 }
2192
2193 let vsize = (vertices.len() * std::mem::size_of::<Vtx>()) as u64;
2195 let isize = (indices.len() * std::mem::size_of::<u16>()) as u64;
2196 let vbuf = self.device.create_buffer(&wgpu::BufferDescriptor {
2197 label: Some("rounded-rect-fill-vbuf"),
2198 size: vsize.max(4),
2199 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
2200 mapped_at_creation: false,
2201 });
2202 let ibuf = self.device.create_buffer(&wgpu::BufferDescriptor {
2203 label: Some("rounded-rect-fill-ibuf"),
2204 size: isize.max(4),
2205 usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
2206 mapped_at_creation: false,
2207 });
2208 if vsize > 0 {
2209 queue.write_buffer(&vbuf, 0, bytemuck::cast_slice(&vertices));
2210 }
2211 if isize > 0 {
2212 queue.write_buffer(&ibuf, 0, bytemuck::cast_slice(&indices));
2213 }
2214 let gpu = crate::upload::GpuScene {
2215 vertex: crate::allocator::OwnedBuffer {
2216 buffer: vbuf,
2217 key: crate::allocator::BufKey {
2218 size: vsize.max(4),
2219 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
2220 },
2221 },
2222 index: crate::allocator::OwnedBuffer {
2223 buffer: ibuf,
2224 key: crate::allocator::BufKey {
2225 size: isize.max(4),
2226 usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
2227 },
2228 },
2229 vertices: vertices.len() as u32,
2230 indices: indices.len() as u32,
2231 };
2232
2233 let vp_bg = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
2235 label: Some("vp-bg-direct-no-msaa"),
2236 layout: self.solid_direct_no_msaa.viewport_bgl(),
2237 entries: &[wgpu::BindGroupEntry {
2238 binding: 0,
2239 resource: self.vp_buffer.as_entire_binding(),
2240 }],
2241 });
2242
2243 let _z_bg = self.create_z_bind_group(0.0, queue);
2246
2247 let depth_attachment = self.depth_texture.as_ref().map(|tex| {
2249 wgpu::RenderPassDepthStencilAttachment {
2250 view: &tex.view,
2251 depth_ops: Some(wgpu::Operations {
2252 load: wgpu::LoadOp::Load, store: wgpu::StoreOp::Store,
2254 }),
2255 stencil_ops: None,
2256 }
2257 });
2258
2259 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2260 label: Some("rounded-rect-fill-pass"),
2261 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2262 view: target_view,
2263 resolve_target: None,
2264 ops: wgpu::Operations {
2265 load: wgpu::LoadOp::Load,
2266 store: wgpu::StoreOp::Store,
2267 },
2268 })],
2269 depth_stencil_attachment: depth_attachment,
2270 occlusion_query_set: None,
2271 timestamp_writes: None,
2272 });
2273 self.solid_direct_no_msaa.record(&mut pass, &vp_bg, &gpu);
2274 }
2275
2276 pub fn render_solids_to_offscreen(
2277 &self,
2278 encoder: &mut wgpu::CommandEncoder,
2279 vp_bg: &wgpu::BindGroup,
2280 targets: &PassTargets,
2281 scene: &GpuScene,
2282 clear_color: wgpu::Color,
2283 queue: &wgpu::Queue,
2284 ) {
2285 let depth_tex = self.device.create_texture(&wgpu::TextureDescriptor {
2287 label: Some("solid-depth-offscreen"),
2288 size: wgpu::Extent3d {
2289 width: targets.color.key.width,
2290 height: targets.color.key.height,
2291 depth_or_array_layers: 1,
2292 },
2293 mip_level_count: 1,
2294 sample_count: 1,
2295 dimension: wgpu::TextureDimension::D2,
2296 format: wgpu::TextureFormat::Depth32Float,
2297 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
2298 view_formats: &[],
2299 });
2300 let depth_view = depth_tex.create_view(&wgpu::TextureViewDescriptor::default());
2301
2302 let _z_bg = self.create_z_bind_group(0.0, queue);
2303 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2304 label: Some("solid-offscreen-pass"),
2305 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2306 view: &targets.color.view,
2307 resolve_target: None,
2308 ops: wgpu::Operations {
2309 load: wgpu::LoadOp::Clear(clear_color),
2310 store: wgpu::StoreOp::Store,
2311 },
2312 })],
2313 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
2314 view: &depth_view,
2315 depth_ops: Some(wgpu::Operations {
2316 load: wgpu::LoadOp::Clear(1.0),
2317 store: wgpu::StoreOp::Store,
2318 }),
2319 stencil_ops: None,
2320 }),
2321 occlusion_query_set: None,
2322 timestamp_writes: None,
2323 });
2324 self.solid_offscreen.record(&mut pass, vp_bg, scene);
2325 }
2326
2327 pub fn composite_to_surface(
2328 &self,
2329 encoder: &mut wgpu::CommandEncoder,
2330 surface_view: &wgpu::TextureView,
2331 offscreen: &PassTargets,
2332 clear: Option<wgpu::Color>,
2333 ) {
2334 let bg = self
2335 .compositor
2336 .bind_group(&self.device, &offscreen.color.view);
2337 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2338 label: Some("composite-pass"),
2339 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2340 view: surface_view,
2341 resolve_target: None,
2342 ops: wgpu::Operations {
2343 load: match clear {
2344 Some(c) => wgpu::LoadOp::Clear(c),
2345 None => wgpu::LoadOp::Load,
2346 },
2347 store: wgpu::StoreOp::Store,
2348 },
2349 })],
2350 depth_stencil_attachment: None,
2351 occlusion_query_set: None,
2352 timestamp_writes: None,
2353 });
2354 self.compositor.record(&mut pass, &bg);
2355 }
2356
2357 pub fn paint_root_to_intermediate(
2360 &self,
2361 encoder: &mut wgpu::CommandEncoder,
2362 bg: &Background,
2363 queue: &wgpu::Queue,
2364 ) {
2365 let intermediate = self
2366 .intermediate_texture
2367 .as_ref()
2368 .expect("intermediate texture must be allocated before painting");
2369 self.paint_root(encoder, &intermediate.view, bg, queue);
2370 }
2371
2372 pub fn paint_root(
2373 &self,
2374 encoder: &mut wgpu::CommandEncoder,
2375 surface_view: &wgpu::TextureView,
2376 bg: &Background,
2377 queue: &wgpu::Queue,
2378 ) {
2379 if let Background::Solid(c) = bg {
2381 let _pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2382 label: Some("bg-solid-pass"),
2383 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2384 view: surface_view,
2385 resolve_target: None,
2386 ops: wgpu::Operations {
2387 load: wgpu::LoadOp::Clear(wgpu::Color {
2388 r: c.r as f64,
2389 g: c.g as f64,
2390 b: c.b as f64,
2391 a: c.a as f64,
2392 }),
2393 store: wgpu::StoreOp::Store,
2394 },
2395 })],
2396 depth_stencil_attachment: None,
2397 occlusion_query_set: None,
2398 timestamp_writes: None,
2399 });
2400 return;
2401 }
2402
2403 let (start_uv, end_uv, stop0, stop1) = match bg {
2405 Background::LinearGradient {
2406 start_uv,
2407 end_uv,
2408 stop0,
2409 stop1,
2410 } => (*start_uv, *end_uv, *stop0, *stop1),
2411 _ => unreachable!(),
2412 };
2413 #[repr(C)]
2414 #[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
2415 struct BgParams {
2416 start: [f32; 2],
2417 end: [f32; 2],
2418 center: [f32; 2],
2419 radius: f32,
2420 stop_count: u32,
2421 mode: u32,
2422 _pad: u32,
2423 }
2424 #[repr(C)]
2425 #[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
2426 struct Stop {
2427 pos: f32,
2428 _pad0: [f32; 3],
2429 color: [f32; 4],
2430 }
2431
2432 let params = BgParams {
2433 start: start_uv,
2434 end: end_uv,
2435 center: [0.5, 0.5],
2436 radius: 1.0,
2437 stop_count: 2,
2438 mode: 1,
2439 _pad: 0,
2440 };
2441 let c0 = stop0.1;
2442 let c1 = stop1.1;
2443 let stops = [
2444 Stop {
2445 pos: stop0.0,
2446 _pad0: [0.0; 3],
2447 color: [c0.r, c0.g, c0.b, c0.a],
2448 },
2449 Stop {
2450 pos: stop1.0,
2451 _pad0: [0.0; 3],
2452 color: [c1.r, c1.g, c1.b, c1.a],
2453 },
2454 Stop {
2455 pos: 0.0,
2456 _pad0: [0.0; 3],
2457 color: [0.0; 4],
2458 },
2459 Stop {
2460 pos: 0.0,
2461 _pad0: [0.0; 3],
2462 color: [0.0; 4],
2463 },
2464 Stop {
2465 pos: 0.0,
2466 _pad0: [0.0; 3],
2467 color: [0.0; 4],
2468 },
2469 Stop {
2470 pos: 0.0,
2471 _pad0: [0.0; 3],
2472 color: [0.0; 4],
2473 },
2474 Stop {
2475 pos: 0.0,
2476 _pad0: [0.0; 3],
2477 color: [0.0; 4],
2478 },
2479 Stop {
2480 pos: 0.0,
2481 _pad0: [0.0; 3],
2482 color: [0.0; 4],
2483 },
2484 ];
2485
2486 queue.write_buffer(&self.bg_param_buffer, 0, bytemuck::bytes_of(¶ms));
2487 queue.write_buffer(&self.bg_stops_buffer, 0, bytemuck::cast_slice(&stops));
2488 let bg_bind = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
2489 label: Some("bg-bind"),
2490 layout: self.bg.bgl(),
2491 entries: &[
2492 wgpu::BindGroupEntry {
2493 binding: 0,
2494 resource: self.bg_param_buffer.as_entire_binding(),
2495 },
2496 wgpu::BindGroupEntry {
2497 binding: 1,
2498 resource: self.bg_stops_buffer.as_entire_binding(),
2499 },
2500 ],
2501 });
2502 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2503 label: Some("bg-grad-pass"),
2504 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2505 view: surface_view,
2506 resolve_target: None,
2507 ops: wgpu::Operations {
2508 load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
2509 store: wgpu::StoreOp::Store,
2510 },
2511 })],
2512 depth_stencil_attachment: None,
2513 occlusion_query_set: None,
2514 timestamp_writes: None,
2515 });
2516 self.bg.record(&mut pass, &bg_bind);
2517 }
2518
2519 pub fn paint_root_linear_gradient_multi_to_intermediate(
2521 &self,
2522 encoder: &mut wgpu::CommandEncoder,
2523 start_uv: [f32; 2],
2524 end_uv: [f32; 2],
2525 stops_in: &[(f32, crate::scene::ColorLinPremul)],
2526 queue: &wgpu::Queue,
2527 ) {
2528 let intermediate = self
2529 .intermediate_texture
2530 .as_ref()
2531 .expect("intermediate texture must be allocated before painting");
2532 self.paint_root_linear_gradient_multi(
2533 encoder,
2534 &intermediate.view,
2535 start_uv,
2536 end_uv,
2537 stops_in,
2538 queue,
2539 );
2540 }
2541
2542 pub fn paint_root_linear_gradient_multi(
2544 &self,
2545 encoder: &mut wgpu::CommandEncoder,
2546 surface_view: &wgpu::TextureView,
2547 start_uv: [f32; 2],
2548 end_uv: [f32; 2],
2549 stops_in: &[(f32, crate::scene::ColorLinPremul)],
2550 queue: &wgpu::Queue,
2551 ) {
2552 let mut sorted: Vec<(f32, crate::scene::ColorLinPremul)> = stops_in
2554 .iter()
2555 .map(|(p, c)| (p.clamp(0.0, 1.0), *c))
2556 .collect();
2557 sorted.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(std::cmp::Ordering::Equal));
2558 let count = sorted.len().min(8).max(2) as u32;
2559 #[repr(C)]
2560 #[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
2561 struct BgParams {
2562 start_end: [f32; 4],
2563 center_radius_stop: [f32; 4],
2564 flags: [f32; 4],
2565 }
2566 #[repr(C)]
2567 #[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
2568 struct Stop {
2569 pos: f32,
2570 _pad0: [f32; 3],
2571 color: [f32; 4],
2572 }
2573 let mut stops: [Stop; 8] = [Stop {
2574 pos: 0.0,
2575 _pad0: [0.0; 3],
2576 color: [0.0; 4],
2577 }; 8];
2578 for (i, (p, c)) in sorted.iter().take(8).enumerate() {
2579 stops[i] = Stop {
2580 pos: *p,
2581 _pad0: [0.0; 3],
2582 color: [c.r, c.g, c.b, c.a],
2583 };
2584 }
2585 let debug_flag = std::env::var("DEBUG_RADIAL")
2586 .ok()
2587 .map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
2588 .unwrap_or(false);
2589 let params = BgParams {
2590 start_end: [start_uv[0], start_uv[1], end_uv[0], end_uv[1]],
2591 center_radius_stop: [0.5, 0.5, 1.0, count as f32],
2592 flags: [1.0, if debug_flag { 1.0 } else { 0.0 }, 0.0, 0.0],
2593 };
2594 queue.write_buffer(&self.bg_param_buffer, 0, bytemuck::bytes_of(¶ms));
2595 queue.write_buffer(&self.bg_stops_buffer, 0, bytemuck::cast_slice(&stops));
2596 let bg_bind = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
2597 label: Some("bg-bind-linear"),
2598 layout: self.bg.bgl(),
2599 entries: &[
2600 wgpu::BindGroupEntry {
2601 binding: 0,
2602 resource: self.bg_param_buffer.as_entire_binding(),
2603 },
2604 wgpu::BindGroupEntry {
2605 binding: 1,
2606 resource: self.bg_stops_buffer.as_entire_binding(),
2607 },
2608 ],
2609 });
2610 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2611 label: Some("bg-linear-pass"),
2612 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2613 view: surface_view,
2614 resolve_target: None,
2615 ops: wgpu::Operations {
2616 load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
2617 store: wgpu::StoreOp::Store,
2618 },
2619 })],
2620 depth_stencil_attachment: None,
2621 occlusion_query_set: None,
2622 timestamp_writes: None,
2623 });
2624 self.bg.record(&mut pass, &bg_bind);
2625 }
2626
2627 pub fn paint_root_radial_gradient_multi_to_intermediate(
2629 &self,
2630 encoder: &mut wgpu::CommandEncoder,
2631 center_uv: [f32; 2],
2632 radius: f32,
2633 stops_in: &[(f32, crate::scene::ColorLinPremul)],
2634 queue: &wgpu::Queue,
2635 width: u32,
2636 height: u32,
2637 ) {
2638 let intermediate = self
2639 .intermediate_texture
2640 .as_ref()
2641 .expect("intermediate texture must be allocated before painting");
2642 self.paint_root_radial_gradient_multi(
2643 encoder,
2644 &intermediate.view,
2645 center_uv,
2646 radius,
2647 stops_in,
2648 queue,
2649 width,
2650 height,
2651 );
2652 }
2653
2654 pub fn paint_root_radial_gradient_multi(
2656 &self,
2657 encoder: &mut wgpu::CommandEncoder,
2658 surface_view: &wgpu::TextureView,
2659 center_uv: [f32; 2],
2660 radius: f32,
2661 stops_in: &[(f32, crate::scene::ColorLinPremul)],
2662 queue: &wgpu::Queue,
2663 width: u32,
2664 height: u32,
2665 ) {
2666 let mut sorted: Vec<(f32, crate::scene::ColorLinPremul)> = stops_in
2668 .iter()
2669 .map(|(p, c)| (p.clamp(0.0, 1.0), *c))
2670 .collect();
2671 sorted.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(std::cmp::Ordering::Equal));
2672 let count = sorted.len().min(8).max(2) as u32;
2673 #[repr(C)]
2674 #[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
2675 struct BgParams {
2676 start_end: [f32; 4],
2677 center_radius_stop: [f32; 4],
2678 flags: [f32; 4],
2679 }
2680 #[repr(C)]
2681 #[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
2682 struct Stop {
2683 pos: f32,
2684 _pad0: [f32; 3],
2685 color: [f32; 4],
2686 }
2687 let mut stops: [Stop; 8] = [Stop {
2688 pos: 0.0,
2689 _pad0: [0.0; 3],
2690 color: [0.0; 4],
2691 }; 8];
2692 for (i, (p, c)) in sorted.iter().take(8).enumerate() {
2693 stops[i] = Stop {
2694 pos: *p,
2695 _pad0: [0.0; 3],
2696 color: [c.r, c.g, c.b, c.a],
2697 };
2698 }
2699 let debug_flag = std::env::var("DEBUG_RADIAL")
2700 .ok()
2701 .map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
2702 .unwrap_or(false);
2703 let aspect_ratio = (width.max(1) as f32) / (height.max(1) as f32);
2704 if debug_flag {
2705 }
2707 let mut adj_center = center_uv;
2711 let mut adj_radius = radius;
2712 #[cfg(target_os = "macos")]
2713 {
2714 let sf = self.scale_factor.max(1.0);
2715 if (adj_center[0] - 0.5).abs() < 1e-3 && (adj_center[1] - 0.5).abs() < 1e-3 {
2717 adj_center = [adj_center[0] / sf, adj_center[1] / sf];
2718 adj_radius = adj_radius / sf;
2719 if debug_flag {
2720 }
2722 }
2723 }
2724 let params = BgParams {
2725 start_end: [0.0, 0.0, 1.0, 1.0],
2726 center_radius_stop: [adj_center[0], adj_center[1], adj_radius, count as f32],
2727 flags: [2.0, if debug_flag { 1.0 } else { 0.0 }, aspect_ratio, 0.0],
2728 };
2729 queue.write_buffer(&self.bg_param_buffer, 0, bytemuck::bytes_of(¶ms));
2730 queue.write_buffer(&self.bg_stops_buffer, 0, bytemuck::cast_slice(&stops));
2731 let bg_bind = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
2732 label: Some("bg-bind-radial"),
2733 layout: self.bg.bgl(),
2734 entries: &[
2735 wgpu::BindGroupEntry {
2736 binding: 0,
2737 resource: self.bg_param_buffer.as_entire_binding(),
2738 },
2739 wgpu::BindGroupEntry {
2740 binding: 1,
2741 resource: self.bg_stops_buffer.as_entire_binding(),
2742 },
2743 ],
2744 });
2745 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2746 label: Some("bg-radial-pass"),
2747 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2748 view: surface_view,
2749 resolve_target: None,
2750 ops: wgpu::Operations {
2751 load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
2752 store: wgpu::StoreOp::Store,
2753 },
2754 })],
2755 depth_stencil_attachment: None,
2756 occlusion_query_set: None,
2757 timestamp_writes: None,
2758 });
2759 self.bg.record(&mut pass, &bg_bind);
2760 }
2761
2762 pub fn paint_root_color(
2764 &self,
2765 encoder: &mut wgpu::CommandEncoder,
2766 surface_view: &wgpu::TextureView,
2767 color: crate::scene::ColorLinPremul,
2768 queue: &wgpu::Queue,
2769 ) {
2770 #[repr(C)]
2772 #[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
2773 struct BgParams {
2774 start_end: [f32; 4],
2775 center_radius_stop: [f32; 4],
2776 flags: [f32; 4],
2777 }
2778 #[repr(C)]
2779 #[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
2780 struct Stop {
2781 pos: f32,
2782 _pad0: [f32; 3],
2783 color: [f32; 4],
2784 }
2785 let params = BgParams {
2786 start_end: [0.0, 0.0, 1.0, 1.0],
2787 center_radius_stop: [0.5, 0.5, 1.0, 1.0],
2788 flags: [0.0, 0.0, 0.0, 0.0], };
2790 let stops: [Stop; 1] = [Stop {
2791 pos: 0.0,
2792 _pad0: [0.0; 3],
2793 color: [color.r, color.g, color.b, color.a],
2794 }];
2795 queue.write_buffer(&self.bg_param_buffer, 0, bytemuck::bytes_of(¶ms));
2797 queue.write_buffer(&self.bg_stops_buffer, 0, bytemuck::cast_slice(&stops));
2798 let bg_bind = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
2799 label: Some("bg-bind-solid"),
2800 layout: self.bg.bgl(),
2801 entries: &[
2802 wgpu::BindGroupEntry {
2803 binding: 0,
2804 resource: self.bg_param_buffer.as_entire_binding(),
2805 },
2806 wgpu::BindGroupEntry {
2807 binding: 1,
2808 resource: self.bg_stops_buffer.as_entire_binding(),
2809 },
2810 ],
2811 });
2812 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2813 label: Some("bg-solid-pass"),
2814 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2815 view: surface_view,
2816 resolve_target: None,
2817 ops: wgpu::Operations {
2818 load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
2819 store: wgpu::StoreOp::Store,
2820 },
2821 })],
2822 depth_stencil_attachment: None,
2823 occlusion_query_set: None,
2824 timestamp_writes: None,
2825 });
2826 self.bg.record(&mut pass, &bg_bind);
2827 }
2828
2829 pub fn paint_root_gradient(
2831 &self,
2832 encoder: &mut wgpu::CommandEncoder,
2833 surface_view: &wgpu::TextureView,
2834 start_uv: [f32; 2],
2835 end_uv: [f32; 2],
2836 stop0: (f32, crate::scene::ColorLinPremul),
2837 stop1: (f32, crate::scene::ColorLinPremul),
2838 queue: &wgpu::Queue,
2839 ) {
2840 #[repr(C)]
2841 #[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
2842 struct BgData {
2843 start_end: [f32; 4],
2844 center_radius_stop: [f32; 4],
2845 flags: [f32; 4],
2846 }
2847 let c0 = stop0.1;
2848 let c1 = stop1.1;
2849 let debug_flag = std::env::var("DEBUG_RADIAL")
2851 .ok()
2852 .map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
2853 .unwrap_or(false);
2854 let params = BgData {
2855 start_end: [start_uv[0], start_uv[1], end_uv[0], end_uv[1]],
2856 center_radius_stop: [0.5, 0.5, 1.0, 2.0],
2857 flags: [1.0, if debug_flag { 1.0 } else { 0.0 }, 0.0, 0.0],
2858 };
2859 #[repr(C)]
2861 #[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
2862 struct Stop {
2863 pos: f32,
2864 _pad0: [f32; 3],
2865 color: [f32; 4],
2866 }
2867 let stops: [Stop; 2] = [
2868 Stop {
2869 pos: stop0.0,
2870 _pad0: [0.0; 3],
2871 color: [c0.r, c0.g, c0.b, c0.a],
2872 },
2873 Stop {
2874 pos: stop1.0,
2875 _pad0: [0.0; 3],
2876 color: [c1.r, c1.g, c1.b, c1.a],
2877 },
2878 ];
2879 queue.write_buffer(&self.bg_param_buffer, 0, bytemuck::bytes_of(¶ms));
2880 queue.write_buffer(&self.bg_stops_buffer, 0, bytemuck::cast_slice(&stops));
2881 let bg_bind = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
2882 label: Some("bg-bind"),
2883 layout: self.bg.bgl(),
2884 entries: &[
2885 wgpu::BindGroupEntry {
2886 binding: 0,
2887 resource: self.bg_param_buffer.as_entire_binding(),
2888 },
2889 wgpu::BindGroupEntry {
2890 binding: 1,
2891 resource: self.bg_stops_buffer.as_entire_binding(),
2892 },
2893 ],
2894 });
2895 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
2896 label: Some("bg-grad-pass"),
2897 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
2898 view: surface_view,
2899 resolve_target: None,
2900 ops: wgpu::Operations {
2901 load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
2902 store: wgpu::StoreOp::Store,
2903 },
2904 })],
2905 depth_stencil_attachment: None,
2906 occlusion_query_set: None,
2907 timestamp_writes: None,
2908 });
2909 self.bg.record(&mut pass, &bg_bind);
2910 }
2911
2912 pub fn render_unified(
2918 &mut self,
2919 encoder: &mut wgpu::CommandEncoder,
2920 allocator: &mut RenderAllocator,
2921 surface_view: &wgpu::TextureView,
2922 width: u32,
2923 height: u32,
2924 scene: &GpuScene,
2925 transparent_scene: &GpuScene,
2926 transparent_batches: &[crate::upload::TransparentBatch],
2927 glyph_draws: &[(
2928 [f32; 2],
2929 crate::text::RasterizedGlyph,
2930 crate::ColorLinPremul,
2931 i32,
2932 )], svg_draws: &[(
2934 std::path::PathBuf,
2935 [f32; 2],
2936 [f32; 2],
2937 Option<crate::SvgStyle>,
2938 i32,
2939 crate::Transform2D,
2940 Option<crate::Rect>,
2941 )],
2942 image_draws: &[(
2943 std::path::PathBuf,
2944 [f32; 2],
2945 [f32; 2],
2946 i32,
2947 Option<crate::Rect>,
2948 )],
2949 external_texture_draws: &[crate::upload::ExtractedExternalTextureDraw],
2950 clear: wgpu::Color,
2951 direct: bool,
2952 queue: &wgpu::Queue,
2953 preserve_surface: bool,
2954 ) {
2955 let logical =
2958 crate::dpi::logical_multiplier(self.logical_pixels, self.scale_factor, self.ui_scale);
2959 let inv_logical = if logical.is_finite() && logical > 0.0 {
2960 1.0 / logical
2961 } else {
2962 1.0
2963 };
2964 let scale = [
2965 (2.0f32 / (width.max(1) as f32)) * logical,
2966 (-2.0f32 / (height.max(1) as f32)) * logical,
2967 ];
2968 let translate = [-1.0f32, 1.0f32];
2969 let vp_data: [f32; 8] = [
2970 scale[0],
2971 scale[1],
2972 translate[0],
2973 translate[1],
2974 self.scroll_offset[0],
2975 self.scroll_offset[1],
2976 0.0, 0.0, ];
2979 let data = bytemuck::bytes_of(&vp_data);
2980 queue.write_buffer(&self.vp_buffer, 0, data);
2981 let transparent_text_z: std::collections::HashSet<i32> =
2982 transparent_batches.iter().map(|b| b.z).collect();
2983
2984 self.ensure_depth_texture(allocator, width.max(1), height.max(1));
2986
2987 let vp_bg_off = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
2989 label: Some("vp-bg-offscreen"),
2990 layout: self.solid_offscreen.viewport_bgl(),
2991 entries: &[wgpu::BindGroupEntry {
2992 binding: 0,
2993 resource: self.vp_buffer.as_entire_binding(),
2994 }],
2995 });
2996 if direct {
2997 let vp_bg = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
2998 label: Some("vp-bg-direct-local"),
2999 layout: self.solid_direct.viewport_bgl(),
3000 entries: &[wgpu::BindGroupEntry {
3001 binding: 0,
3002 resource: self.vp_buffer.as_entire_binding(),
3003 }],
3004 });
3005
3006 let _z_bg = self.create_z_bind_group(0.0, queue);
3008
3009 let mut image_views: Vec<(
3011 wgpu::TextureView,
3012 [f32; 2],
3013 [f32; 2],
3014 f32,
3015 Option<crate::Rect>,
3016 )> = Vec::new();
3017 for (path, origin, size, z, clip) in image_draws.iter() {
3018 let tex_opt =
3019 if let Some(view) = self.try_get_image_view(std::path::Path::new(path)) {
3020 Some(view)
3021 } else {
3022 self.load_image_to_view(std::path::Path::new(path), queue)
3023 };
3024 if let Some((tex_view, _w, _h)) = tex_opt {
3025 image_views.push((tex_view, *origin, *size, *z as f32, *clip));
3026 }
3027 }
3028
3029 let mut svg_views: Vec<(
3031 wgpu::TextureView,
3032 [f32; 2],
3033 [f32; 2],
3034 f32,
3035 Option<crate::Rect>,
3036 )> = Vec::new();
3037 for (path, origin, max_size, style, _z, transform, clip) in svg_draws.iter() {
3038 if let Some((_view, w, h)) =
3039 self.rasterize_svg_to_view(std::path::Path::new(path), 1.0, *style, queue)
3040 {
3041 let base_w = w.max(1) as f32;
3042 let base_h = h.max(1) as f32;
3043 let scale = (max_size[0] / base_w).min(max_size[1] / base_h).max(0.0);
3044
3045 if let Some((view_scaled, _sw, _sh)) =
3046 self.rasterize_svg_to_view(std::path::Path::new(path), scale, *style, queue)
3047 {
3048 let draw_w = base_w * scale;
3049 let draw_h = base_h * scale;
3050 let transformed_origin = apply_transform_to_point(*origin, *transform);
3051 svg_views.push((
3052 view_scaled,
3053 transformed_origin,
3054 [draw_w, draw_h],
3055 *_z as f32,
3056 *clip,
3057 ));
3058 }
3059 }
3060 }
3061
3062 let mut text_by_z: std::collections::HashMap<
3065 i32,
3066 Vec<(
3067 usize,
3068 [f32; 2],
3069 &crate::text::RasterizedGlyph,
3070 &crate::ColorLinPremul,
3071 )>,
3072 > = std::collections::HashMap::new();
3073 for (idx, (origin, glyph, color, z)) in glyph_draws.iter().enumerate() {
3074 text_by_z
3075 .entry(*z)
3076 .or_insert_with(Vec::new)
3077 .push((idx, *origin, glyph, color));
3078 }
3079 let mut text_groups = if !glyph_draws.is_empty() {
3083 if self.prev_atlas_max_x > 0 && self.prev_atlas_max_y > 0 {
3085 let clear_width = self.prev_atlas_max_x.min(4096);
3086 let clear_height = self.prev_atlas_max_y.min(4096);
3087 let clear_size = (clear_width * clear_height * 4) as usize;
3088 let clear_data = vec![0u8; clear_size];
3089 queue.write_texture(
3090 wgpu::ImageCopyTexture {
3091 texture: &self.text_mask_atlas,
3092 mip_level: 0,
3093 origin: wgpu::Origin3d { x: 0, y: 0, z: 0 },
3094 aspect: wgpu::TextureAspect::All,
3095 },
3096 &clear_data,
3097 wgpu::ImageDataLayout {
3098 offset: 0,
3099 bytes_per_row: Some(clear_width * 4),
3100 rows_per_image: Some(clear_height),
3101 },
3102 wgpu::Extent3d {
3103 width: clear_width,
3104 height: clear_height,
3105 depth_or_array_layers: 1,
3106 },
3107 );
3108 }
3109
3110 let mut atlas_cursor_x = 0u32;
3111 let mut atlas_cursor_y = 0u32;
3112 let mut next_row_height = 0u32;
3113 let mut atlas_max_x = 0u32;
3114 let mut atlas_max_y = 0u32;
3115 let mut all_text_groups: Vec<(i32, Vec<TextQuadVtx>)> = Vec::new();
3116
3117 for (z_index, glyphs) in text_by_z.iter() {
3119 let mut vertices: Vec<TextQuadVtx> = Vec::new();
3120 let force_grayscale = transparent_text_z.contains(z_index);
3121 let mut local_idx = 0;
3124 for (_idx, origin, glyph, color) in glyphs.iter() {
3125 let (w, h, data) = glyph_mask_for_atlas(&glyph.mask, force_grayscale);
3126 if local_idx == 0 {
3127 }
3130 local_idx += 1;
3131
3132 if atlas_cursor_x + w >= 4096 {
3133 atlas_cursor_x = 0;
3134 atlas_cursor_y += next_row_height;
3135 next_row_height = 0;
3136 }
3137 next_row_height = next_row_height.max(h);
3138
3139 atlas_max_x = atlas_max_x.max(atlas_cursor_x + w);
3141 atlas_max_y = atlas_max_y.max(atlas_cursor_y + h);
3142
3143 queue.write_texture(
3144 wgpu::ImageCopyTexture {
3145 texture: &self.text_mask_atlas,
3146 mip_level: 0,
3147 origin: wgpu::Origin3d {
3148 x: atlas_cursor_x,
3149 y: atlas_cursor_y,
3150 z: 0,
3151 },
3152 aspect: wgpu::TextureAspect::All,
3153 },
3154 data.as_ref(),
3155 wgpu::ImageDataLayout {
3156 offset: 0,
3157 bytes_per_row: Some(w * 4),
3158 rows_per_image: Some(h),
3159 },
3160 wgpu::Extent3d {
3161 width: w,
3162 height: h,
3163 depth_or_array_layers: 1,
3164 },
3165 );
3166
3167 let u0 = atlas_cursor_x as f32 / 4096.0;
3168 let v0 = atlas_cursor_y as f32 / 4096.0;
3169 let u1 = (atlas_cursor_x + w) as f32 / 4096.0;
3170 let v1 = (atlas_cursor_y + h) as f32 / 4096.0;
3171
3172 let quad_w = (w as f32) * inv_logical;
3177 let quad_h = (h as f32) * inv_logical;
3178
3179 if local_idx == 1 {
3180 }
3183
3184 vertices.extend_from_slice(&[
3185 TextQuadVtx {
3186 pos: [origin[0], origin[1]],
3187 uv: [u0, v0],
3188 color: [color.r, color.g, color.b, color.a],
3189 },
3190 TextQuadVtx {
3191 pos: [origin[0] + quad_w, origin[1]],
3192 uv: [u1, v0],
3193 color: [color.r, color.g, color.b, color.a],
3194 },
3195 TextQuadVtx {
3196 pos: [origin[0] + quad_w, origin[1] + quad_h],
3197 uv: [u1, v1],
3198 color: [color.r, color.g, color.b, color.a],
3199 },
3200 TextQuadVtx {
3201 pos: [origin[0], origin[1] + quad_h],
3202 uv: [u0, v1],
3203 color: [color.r, color.g, color.b, color.a],
3204 },
3205 ]);
3206
3207 atlas_cursor_x += w;
3208 }
3209
3210 if !vertices.is_empty() {
3212 all_text_groups.push((*z_index, vertices));
3213 }
3214 }
3215
3216 let mut text_resources: Vec<(
3219 i32,
3220 wgpu::Buffer,
3221 wgpu::Buffer,
3222 u32,
3223 wgpu::BindGroup,
3224 wgpu::Buffer,
3225 )> = Vec::new();
3226 for (z_index, vertices) in all_text_groups {
3227 let quad_count = vertices.len() / 4;
3233 let mut indices: Vec<u16> = Vec::with_capacity(quad_count * 6);
3234 for i in 0..quad_count {
3235 let base = (i * 4) as u16;
3236 indices.extend_from_slice(&[
3237 base,
3238 base + 1,
3239 base + 2,
3240 base,
3241 base + 2,
3242 base + 3,
3243 ]);
3244 }
3245
3246 let vbuf = self.device.create_buffer(&wgpu::BufferDescriptor {
3248 label: Some("text-vertex-buffer-group"),
3249 size: (vertices.len() * std::mem::size_of::<TextQuadVtx>()) as u64,
3250 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
3251 mapped_at_creation: false,
3252 });
3253
3254 let ibuf = self.device.create_buffer(&wgpu::BufferDescriptor {
3256 label: Some("text-index-buffer-group"),
3257 size: (indices.len() * std::mem::size_of::<u16>()) as u64,
3258 usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
3259 mapped_at_creation: false,
3260 });
3261
3262 queue.write_buffer(&vbuf, 0, bytemuck::cast_slice(&vertices));
3263 queue.write_buffer(&ibuf, 0, bytemuck::cast_slice(&indices));
3264
3265 let (z_bg, z_buf) = self.create_group_z_bind_group(z_index as f32, queue);
3269
3270 text_resources.push((z_index, vbuf, ibuf, indices.len() as u32, z_bg, z_buf));
3271 }
3272
3273 self.prev_atlas_max_x = atlas_max_x;
3275 self.prev_atlas_max_y = atlas_max_y;
3276
3277 text_resources
3278 } else {
3279 Vec::new()
3280 };
3281
3282 text_groups.sort_by_key(|(z, _, _, _, _, _)| *z);
3284
3285 let vp_bg_text = self.text.vp_bind_group(&self.device, &self.vp_buffer);
3287
3288 let mut image_resources: Vec<(
3290 wgpu::Buffer,
3291 wgpu::Buffer,
3292 wgpu::BindGroup,
3293 wgpu::BindGroup,
3294 wgpu::BindGroup,
3295 wgpu::BindGroup,
3296 wgpu::Buffer,
3297 wgpu::Buffer,
3298 Option<crate::Rect>,
3299 )> = Vec::new();
3300 let mut image_z_vals: Vec<i32> = Vec::new();
3301 for (tex_view, origin, size, z_val, clip) in image_views.iter() {
3302 let verts = [
3303 ImageQuadVtx {
3304 pos: [origin[0], origin[1]],
3305 uv: [0.0, 0.0],
3306 },
3307 ImageQuadVtx {
3308 pos: [origin[0] + size[0], origin[1]],
3309 uv: [1.0, 0.0],
3310 },
3311 ImageQuadVtx {
3312 pos: [origin[0] + size[0], origin[1] + size[1]],
3313 uv: [1.0, 1.0],
3314 },
3315 ImageQuadVtx {
3316 pos: [origin[0], origin[1] + size[1]],
3317 uv: [0.0, 1.0],
3318 },
3319 ];
3320 let idx: [u16; 6] = [0, 1, 2, 0, 2, 3];
3321
3322 let vbuf = self.device.create_buffer(&wgpu::BufferDescriptor {
3323 label: Some("image-vbuf-unified"),
3324 size: (verts.len() * std::mem::size_of::<ImageQuadVtx>()) as u64,
3325 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
3326 mapped_at_creation: false,
3327 });
3328 let ibuf = self.device.create_buffer(&wgpu::BufferDescriptor {
3329 label: Some("image-ibuf-unified"),
3330 size: (idx.len() * std::mem::size_of::<u16>()) as u64,
3331 usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
3332 mapped_at_creation: false,
3333 });
3334 queue.write_buffer(&vbuf, 0, bytemuck::cast_slice(&verts));
3335 queue.write_buffer(&ibuf, 0, bytemuck::cast_slice(&idx));
3336
3337 let vp_bg_img = self.image.vp_bind_group(&self.device, &self.vp_buffer);
3338 let (z_bg_img, z_buf_img) = self.create_group_z_bind_group(*z_val as f32, queue);
3340 let tex_bg = self.image.tex_bind_group(&self.device, tex_view);
3341 let (params_bg, params_buf) =
3342 self.image.params_bind_group(&self.device, 1.0, false);
3343
3344 image_z_vals.push(*z_val as i32);
3345 image_resources.push((
3346 vbuf, ibuf, vp_bg_img, z_bg_img, tex_bg, params_bg, z_buf_img, params_buf,
3347 *clip,
3348 ));
3349 }
3350
3351 let mut svg_z_vals: Vec<i32> = Vec::new();
3353 let mut svg_resources: Vec<(
3354 wgpu::Buffer,
3355 wgpu::Buffer,
3356 wgpu::BindGroup,
3357 wgpu::BindGroup,
3358 wgpu::BindGroup,
3359 wgpu::BindGroup,
3360 wgpu::Buffer,
3361 wgpu::Buffer,
3362 Option<crate::Rect>,
3363 )> = Vec::new();
3364 for (view_scaled, origin, size, z_val, clip) in svg_views.iter() {
3365 let verts = [
3366 ImageQuadVtx {
3367 pos: [origin[0], origin[1]],
3368 uv: [0.0, 0.0],
3369 },
3370 ImageQuadVtx {
3371 pos: [origin[0] + size[0], origin[1]],
3372 uv: [1.0, 0.0],
3373 },
3374 ImageQuadVtx {
3375 pos: [origin[0] + size[0], origin[1] + size[1]],
3376 uv: [1.0, 1.0],
3377 },
3378 ImageQuadVtx {
3379 pos: [origin[0], origin[1] + size[1]],
3380 uv: [0.0, 1.0],
3381 },
3382 ];
3383 let idx: [u16; 6] = [0, 1, 2, 0, 2, 3];
3384
3385 let vbuf = self.device.create_buffer(&wgpu::BufferDescriptor {
3386 label: Some("svg-vbuf-unified"),
3387 size: (verts.len() * std::mem::size_of::<ImageQuadVtx>()) as u64,
3388 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
3389 mapped_at_creation: false,
3390 });
3391 let ibuf = self.device.create_buffer(&wgpu::BufferDescriptor {
3392 label: Some("svg-ibuf-unified"),
3393 size: (idx.len() * std::mem::size_of::<u16>()) as u64,
3394 usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
3395 mapped_at_creation: false,
3396 });
3397 queue.write_buffer(&vbuf, 0, bytemuck::cast_slice(&verts));
3398 queue.write_buffer(&ibuf, 0, bytemuck::cast_slice(&idx));
3399
3400 let vp_bg_svg = self.image.vp_bind_group(&self.device, &self.vp_buffer);
3401 let (z_bg_svg, z_buf_svg) = self.create_group_z_bind_group(*z_val as f32, queue);
3403 let tex_bg = self.image.tex_bind_group(&self.device, view_scaled);
3404 let (params_bg, params_buf) =
3405 self.image.params_bind_group(&self.device, 1.0, false);
3406
3407 svg_z_vals.push(*z_val as i32);
3408 svg_resources.push((
3409 vbuf, ibuf, vp_bg_svg, z_bg_svg, tex_bg, params_bg, z_buf_svg, params_buf,
3410 *clip,
3411 ));
3412 }
3413
3414 let mut ext_z_vals: Vec<i32> = Vec::new();
3416 let mut ext_resources: Vec<(
3417 wgpu::Buffer,
3418 wgpu::Buffer,
3419 wgpu::BindGroup,
3420 wgpu::BindGroup,
3421 wgpu::BindGroup,
3422 wgpu::BindGroup,
3423 wgpu::Buffer,
3424 wgpu::Buffer,
3425 )> = Vec::new();
3426 for etd in external_texture_draws.iter() {
3427 let Some(tex_view) = self.external_textures.get(&etd.texture_id) else {
3428 continue;
3429 };
3430 let verts = [
3431 ImageQuadVtx {
3432 pos: [etd.origin[0], etd.origin[1]],
3433 uv: [0.0, 0.0],
3434 },
3435 ImageQuadVtx {
3436 pos: [etd.origin[0] + etd.size[0], etd.origin[1]],
3437 uv: [1.0, 0.0],
3438 },
3439 ImageQuadVtx {
3440 pos: [etd.origin[0] + etd.size[0], etd.origin[1] + etd.size[1]],
3441 uv: [1.0, 1.0],
3442 },
3443 ImageQuadVtx {
3444 pos: [etd.origin[0], etd.origin[1] + etd.size[1]],
3445 uv: [0.0, 1.0],
3446 },
3447 ];
3448 let idx: [u16; 6] = [0, 1, 2, 0, 2, 3];
3449
3450 let vbuf = self.device.create_buffer(&wgpu::BufferDescriptor {
3451 label: Some("ext-tex-vbuf-unified"),
3452 size: (verts.len() * std::mem::size_of::<ImageQuadVtx>()) as u64,
3453 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
3454 mapped_at_creation: false,
3455 });
3456 let ibuf = self.device.create_buffer(&wgpu::BufferDescriptor {
3457 label: Some("ext-tex-ibuf-unified"),
3458 size: (idx.len() * std::mem::size_of::<u16>()) as u64,
3459 usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
3460 mapped_at_creation: false,
3461 });
3462 queue.write_buffer(&vbuf, 0, bytemuck::cast_slice(&verts));
3463 queue.write_buffer(&ibuf, 0, bytemuck::cast_slice(&idx));
3464
3465 let vp_bg_ext = self.image.vp_bind_group(&self.device, &self.vp_buffer);
3466 let (z_bg_ext, z_buf_ext) = self.create_group_z_bind_group(etd.z as f32, queue);
3467 let tex_bg = self.image.tex_bind_group(&self.device, tex_view);
3468 let (params_bg, params_buf) =
3469 self.image
3470 .params_bind_group(&self.device, etd.opacity, etd.premultiplied);
3471
3472 ext_z_vals.push(etd.z);
3473 ext_resources.push((
3474 vbuf, ibuf, vp_bg_ext, z_bg_ext, tex_bg, params_bg, z_buf_ext, params_buf,
3475 ));
3476 }
3477
3478 let depth_attachment = Some(wgpu::RenderPassDepthStencilAttachment {
3480 view: self.depth_view(),
3481 depth_ops: Some(wgpu::Operations {
3482 load: if preserve_surface {
3483 wgpu::LoadOp::Load
3484 } else {
3485 wgpu::LoadOp::Clear(1.0)
3486 },
3487 store: wgpu::StoreOp::Store,
3488 }),
3489 stencil_ops: None,
3490 });
3491
3492 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
3494 label: Some("unified-render-pass"),
3495 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
3496 view: surface_view,
3497 resolve_target: None,
3498 ops: wgpu::Operations {
3499 load: if preserve_surface {
3500 wgpu::LoadOp::Load
3501 } else {
3502 wgpu::LoadOp::Clear(clear)
3503 },
3504 store: wgpu::StoreOp::Store,
3505 },
3506 })],
3507 depth_stencil_attachment: depth_attachment,
3508 occlusion_query_set: None,
3509 timestamp_writes: None,
3510 });
3511
3512 self.solid_direct.record(&mut pass, &vp_bg, scene);
3514
3515 #[derive(Clone, Copy)]
3521 enum DrawItem {
3522 TransparentBatch(usize),
3523 TextGroup(usize),
3524 Image(usize),
3525 Svg(usize),
3526 ExternalTexture(usize),
3527 }
3528 let mut all_items: Vec<(i32, DrawItem)> = Vec::new();
3529 for (i, batch) in transparent_batches.iter().enumerate() {
3530 all_items.push((batch.z, DrawItem::TransparentBatch(i)));
3531 }
3532 for (i, (z, _, _, _, _, _)) in text_groups.iter().enumerate() {
3533 all_items.push((*z, DrawItem::TextGroup(i)));
3534 }
3535 for (i, z) in image_z_vals.iter().enumerate() {
3536 all_items.push((*z, DrawItem::Image(i)));
3537 }
3538 for (i, z) in svg_z_vals.iter().enumerate() {
3539 all_items.push((*z, DrawItem::Svg(i)));
3540 }
3541 for (i, z) in ext_z_vals.iter().enumerate() {
3542 all_items.push((*z, DrawItem::ExternalTexture(i)));
3543 }
3544 all_items.sort_by_key(|(z, _)| *z);
3546
3547 for &(_, item) in all_items.iter() {
3548 match item {
3549 DrawItem::TransparentBatch(i) => {
3550 let batch = &transparent_batches[i];
3551 self.transparent_solid_direct.record_index_range(
3552 &mut pass,
3553 &vp_bg,
3554 transparent_scene,
3555 batch.index_start,
3556 batch.index_count,
3557 );
3558 }
3559 DrawItem::TextGroup(i) => {
3560 let (_z, vbuf, ibuf, index_count, z_bg, _z_buf) = &text_groups[i];
3561 if *index_count > 0 {
3562 pass.set_pipeline(&self.text.pipeline);
3563 pass.set_bind_group(0, &vp_bg_text, &[]);
3564 pass.set_bind_group(1, z_bg, &[]);
3565 pass.set_bind_group(2, &self.text_bind_group, &[]);
3566 pass.set_vertex_buffer(0, vbuf.slice(..));
3567 pass.set_index_buffer(ibuf.slice(..), wgpu::IndexFormat::Uint16);
3568 pass.draw_indexed(0..*index_count, 0, 0..1);
3569 }
3570 }
3571 DrawItem::Image(i) => {
3572 let (vbuf, ibuf, vp_bg_img, z_bg_img, tex_bg, params_bg, _, _, clip) =
3573 &image_resources[i];
3574 if let Some(c) = clip {
3575 pass.set_scissor_rect(
3576 c.x.max(0.0) as u32,
3577 c.y.max(0.0) as u32,
3578 (c.w.max(1.0) as u32)
3579 .min(width.saturating_sub(c.x.max(0.0) as u32)),
3580 (c.h.max(1.0) as u32)
3581 .min(height.saturating_sub(c.y.max(0.0) as u32)),
3582 );
3583 }
3584 self.image.record(
3585 &mut pass, vp_bg_img, z_bg_img, tex_bg, params_bg, vbuf, ibuf, 6,
3586 );
3587 if clip.is_some() {
3588 pass.set_scissor_rect(0, 0, width, height);
3589 }
3590 }
3591 DrawItem::Svg(i) => {
3592 let (vbuf, ibuf, vp_bg_svg, z_bg_svg, tex_bg, params_bg, _, _, clip) =
3593 &svg_resources[i];
3594 if let Some(c) = clip {
3595 pass.set_scissor_rect(
3596 c.x.max(0.0) as u32,
3597 c.y.max(0.0) as u32,
3598 (c.w.max(1.0) as u32)
3599 .min(width.saturating_sub(c.x.max(0.0) as u32)),
3600 (c.h.max(1.0) as u32)
3601 .min(height.saturating_sub(c.y.max(0.0) as u32)),
3602 );
3603 }
3604 self.image.record(
3605 &mut pass, vp_bg_svg, z_bg_svg, tex_bg, params_bg, vbuf, ibuf, 6,
3606 );
3607 if clip.is_some() {
3608 pass.set_scissor_rect(0, 0, width, height);
3609 }
3610 }
3611 DrawItem::ExternalTexture(i) => {
3612 let (vbuf, ibuf, vp_bg_ext, z_bg_ext, tex_bg, params_bg, _, _) =
3613 &ext_resources[i];
3614 self.image.record(
3615 &mut pass, vp_bg_ext, z_bg_ext, tex_bg, params_bg, vbuf, ibuf, 6,
3616 );
3617 }
3618 }
3619 }
3620
3621 drop(pass);
3623 return;
3625 }
3626
3627 let targets = self.alloc_targets(allocator, width.max(1), height.max(1));
3629
3630 let mut image_views_off: Vec<(
3632 wgpu::TextureView,
3633 [f32; 2],
3634 [f32; 2],
3635 f32,
3636 Option<crate::Rect>,
3637 )> = Vec::new();
3638 for (path, origin, size, z, clip) in image_draws.iter() {
3640 let tex_opt = if let Some(view) = self.try_get_image_view(std::path::Path::new(path)) {
3642 Some(view)
3643 } else {
3644 self.load_image_to_view(std::path::Path::new(path), queue)
3645 };
3646 if let Some((tex_view, _w, _h)) = tex_opt {
3647 image_views_off.push((tex_view, *origin, *size, *z as f32, *clip));
3648 }
3649 }
3650
3651 let mut svg_views_off: Vec<(
3653 wgpu::TextureView,
3654 [f32; 2],
3655 [f32; 2],
3656 f32,
3657 Option<crate::Rect>,
3658 )> = Vec::new();
3659 for (path, origin, max_size, style, _z, transform, clip) in svg_draws.iter() {
3660 if let Some((_view, w, h)) =
3661 self.rasterize_svg_to_view(std::path::Path::new(path), 1.0, *style, queue)
3662 {
3663 let base_w = w.max(1) as f32;
3664 let base_h = h.max(1) as f32;
3665 let scale = (max_size[0] / base_w).min(max_size[1] / base_h).max(0.0);
3666
3667 if let Some((view_scaled, _sw, _sh)) =
3668 self.rasterize_svg_to_view(std::path::Path::new(path), scale, *style, queue)
3669 {
3670 let draw_w = base_w * scale;
3672 let draw_h = base_h * scale;
3673 let transformed_origin = apply_transform_to_point(*origin, *transform);
3674 svg_views_off.push((
3675 view_scaled,
3676 transformed_origin,
3677 [draw_w, draw_h],
3678 *_z as f32,
3679 *clip,
3680 ));
3681 }
3682 }
3683 }
3684
3685 let mut text_by_z_off: std::collections::HashMap<
3687 i32,
3688 Vec<(
3689 usize,
3690 [f32; 2],
3691 &crate::text::RasterizedGlyph,
3692 &crate::ColorLinPremul,
3693 )>,
3694 > = std::collections::HashMap::new();
3695 for (idx, (origin, glyph, color, z)) in glyph_draws.iter().enumerate() {
3696 text_by_z_off
3697 .entry(*z)
3698 .or_insert_with(Vec::new)
3699 .push((idx, *origin, glyph, color));
3700 }
3701
3702 let mut text_groups_off = if !glyph_draws.is_empty() {
3704 if self.prev_atlas_max_x > 0 && self.prev_atlas_max_y > 0 {
3707 let clear_width = self.prev_atlas_max_x.min(4096);
3708 let clear_height = self.prev_atlas_max_y.min(4096);
3709 let clear_size = (clear_width * clear_height * 4) as usize;
3710 let clear_data = vec![0u8; clear_size];
3711 queue.write_texture(
3712 wgpu::ImageCopyTexture {
3713 texture: &self.text_mask_atlas,
3714 mip_level: 0,
3715 origin: wgpu::Origin3d { x: 0, y: 0, z: 0 },
3716 aspect: wgpu::TextureAspect::All,
3717 },
3718 &clear_data,
3719 wgpu::ImageDataLayout {
3720 offset: 0,
3721 bytes_per_row: Some(clear_width * 4),
3722 rows_per_image: Some(clear_height),
3723 },
3724 wgpu::Extent3d {
3725 width: clear_width,
3726 height: clear_height,
3727 depth_or_array_layers: 1,
3728 },
3729 );
3730 }
3731
3732 let mut atlas_cursor_x = 0u32;
3733 let mut atlas_cursor_y = 0u32;
3734 let mut next_row_height = 0u32;
3735 let mut atlas_max_x = 0u32;
3736 let mut atlas_max_y = 0u32;
3737 let mut all_text_groups: Vec<(i32, Vec<TextQuadVtx>)> = Vec::new();
3738
3739 for (z_index, glyphs) in text_by_z_off.iter() {
3741 let mut vertices: Vec<TextQuadVtx> = Vec::new();
3742 let force_grayscale = transparent_text_z.contains(z_index);
3743
3744 for (_idx, origin, glyph, color) in glyphs.iter() {
3745 let (w, h, data) = glyph_mask_for_atlas(&glyph.mask, force_grayscale);
3746
3747 if atlas_cursor_x + w >= 4096 {
3748 atlas_cursor_x = 0;
3749 atlas_cursor_y += next_row_height;
3750 next_row_height = 0;
3751 }
3752 next_row_height = next_row_height.max(h);
3753
3754 atlas_max_x = atlas_max_x.max(atlas_cursor_x + w);
3756 atlas_max_y = atlas_max_y.max(atlas_cursor_y + h);
3757
3758 queue.write_texture(
3759 wgpu::ImageCopyTexture {
3760 texture: &self.text_mask_atlas,
3761 mip_level: 0,
3762 origin: wgpu::Origin3d {
3763 x: atlas_cursor_x,
3764 y: atlas_cursor_y,
3765 z: 0,
3766 },
3767 aspect: wgpu::TextureAspect::All,
3768 },
3769 data.as_ref(),
3770 wgpu::ImageDataLayout {
3771 offset: 0,
3772 bytes_per_row: Some(w * 4),
3773 rows_per_image: Some(h),
3774 },
3775 wgpu::Extent3d {
3776 width: w,
3777 height: h,
3778 depth_or_array_layers: 1,
3779 },
3780 );
3781
3782 let u0 = atlas_cursor_x as f32 / 4096.0;
3783 let v0 = atlas_cursor_y as f32 / 4096.0;
3784 let u1 = (atlas_cursor_x + w) as f32 / 4096.0;
3785 let v1 = (atlas_cursor_y + h) as f32 / 4096.0;
3786
3787 let quad_w = (w as f32) * inv_logical;
3791 let quad_h = (h as f32) * inv_logical;
3792
3793 vertices.extend_from_slice(&[
3794 TextQuadVtx {
3795 pos: [origin[0], origin[1]],
3796 uv: [u0, v0],
3797 color: [color.r, color.g, color.b, color.a],
3798 },
3799 TextQuadVtx {
3800 pos: [origin[0] + quad_w, origin[1]],
3801 uv: [u1, v0],
3802 color: [color.r, color.g, color.b, color.a],
3803 },
3804 TextQuadVtx {
3805 pos: [origin[0] + quad_w, origin[1] + quad_h],
3806 uv: [u1, v1],
3807 color: [color.r, color.g, color.b, color.a],
3808 },
3809 TextQuadVtx {
3810 pos: [origin[0], origin[1] + quad_h],
3811 uv: [u0, v1],
3812 color: [color.r, color.g, color.b, color.a],
3813 },
3814 ]);
3815
3816 atlas_cursor_x += w;
3817 }
3818
3819 if !vertices.is_empty() {
3821 all_text_groups.push((*z_index, vertices));
3822 }
3823 }
3824
3825 let mut text_resources: Vec<(
3827 i32,
3828 wgpu::Buffer,
3829 wgpu::Buffer,
3830 u32,
3831 wgpu::BindGroup,
3832 wgpu::Buffer,
3833 )> = Vec::new();
3834 for (z_index, vertices) in all_text_groups {
3835 let quad_count = vertices.len() / 4;
3836 let mut indices: Vec<u16> = Vec::with_capacity(quad_count * 6);
3837 for i in 0..quad_count {
3838 let base = (i * 4) as u16;
3839 indices.extend_from_slice(&[
3840 base,
3841 base + 1,
3842 base + 2,
3843 base,
3844 base + 2,
3845 base + 3,
3846 ]);
3847 }
3848
3849 let vbuf = self.device.create_buffer(&wgpu::BufferDescriptor {
3851 label: Some("text-vertex-buffer-group-off"),
3852 size: (vertices.len() * std::mem::size_of::<TextQuadVtx>()) as u64,
3853 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
3854 mapped_at_creation: false,
3855 });
3856
3857 let ibuf = self.device.create_buffer(&wgpu::BufferDescriptor {
3859 label: Some("text-index-buffer-group-off"),
3860 size: (indices.len() * std::mem::size_of::<u16>()) as u64,
3861 usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
3862 mapped_at_creation: false,
3863 });
3864
3865 queue.write_buffer(&vbuf, 0, bytemuck::cast_slice(&vertices));
3866 queue.write_buffer(&ibuf, 0, bytemuck::cast_slice(&indices));
3867
3868 let (z_bg, z_buf) = self.create_group_z_bind_group(z_index as f32, queue);
3871
3872 text_resources.push((z_index, vbuf, ibuf, indices.len() as u32, z_bg, z_buf));
3873 }
3874
3875 self.prev_atlas_max_x = atlas_max_x;
3877 self.prev_atlas_max_y = atlas_max_y;
3878
3879 text_resources
3880 } else {
3881 Vec::new()
3882 };
3883
3884 text_groups_off.sort_by_key(|(z, _, _, _, _, _)| *z);
3886
3887 let vp_bg_text_off = self
3889 .text_offscreen
3890 .vp_bind_group(&self.device, &self.vp_buffer);
3891
3892 let mut image_z_vals_off: Vec<i32> = Vec::new();
3894 let mut image_resources_off: Vec<(
3895 wgpu::Buffer,
3896 wgpu::Buffer,
3897 wgpu::BindGroup,
3898 wgpu::BindGroup,
3899 wgpu::BindGroup,
3900 wgpu::BindGroup,
3901 wgpu::Buffer,
3902 wgpu::Buffer,
3903 Option<crate::Rect>,
3904 )> = Vec::new();
3905 for (tex_view, origin, size, z_val, clip) in image_views_off.iter() {
3906 let verts = [
3907 ImageQuadVtx {
3908 pos: [origin[0], origin[1]],
3909 uv: [0.0, 0.0],
3910 },
3911 ImageQuadVtx {
3912 pos: [origin[0] + size[0], origin[1]],
3913 uv: [1.0, 0.0],
3914 },
3915 ImageQuadVtx {
3916 pos: [origin[0] + size[0], origin[1] + size[1]],
3917 uv: [1.0, 1.0],
3918 },
3919 ImageQuadVtx {
3920 pos: [origin[0], origin[1] + size[1]],
3921 uv: [0.0, 1.0],
3922 },
3923 ];
3924 let idx: [u16; 6] = [0, 1, 2, 0, 2, 3];
3925
3926 let vbuf = self.device.create_buffer(&wgpu::BufferDescriptor {
3927 label: Some("image-vbuf-unified-offscreen"),
3928 size: (verts.len() * std::mem::size_of::<ImageQuadVtx>()) as u64,
3929 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
3930 mapped_at_creation: false,
3931 });
3932 let ibuf = self.device.create_buffer(&wgpu::BufferDescriptor {
3933 label: Some("image-ibuf-unified-offscreen"),
3934 size: (idx.len() * std::mem::size_of::<u16>()) as u64,
3935 usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
3936 mapped_at_creation: false,
3937 });
3938 queue.write_buffer(&vbuf, 0, bytemuck::cast_slice(&verts));
3939 queue.write_buffer(&ibuf, 0, bytemuck::cast_slice(&idx));
3940
3941 let vp_bg_img = self
3942 .image_offscreen
3943 .vp_bind_group(&self.device, &self.vp_buffer);
3944 let (z_bg_img, z_buf_img) = self.create_group_z_bind_group(*z_val as f32, queue);
3946 let tex_bg = self.image_offscreen.tex_bind_group(&self.device, tex_view);
3947 let (params_bg, params_buf) =
3948 self.image_offscreen
3949 .params_bind_group(&self.device, 1.0, false);
3950
3951 image_z_vals_off.push(*z_val as i32);
3952 image_resources_off.push((
3953 vbuf, ibuf, vp_bg_img, z_bg_img, tex_bg, params_bg, z_buf_img, params_buf, *clip,
3954 ));
3955 }
3956
3957 let mut svg_z_vals_off: Vec<i32> = Vec::new();
3959 let mut svg_resources_off: Vec<(
3960 wgpu::Buffer,
3961 wgpu::Buffer,
3962 wgpu::BindGroup,
3963 wgpu::BindGroup,
3964 wgpu::BindGroup,
3965 wgpu::BindGroup,
3966 wgpu::Buffer,
3967 wgpu::Buffer,
3968 Option<crate::Rect>,
3969 )> = Vec::new();
3970 for (view_scaled, origin, size, z_val, clip) in svg_views_off.iter() {
3971 let verts = [
3972 ImageQuadVtx {
3973 pos: [origin[0], origin[1]],
3974 uv: [0.0, 0.0],
3975 },
3976 ImageQuadVtx {
3977 pos: [origin[0] + size[0], origin[1]],
3978 uv: [1.0, 0.0],
3979 },
3980 ImageQuadVtx {
3981 pos: [origin[0] + size[0], origin[1] + size[1]],
3982 uv: [1.0, 1.0],
3983 },
3984 ImageQuadVtx {
3985 pos: [origin[0], origin[1] + size[1]],
3986 uv: [0.0, 1.0],
3987 },
3988 ];
3989 let idx: [u16; 6] = [0, 1, 2, 0, 2, 3];
3990
3991 let vbuf = self.device.create_buffer(&wgpu::BufferDescriptor {
3992 label: Some("svg-vbuf-unified-offscreen"),
3993 size: (verts.len() * std::mem::size_of::<ImageQuadVtx>()) as u64,
3994 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
3995 mapped_at_creation: false,
3996 });
3997 let ibuf = self.device.create_buffer(&wgpu::BufferDescriptor {
3998 label: Some("svg-ibuf-unified-offscreen"),
3999 size: (idx.len() * std::mem::size_of::<u16>()) as u64,
4000 usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
4001 mapped_at_creation: false,
4002 });
4003 queue.write_buffer(&vbuf, 0, bytemuck::cast_slice(&verts));
4004 queue.write_buffer(&ibuf, 0, bytemuck::cast_slice(&idx));
4005
4006 let vp_bg_svg = self
4007 .image_offscreen
4008 .vp_bind_group(&self.device, &self.vp_buffer);
4009 let (z_bg_svg, z_buf_svg) = self.create_group_z_bind_group(*z_val as f32, queue);
4011 let tex_bg = self
4012 .image_offscreen
4013 .tex_bind_group(&self.device, view_scaled);
4014 let (params_bg, params_buf) =
4015 self.image_offscreen
4016 .params_bind_group(&self.device, 1.0, false);
4017
4018 svg_z_vals_off.push(*z_val as i32);
4019 svg_resources_off.push((
4020 vbuf, ibuf, vp_bg_svg, z_bg_svg, tex_bg, params_bg, z_buf_svg, params_buf, *clip,
4021 ));
4022 }
4023
4024 let mut ext_z_vals_off: Vec<i32> = Vec::new();
4026 let mut ext_resources_off: Vec<(
4027 wgpu::Buffer,
4028 wgpu::Buffer,
4029 wgpu::BindGroup,
4030 wgpu::BindGroup,
4031 wgpu::BindGroup,
4032 wgpu::BindGroup,
4033 wgpu::Buffer,
4034 wgpu::Buffer,
4035 )> = Vec::new();
4036 for etd in external_texture_draws.iter() {
4037 let Some(tex_view) = self.external_textures.get(&etd.texture_id) else {
4038 continue;
4039 };
4040 let verts = [
4041 ImageQuadVtx {
4042 pos: [etd.origin[0], etd.origin[1]],
4043 uv: [0.0, 0.0],
4044 },
4045 ImageQuadVtx {
4046 pos: [etd.origin[0] + etd.size[0], etd.origin[1]],
4047 uv: [1.0, 0.0],
4048 },
4049 ImageQuadVtx {
4050 pos: [etd.origin[0] + etd.size[0], etd.origin[1] + etd.size[1]],
4051 uv: [1.0, 1.0],
4052 },
4053 ImageQuadVtx {
4054 pos: [etd.origin[0], etd.origin[1] + etd.size[1]],
4055 uv: [0.0, 1.0],
4056 },
4057 ];
4058 let idx: [u16; 6] = [0, 1, 2, 0, 2, 3];
4059
4060 let vbuf = self.device.create_buffer(&wgpu::BufferDescriptor {
4061 label: Some("ext-tex-vbuf-unified-offscreen"),
4062 size: (verts.len() * std::mem::size_of::<ImageQuadVtx>()) as u64,
4063 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
4064 mapped_at_creation: false,
4065 });
4066 let ibuf = self.device.create_buffer(&wgpu::BufferDescriptor {
4067 label: Some("ext-tex-ibuf-unified-offscreen"),
4068 size: (idx.len() * std::mem::size_of::<u16>()) as u64,
4069 usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
4070 mapped_at_creation: false,
4071 });
4072 queue.write_buffer(&vbuf, 0, bytemuck::cast_slice(&verts));
4073 queue.write_buffer(&ibuf, 0, bytemuck::cast_slice(&idx));
4074
4075 let vp_bg_ext = self
4076 .image_offscreen
4077 .vp_bind_group(&self.device, &self.vp_buffer);
4078 let (z_bg_ext, z_buf_ext) = self.create_group_z_bind_group(etd.z as f32, queue);
4079 let tex_bg = self.image_offscreen.tex_bind_group(&self.device, tex_view);
4080 let (params_bg, params_buf) = self.image_offscreen.params_bind_group(
4081 &self.device,
4082 etd.opacity,
4083 etd.premultiplied,
4084 );
4085
4086 ext_z_vals_off.push(etd.z);
4087 ext_resources_off.push((
4088 vbuf, ibuf, vp_bg_ext, z_bg_ext, tex_bg, params_bg, z_buf_ext, params_buf,
4089 ));
4090 }
4091
4092 let depth_attachment = Some(wgpu::RenderPassDepthStencilAttachment {
4093 view: self.depth_view(),
4094 depth_ops: Some(wgpu::Operations {
4095 load: wgpu::LoadOp::Clear(1.0),
4096 store: wgpu::StoreOp::Store,
4097 }),
4098 stencil_ops: None,
4099 });
4100
4101 let _z_bg = self.create_z_bind_group(0.0, queue);
4102
4103 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
4104 label: Some("unified-offscreen-pass"),
4105 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
4106 view: &targets.color.view,
4107 resolve_target: None,
4108 ops: wgpu::Operations {
4109 load: wgpu::LoadOp::Clear(clear),
4110 store: wgpu::StoreOp::Store,
4111 },
4112 })],
4113 depth_stencil_attachment: depth_attachment,
4114 occlusion_query_set: None,
4115 timestamp_writes: None,
4116 });
4117
4118 self.solid_offscreen.record(&mut pass, &vp_bg_off, scene);
4121
4122 {
4125 #[derive(Clone, Copy)]
4126 enum DrawItemOff {
4127 TransparentBatch(usize),
4128 TextGroup(usize),
4129 Image(usize),
4130 Svg(usize),
4131 ExternalTexture(usize),
4132 }
4133 let mut all_items: Vec<(i32, DrawItemOff)> = Vec::new();
4134 for (i, batch) in transparent_batches.iter().enumerate() {
4135 all_items.push((batch.z, DrawItemOff::TransparentBatch(i)));
4136 }
4137 for (i, (z, _, _, _, _, _)) in text_groups_off.iter().enumerate() {
4138 all_items.push((*z, DrawItemOff::TextGroup(i)));
4139 }
4140 for (i, z) in image_z_vals_off.iter().enumerate() {
4141 all_items.push((*z, DrawItemOff::Image(i)));
4142 }
4143 for (i, z) in svg_z_vals_off.iter().enumerate() {
4144 all_items.push((*z, DrawItemOff::Svg(i)));
4145 }
4146 for (i, z) in ext_z_vals_off.iter().enumerate() {
4147 all_items.push((*z, DrawItemOff::ExternalTexture(i)));
4148 }
4149 all_items.sort_by_key(|(z, _)| *z);
4150
4151 for &(_, item) in all_items.iter() {
4152 match item {
4153 DrawItemOff::TransparentBatch(i) => {
4154 let batch = &transparent_batches[i];
4155 self.transparent_solid_offscreen.record_index_range(
4156 &mut pass,
4157 &vp_bg_off,
4158 transparent_scene,
4159 batch.index_start,
4160 batch.index_count,
4161 );
4162 }
4163 DrawItemOff::TextGroup(i) => {
4164 let (_z, vbuf, ibuf, index_count, z_bg, _z_buf) = &text_groups_off[i];
4165 if *index_count > 0 {
4166 pass.set_pipeline(&self.text_offscreen.pipeline);
4167 pass.set_bind_group(0, &vp_bg_text_off, &[]);
4168 pass.set_bind_group(1, z_bg, &[]);
4169 pass.set_bind_group(2, &self.text_bind_group, &[]);
4170 pass.set_vertex_buffer(0, vbuf.slice(..));
4171 pass.set_index_buffer(ibuf.slice(..), wgpu::IndexFormat::Uint16);
4172 pass.draw_indexed(0..*index_count, 0, 0..1);
4173 }
4174 }
4175 DrawItemOff::Image(i) => {
4176 let (vbuf, ibuf, vp_bg_img, z_bg_img, tex_bg, params_bg, _, _, clip) =
4177 &image_resources_off[i];
4178 if let Some(c) = clip {
4179 pass.set_scissor_rect(
4180 c.x.max(0.0) as u32,
4181 c.y.max(0.0) as u32,
4182 (c.w.max(1.0) as u32)
4183 .min(width.saturating_sub(c.x.max(0.0) as u32)),
4184 (c.h.max(1.0) as u32)
4185 .min(height.saturating_sub(c.y.max(0.0) as u32)),
4186 );
4187 }
4188 self.image_offscreen.record(
4189 &mut pass, vp_bg_img, z_bg_img, tex_bg, params_bg, vbuf, ibuf, 6,
4190 );
4191 if clip.is_some() {
4192 pass.set_scissor_rect(0, 0, width, height);
4193 }
4194 }
4195 DrawItemOff::Svg(i) => {
4196 let (vbuf, ibuf, vp_bg_svg, z_bg_svg, tex_bg, params_bg, _, _, clip) =
4197 &svg_resources_off[i];
4198 if let Some(c) = clip {
4199 pass.set_scissor_rect(
4200 c.x.max(0.0) as u32,
4201 c.y.max(0.0) as u32,
4202 (c.w.max(1.0) as u32)
4203 .min(width.saturating_sub(c.x.max(0.0) as u32)),
4204 (c.h.max(1.0) as u32)
4205 .min(height.saturating_sub(c.y.max(0.0) as u32)),
4206 );
4207 }
4208 self.image_offscreen.record(
4209 &mut pass, vp_bg_svg, z_bg_svg, tex_bg, params_bg, vbuf, ibuf, 6,
4210 );
4211 if clip.is_some() {
4212 pass.set_scissor_rect(0, 0, width, height);
4213 }
4214 }
4215 DrawItemOff::ExternalTexture(i) => {
4216 let (vbuf, ibuf, vp_bg_ext, z_bg_ext, tex_bg, params_bg, _, _) =
4217 &ext_resources_off[i];
4218 self.image_offscreen.record(
4219 &mut pass, vp_bg_ext, z_bg_ext, tex_bg, params_bg, vbuf, ibuf, 6,
4220 );
4221 }
4222 }
4223 }
4224 }
4225
4226 drop(pass);
4228
4229 self.composite_to_surface(encoder, surface_view, &targets, Some(clear));
4231 allocator.release_texture(targets.color);
4232 }
4233}