iced_wgpu/
color.rs

1use std::borrow::Cow;
2
3use wgpu::util::DeviceExt;
4
5pub fn convert(
6    device: &wgpu::Device,
7    encoder: &mut wgpu::CommandEncoder,
8    source: wgpu::Texture,
9    format: wgpu::TextureFormat,
10) -> wgpu::Texture {
11    if source.format() == format {
12        return source;
13    }
14
15    let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
16        label: Some("iced_wgpu.offscreen.sampler"),
17        ..wgpu::SamplerDescriptor::default()
18    });
19
20    #[derive(Debug, Clone, Copy, bytemuck::Zeroable, bytemuck::Pod)]
21    #[repr(C)]
22    struct Ratio {
23        u: f32,
24        v: f32,
25        // Padding field for 16-byte alignment.
26        // See https://docs.rs/wgpu/latest/wgpu/struct.DownlevelFlags.html#associatedconstant.BUFFER_BINDINGS_NOT_16_BYTE_ALIGNED
27        _padding: [f32; 2],
28    }
29
30    let ratio = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
31        label: Some("iced-wgpu::triangle::msaa ratio"),
32        contents: bytemuck::bytes_of(&Ratio {
33            u: 1.0,
34            v: 1.0,
35            _padding: [0.0; 2],
36        }),
37        usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::UNIFORM,
38    });
39
40    let constant_layout =
41        device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
42            label: Some("iced_wgpu.offscreen.blit.sampler_layout"),
43            entries: &[
44                wgpu::BindGroupLayoutEntry {
45                    binding: 0,
46                    visibility: wgpu::ShaderStages::FRAGMENT,
47                    ty: wgpu::BindingType::Sampler(
48                        wgpu::SamplerBindingType::NonFiltering,
49                    ),
50                    count: None,
51                },
52                wgpu::BindGroupLayoutEntry {
53                    binding: 1,
54                    visibility: wgpu::ShaderStages::VERTEX,
55                    ty: wgpu::BindingType::Buffer {
56                        ty: wgpu::BufferBindingType::Uniform,
57                        has_dynamic_offset: false,
58                        min_binding_size: None,
59                    },
60                    count: None,
61                },
62            ],
63        });
64
65    let constant_bind_group =
66        device.create_bind_group(&wgpu::BindGroupDescriptor {
67            label: Some("iced_wgpu.offscreen.sampler.bind_group"),
68            layout: &constant_layout,
69            entries: &[
70                wgpu::BindGroupEntry {
71                    binding: 0,
72                    resource: wgpu::BindingResource::Sampler(&sampler),
73                },
74                wgpu::BindGroupEntry {
75                    binding: 1,
76                    resource: ratio.as_entire_binding(),
77                },
78            ],
79        });
80
81    let texture_layout =
82        device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
83            label: Some("iced_wgpu.offscreen.blit.texture_layout"),
84            entries: &[wgpu::BindGroupLayoutEntry {
85                binding: 0,
86                visibility: wgpu::ShaderStages::FRAGMENT,
87                ty: wgpu::BindingType::Texture {
88                    sample_type: wgpu::TextureSampleType::Float {
89                        filterable: false,
90                    },
91                    view_dimension: wgpu::TextureViewDimension::D2,
92                    multisampled: false,
93                },
94                count: None,
95            }],
96        });
97
98    let pipeline_layout =
99        device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
100            label: Some("iced_wgpu.offscreen.blit.pipeline_layout"),
101            bind_group_layouts: &[&constant_layout, &texture_layout],
102            push_constant_ranges: &[],
103        });
104
105    let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
106        label: Some("iced_wgpu.offscreen.blit.shader"),
107        source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!(
108            "shader/blit.wgsl"
109        ))),
110    });
111
112    let pipeline =
113        device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
114            label: Some("iced_wgpu.offscreen.blit.pipeline"),
115            layout: Some(&pipeline_layout),
116            vertex: wgpu::VertexState {
117                module: &shader,
118                entry_point: Some("vs_main"),
119                buffers: &[],
120                compilation_options: wgpu::PipelineCompilationOptions::default(
121                ),
122            },
123            fragment: Some(wgpu::FragmentState {
124                module: &shader,
125                entry_point: Some("fs_main"),
126                targets: &[Some(wgpu::ColorTargetState {
127                    format,
128                    blend: Some(wgpu::BlendState {
129                        color: wgpu::BlendComponent {
130                            src_factor: wgpu::BlendFactor::SrcAlpha,
131                            dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
132                            operation: wgpu::BlendOperation::Add,
133                        },
134                        alpha: wgpu::BlendComponent {
135                            src_factor: wgpu::BlendFactor::One,
136                            dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
137                            operation: wgpu::BlendOperation::Add,
138                        },
139                    }),
140                    write_mask: wgpu::ColorWrites::ALL,
141                })],
142                compilation_options: wgpu::PipelineCompilationOptions::default(
143                ),
144            }),
145            primitive: wgpu::PrimitiveState {
146                topology: wgpu::PrimitiveTopology::TriangleList,
147                front_face: wgpu::FrontFace::Cw,
148                ..wgpu::PrimitiveState::default()
149            },
150            depth_stencil: None,
151            multisample: wgpu::MultisampleState::default(),
152            multiview: None,
153            cache: None,
154        });
155
156    let texture = device.create_texture(&wgpu::TextureDescriptor {
157        label: Some("iced_wgpu.offscreen.conversion.source_texture"),
158        size: source.size(),
159        mip_level_count: 1,
160        sample_count: 1,
161        dimension: wgpu::TextureDimension::D2,
162        format,
163        usage: wgpu::TextureUsages::RENDER_ATTACHMENT
164            | wgpu::TextureUsages::COPY_SRC,
165        view_formats: &[],
166    });
167
168    let view = &texture.create_view(&wgpu::TextureViewDescriptor::default());
169
170    let texture_bind_group =
171        device.create_bind_group(&wgpu::BindGroupDescriptor {
172            label: Some("iced_wgpu.offscreen.blit.texture_bind_group"),
173            layout: &texture_layout,
174            entries: &[wgpu::BindGroupEntry {
175                binding: 0,
176                resource: wgpu::BindingResource::TextureView(
177                    &source
178                        .create_view(&wgpu::TextureViewDescriptor::default()),
179                ),
180            }],
181        });
182
183    let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
184        label: Some("iced_wgpu.offscreen.blit.render_pass"),
185        color_attachments: &[Some(wgpu::RenderPassColorAttachment {
186            view,
187            resolve_target: None,
188            ops: wgpu::Operations {
189                load: wgpu::LoadOp::Load,
190                store: wgpu::StoreOp::Store,
191            },
192        })],
193        depth_stencil_attachment: None,
194        timestamp_writes: None,
195        occlusion_query_set: None,
196    });
197
198    pass.set_pipeline(&pipeline);
199    pass.set_bind_group(0, &constant_bind_group, &[]);
200    pass.set_bind_group(1, &texture_bind_group, &[]);
201    pass.draw(0..6, 0..1);
202
203    texture
204}