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