wgpu_native_texture_interop/raw_gl/
texture_normalizer.rs1use dpi::PhysicalSize;
8
9pub struct ImportedTextureNormalizer {
13 sampler: wgpu::Sampler,
14 render_pipeline: wgpu::RenderPipeline,
15 bind_group_layout: wgpu::BindGroupLayout,
16}
17
18impl ImportedTextureNormalizer {
19 pub fn new(device: &wgpu::Device) -> Self {
20 let vertex_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
21 label: Some("texture-normalize-vs"),
22 source: wgpu::ShaderSource::Wgsl(
23 r#"
24 @vertex
25 fn vs_main(@builtin(vertex_index) vertex_index: u32) -> @builtin(position) vec4<f32> {
26 let uv = vec2<f32>(f32(vertex_index >> 1u), f32(vertex_index & 1u)) * 2.0;
27 return vec4<f32>(uv * vec2<f32>(2.0, -2.0) + vec2<f32>(-1.0, 1.0), 0.0, 1.0);
28 }
29 "#
30 .into(),
31 ),
32 });
33
34 let fragment_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
35 label: Some("texture-normalize-fs"),
36 source: wgpu::ShaderSource::Wgsl(
37 r#"
38 @group(0) @binding(0) var source_texture: texture_2d<f32>;
39 @group(0) @binding(1) var source_sampler: sampler;
40
41 @fragment
42 fn fs_main(@builtin(position) position: vec4<f32>) -> @location(0) vec4<f32> {
43 let size = textureDimensions(source_texture);
44 let uv = position.xy / vec2<f32>(f32(size.x), f32(size.y));
45 let flipped_uv = vec2<f32>(uv.x, 1.0 - uv.y);
46 return textureSample(source_texture, source_sampler, flipped_uv);
47 }
48 "#
49 .into(),
50 ),
51 });
52
53 let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
54 label: Some("texture-normalize-bind-group-layout"),
55 entries: &[
56 wgpu::BindGroupLayoutEntry {
57 binding: 0,
58 visibility: wgpu::ShaderStages::FRAGMENT,
59 ty: wgpu::BindingType::Texture {
60 multisampled: false,
61 view_dimension: wgpu::TextureViewDimension::D2,
62 sample_type: wgpu::TextureSampleType::Float { filterable: true },
63 },
64 count: None,
65 },
66 wgpu::BindGroupLayoutEntry {
67 binding: 1,
68 visibility: wgpu::ShaderStages::FRAGMENT,
69 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
70 count: None,
71 },
72 ],
73 });
74
75 let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
76 label: Some("texture-normalize-sampler"),
77 compare: None,
78 border_color: None,
79 lod_min_clamp: 0.0,
80 lod_max_clamp: 0.0,
81 anisotropy_clamp: 1,
82 mag_filter: wgpu::FilterMode::Linear,
83 min_filter: wgpu::FilterMode::Linear,
84 mipmap_filter: wgpu::MipmapFilterMode::Nearest,
85 address_mode_u: wgpu::AddressMode::ClampToEdge,
86 address_mode_v: wgpu::AddressMode::ClampToEdge,
87 address_mode_w: wgpu::AddressMode::ClampToEdge,
88 });
89
90 let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
91 label: Some("texture-normalize-pipeline-layout"),
92 bind_group_layouts: &[Some(&bind_group_layout)],
93 immediate_size: 0,
94 });
95
96 let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
97 label: Some("texture-normalize-pipeline"),
98 layout: Some(&pipeline_layout),
99 vertex: wgpu::VertexState {
100 module: &vertex_shader,
101 entry_point: Some("vs_main"),
102 buffers: &[],
103 compilation_options: wgpu::PipelineCompilationOptions::default(),
104 },
105 fragment: Some(wgpu::FragmentState {
106 module: &fragment_shader,
107 entry_point: Some("fs_main"),
108 targets: &[Some(wgpu::ColorTargetState {
109 format: wgpu::TextureFormat::Rgba8Unorm,
110 blend: None,
111 write_mask: wgpu::ColorWrites::ALL,
112 })],
113 compilation_options: wgpu::PipelineCompilationOptions::default(),
114 }),
115 primitive: wgpu::PrimitiveState::default(),
116 depth_stencil: None,
117 multisample: wgpu::MultisampleState::default(),
118 multiview_mask: None,
119 cache: None,
120 });
121
122 Self {
123 bind_group_layout,
124 render_pipeline,
125 sampler,
126 }
127 }
128
129 pub fn normalize(
134 &self,
135 device: &wgpu::Device,
136 queue: &wgpu::Queue,
137 source_texture: &wgpu::Texture,
138 size: PhysicalSize<u32>,
139 ) -> wgpu::Texture {
140 let output = device.create_texture(&wgpu::TextureDescriptor {
141 label: Some("normalized-imported-frame"),
142 size: wgpu::Extent3d {
143 width: size.width,
144 height: size.height,
145 depth_or_array_layers: 1,
146 },
147 mip_level_count: 1,
148 sample_count: 1,
149 dimension: wgpu::TextureDimension::D2,
150 format: wgpu::TextureFormat::Rgba8Unorm,
151 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::RENDER_ATTACHMENT,
152 view_formats: &[],
153 });
154
155 let source_view = source_texture.create_view(&wgpu::TextureViewDescriptor::default());
156 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
157 label: Some("texture-normalize-bind-group"),
158 layout: &self.bind_group_layout,
159 entries: &[
160 wgpu::BindGroupEntry {
161 binding: 0,
162 resource: wgpu::BindingResource::TextureView(&source_view),
163 },
164 wgpu::BindGroupEntry {
165 binding: 1,
166 resource: wgpu::BindingResource::Sampler(&self.sampler),
167 },
168 ],
169 });
170
171 let target_view = output.create_view(&wgpu::TextureViewDescriptor::default());
172 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
173 label: Some("texture-normalize-encoder"),
174 });
175
176 {
177 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
178 label: Some("texture-normalize-pass"),
179 timestamp_writes: None,
180 occlusion_query_set: None,
181 depth_stencil_attachment: None,
182 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
183 view: &target_view,
184 depth_slice: None,
185 resolve_target: None,
186 ops: wgpu::Operations {
187 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
188 store: wgpu::StoreOp::Store,
189 },
190 })],
191 multiview_mask: None,
192 });
193 pass.set_pipeline(&self.render_pipeline);
194 pass.set_bind_group(0, &bind_group, &[]);
195 pass.draw(0..3, 0..1);
196 }
197
198 queue.submit(std::iter::once(encoder.finish()));
199 output
200 }
201}