use super::effect::{self, EffectPipeline};
use super::params::ParamValue;
use super::preview::{fit_preview_dims, PREVIEW_DT};
use super::veil::{Veil, VeilRegistry};
struct PreviewTextures {
width: u32,
height: u32,
pingpong: [wgpu::Texture; 2],
views: [wgpu::TextureView; 2],
}
pub struct VeilPreviewRenderer {
textures: Option<PreviewTextures>,
sampler: Option<wgpu::Sampler>,
downscale: Option<EffectPipeline>,
}
impl VeilPreviewRenderer {
pub fn new() -> Self {
Self {
textures: None,
sampler: None,
downscale: None,
}
}
pub fn load_source(
&mut self,
device: &wgpu::Device,
queue: &wgpu::Queue,
source_view: &wgpu::TextureView,
source_width: u32,
source_height: u32,
format: wgpu::TextureFormat,
) {
self.ensure_sampler(device);
self.ensure_downscale(device, format);
let (pw, ph) = fit_preview_dims(source_width, source_height);
let realloc = match &self.textures {
Some(t) => t.width != pw || t.height != ph,
None => true,
};
if realloc {
self.textures = Some(make_textures(device, pw, ph, format));
}
let textures = self.textures.as_ref().unwrap();
let downscale = self.downscale.as_ref().unwrap();
let source_bg = effect::create_blit_bind_group(
device,
&downscale.bind_group_layout,
source_view,
self.sampler.as_ref().unwrap(),
"veil-preview-source-bg",
);
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("veil-preview-load-source"),
});
{
let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("veil-preview-downscale"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &textures.views[0],
resolve_target: None,
depth_slice: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
store: wgpu::StoreOp::Store,
},
})],
..Default::default()
});
rpass.set_pipeline(&downscale.pipeline);
rpass.set_bind_group(0, &source_bg, &[]);
rpass.draw(0..3, 0..1);
}
queue.submit([encoder.finish()]);
}
pub fn build_veil(
&self,
device: &wgpu::Device,
queue: &wgpu::Queue,
registry: &mut VeilRegistry,
type_id: &str,
params: &[ParamValue],
format: wgpu::TextureFormat,
) -> (Box<dyn Veil>, super::effect::EffectCache) {
let textures = self
.textures
.as_ref()
.expect("load_source must run before build_veil");
let veil = registry.create_veil(type_id, params, device, format);
let cache = veil.create_cache(
device,
queue,
&textures.views,
self.sampler.as_ref().unwrap(),
textures.width,
textures.height,
);
(veil, cache)
}
pub fn preview_size(&self) -> (u32, u32) {
self.textures
.as_ref()
.map(|t| (t.width, t.height))
.unwrap_or((0, 0))
}
pub fn frame_dt(&self) -> f32 {
PREVIEW_DT
}
pub fn encode_frame(
&self,
encoder: &mut wgpu::CommandEncoder,
veil: &dyn Veil,
cache: &super::effect::EffectCache,
) {
let textures = self.textures.as_ref().unwrap();
veil.encode(encoder, cache, 0, &textures.views[1]);
}
pub fn output_texture(&self) -> &wgpu::Texture {
&self.textures.as_ref().unwrap().pingpong[1]
}
fn ensure_sampler(&mut self, device: &wgpu::Device) {
if self.sampler.is_none() {
self.sampler = Some(device.create_sampler(&wgpu::SamplerDescriptor {
label: Some("veil-preview-sampler"),
mag_filter: wgpu::FilterMode::Linear,
min_filter: wgpu::FilterMode::Linear,
..Default::default()
}));
}
}
fn ensure_downscale(&mut self, device: &wgpu::Device, format: wgpu::TextureFormat) {
if self.downscale.is_none() {
self.downscale = Some(effect::create_downscale_pipeline(
device,
format,
"veil-preview-downscale",
));
}
}
}
impl Default for VeilPreviewRenderer {
fn default() -> Self {
Self::new()
}
}
fn make_textures(
device: &wgpu::Device,
width: u32,
height: u32,
format: wgpu::TextureFormat,
) -> PreviewTextures {
let make = |label: &str| {
device.create_texture(&wgpu::TextureDescriptor {
label: Some(label),
size: wgpu::Extent3d {
width,
height,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT
| wgpu::TextureUsages::TEXTURE_BINDING
| wgpu::TextureUsages::COPY_SRC
| wgpu::TextureUsages::COPY_DST,
view_formats: &[],
})
};
let pingpong = [make("veil-preview-input"), make("veil-preview-output")];
let views = [
pingpong[0].create_view(&wgpu::TextureViewDescriptor::default()),
pingpong[1].create_view(&wgpu::TextureViewDescriptor::default()),
];
PreviewTextures {
width,
height,
pingpong,
views,
}
}