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 = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
41        label: Some("iced_wgpu.offscreen.blit.sampler_layout"),
42        entries: &[
43            wgpu::BindGroupLayoutEntry {
44                binding: 0,
45                visibility: wgpu::ShaderStages::FRAGMENT,
46                ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::NonFiltering),
47                count: None,
48            },
49            wgpu::BindGroupLayoutEntry {
50                binding: 1,
51                visibility: wgpu::ShaderStages::VERTEX,
52                ty: wgpu::BindingType::Buffer {
53                    ty: wgpu::BufferBindingType::Uniform,
54                    has_dynamic_offset: false,
55                    min_binding_size: None,
56                },
57                count: None,
58            },
59        ],
60    });
61
62    let constant_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
63        label: Some("iced_wgpu.offscreen.sampler.bind_group"),
64        layout: &constant_layout,
65        entries: &[
66            wgpu::BindGroupEntry {
67                binding: 0,
68                resource: wgpu::BindingResource::Sampler(&sampler),
69            },
70            wgpu::BindGroupEntry {
71                binding: 1,
72                resource: ratio.as_entire_binding(),
73            },
74        ],
75    });
76
77    let texture_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
78        label: Some("iced_wgpu.offscreen.blit.texture_layout"),
79        entries: &[wgpu::BindGroupLayoutEntry {
80            binding: 0,
81            visibility: wgpu::ShaderStages::FRAGMENT,
82            ty: wgpu::BindingType::Texture {
83                sample_type: wgpu::TextureSampleType::Float { filterable: false },
84                view_dimension: wgpu::TextureViewDimension::D2,
85                multisampled: false,
86            },
87            count: None,
88        }],
89    });
90
91    let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
92        label: Some("iced_wgpu.offscreen.blit.pipeline_layout"),
93        bind_group_layouts: &[&constant_layout, &texture_layout],
94        push_constant_ranges: &[],
95    });
96
97    let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
98        label: Some("iced_wgpu.offscreen.blit.shader"),
99        source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader/blit.wgsl"))),
100    });
101
102    let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
103        label: Some("iced_wgpu.offscreen.blit.pipeline"),
104        layout: Some(&pipeline_layout),
105        vertex: wgpu::VertexState {
106            module: &shader,
107            entry_point: Some("vs_main"),
108            buffers: &[],
109            compilation_options: wgpu::PipelineCompilationOptions::default(),
110        },
111        fragment: Some(wgpu::FragmentState {
112            module: &shader,
113            entry_point: Some("fs_main"),
114            targets: &[Some(wgpu::ColorTargetState {
115                format,
116                blend: Some(wgpu::BlendState {
117                    color: wgpu::BlendComponent {
118                        src_factor: wgpu::BlendFactor::SrcAlpha,
119                        dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
120                        operation: wgpu::BlendOperation::Add,
121                    },
122                    alpha: wgpu::BlendComponent {
123                        src_factor: wgpu::BlendFactor::One,
124                        dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
125                        operation: wgpu::BlendOperation::Add,
126                    },
127                }),
128                write_mask: wgpu::ColorWrites::ALL,
129            })],
130            compilation_options: wgpu::PipelineCompilationOptions::default(),
131        }),
132        primitive: wgpu::PrimitiveState {
133            topology: wgpu::PrimitiveTopology::TriangleList,
134            front_face: wgpu::FrontFace::Cw,
135            ..wgpu::PrimitiveState::default()
136        },
137        depth_stencil: None,
138        multisample: wgpu::MultisampleState::default(),
139        multiview: None,
140        cache: None,
141    });
142
143    let texture = device.create_texture(&wgpu::TextureDescriptor {
144        label: Some("iced_wgpu.offscreen.conversion.source_texture"),
145        size: source.size(),
146        mip_level_count: 1,
147        sample_count: 1,
148        dimension: wgpu::TextureDimension::D2,
149        format,
150        usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
151        view_formats: &[],
152    });
153
154    let view = &texture.create_view(&wgpu::TextureViewDescriptor::default());
155
156    let texture_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
157        label: Some("iced_wgpu.offscreen.blit.texture_bind_group"),
158        layout: &texture_layout,
159        entries: &[wgpu::BindGroupEntry {
160            binding: 0,
161            resource: wgpu::BindingResource::TextureView(
162                &source.create_view(&wgpu::TextureViewDescriptor::default()),
163            ),
164        }],
165    });
166
167    let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
168        label: Some("iced_wgpu.offscreen.blit.render_pass"),
169        color_attachments: &[Some(wgpu::RenderPassColorAttachment {
170            view,
171            depth_slice: None,
172            resolve_target: None,
173            ops: wgpu::Operations {
174                load: wgpu::LoadOp::Load,
175                store: wgpu::StoreOp::Store,
176            },
177        })],
178        depth_stencil_attachment: None,
179        timestamp_writes: None,
180        occlusion_query_set: None,
181    });
182
183    pass.set_pipeline(&pipeline);
184    pass.set_bind_group(0, &constant_bind_group, &[]);
185    pass.set_bind_group(1, &texture_bind_group, &[]);
186    pass.draw(0..6, 0..1);
187
188    texture
189}