diff options
Diffstat (limited to 'wgpu/src')
| -rw-r--r-- | wgpu/src/lib.rs | 2 | ||||
| -rw-r--r-- | wgpu/src/renderer.rs | 9 | ||||
| -rw-r--r-- | wgpu/src/settings.rs | 22 | ||||
| -rw-r--r-- | wgpu/src/shader/blit.frag | 12 | ||||
| -rw-r--r-- | wgpu/src/shader/blit.frag.spv | bin | 0 -> 684 bytes | |||
| -rw-r--r-- | wgpu/src/shader/blit.vert | 26 | ||||
| -rw-r--r-- | wgpu/src/shader/blit.vert.spv | bin | 0 -> 1384 bytes | |||
| -rw-r--r-- | wgpu/src/shader/image.vert | 24 | ||||
| -rw-r--r-- | wgpu/src/triangle.rs | 233 | ||||
| -rw-r--r-- | wgpu/src/triangle/msaa.rs | 255 | 
10 files changed, 513 insertions, 70 deletions
diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index d38e2a31..90d28353 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -25,6 +25,7 @@  #![forbid(unsafe_code)]  #![forbid(rust_2018_idioms)]  pub mod defaults; +pub mod settings;  pub mod triangle;  pub mod widget;  pub mod window; @@ -33,7 +34,6 @@ mod image;  mod primitive;  mod quad;  mod renderer; -mod settings;  mod target;  mod text;  mod transformation; diff --git a/wgpu/src/renderer.rs b/wgpu/src/renderer.rs index 25b2e99a..29adcfb6 100644 --- a/wgpu/src/renderer.rs +++ b/wgpu/src/renderer.rs @@ -51,7 +51,8 @@ impl Renderer {          let text_pipeline = text::Pipeline::new(device, settings.default_font);          let quad_pipeline = quad::Pipeline::new(device);          let image_pipeline = crate::image::Pipeline::new(device); -        let triangle_pipeline = triangle::Pipeline::new(device); +        let triangle_pipeline = +            triangle::Pipeline::new(device, settings.antialiasing);          Self {              quad_pipeline, @@ -105,6 +106,8 @@ impl Renderer {                  &layer,                  encoder,                  target.texture, +                width, +                height,              );          } @@ -308,6 +311,8 @@ impl Renderer {          layer: &Layer<'_>,          encoder: &mut wgpu::CommandEncoder,          target: &wgpu::TextureView, +        target_width: u32, +        target_height: u32,      ) {          let bounds = layer.bounds * scale_factor; @@ -323,6 +328,8 @@ impl Renderer {                  device,                  encoder,                  target, +                target_width, +                target_height,                  translated,                  &layer.meshes,                  bounds, diff --git a/wgpu/src/settings.rs b/wgpu/src/settings.rs index dbe81830..c8a0cadf 100644 --- a/wgpu/src/settings.rs +++ b/wgpu/src/settings.rs @@ -7,4 +7,26 @@ pub struct Settings {      ///      /// If `None` is provided, a default system font will be chosen.      pub default_font: Option<&'static [u8]>, + +    /// The antialiasing strategy that will be used for triangle primitives. +    pub antialiasing: Option<MSAA>, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum MSAA { +    X2, +    X4, +    X8, +    X16, +} + +impl MSAA { +    pub(crate) fn sample_count(&self) -> u32 { +        match self { +            MSAA::X2 => 2, +            MSAA::X4 => 4, +            MSAA::X8 => 8, +            MSAA::X16 => 16, +        } +    }  } diff --git a/wgpu/src/shader/blit.frag b/wgpu/src/shader/blit.frag new file mode 100644 index 00000000..dfed960f --- /dev/null +++ b/wgpu/src/shader/blit.frag @@ -0,0 +1,12 @@ +#version 450 + +layout(location = 0) in vec2 v_Uv; + +layout(set = 0, binding = 0) uniform sampler u_Sampler; +layout(set = 1, binding = 0) uniform texture2D u_Texture; + +layout(location = 0) out vec4 o_Color; + +void main() { +    o_Color = texture(sampler2D(u_Texture, u_Sampler), v_Uv); +} diff --git a/wgpu/src/shader/blit.frag.spv b/wgpu/src/shader/blit.frag.spv Binary files differnew file mode 100644 index 00000000..2c5638b5 --- /dev/null +++ b/wgpu/src/shader/blit.frag.spv diff --git a/wgpu/src/shader/blit.vert b/wgpu/src/shader/blit.vert new file mode 100644 index 00000000..1c081b9e --- /dev/null +++ b/wgpu/src/shader/blit.vert @@ -0,0 +1,26 @@ +#version 450 + +layout(location = 0) out vec2 o_Uv; + +const vec2 positions[6] = vec2[6]( +    vec2(-1.0, -1.0), +    vec2(-1.0, 1.0), +    vec2(1.0, 1.0), +    vec2(-1.0, -1.0), +    vec2(1.0, -1.0), +    vec2(1.0, 1.0) +); + +const vec2 uvs[6] = vec2[6]( +    vec2(0.0, 0.0), +    vec2(0.0, 1.0), +    vec2(1.0, 1.0), +    vec2(0.0, 0.0), +    vec2(1.0, 0.0), +    vec2(1.0, 1.0) +); + +void main() { +    o_Uv = uvs[gl_VertexIndex]; +    gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); +} diff --git a/wgpu/src/shader/blit.vert.spv b/wgpu/src/shader/blit.vert.spv Binary files differnew file mode 100644 index 00000000..ad697d48 --- /dev/null +++ b/wgpu/src/shader/blit.vert.spv diff --git a/wgpu/src/shader/image.vert b/wgpu/src/shader/image.vert deleted file mode 100644 index 688c2311..00000000 --- a/wgpu/src/shader/image.vert +++ /dev/null @@ -1,24 +0,0 @@ -#version 450 - -layout(location = 0) in vec2 v_Pos; -layout(location = 1) in vec2 i_Pos; -layout(location = 2) in vec2 i_Scale; - -layout (set = 0, binding = 0) uniform Globals { -    mat4 u_Transform; -}; - -layout(location = 0) out vec2 o_Uv; - -void main() { -    o_Uv = v_Pos; - -    mat4 i_Transform = mat4( -        vec4(i_Scale.x, 0.0, 0.0, 0.0), -        vec4(0.0, i_Scale.y, 0.0, 0.0), -        vec4(0.0, 0.0, 1.0, 0.0), -        vec4(i_Pos, 0.0, 1.0) -    ); - -    gl_Position = u_Transform * i_Transform * vec4(v_Pos, 0.0, 1.0); -} diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs index 3cc1d3fb..9159b0a2 100644 --- a/wgpu/src/triangle.rs +++ b/wgpu/src/triangle.rs @@ -1,32 +1,82 @@  //! Draw meshes of triangles. -use crate::Transformation; +use crate::{settings, Transformation};  use iced_native::{Point, Rectangle};  use std::{mem, sync::Arc}; +mod msaa; + +const UNIFORM_BUFFER_SIZE: usize = 100; +const VERTEX_BUFFER_SIZE: usize = 100_000; +const INDEX_BUFFER_SIZE: usize = 100_000; +  #[derive(Debug)]  pub(crate) struct Pipeline {      pipeline: wgpu::RenderPipeline, +    blit: Option<msaa::Blit>,      constants: wgpu::BindGroup, -    constants_buffer: wgpu::Buffer, +    uniforms_buffer: Buffer<Uniforms>, +    vertex_buffer: Buffer<Vertex2D>, +    index_buffer: Buffer<u32>, +} + +#[derive(Debug)] +struct Buffer<T> { +    raw: wgpu::Buffer, +    size: usize, +    usage: wgpu::BufferUsage, +    _type: std::marker::PhantomData<T>, +} + +impl<T> Buffer<T> { +    pub fn new( +        device: &wgpu::Device, +        size: usize, +        usage: wgpu::BufferUsage, +    ) -> Self { +        let raw = device.create_buffer(&wgpu::BufferDescriptor { +            size: (std::mem::size_of::<T>() * size) as u64, +            usage, +        }); + +        Buffer { +            raw, +            size, +            usage, +            _type: std::marker::PhantomData, +        } +    } + +    pub fn ensure_capacity(&mut self, device: &wgpu::Device, size: usize) { +        if self.size < size { +            self.raw = device.create_buffer(&wgpu::BufferDescriptor { +                size: (std::mem::size_of::<T>() * size) as u64, +                usage: self.usage, +            }); + +            self.size = size; +        } +    }  }  impl Pipeline { -    pub fn new(device: &mut wgpu::Device) -> Pipeline { +    pub fn new( +        device: &mut wgpu::Device, +        antialiasing: Option<settings::MSAA>, +    ) -> Pipeline {          let constant_layout =              device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {                  bindings: &[wgpu::BindGroupLayoutBinding {                      binding: 0,                      visibility: wgpu::ShaderStage::VERTEX, -                    ty: wgpu::BindingType::UniformBuffer { dynamic: false }, +                    ty: wgpu::BindingType::UniformBuffer { dynamic: true },                  }],              }); -        let constants_buffer = device -            .create_buffer_mapped( -                1, -                wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, -            ) -            .fill_from_slice(&[Uniforms::default()]); +        let constants_buffer = Buffer::new( +            device, +            UNIFORM_BUFFER_SIZE, +            wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, +        );          let constant_bind_group =              device.create_bind_group(&wgpu::BindGroupDescriptor { @@ -34,7 +84,7 @@ impl Pipeline {                  bindings: &[wgpu::Binding {                      binding: 0,                      resource: wgpu::BindingResource::Buffer { -                        buffer: &constants_buffer, +                        buffer: &constants_buffer.raw,                          range: 0..std::mem::size_of::<Uniforms>() as u64,                      },                  }], @@ -110,15 +160,28 @@ impl Pipeline {                          },                      ],                  }], -                sample_count: 1, +                sample_count: antialiasing +                    .map(|a| a.sample_count()) +                    .unwrap_or(1),                  sample_mask: !0,                  alpha_to_coverage_enabled: false,              });          Pipeline {              pipeline, +            blit: antialiasing.map(|a| msaa::Blit::new(device, a)),              constants: constant_bind_group, -            constants_buffer, +            uniforms_buffer: constants_buffer, +            vertex_buffer: Buffer::new( +                device, +                VERTEX_BUFFER_SIZE, +                wgpu::BufferUsage::VERTEX | wgpu::BufferUsage::COPY_DST, +            ), +            index_buffer: Buffer::new( +                device, +                INDEX_BUFFER_SIZE, +                wgpu::BufferUsage::INDEX | wgpu::BufferUsage::COPY_DST, +            ),          }      } @@ -127,50 +190,116 @@ impl Pipeline {          device: &mut wgpu::Device,          encoder: &mut wgpu::CommandEncoder,          target: &wgpu::TextureView, +        target_width: u32, +        target_height: u32,          transformation: Transformation,          meshes: &Vec<(Point, Arc<Mesh2D>)>,          bounds: Rectangle<u32>,      ) { +        // This looks a bit crazy, but we are just counting how many vertices +        // and indices we will need to handle. +        // TODO: Improve readability +        let (total_vertices, total_indices) = meshes +            .iter() +            .map(|(_, mesh)| (mesh.vertices.len(), mesh.indices.len())) +            .fold((0, 0), |(total_v, total_i), (v, i)| { +                (total_v + v, total_i + i) +            }); + +        // Then we ensure the current buffers are big enough, resizing if +        // necessary +        self.uniforms_buffer.ensure_capacity(device, meshes.len()); +        self.vertex_buffer.ensure_capacity(device, total_vertices); +        self.index_buffer.ensure_capacity(device, total_indices); + +        let mut uniforms: Vec<Uniforms> = Vec::with_capacity(meshes.len()); +        let mut offsets: Vec<( +            wgpu::BufferAddress, +            wgpu::BufferAddress, +            usize, +        )> = Vec::with_capacity(meshes.len()); +        let mut last_vertex = 0; +        let mut last_index = 0; + +        // We upload everything upfront          for (origin, mesh) in meshes { -            let uniforms = Uniforms { +            let transform = Uniforms {                  transform: (transformation                      * Transformation::translate(origin.x, origin.y))                  .into(),              }; -            let constants_buffer = device -                .create_buffer_mapped(1, wgpu::BufferUsage::COPY_SRC) -                .fill_from_slice(&[uniforms]); - -            encoder.copy_buffer_to_buffer( -                &constants_buffer, -                0, -                &self.constants_buffer, -                0, -                std::mem::size_of::<Uniforms>() as u64, -            ); - -            let vertices_buffer = device +            let vertex_buffer = device                  .create_buffer_mapped(                      mesh.vertices.len(), -                    wgpu::BufferUsage::VERTEX, +                    wgpu::BufferUsage::COPY_SRC,                  )                  .fill_from_slice(&mesh.vertices); -            let indices_buffer = device +            let index_buffer = device                  .create_buffer_mapped(                      mesh.indices.len(), -                    wgpu::BufferUsage::INDEX, +                    wgpu::BufferUsage::COPY_SRC,                  )                  .fill_from_slice(&mesh.indices); +            encoder.copy_buffer_to_buffer( +                &vertex_buffer, +                0, +                &self.vertex_buffer.raw, +                last_vertex as u64, +                (std::mem::size_of::<Vertex2D>() * mesh.vertices.len()) as u64, +            ); + +            encoder.copy_buffer_to_buffer( +                &index_buffer, +                0, +                &self.index_buffer.raw, +                last_index as u64, +                (std::mem::size_of::<u32>() * mesh.indices.len()) as u64, +            ); + +            uniforms.push(transform); +            offsets.push(( +                last_vertex as u64, +                last_index as u64, +                mesh.indices.len(), +            )); + +            last_vertex += mesh.vertices.len(); +            last_index += mesh.indices.len(); +        } + +        let uniforms_buffer = device +            .create_buffer_mapped(uniforms.len(), wgpu::BufferUsage::COPY_SRC) +            .fill_from_slice(&uniforms); + +        encoder.copy_buffer_to_buffer( +            &uniforms_buffer, +            0, +            &self.uniforms_buffer.raw, +            0, +            (std::mem::size_of::<Uniforms>() * uniforms.len()) as u64, +        ); + +        { +            let (attachment, resolve_target, load_op) = +                if let Some(blit) = &mut self.blit { +                    let (attachment, resolve_target) = +                        blit.targets(device, target_width, target_height); + +                    (attachment, Some(resolve_target), wgpu::LoadOp::Clear) +                } else { +                    (target, None, wgpu::LoadOp::Load) +                }; +              let mut render_pass =                  encoder.begin_render_pass(&wgpu::RenderPassDescriptor {                      color_attachments: &[                          wgpu::RenderPassColorAttachmentDescriptor { -                            attachment: target, -                            resolve_target: None, -                            load_op: wgpu::LoadOp::Load, +                            attachment, +                            resolve_target, +                            load_op,                              store_op: wgpu::StoreOp::Store,                              clear_color: wgpu::Color {                                  r: 0.0, @@ -183,18 +312,34 @@ impl Pipeline {                      depth_stencil_attachment: None,                  }); -            render_pass.set_pipeline(&self.pipeline); -            render_pass.set_bind_group(0, &self.constants, &[]); -            render_pass.set_index_buffer(&indices_buffer, 0); -            render_pass.set_vertex_buffers(0, &[(&vertices_buffer, 0)]); -            render_pass.set_scissor_rect( -                bounds.x, -                bounds.y, -                bounds.width, -                bounds.height, -            ); +            for (i, (vertex_offset, index_offset, indices)) in +                offsets.drain(..).enumerate() +            { +                render_pass.set_pipeline(&self.pipeline); +                render_pass.set_bind_group( +                    0, +                    &self.constants, +                    &[(std::mem::size_of::<Uniforms>() * i) as u64], +                ); +                render_pass +                    .set_index_buffer(&self.index_buffer.raw, index_offset); +                render_pass.set_vertex_buffers( +                    0, +                    &[(&self.vertex_buffer.raw, vertex_offset)], +                ); +                render_pass.set_scissor_rect( +                    bounds.x, +                    bounds.y, +                    bounds.width, +                    bounds.height, +                ); + +                render_pass.draw_indexed(0..indices as u32, 0, 0..1); +            } +        } -            render_pass.draw_indexed(0..mesh.indices.len() as u32, 0, 0..1); +        if let Some(blit) = &mut self.blit { +            blit.draw(encoder, target);          }      }  } diff --git a/wgpu/src/triangle/msaa.rs b/wgpu/src/triangle/msaa.rs new file mode 100644 index 00000000..93fbe49b --- /dev/null +++ b/wgpu/src/triangle/msaa.rs @@ -0,0 +1,255 @@ +use crate::settings; + +#[derive(Debug)] +pub struct Blit { +    pipeline: wgpu::RenderPipeline, +    constants: wgpu::BindGroup, +    texture_layout: wgpu::BindGroupLayout, +    sample_count: u32, +    targets: Option<Targets>, +} + +impl Blit { +    pub fn new(device: &wgpu::Device, antialiasing: settings::MSAA) -> Blit { +        let sampler = device.create_sampler(&wgpu::SamplerDescriptor { +            address_mode_u: wgpu::AddressMode::ClampToEdge, +            address_mode_v: wgpu::AddressMode::ClampToEdge, +            address_mode_w: wgpu::AddressMode::ClampToEdge, +            mag_filter: wgpu::FilterMode::Linear, +            min_filter: wgpu::FilterMode::Linear, +            mipmap_filter: wgpu::FilterMode::Linear, +            lod_min_clamp: -100.0, +            lod_max_clamp: 100.0, +            compare_function: wgpu::CompareFunction::Always, +        }); + +        let constant_layout = +            device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { +                bindings: &[wgpu::BindGroupLayoutBinding { +                    binding: 0, +                    visibility: wgpu::ShaderStage::FRAGMENT, +                    ty: wgpu::BindingType::Sampler, +                }], +            }); + +        let constant_bind_group = +            device.create_bind_group(&wgpu::BindGroupDescriptor { +                layout: &constant_layout, +                bindings: &[wgpu::Binding { +                    binding: 0, +                    resource: wgpu::BindingResource::Sampler(&sampler), +                }], +            }); + +        let texture_layout = +            device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { +                bindings: &[wgpu::BindGroupLayoutBinding { +                    binding: 0, +                    visibility: wgpu::ShaderStage::FRAGMENT, +                    ty: wgpu::BindingType::SampledTexture { +                        multisampled: false, +                        dimension: wgpu::TextureViewDimension::D2, +                    }, +                }], +            }); + +        let layout = +            device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { +                bind_group_layouts: &[&constant_layout, &texture_layout], +            }); + +        let vs = include_bytes!("../shader/blit.vert.spv"); +        let vs_module = device.create_shader_module( +            &wgpu::read_spirv(std::io::Cursor::new(&vs[..])) +                .expect("Read blit vertex shader as SPIR-V"), +        ); + +        let fs = include_bytes!("../shader/blit.frag.spv"); +        let fs_module = device.create_shader_module( +            &wgpu::read_spirv(std::io::Cursor::new(&fs[..])) +                .expect("Read blit fragment shader as SPIR-V"), +        ); + +        let pipeline = +            device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { +                layout: &layout, +                vertex_stage: wgpu::ProgrammableStageDescriptor { +                    module: &vs_module, +                    entry_point: "main", +                }, +                fragment_stage: Some(wgpu::ProgrammableStageDescriptor { +                    module: &fs_module, +                    entry_point: "main", +                }), +                rasterization_state: Some(wgpu::RasterizationStateDescriptor { +                    front_face: wgpu::FrontFace::Cw, +                    cull_mode: wgpu::CullMode::None, +                    depth_bias: 0, +                    depth_bias_slope_scale: 0.0, +                    depth_bias_clamp: 0.0, +                }), +                primitive_topology: wgpu::PrimitiveTopology::TriangleList, +                color_states: &[wgpu::ColorStateDescriptor { +                    format: wgpu::TextureFormat::Bgra8UnormSrgb, +                    color_blend: wgpu::BlendDescriptor { +                        src_factor: wgpu::BlendFactor::SrcAlpha, +                        dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, +                        operation: wgpu::BlendOperation::Add, +                    }, +                    alpha_blend: wgpu::BlendDescriptor { +                        src_factor: wgpu::BlendFactor::One, +                        dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, +                        operation: wgpu::BlendOperation::Add, +                    }, +                    write_mask: wgpu::ColorWrite::ALL, +                }], +                depth_stencil_state: None, +                index_format: wgpu::IndexFormat::Uint16, +                vertex_buffers: &[], +                sample_count: 1, +                sample_mask: !0, +                alpha_to_coverage_enabled: false, +            }); + +        Blit { +            pipeline, +            constants: constant_bind_group, +            texture_layout: texture_layout, +            sample_count: antialiasing.sample_count(), +            targets: None, +        } +    } + +    pub fn targets( +        &mut self, +        device: &wgpu::Device, +        width: u32, +        height: u32, +    ) -> (&wgpu::TextureView, &wgpu::TextureView) { +        match &mut self.targets { +            None => { +                self.targets = Some(Targets::new( +                    &device, +                    &self.texture_layout, +                    self.sample_count, +                    width, +                    height, +                )); +            } +            Some(targets) => { +                if targets.width != width || targets.height != height { +                    self.targets = Some(Targets::new( +                        &device, +                        &self.texture_layout, +                        self.sample_count, +                        width, +                        height, +                    )); +                } +            } +        } + +        let targets = self.targets.as_ref().unwrap(); + +        (&targets.attachment, &targets.resolve) +    } + +    pub fn draw( +        &self, +        encoder: &mut wgpu::CommandEncoder, +        target: &wgpu::TextureView, +    ) { +        let mut render_pass = +            encoder.begin_render_pass(&wgpu::RenderPassDescriptor { +                color_attachments: &[ +                    wgpu::RenderPassColorAttachmentDescriptor { +                        attachment: target, +                        resolve_target: None, +                        load_op: wgpu::LoadOp::Load, +                        store_op: wgpu::StoreOp::Store, +                        clear_color: wgpu::Color { +                            r: 0.0, +                            g: 0.0, +                            b: 0.0, +                            a: 0.0, +                        }, +                    }, +                ], +                depth_stencil_attachment: None, +            }); + +        render_pass.set_pipeline(&self.pipeline); +        render_pass.set_bind_group(0, &self.constants, &[]); +        render_pass.set_bind_group( +            1, +            &self.targets.as_ref().unwrap().bind_group, +            &[], +        ); +        render_pass.draw(0..6, 0..1); +    } +} + +#[derive(Debug)] +struct Targets { +    attachment: wgpu::TextureView, +    resolve: wgpu::TextureView, +    bind_group: wgpu::BindGroup, +    width: u32, +    height: u32, +} + +impl Targets { +    pub fn new( +        device: &wgpu::Device, +        texture_layout: &wgpu::BindGroupLayout, +        sample_count: u32, +        width: u32, +        height: u32, +    ) -> Targets { +        let extent = wgpu::Extent3d { +            width, +            height, +            depth: 1, +        }; + +        let attachment = device.create_texture(&wgpu::TextureDescriptor { +            size: extent, +            array_layer_count: 1, +            mip_level_count: 1, +            sample_count, +            dimension: wgpu::TextureDimension::D2, +            format: wgpu::TextureFormat::Bgra8UnormSrgb, +            usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, +        }); + +        let resolve = device.create_texture(&wgpu::TextureDescriptor { +            size: extent, +            array_layer_count: 1, +            mip_level_count: 1, +            sample_count: 1, +            dimension: wgpu::TextureDimension::D2, +            format: wgpu::TextureFormat::Bgra8UnormSrgb, +            usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT +                | wgpu::TextureUsage::SAMPLED, +        }); + +        let attachment = attachment.create_default_view(); +        let resolve = resolve.create_default_view(); + +        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { +            layout: texture_layout, +            bindings: &[wgpu::Binding { +                binding: 0, +                resource: wgpu::BindingResource::TextureView(&resolve), +            }], +        }); + +        Targets { +            attachment, +            resolve, +            bind_group, +            width, +            height, +        } +    } +}  | 
