1mod coverage_cache;
3#[cfg(all(unix, feature = "dma-buf"))]
4pub mod dma_buf;
5use anyhow::{Result, ensure};
6pub(crate) use coverage_cache::{CoverageCache, CoverageKey};
7use gpui::{CustomDraw, DevicePixels, Size, size};
8use std::sync::{
9 Arc, Mutex,
10 atomic::{AtomicU8, Ordering},
11};
12#[cfg(test)]
13use wgpu::util::DeviceExt;
14
15const PENDING: u8 = 0;
16const SUBMITTED: u8 = 1;
17const ABANDONED: u8 = 2;
18const STAGED: u8 = 3;
19
20struct Recording {
21 guards: Mutex<Vec<ImageCopyCompletion>>,
22 state: AtomicU8,
23 batch: Option<Arc<()>>,
24}
25impl Recording {
26 fn new(batch: Option<Arc<()>>) -> Self {
27 Self {
28 guards: Mutex::new(Vec::new()),
29 state: AtomicU8::new(PENDING),
30 batch,
31 }
32 }
33 fn can_reuse(self: &Arc<Self>, output: &Arc<Self>) -> bool {
34 match output.state.load(Ordering::Acquire) {
35 SUBMITTED => true,
36 PENDING => Arc::ptr_eq(output, self),
37 STAGED => self
38 .batch
39 .as_ref()
40 .zip(output.batch.as_ref())
41 .is_some_and(|(a, b)| Arc::ptr_eq(a, b)),
42 _ => false,
43 }
44 }
45 fn abandon(&self, from: u8) {
46 if self
47 .state
48 .compare_exchange(from, ABANDONED, Ordering::AcqRel, Ordering::Acquire)
49 .is_ok()
50 {
51 self.guards.lock().unwrap().clear();
52 }
53 }
54}
55
56impl std::fmt::Debug for Recording {
57 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58 f.debug_struct("Recording")
59 .field("state", &self.state)
60 .finish()
61 }
62}
63fn release_after_completion(queue: &wgpu::Queue, recording: &Recording) {
64 let guards = std::mem::take(&mut *recording.guards.lock().unwrap());
65 if !guards.is_empty() {
66 #[cfg(not(target_family = "wasm"))]
67 queue.on_submitted_work_done(move || drop(guards));
68 #[cfg(target_family = "wasm")]
69 browser_completions::retain(queue, guards);
70 }
71}
72
73#[cfg(any(target_family = "wasm", test))]
77mod browser_completions {
78 use std::sync::{
79 Arc,
80 atomic::{AtomicBool, Ordering},
81 };
82
83 pub(super) struct Pending<T> {
84 pub(super) done: Arc<AtomicBool>,
85 pub(super) value: T,
86 }
87
88 pub(super) fn take_completed<T>(pending: &mut Vec<Pending<T>>) -> Vec<T> {
89 let mut completed = Vec::new();
90 let mut i = 0;
91 while i < pending.len() {
92 if pending[i].done.load(Ordering::Acquire) {
93 completed.push(pending.swap_remove(i).value);
94 } else {
95 i += 1;
96 }
97 }
98 completed
99 }
100
101 #[cfg(target_family = "wasm")]
102 thread_local! {
103 static PENDING: std::cell::RefCell<Vec<Pending<Vec<super::ImageCopyCompletion>>>> =
104 const { std::cell::RefCell::new(Vec::new()) };
105 }
106
107 #[cfg(target_family = "wasm")]
108 pub(super) fn collect() {
109 let completed = PENDING.with(|pending| take_completed(&mut pending.borrow_mut()));
111 drop(completed);
112 }
113
114 #[cfg(target_family = "wasm")]
115 pub(super) fn retain(queue: &wgpu::Queue, guards: Vec<super::ImageCopyCompletion>) {
116 let done = Arc::new(AtomicBool::new(false));
117 PENDING.with(|pending| {
118 pending.borrow_mut().push(Pending {
119 done: done.clone(),
120 value: guards,
121 })
122 });
123 queue.on_submitted_work_done(move || {
124 done.store(true, Ordering::Release);
125 collect();
128 });
129 }
130}
131
132struct EncoderState {
133 encoder: Option<wgpu::CommandEncoder>,
134 finished: bool,
135 failed: bool,
136}
137
138pub(crate) struct PreparationBatch {
141 device: Arc<wgpu::Device>,
142 queue: Arc<wgpu::Queue>,
143 id: Arc<()>,
144 pending: Vec<(Option<wgpu::CommandBuffer>, Arc<Recording>)>,
145}
146impl PreparationBatch {
147 pub fn new(device: Arc<wgpu::Device>, queue: &Arc<wgpu::Queue>) -> Self {
148 Self {
149 device,
150 queue: queue.clone(),
151 id: Arc::new(()),
152 pending: Vec::new(),
153 }
154 }
155 pub fn prepare<T>(
157 &mut self,
158 prepare: impl FnOnce(&WgpuCustomContext) -> Result<T>,
159 ) -> Result<T> {
160 let context =
161 WgpuCustomContext::with_batch(self.device.clone(), &self.queue, Some(self.id.clone()));
162 let output = prepare(&context)?;
163 let command = context.finish()?;
164 context.recording.state.store(STAGED, Ordering::Release);
165 self.pending.push((command, context.recording.clone()));
166 Ok(output)
167 }
168 pub fn submit(mut self) -> Result<()> {
169 if self.pending.iter().any(|(command, _)| command.is_some()) {
170 self.queue.submit(
171 self.pending
172 .iter_mut()
173 .filter_map(|(command, _)| command.take()),
174 );
175 }
176 for (_, recording) in &self.pending {
177 recording.state.store(SUBMITTED, Ordering::Release);
178 release_after_completion(&self.queue, recording);
179 }
180 Ok(())
181 }
182}
183impl Drop for PreparationBatch {
184 fn drop(&mut self) {
185 for (_, recording) in &self.pending {
186 recording.abandon(STAGED);
187 }
188 }
189}
190
191pub struct WgpuCustomContext {
193 pub device: Arc<wgpu::Device>,
195 key: usize,
196 queue: Arc<wgpu::Queue>,
197 encoder: Mutex<EncoderState>,
198 recording: Arc<Recording>,
199}
200
201impl WgpuCustomContext {
202 #[cfg(test)]
203 pub(crate) fn new(device: Arc<wgpu::Device>, queue: &Arc<wgpu::Queue>) -> Self {
204 Self::with_batch(device, queue, None)
205 }
206 fn with_batch(
207 device: Arc<wgpu::Device>,
208 queue: &Arc<wgpu::Queue>,
209 batch: Option<Arc<()>>,
210 ) -> Self {
211 #[cfg(target_family = "wasm")]
212 browser_completions::collect();
213 Self {
214 device,
215 key: Arc::as_ptr(queue) as usize,
216 queue: queue.clone(),
217 encoder: Mutex::new(EncoderState {
218 encoder: None,
219 finished: false,
220 failed: false,
221 }),
222 recording: Arc::new(Recording::new(batch)),
223 }
224 }
225 pub fn cache_key(&self) -> usize {
227 self.key
228 }
229 pub fn create_output(&self, extent: Size<DevicePixels>) -> Result<WgpuCustomOutput> {
231 ensure!(
232 self.recording.state.load(Ordering::Acquire) == PENDING,
233 "custom preparation is no longer writable"
234 );
235 let texture = new_texture(&self.device, extent, wgpu::TextureFormat::Rgba8Unorm)?;
236 Ok(WgpuCustomOutput {
237 texture,
238 source_sized: false,
239 key: self.key,
240 _queue: self.queue.clone(),
241 recording: self.recording.clone(),
242 })
243 }
244 pub fn image_upload_context(&self) -> WgpuImageUploadContext {
247 WgpuImageUploadContext {
248 device: self.device.clone(),
249 queue: self.queue.clone(),
250 }
251 }
252 pub fn create_image_output(&self, data: &gpui::ImageTextureData) -> Result<WgpuCustomOutput> {
255 ensure!(
256 self.recording.state.load(Ordering::Acquire) == PENDING,
257 "image preparation is no longer writable"
258 );
259 let staged = self.image_upload_context().stage(data)?;
260 self.adopt_image_upload(&staged)
261 }
262 pub fn adopt_image_upload(&self, staged: &WgpuStagedImage) -> Result<WgpuCustomOutput> {
265 self.adopt_image_upload_with_guard(staged, None)
266 }
267 pub fn adopt_image_upload_with_guard(
270 &self,
271 staged: &WgpuStagedImage,
272 guard: Option<Arc<dyn Send + Sync>>,
273 ) -> Result<WgpuCustomOutput> {
274 ensure!(
275 Arc::ptr_eq(&staged.queue, &self.queue),
276 "foreign staged image queue"
277 );
278 ensure!(
279 Arc::ptr_eq(&staged.device, &self.device),
280 "foreign staged image device"
281 );
282 self.record(|encoder| {
283 for (level, mip) in staged.mips.iter().enumerate() {
284 encoder.copy_buffer_to_texture(
285 wgpu::TexelCopyBufferInfo {
286 buffer: staged.allocation.resource(),
287 layout: wgpu::TexelCopyBufferLayout {
288 offset: mip.offset,
289 bytes_per_row: Some(mip.row),
290 rows_per_image: None,
291 },
292 },
293 wgpu::TexelCopyTextureInfo {
294 texture: &staged.texture,
295 mip_level: level as u32,
296 origin: wgpu::Origin3d::ZERO,
297 aspect: wgpu::TextureAspect::All,
298 },
299 wgpu::Extent3d {
300 width: mip.width,
301 height: mip.height,
302 depth_or_array_layers: 1,
303 },
304 );
305 }
306 Ok(())
307 })?;
308 self.recording
311 .guards
312 .lock()
313 .unwrap()
314 .push(ImageCopyCompletion {
315 _staging: staged.allocation.clone(),
316 _admission: guard,
317 });
318 Ok(WgpuCustomOutput {
319 texture: staged.texture.clone(),
320 source_sized: true,
321 key: self.key,
322 _queue: self.queue.clone(),
323 recording: self.recording.clone(),
324 })
325 }
326 pub fn record<T>(
329 &self,
330 encode: impl FnOnce(&mut wgpu::CommandEncoder) -> Result<T>,
331 ) -> Result<T> {
332 let mut state = self
333 .encoder
334 .lock()
335 .map_err(|_| anyhow::anyhow!("custom encoder poisoned"))?;
336 ensure!(
337 !state.finished && !state.failed,
338 "custom preparation already finished or failed"
339 );
340 let encoder = state.encoder.get_or_insert_with(|| {
341 self.device
342 .create_command_encoder(&wgpu::CommandEncoderDescriptor {
343 label: Some("custom_preparation"),
344 })
345 });
346 let result = encode(encoder);
347 if result.is_err() {
348 state.failed = true;
349 state.encoder.take();
350 self.recording.abandon(PENDING);
351 }
352 result
353 }
354 pub fn can_reuse(&self, output: &WgpuCustomOutput) -> bool {
356 output.key == self.key && self.recording.can_reuse(&output.recording)
357 }
358 fn finish(&self) -> Result<Option<wgpu::CommandBuffer>> {
359 let mut state = self
360 .encoder
361 .lock()
362 .map_err(|_| anyhow::anyhow!("custom encoder poisoned"))?;
363 ensure!(
364 !state.finished && !state.failed,
365 "custom preparation already finished or failed"
366 );
367 state.finished = true;
368 Ok(state.encoder.take().map(|encoder| encoder.finish()))
369 }
370 #[cfg(test)]
371 pub(crate) fn submit(&self) -> Result<()> {
372 if let Some(command) = self.finish()? {
373 self.queue.submit([command]);
374 }
375 self.recording.state.store(SUBMITTED, Ordering::Release);
376 release_after_completion(&self.queue, &self.recording);
377 Ok(())
378 }
379}
380
381impl Drop for WgpuCustomContext {
382 fn drop(&mut self) {
383 self.recording.abandon(PENDING);
384 }
385}
386
387#[derive(Clone)]
389pub struct WgpuImageUploadContext {
390 device: Arc<wgpu::Device>,
391 queue: Arc<wgpu::Queue>,
392}
393struct ImageCopyCompletion {
394 _staging: Arc<gpui::ImageStagingLease<wgpu::Buffer>>,
395 _admission: Option<Arc<dyn Send + Sync>>,
396}
397struct StagedMip {
398 offset: u64,
399 row: u32,
400 width: u32,
401 height: u32,
402}
403#[derive(Clone)]
406pub struct WgpuImageStagingPool {
407 device: Arc<wgpu::Device>,
408 queue: Arc<wgpu::Queue>,
409 buffers: Arc<gpui::ImageStagingPool<wgpu::Buffer>>,
410}
411impl WgpuImageStagingPool {
412 pub fn stats(&self) -> gpui::ImageStagingPoolStats {
413 self.buffers.stats()
414 }
415}
416pub struct WgpuStagedImage {
418 device: Arc<wgpu::Device>,
419 queue: Arc<wgpu::Queue>,
420 texture: wgpu::Texture,
421 allocation: Arc<gpui::ImageStagingLease<wgpu::Buffer>>,
422 mips: Vec<StagedMip>,
423}
424impl WgpuImageUploadContext {
425 pub fn staging_pool(&self, capacity: usize) -> WgpuImageStagingPool {
426 WgpuImageStagingPool {
427 device: self.device.clone(),
428 queue: self.queue.clone(),
429 buffers: gpui::ImageStagingPool::new(capacity),
430 }
431 }
432 pub fn stage(&self, data: &gpui::ImageTextureData) -> Result<WgpuStagedImage> {
434 self.stage_with_pool(data, &self.staging_pool(data.staging_byte_len()))
435 }
436 pub fn stage_with_pool(
439 &self,
440 data: &gpui::ImageTextureData,
441 pool: &WgpuImageStagingPool,
442 ) -> Result<WgpuStagedImage> {
443 ensure!(
444 Arc::ptr_eq(&self.device, &pool.device) && Arc::ptr_eq(&self.queue, &pool.queue),
445 "foreign staging pool"
446 );
447 ensure!(
448 data.width().max(data.height()) <= self.device.limits().max_texture_dimension_2d,
449 "image exceeds device texture limits"
450 );
451 #[cfg(target_family = "wasm")]
452 browser_completions::collect();
453 let bytes = data.staging_byte_len();
454 ensure!(
455 bytes as u64 <= self.device.limits().max_buffer_size,
456 "image staging exceeds device buffer limit"
457 );
458 let allocation = pool.buffers.checkout(bytes as u64, bytes, || {
459 Ok(self.device.create_buffer(&wgpu::BufferDescriptor {
460 label: Some("media_staging_pool"),
461 size: bytes as u64,
462 usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC,
463 mapped_at_creation: true,
464 }))
465 })?;
466 let fill = (|| -> Result<Vec<StagedMip>> {
467 let buffer = allocation.resource();
468 if allocation.was_reused() {
469 let (sender, receiver) = std::sync::mpsc::channel();
470 buffer
471 .slice(..)
472 .map_async(wgpu::MapMode::Write, move |result| {
473 let _ = sender.send(result);
474 });
475 self.device.poll(wgpu::PollType::Wait {
476 submission_index: None,
477 timeout: Some(std::time::Duration::from_secs(5)),
478 })?;
479 receiver.recv_timeout(std::time::Duration::from_secs(5))??;
480 }
481 let mut mapped = buffer.slice(..).get_mapped_range_mut()?;
482 let mut mips = Vec::new();
483 let mut offset = 0usize;
484 for (level, pixels) in data.levels().iter().enumerate() {
485 let width = (data.width() >> level).max(1);
486 let height = (data.height() >> level).max(1);
487 let bpp = data.bytes_per_pixel();
488 let row = (width * bpp as u32).div_ceil(256) * 256;
489 for y in 0..height as usize {
490 mapped
491 .slice(
492 offset + y * row as usize
493 ..offset + y * row as usize + width as usize * bpp,
494 )
495 .copy_from_slice(
496 &pixels[y * width as usize * bpp..(y + 1) * width as usize * bpp],
497 );
498 }
499 mips.push(StagedMip {
500 offset: offset as u64,
501 row,
502 width,
503 height,
504 });
505 offset += row as usize * height as usize;
506 }
507 drop(mapped);
508 buffer.unmap();
509 Ok(mips)
510 })();
511 let mips = match fill {
512 Ok(mips) => mips,
513 Err(error) => {
514 allocation.resource().unmap();
515 allocation.discard();
516 return Err(error);
517 }
518 };
519 let texture = self.device.create_texture(&wgpu::TextureDescriptor {
520 label: Some("media_image"),
521 size: wgpu::Extent3d {
522 width: data.width(),
523 height: data.height(),
524 depth_or_array_layers: 1,
525 },
526 mip_level_count: data.levels().len() as u32,
527 sample_count: 1,
528 dimension: wgpu::TextureDimension::D2,
529 format: match data.format() {
530 gpui::ImageTextureFormat::Rgba8Srgb => wgpu::TextureFormat::Rgba8Unorm,
531 gpui::ImageTextureFormat::LinearP3Rgba16Float
532 | gpui::ImageTextureFormat::LinearRec2020Rgba16Float => {
533 wgpu::TextureFormat::Rgba16Float
534 }
535 },
536 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
537 view_formats: &[],
538 });
539 Ok(WgpuStagedImage {
540 device: self.device.clone(),
541 queue: self.queue.clone(),
542 texture,
543 allocation: Arc::new(allocation),
544 mips,
545 })
546 }
547}
548
549#[derive(Clone, Debug)]
551pub struct WgpuCustomOutput {
552 texture: wgpu::Texture,
553 source_sized: bool,
554 key: usize,
555 _queue: Arc<wgpu::Queue>,
556 recording: Arc<Recording>,
557}
558impl WgpuCustomOutput {
559 pub fn is_submitted(&self) -> bool {
561 self.recording.state.load(Ordering::Acquire) == SUBMITTED
562 }
563
564 pub fn texture(&self) -> &wgpu::Texture {
566 &self.texture
567 }
568 pub(crate) fn validate(
569 self,
570 context: &WgpuCustomContext,
571 extent: Size<DevicePixels>,
572 ) -> Result<Self> {
573 ensure!(
574 context.can_reuse(&self),
575 "custom output belongs to another queue or abandoned recording"
576 );
577 ensure!(
578 self.source_sized
579 || (self.texture.width() == extent.width.0 as u32
580 && self.texture.height() == extent.height.0 as u32),
581 "invalid custom texture extent"
582 );
583 Ok(self)
584 }
585}
586
587pub(crate) fn new_texture(
588 device: &wgpu::Device,
589 extent: Size<DevicePixels>,
590 format: wgpu::TextureFormat,
591) -> Result<wgpu::Texture> {
592 ensure!(
593 extent.width.0 > 0
594 && extent.height.0 > 0
595 && extent.width.0 as u32 <= device.limits().max_texture_dimension_2d
596 && extent.height.0 as u32 <= device.limits().max_texture_dimension_2d
597 && extent.width.0 as u64 * extent.height.0 as u64 * 4 <= 64 * 1024 * 1024,
598 "invalid custom texture extent"
599 );
600 Ok(device.create_texture(&wgpu::TextureDescriptor {
601 label: Some("custom_paint"),
602 size: wgpu::Extent3d {
603 width: extent.width.0 as u32,
604 height: extent.height.0 as u32,
605 depth_or_array_layers: 1,
606 },
607 mip_level_count: 1,
608 sample_count: 1,
609 dimension: wgpu::TextureDimension::D2,
610 format,
611 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
612 view_formats: &[],
613 }))
614}
615
616pub(crate) fn extent(draw: &CustomDraw) -> Result<Size<DevicePixels>> {
617 ensure!(
618 draw.opacity.is_finite() && (0.0..=1.0).contains(&draw.opacity),
619 "invalid custom opacity"
620 );
621 ensure!(
622 [
623 draw.bounds.origin.x.0,
624 draw.bounds.origin.y.0,
625 draw.bounds.size.width.0,
626 draw.bounds.size.height.0,
627 draw.content_mask.bounds.origin.x.0,
628 draw.content_mask.bounds.origin.y.0,
629 draw.content_mask.bounds.size.width.0,
630 draw.content_mask.bounds.size.height.0,
631 draw.corner_radii.top_left.0,
632 draw.corner_radii.top_right.0,
633 draw.corner_radii.bottom_right.0,
634 draw.corner_radii.bottom_left.0
635 ]
636 .iter()
637 .all(|v| v.is_finite()),
638 "non-finite custom geometry"
639 );
640 if let Some(source) = draw.content.image_source_size() {
641 ensure!(
642 draw.coverage.is_none()
643 && draw.bounds.size.width.0.is_finite()
644 && draw.bounds.size.height.0.is_finite()
645 && draw.bounds.size.width.0 > 0.
646 && draw.bounds.size.height.0 > 0.
647 && source.width.0 > 0
648 && source.height.0 > 0
649 && source.width.0 <= 16384
650 && source.height.0 <= 16384,
651 "invalid source-sized image preparation"
652 );
653 return Ok(source);
654 }
655 ensure!(
656 draw.bounds.size.width.0 > 0.
657 && draw.bounds.size.height.0 > 0.
658 && draw.bounds.size.width.0 <= 16384.
659 && draw.bounds.size.height.0 <= 16384.,
660 "invalid custom extent"
661 );
662 Ok(size(
663 DevicePixels(draw.bounds.size.width.0.ceil() as i32),
664 DevicePixels(draw.bounds.size.height.0.ceil() as i32),
665 ))
666}
667
668pub(crate) struct PreparedDraw {
669 pub bind_group: wgpu::BindGroup,
670 pub uniform_offset: u32,
671}
672
673const UNIFORM_BYTES: u64 = 144;
674struct UniformArena {
677 buffer: Option<wgpu::Buffer>,
678 bytes: Vec<u8>,
679 stride: u64,
680 capacity: u64,
681 cursor: usize,
682 #[cfg(test)]
683 creations: usize,
684}
685impl UniformArena {
686 fn new(device: &wgpu::Device) -> Self {
687 Self {
688 buffer: None,
689 bytes: Vec::new(),
690 stride: UNIFORM_BYTES
691 .next_multiple_of(device.limits().min_uniform_buffer_offset_alignment as u64),
692 capacity: 0,
693 cursor: 0,
694 #[cfg(test)]
695 creations: 0,
696 }
697 }
698 fn begin(&mut self, device: &wgpu::Device, count: usize) -> Result<()> {
699 let required = (count as u64)
700 .checked_mul(self.stride)
701 .ok_or_else(|| anyhow::anyhow!("custom uniform size overflow"))?;
702 let limit = device.limits().max_buffer_size.min(u32::MAX as u64);
703 ensure!(
704 required <= limit,
705 "custom uniforms exceed buffer/offset limits"
706 );
707 if required > self.capacity {
708 let capacity = required
709 .checked_next_power_of_two()
710 .unwrap_or(required)
711 .min(limit);
712 self.buffer = Some(device.create_buffer(&wgpu::BufferDescriptor {
713 label: Some("custom_params_arena"),
714 size: capacity,
715 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
716 mapped_at_creation: false,
717 }));
718 self.capacity = capacity;
719 #[cfg(test)]
720 {
721 self.creations += 1;
722 }
723 }
724 self.bytes.resize(required as usize, 0);
725 self.cursor = 0;
726 Ok(())
727 }
728 fn write(&mut self, params: &[[f32; 4]; 9]) -> u32 {
729 let offset = self.cursor * self.stride as usize;
730 self.cursor += 1;
731 self.bytes[offset..offset + UNIFORM_BYTES as usize]
732 .copy_from_slice(bytemuck::cast_slice(params));
733 offset as u32
734 }
735 fn upload(&self, queue: &wgpu::Queue) {
736 debug_assert_eq!(self.cursor as u64 * self.stride, self.bytes.len() as u64);
737 if !self.bytes.is_empty() {
738 queue.write_buffer(self.buffer.as_ref().expect("uniform arena"), 0, &self.bytes);
739 }
740 }
741}
742pub(crate) struct CustomCompositor {
743 pub pipeline: wgpu::RenderPipeline,
744 layout: wgpu::BindGroupLayout,
745 sampler: wgpu::Sampler,
746 uniforms: UniformArena,
747 pub masks: CoverageCache<wgpu::Texture>,
748 pub mask_bytes: usize,
749}
750impl CustomCompositor {
751 pub fn new(device: &wgpu::Device, format: wgpu::TextureFormat) -> Self {
752 let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
753 label: Some("custom_compositor"),
754 source: wgpu::ShaderSource::Wgsl(include_str!("custom_draw.wgsl").into()),
755 });
756 let entries = [
757 wgpu::BindGroupLayoutEntry {
758 binding: 0,
759 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
760 ty: wgpu::BindingType::Buffer {
761 ty: wgpu::BufferBindingType::Uniform,
762 has_dynamic_offset: true,
763 min_binding_size: wgpu::BufferSize::new(UNIFORM_BYTES),
764 },
765 count: None,
766 },
767 wgpu::BindGroupLayoutEntry {
768 binding: 1,
769 visibility: wgpu::ShaderStages::FRAGMENT,
770 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
771 count: None,
772 },
773 wgpu::BindGroupLayoutEntry {
774 binding: 2,
775 visibility: wgpu::ShaderStages::FRAGMENT,
776 ty: wgpu::BindingType::Texture {
777 sample_type: wgpu::TextureSampleType::Float { filterable: true },
778 view_dimension: wgpu::TextureViewDimension::D2,
779 multisampled: false,
780 },
781 count: None,
782 },
783 wgpu::BindGroupLayoutEntry {
784 binding: 3,
785 visibility: wgpu::ShaderStages::FRAGMENT,
786 ty: wgpu::BindingType::Texture {
787 sample_type: wgpu::TextureSampleType::Float { filterable: true },
788 view_dimension: wgpu::TextureViewDimension::D2,
789 multisampled: false,
790 },
791 count: None,
792 },
793 ];
794 let layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
795 label: Some("custom_paint"),
796 entries: &entries,
797 });
798 let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
799 label: Some("custom_paint"),
800 bind_group_layouts: &[Some(&layout)],
801 immediate_size: 0,
802 });
803 let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
804 label: Some("custom_paint"),
805 layout: Some(&pipeline_layout),
806 vertex: wgpu::VertexState {
807 module: &shader,
808 entry_point: Some("custom_vertex"),
809 buffers: &[],
810 compilation_options: Default::default(),
811 },
812 fragment: Some(wgpu::FragmentState {
813 module: &shader,
814 entry_point: Some("custom_fragment"),
815 targets: &[Some(wgpu::ColorTargetState {
816 format,
817 blend: Some(wgpu::BlendState::ALPHA_BLENDING),
818 write_mask: wgpu::ColorWrites::ALL,
819 })],
820 compilation_options: Default::default(),
821 }),
822 primitive: Default::default(),
823 depth_stencil: None,
824 multisample: Default::default(),
825 multiview_mask: None,
826 cache: None,
827 });
828 let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
829 label: Some("custom_paint"),
830 mag_filter: wgpu::FilterMode::Linear,
831 min_filter: wgpu::FilterMode::Linear,
832 mipmap_filter: wgpu::MipmapFilterMode::Linear,
833 ..Default::default()
834 });
835 Self {
836 pipeline,
837 layout,
838 sampler,
839 uniforms: UniformArena::new(device),
840 masks: Default::default(),
841 mask_bytes: 0,
842 }
843 }
844 pub(crate) fn begin_uniforms(&mut self, device: &wgpu::Device, count: usize) -> Result<()> {
845 self.uniforms.begin(device, count)
846 }
847 pub(crate) fn upload_uniforms(&self, queue: &wgpu::Queue) {
848 self.uniforms.upload(queue);
849 }
850 pub fn prepare(
851 &mut self,
852 device: &wgpu::Device,
853 draw: &CustomDraw,
854 source: &wgpu::Texture,
855 mask: Option<&wgpu::Texture>,
856 native: bool,
857 viewport: Size<DevicePixels>,
858 ) -> PreparedDraw {
859 let adjustments = draw.content.image_linear_adjustments();
860 let transform = adjustments
861 .map(|v| v.rows())
862 .unwrap_or_else(|| draw.content.image_color_transform());
863 let vignette = adjustments
864 .map(|v| v.vignette())
865 .unwrap_or([0., 0., 1., 1.]);
866 let linear = [
867 if adjustments.is_some() { 1. } else { 0. },
868 draw.content.image_color_mode().shader_value(),
869 if adjustments.is_some_and(|v| v.is_p3()) {
870 1.
871 } else {
872 0.
873 },
874 0.,
875 ];
876 let params: [[f32; 4]; 9] = [
877 [
878 draw.bounds.origin.x.0,
879 draw.bounds.origin.y.0,
880 draw.bounds.size.width.0,
881 draw.bounds.size.height.0,
882 ],
883 [
884 draw.content_mask.bounds.origin.x.0,
885 draw.content_mask.bounds.origin.y.0,
886 draw.content_mask.bounds.size.width.0,
887 draw.content_mask.bounds.size.height.0,
888 ],
889 [
890 draw.corner_radii.top_left.0,
891 draw.corner_radii.top_right.0,
892 draw.corner_radii.bottom_right.0,
893 draw.corner_radii.bottom_left.0,
894 ],
895 [
896 viewport.width.0 as f32,
897 viewport.height.0 as f32,
898 draw.opacity * draw.content.image_sample_opacity().clamp(0., 1.),
899 u32::from(mask.is_some()) as f32 + if native { 2. } else { 0. },
900 ],
901 transform[0],
902 transform[1],
903 transform[2],
904 vignette,
905 linear,
906 ];
907 let uniform_offset = self.uniforms.write(¶ms);
908 let uniform = self.uniforms.buffer.as_ref().expect("uniform arena");
909 let source = source.create_view(&Default::default());
910 let mask = mask
911 .map(|t| t.create_view(&Default::default()))
912 .unwrap_or_else(|| source.clone());
913 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
914 label: Some("custom_paint"),
915 layout: &self.layout,
916 entries: &[
917 wgpu::BindGroupEntry {
918 binding: 0,
919 resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
920 buffer: uniform,
921 offset: 0,
922 size: wgpu::BufferSize::new(UNIFORM_BYTES),
923 }),
924 },
925 wgpu::BindGroupEntry {
926 binding: 1,
927 resource: wgpu::BindingResource::Sampler(&self.sampler),
928 },
929 wgpu::BindGroupEntry {
930 binding: 2,
931 resource: wgpu::BindingResource::TextureView(&source),
932 },
933 wgpu::BindGroupEntry {
934 binding: 3,
935 resource: wgpu::BindingResource::TextureView(&mask),
936 },
937 ],
938 });
939 PreparedDraw {
940 bind_group,
941 uniform_offset,
942 }
943 }
944}
945
946pub(crate) fn coverage_scene(draw: &CustomDraw, extent: Size<DevicePixels>) -> Result<gpui::Scene> {
947 use gpui::*;
948 let mut scene = Scene::default();
949 let bounds = Bounds::new(
950 point(ScaledPixels(0.), ScaledPixels(0.)),
951 size(
952 ScaledPixels(extent.width.0 as f32),
953 ScaledPixels(extent.height.0 as f32),
954 ),
955 );
956 let mask = ContentMask { bounds };
957 match draw.coverage.as_ref().expect("coverage") {
958 CustomCoverage::Border { widths, style } => {
959 ensure!(
960 [widths.top.0, widths.right.0, widths.bottom.0, widths.left.0]
961 .iter()
962 .all(|v| v.is_finite() && *v >= 0.),
963 "invalid border widths"
964 );
965 scene.insert_primitive(Quad {
966 bounds,
967 content_mask: mask,
968 background: transparent_black().into(),
969 border_color: white(),
970 border_widths: *widths,
971 corner_radii: draw.corner_radii,
972 border_style: *style,
973 ..Default::default()
974 });
975 }
976 CustomCoverage::Path(path) => {
977 ensure!(path.vertices.len() <= 1_000_000, "coverage path too large");
978 let mut path = path.as_ref().clone();
979 let sx = bounds.size.width.0 / draw.bounds.size.width.0;
980 let sy = bounds.size.height.0 / draw.bounds.size.height.0;
981 for vertex in &mut path.vertices {
982 vertex.xy_position.x =
983 ScaledPixels((vertex.xy_position.x.0 - draw.bounds.origin.x.0) * sx);
984 vertex.xy_position.y =
985 ScaledPixels((vertex.xy_position.y.0 - draw.bounds.origin.y.0) * sy);
986 ensure!(
987 [
988 vertex.xy_position.x.0,
989 vertex.xy_position.y.0,
990 vertex.st_position.x,
991 vertex.st_position.y
992 ]
993 .iter()
994 .all(|v| v.is_finite()),
995 "non-finite coverage path"
996 );
997 vertex.content_mask = mask;
998 }
999 path.bounds = bounds;
1000 path.content_mask = mask;
1001 path.color = white().into();
1002 scene.insert_primitive(path);
1003 }
1004 }
1005 scene.finish();
1006 Ok(scene)
1007}
1008
1009#[cfg(test)]
1010mod tests;