1use core::ops::Deref;
3use craballoc::{
4 prelude::{Hybrid, SlabAllocator},
5 runtime::WgpuRuntime,
6};
7use std::sync::{Arc, RwLock};
8
9use crate::texture::Texture;
10
11use super::TonemapConstants;
12
13pub fn bindgroup_layout(device: &wgpu::Device, label: Option<&str>) -> wgpu::BindGroupLayout {
14 device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
15 label,
16 entries: &[
17 wgpu::BindGroupLayoutEntry {
19 binding: 0,
20 visibility: wgpu::ShaderStages::FRAGMENT,
21 ty: wgpu::BindingType::Buffer {
22 ty: wgpu::BufferBindingType::Storage { read_only: true },
23 has_dynamic_offset: false,
24 min_binding_size: None,
25 },
26 count: None,
27 },
28 wgpu::BindGroupLayoutEntry {
30 binding: 1,
31 visibility: wgpu::ShaderStages::FRAGMENT,
32 ty: wgpu::BindingType::Texture {
33 sample_type: wgpu::TextureSampleType::Float { filterable: true },
34 view_dimension: wgpu::TextureViewDimension::D2,
35 multisampled: false,
36 },
37 count: None,
38 },
39 wgpu::BindGroupLayoutEntry {
41 binding: 2,
42 visibility: wgpu::ShaderStages::FRAGMENT,
43 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
44 count: None,
45 },
46 ],
47 })
48}
49
50pub fn create_bindgroup(
51 device: &wgpu::Device,
52 label: Option<&str>,
53 hdr_texture: &Texture,
54 slab_buffer: &wgpu::Buffer,
55) -> wgpu::BindGroup {
56 device.create_bind_group(&wgpu::BindGroupDescriptor {
57 label,
58 layout: &bindgroup_layout(device, label),
59 entries: &[
60 wgpu::BindGroupEntry {
61 binding: 0,
62 resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
63 buffer: slab_buffer,
64 offset: 0,
65 size: None,
66 }),
67 },
68 wgpu::BindGroupEntry {
69 binding: 1,
70 resource: wgpu::BindingResource::TextureView(&hdr_texture.view),
71 },
72 wgpu::BindGroupEntry {
73 binding: 2,
74 resource: wgpu::BindingResource::Sampler(&hdr_texture.sampler),
75 },
76 ],
77 })
78}
79
80#[derive(Clone)]
89pub struct Tonemapping {
90 slab: SlabAllocator<WgpuRuntime>,
91 config: Hybrid<TonemapConstants>,
92 hdr_texture: Arc<RwLock<Texture>>,
93 bindgroup: Arc<RwLock<wgpu::BindGroup>>,
94 pipeline: Arc<wgpu::RenderPipeline>,
95}
96
97impl Tonemapping {
98 pub fn new(
99 runtime: &WgpuRuntime,
100 frame_texture_format: wgpu::TextureFormat,
101 hdr_texture: &Texture,
102 ) -> Self {
103 let slab = SlabAllocator::new(runtime, "tonemapping-slab", wgpu::BufferUsages::empty());
104 let config = slab.new_value(TonemapConstants::default());
105
106 let label = Some("tonemapping");
107 let slab_buffer = slab.commit();
108 let bindgroup = Arc::new(RwLock::new(create_bindgroup(
109 &runtime.device,
110 label,
111 hdr_texture,
112 &slab_buffer,
113 )));
114
115 let device = &runtime.device;
116 let vertex_linkage = crate::linkage::tonemapping_vertex::linkage(device);
117 let fragment_linkage = crate::linkage::tonemapping_fragment::linkage(device);
118 let hdr_layout = bindgroup_layout(device, label);
119 let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
120 label,
121 bind_group_layouts: &[&hdr_layout],
122 push_constant_ranges: &[],
123 });
124 let pipeline = Arc::new(
125 device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
126 label,
127 layout: Some(&layout),
128 vertex: wgpu::VertexState {
129 module: &vertex_linkage.module,
130 entry_point: Some(vertex_linkage.entry_point),
131 buffers: &[],
132 compilation_options: Default::default(),
133 },
134 primitive: wgpu::PrimitiveState {
135 topology: wgpu::PrimitiveTopology::TriangleList,
136 strip_index_format: None,
137 front_face: wgpu::FrontFace::Ccw,
138 cull_mode: Some(wgpu::Face::Back),
139 unclipped_depth: false,
140 polygon_mode: wgpu::PolygonMode::Fill,
141 conservative: false,
142 },
143 depth_stencil: None,
144 fragment: Some(wgpu::FragmentState {
145 module: &fragment_linkage.module,
146 entry_point: Some(fragment_linkage.entry_point),
147 targets: &[Some(wgpu::ColorTargetState {
148 format: frame_texture_format,
149 blend: Some(wgpu::BlendState::ALPHA_BLENDING),
150 write_mask: wgpu::ColorWrites::ALL,
151 })],
152 compilation_options: Default::default(),
153 }),
154 multisample: wgpu::MultisampleState::default(),
155 multiview: None,
156 cache: None,
157 }),
158 );
159 Self {
160 slab,
161 config,
162 hdr_texture: Arc::new(RwLock::new(hdr_texture.clone())),
163 bindgroup,
164 pipeline,
165 }
166 }
167
168 pub(crate) fn slab_allocator(&self) -> &SlabAllocator<WgpuRuntime> {
169 &self.slab
170 }
171
172 pub fn set_hdr_texture(&self, device: &wgpu::Device, hdr_texture: &Texture) {
173 let slab_buffer = self.slab.get_buffer().unwrap();
176 let bindgroup = create_bindgroup(device, Some("tonemapping"), hdr_texture, &slab_buffer);
177 *self.bindgroup.write().unwrap() = bindgroup;
179 *self.hdr_texture.write().unwrap() = hdr_texture.clone();
180 }
181
182 pub fn get_tonemapping_config(&self) -> TonemapConstants {
183 self.config.get()
184 }
185
186 pub fn set_tonemapping_config(&self, config: TonemapConstants) {
187 self.config.set(config);
188 }
189
190 pub fn render(&self, device: &wgpu::Device, queue: &wgpu::Queue, view: &wgpu::TextureView) {
191 let label = Some("tonemapping render");
192 assert!(!self.slab.commit().is_new_this_commit());
193
194 let bindgroup = self.bindgroup.read().unwrap();
196 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label });
197 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
198 label,
199 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
200 view,
201 resolve_target: None,
202 ops: wgpu::Operations {
203 load: wgpu::LoadOp::Load,
204 store: wgpu::StoreOp::Store,
205 },
206 depth_slice: None,
207 })],
208 depth_stencil_attachment: None,
209 ..Default::default()
210 });
211 render_pass.set_pipeline(&self.pipeline);
212 render_pass.set_bind_group(0, Some(bindgroup.deref()), &[]);
213 let id = self.config.id().into();
214 render_pass.draw(0..6, id..id + 1);
215 drop(render_pass);
216
217 queue.submit(std::iter::once(encoder.finish()));
218 }
219}