| 1 | /// Carries frames to a window whose compositor blends premultiplied sRGB, as Core Animation's |
| 2 | /// transparent layers do. The renderer blends in linear light into a texture of its own; on |
| 3 | /// the way to the window each pixel is premultiplied again in sRGB, so a translucent colour |
| 4 | /// over whatever shows through the window looks as it would over an opaque one. |
| 5 | pub struct Translucent { |
| 6 | frame: Option<wgpu::Texture>, |
| 7 | format: wgpu::TextureFormat, |
| 8 | pipeline: wgpu::RenderPipeline, |
| 9 | } |
| 10 | |
| 11 | impl Translucent { |
| 12 | /// Frames the renderer draws in `format` for a window view in `window`, which stores its |
| 13 | /// bytes as given. |
| 14 | pub fn new( |
| 15 | device: &wgpu::Device, |
| 16 | format: wgpu::TextureFormat, |
| 17 | window: wgpu::TextureFormat, |
| 18 | ) -> Self { |
| 19 | let shader = device.create_shader_module(wgpu::include_wgsl!("translucent.wgsl")); |
| 20 | let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { |
| 21 | label: Some("Translucent window"), |
| 22 | layout: None, |
| 23 | vertex: wgpu::VertexState { |
| 24 | module: &shader, |
| 25 | entry_point: Some("vertex"), |
| 26 | compilation_options: Default::default(), |
| 27 | buffers: &[], |
| 28 | }, |
| 29 | fragment: Some(wgpu::FragmentState { |
| 30 | module: &shader, |
| 31 | entry_point: Some("fragment"), |
| 32 | compilation_options: Default::default(), |
| 33 | targets: &[Some(window.into())], |
| 34 | }), |
| 35 | primitive: Default::default(), |
| 36 | depth_stencil: None, |
| 37 | multisample: Default::default(), |
| 38 | multiview_mask: None, |
| 39 | cache: None, |
| 40 | }); |
| 41 | Self { |
| 42 | frame: None, |
| 43 | format, |
| 44 | pipeline, |
| 45 | } |
| 46 | } |
| 47 | |
| 48 | /// The view to draw a frame `size` pixels large into. |
| 49 | pub fn target(&mut self, device: &wgpu::Device, size: [u32; 2]) -> wgpu::TextureView { |
| 50 | let extent = wgpu::Extent3d { |
| 51 | width: size[0], |
| 52 | height: size[1], |
| 53 | depth_or_array_layers: 1, |
| 54 | }; |
| 55 | let frame = match &self.frame { |
| 56 | Some(frame) if frame.size() == extent => frame, |
| 57 | _ => self |
| 58 | .frame |
| 59 | .insert(device.create_texture(&wgpu::TextureDescriptor { |
| 60 | label: Some("Translucent frame"), |
| 61 | size: extent, |
| 62 | mip_level_count: 1, |
| 63 | sample_count: 1, |
| 64 | dimension: wgpu::TextureDimension::D2, |
| 65 | format: self.format, |
| 66 | usage: wgpu::TextureUsages::RENDER_ATTACHMENT |
| 67 | | wgpu::TextureUsages::TEXTURE_BINDING, |
| 68 | view_formats: &[], |
| 69 | })), |
| 70 | }; |
| 71 | frame.create_view(&Default::default()) |
| 72 | } |
| 73 | |
| 74 | /// Copies the frame last drawn into `target` to `window`. |
| 75 | pub fn present(&self, device: &wgpu::Device, queue: &wgpu::Queue, window: &wgpu::TextureView) { |
| 76 | let Some(frame) = &self.frame else { |
| 77 | return; |
| 78 | }; |
| 79 | let binding = device.create_bind_group(&wgpu::BindGroupDescriptor { |
| 80 | label: Some("Translucent frame"), |
| 81 | layout: &self.pipeline.get_bind_group_layout(0), |
| 82 | entries: &[wgpu::BindGroupEntry { |
| 83 | binding: 0, |
| 84 | resource: wgpu::BindingResource::TextureView( |
| 85 | &frame.create_view(&Default::default()), |
| 86 | ), |
| 87 | }], |
| 88 | }); |
| 89 | let mut encoder = device.create_command_encoder(&Default::default()); |
| 90 | { |
| 91 | let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { |
| 92 | label: Some("Translucent window"), |
| 93 | color_attachments: &[Some(wgpu::RenderPassColorAttachment { |
| 94 | view: window, |
| 95 | depth_slice: None, |
| 96 | resolve_target: None, |
| 97 | ops: wgpu::Operations { |
| 98 | load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT), |
| 99 | store: wgpu::StoreOp::Store, |
| 100 | }, |
| 101 | })], |
| 102 | ..Default::default() |
| 103 | }); |
| 104 | pass.set_pipeline(&self.pipeline); |
| 105 | pass.set_bind_group(0, &binding, &[]); |
| 106 | pass.draw(0..3, 0..1); |
| 107 | } |
| 108 | queue.submit([encoder.finish()]); |
| 109 | } |
| 110 | } |