1use crate::chain_textures::ChainTextures;
4use crate::config::RenderConfig;
5use crate::error::Result;
6use crate::noise::{NoisePack, NoiseTexture};
7use std::sync::Arc;
8
9pub struct GpuContext {
17 pub device: Arc<wgpu::Device>,
19
20 pub queue: Arc<wgpu::Queue>,
22
23 pub config: RenderConfig,
25
26 pub noise_lq_texture: wgpu::Texture,
31 pub noise_lq_view: wgpu::TextureView,
32 pub noise_mq_texture: wgpu::Texture,
33 pub noise_mq_view: wgpu::TextureView,
34 pub noise_hq_texture: wgpu::Texture,
35 pub noise_hq_view: wgpu::TextureView,
36 pub noisevol_lq_texture: wgpu::Texture,
37 pub noisevol_lq_view: wgpu::TextureView,
38 pub noisevol_hq_texture: wgpu::Texture,
39 pub noisevol_hq_view: wgpu::TextureView,
40 pub noise_texsizes: NoiseTexsizes,
43}
44
45#[derive(Debug, Clone, Copy)]
50pub struct NoiseTexsizes {
51 pub noise_lq: [f32; 4],
52 pub noise_mq: [f32; 4],
53 pub noise_hq: [f32; 4],
54 pub noisevol_lq: [f32; 4],
55 pub noisevol_hq: [f32; 4],
56}
57
58impl GpuContext {
59 pub async fn new(config: RenderConfig) -> Result<Self> {
61 let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
62 backends: wgpu::Backends::all(),
63 ..wgpu::InstanceDescriptor::new_without_display_handle()
64 });
65
66 let adapter = instance
67 .request_adapter(&wgpu::RequestAdapterOptions {
68 power_preference: wgpu::PowerPreference::HighPerformance,
69 compatible_surface: None,
70 force_fallback_adapter: false,
71 })
72 .await?;
73
74 let (device, queue) = adapter
79 .request_device(&wgpu::DeviceDescriptor {
80 label: Some("Milkdrop Device"),
81 required_features: wgpu::Features::empty(),
82 required_limits: wgpu::Limits {
83 max_sampled_textures_per_shader_stage: 24,
84 ..wgpu::Limits::default()
85 },
86 memory_hints: Default::default(),
87 experimental_features: wgpu::ExperimentalFeatures::default(),
88 trace: wgpu::Trace::Off,
89 })
90 .await?;
91
92 let device = Arc::new(device);
93 let queue = Arc::new(queue);
94
95 Ok(Self::build(device, queue, config))
96 }
97
98 pub fn from_device(
102 device: Arc<wgpu::Device>,
103 queue: Arc<wgpu::Queue>,
104 config: RenderConfig,
105 ) -> Self {
106 Self::build(device, queue, config)
107 }
108
109 fn build(device: Arc<wgpu::Device>, queue: Arc<wgpu::Queue>, config: RenderConfig) -> Self {
110 let noise = NoisePack::generate();
111 let (noise_lq_texture, noise_lq_view) =
112 Self::make_noise_2d(&device, &queue, &noise.noise_lq, "Noise LQ");
113 let (noise_mq_texture, noise_mq_view) =
114 Self::make_noise_2d(&device, &queue, &noise.noise_mq, "Noise MQ");
115 let (noise_hq_texture, noise_hq_view) =
116 Self::make_noise_2d(&device, &queue, &noise.noise_hq, "Noise HQ");
117 let (noisevol_lq_texture, noisevol_lq_view) =
118 Self::make_noise_3d(&device, &queue, &noise.noisevol_lq, "NoiseVol LQ");
119 let (noisevol_hq_texture, noisevol_hq_view) =
120 Self::make_noise_3d(&device, &queue, &noise.noisevol_hq, "NoiseVol HQ");
121 let noise_texsizes = NoiseTexsizes {
122 noise_lq: noise.noise_lq.texsize(),
123 noise_mq: noise.noise_mq.texsize(),
124 noise_hq: noise.noise_hq.texsize(),
125 noisevol_lq: noise.noisevol_lq.texsize(),
126 noisevol_hq: noise.noisevol_hq.texsize(),
127 };
128
129 Self {
130 device,
131 queue,
132 config,
133 noise_lq_texture,
134 noise_lq_view,
135 noise_mq_texture,
136 noise_mq_view,
137 noise_hq_texture,
138 noise_hq_view,
139 noisevol_lq_texture,
140 noisevol_lq_view,
141 noisevol_hq_texture,
142 noisevol_hq_view,
143 noise_texsizes,
144 }
145 }
146
147 fn make_noise_2d(
152 device: &wgpu::Device,
153 queue: &wgpu::Queue,
154 n: &NoiseTexture,
155 label: &str,
156 ) -> (wgpu::Texture, wgpu::TextureView) {
157 let tex = device.create_texture(&wgpu::TextureDescriptor {
158 label: Some(label),
159 size: wgpu::Extent3d {
160 width: n.width,
161 height: n.height,
162 depth_or_array_layers: 1,
163 },
164 mip_level_count: 1,
165 sample_count: 1,
166 dimension: wgpu::TextureDimension::D2,
167 format: wgpu::TextureFormat::Rgba8Unorm,
168 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
169 view_formats: &[],
170 });
171 queue.write_texture(
172 wgpu::TexelCopyTextureInfo {
173 texture: &tex,
174 mip_level: 0,
175 origin: wgpu::Origin3d::ZERO,
176 aspect: wgpu::TextureAspect::All,
177 },
178 &n.bytes,
179 wgpu::TexelCopyBufferLayout {
180 offset: 0,
181 bytes_per_row: Some(n.width * 4),
182 rows_per_image: Some(n.height),
183 },
184 wgpu::Extent3d {
185 width: n.width,
186 height: n.height,
187 depth_or_array_layers: 1,
188 },
189 );
190 let view = tex.create_view(&wgpu::TextureViewDescriptor::default());
191 (tex, view)
192 }
193
194 fn make_noise_3d(
196 device: &wgpu::Device,
197 queue: &wgpu::Queue,
198 n: &NoiseTexture,
199 label: &str,
200 ) -> (wgpu::Texture, wgpu::TextureView) {
201 let tex = device.create_texture(&wgpu::TextureDescriptor {
202 label: Some(label),
203 size: wgpu::Extent3d {
204 width: n.width,
205 height: n.height,
206 depth_or_array_layers: n.depth,
207 },
208 mip_level_count: 1,
209 sample_count: 1,
210 dimension: wgpu::TextureDimension::D3,
211 format: wgpu::TextureFormat::Rgba8Unorm,
212 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
213 view_formats: &[],
214 });
215 queue.write_texture(
216 wgpu::TexelCopyTextureInfo {
217 texture: &tex,
218 mip_level: 0,
219 origin: wgpu::Origin3d::ZERO,
220 aspect: wgpu::TextureAspect::All,
221 },
222 &n.bytes,
223 wgpu::TexelCopyBufferLayout {
224 offset: 0,
225 bytes_per_row: Some(n.width * 4),
226 rows_per_image: Some(n.height),
227 },
228 wgpu::Extent3d {
229 width: n.width,
230 height: n.height,
231 depth_or_array_layers: n.depth,
232 },
233 );
234 let view = tex.create_view(&wgpu::TextureViewDescriptor {
235 dimension: Some(wgpu::TextureViewDimension::D3),
236 ..Default::default()
237 });
238 (tex, view)
239 }
240
241 pub fn comp_aux_views_for<'a>(
246 &'a self,
247 chain: &'a ChainTextures,
248 ) -> crate::comp_pipeline::CompAuxViews<'a> {
249 crate::comp_pipeline::CompAuxViews {
250 blur1: &chain.blur1_texture_view,
251 blur2: &chain.blur2_texture_view,
252 blur3: &chain.blur3_texture_view,
253 noise_lq: &self.noise_lq_view,
254 noise_mq: &self.noise_mq_view,
255 noise_hq: &self.noise_hq_view,
256 noisevol_lq: &self.noisevol_lq_view,
257 noisevol_hq: &self.noisevol_hq_view,
258 prev_main: &chain.prev_texture_view,
259 }
260 }
261
262 pub fn set_resolution(&mut self, width: u32, height: u32) {
265 self.config.width = width;
266 self.config.height = height;
267 }
268
269 pub fn aspect_ratio(&self) -> f32 {
271 self.config.width as f32 / self.config.height as f32
272 }
273}
274
275#[cfg(test)]
276mod tests {
277 use super::*;
278
279 #[test]
280 fn test_gpu_context_creation() {
281 let config = RenderConfig::default();
282 let context = pollster::block_on(GpuContext::new(config));
283 assert!(context.is_ok());
284 }
285
286 #[test]
287 fn test_aspect_ratio() {
288 let config = RenderConfig {
289 width: 1920,
290 height: 1080,
291 ..Default::default()
292 };
293 let context = pollster::block_on(GpuContext::new(config)).unwrap();
294 assert!((context.aspect_ratio() - 16.0 / 9.0).abs() < 0.01);
295 }
296}