1#![cfg(feature = "wgsl")]
2
3use wgt::BlendState;
4
5use crate::{
6 include_wgsl, AddressMode, BindGroupDescriptor, BindGroupEntry, BindGroupLayout,
7 BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingType, ColorTargetState, ColorWrites,
8 CommandEncoder, Device, FilterMode, FragmentState, FrontFace, LoadOp, MultisampleState,
9 PipelineCompilationOptions, PipelineLayoutDescriptor, PrimitiveState, PrimitiveTopology,
10 RenderPassDescriptor, RenderPipeline, RenderPipelineDescriptor, Sampler, SamplerBindingType,
11 SamplerDescriptor, ShaderStages, StoreOp, TextureFormat, TextureSampleType, TextureView,
12 TextureViewDimension, VertexState,
13};
14
15pub struct TextureBlitterBuilder<'a> {
18 device: &'a Device,
19 format: TextureFormat,
20 sample_type: FilterMode,
21 blend_state: Option<BlendState>,
22}
23
24impl<'a> TextureBlitterBuilder<'a> {
25 pub fn new(device: &'a Device, format: TextureFormat) -> Self {
31 Self {
32 device,
33 format,
34 sample_type: FilterMode::Nearest,
35 blend_state: None,
36 }
37 }
38
39 pub fn sample_type(mut self, sample_type: FilterMode) -> Self {
41 self.sample_type = sample_type;
42 self
43 }
44
45 pub fn blend_state(mut self, blend_state: BlendState) -> Self {
47 self.blend_state = Some(blend_state);
48 self
49 }
50
51 pub fn build(self) -> TextureBlitter {
53 let sampler = self.device.create_sampler(&SamplerDescriptor {
54 label: Some("wgpu::util::TextureBlitter::sampler"),
55 address_mode_u: AddressMode::ClampToEdge,
56 address_mode_v: AddressMode::ClampToEdge,
57 address_mode_w: AddressMode::ClampToEdge,
58 mag_filter: self.sample_type,
59 ..Default::default()
60 });
61
62 let bind_group_layout = self
63 .device
64 .create_bind_group_layout(&BindGroupLayoutDescriptor {
65 label: Some("wgpu::util::TextureBlitter::bind_group_layout"),
66 entries: &[
67 BindGroupLayoutEntry {
68 binding: 0,
69 visibility: ShaderStages::FRAGMENT,
70 ty: BindingType::Texture {
71 sample_type: TextureSampleType::Float {
72 filterable: self.sample_type == FilterMode::Linear,
73 },
74 view_dimension: TextureViewDimension::D2,
75 multisampled: false,
76 },
77 count: None,
78 },
79 BindGroupLayoutEntry {
80 binding: 1,
81 visibility: ShaderStages::FRAGMENT,
82 ty: BindingType::Sampler(if self.sample_type == FilterMode::Linear {
83 SamplerBindingType::Filtering
84 } else {
85 SamplerBindingType::NonFiltering
86 }),
87 count: None,
88 },
89 ],
90 });
91
92 let pipeline_layout = self
93 .device
94 .create_pipeline_layout(&PipelineLayoutDescriptor {
95 label: Some("wgpu::util::TextureBlitter::pipeline_layout"),
96 bind_group_layouts: &[&bind_group_layout],
97 push_constant_ranges: &[],
98 });
99
100 let shader = self.device.create_shader_module(include_wgsl!("blit.wgsl"));
101 let pipeline = self
102 .device
103 .create_render_pipeline(&RenderPipelineDescriptor {
104 label: Some("wgpu::uti::TextureBlitter::pipeline"),
105 layout: Some(&pipeline_layout),
106 vertex: VertexState {
107 module: &shader,
108 entry_point: Some("vs_main"),
109 compilation_options: PipelineCompilationOptions::default(),
110 buffers: &[],
111 },
112 primitive: PrimitiveState {
113 topology: PrimitiveTopology::TriangleList,
114 strip_index_format: None,
115 front_face: FrontFace::Ccw,
116 cull_mode: None,
117 unclipped_depth: false,
118 polygon_mode: wgt::PolygonMode::Fill,
119 conservative: false,
120 },
121 depth_stencil: None,
122 multisample: MultisampleState::default(),
123 fragment: Some(FragmentState {
124 module: &shader,
125 entry_point: Some("fs_main"),
126 compilation_options: PipelineCompilationOptions::default(),
127 targets: &[Some(ColorTargetState {
128 format: self.format,
129 blend: self.blend_state,
130 write_mask: ColorWrites::ALL,
131 })],
132 }),
133 multiview: None,
134 cache: None,
135 });
136
137 TextureBlitter {
138 pipeline,
139 bind_group_layout,
140 sampler,
141 }
142 }
143}
144
145pub struct TextureBlitter {
152 pipeline: RenderPipeline,
153 bind_group_layout: BindGroupLayout,
154 sampler: Sampler,
155}
156
157impl TextureBlitter {
158 pub fn new(device: &Device, format: TextureFormat) -> Self {
160 TextureBlitterBuilder::new(device, format).build()
161 }
162
163 pub fn copy(
171 &self,
172 device: &Device,
173 encoder: &mut CommandEncoder,
174 source: &TextureView,
175 target: &TextureView,
176 ) {
177 let bind_group = device.create_bind_group(&BindGroupDescriptor {
178 label: Some("wgpu::util::TextureBlitter::bind_group"),
179 layout: &self.bind_group_layout,
180 entries: &[
181 BindGroupEntry {
182 binding: 0,
183 resource: crate::BindingResource::TextureView(source),
184 },
185 BindGroupEntry {
186 binding: 1,
187 resource: crate::BindingResource::Sampler(&self.sampler),
188 },
189 ],
190 });
191
192 let mut pass = encoder.begin_render_pass(&RenderPassDescriptor {
193 label: Some("wgpu::util::TextureBlitter::pass"),
194 color_attachments: &[Some(crate::RenderPassColorAttachment {
195 view: target,
196 resolve_target: None,
197 ops: wgt::Operations {
198 load: LoadOp::Load,
199 store: StoreOp::Store,
200 },
201 })],
202 depth_stencil_attachment: None,
203 timestamp_writes: None,
204 occlusion_query_set: None,
205 });
206 pass.set_pipeline(&self.pipeline);
207 pass.set_bind_group(0, &bind_group, &[]);
208 pass.draw(0..3, 0..1);
209 }
210}