const READ_MIRROR_INITIAL_DIM: u32 = 1;
pub struct Scratch {
write_texture: wgpu::Texture,
write_view: wgpu::TextureView,
write_bind_group: wgpu::BindGroup,
write_w: u32,
write_h: u32,
read_mirror_texture: wgpu::Texture,
read_mirror_view: wgpu::TextureView,
read_mirror_bind_group: wgpu::BindGroup,
read_w: u32,
read_h: u32,
read_origin_cache: Option<[u32; 2]>,
canvas_copy_bgl: wgpu::BindGroupLayout,
read_mirror_sampler: wgpu::Sampler,
write_sampler: wgpu::Sampler,
}
impl Scratch {
pub fn new(
device: &wgpu::Device,
layer_w: u32,
layer_h: u32,
canvas_copy_bgl: &wgpu::BindGroupLayout,
canvas_copy_sampler: &wgpu::Sampler,
) -> Self {
let write_sampler = device.create_sampler(&wgpu::SamplerDescriptor {
label: Some("scratch-write-sampler"),
mag_filter: wgpu::FilterMode::Nearest,
min_filter: wgpu::FilterMode::Nearest,
..Default::default()
});
let read_mirror_sampler = canvas_copy_sampler.clone();
let (write_texture, write_view) = create_write_texture(device, layer_w, layer_h);
let write_bind_group =
build_write_bind_group(device, canvas_copy_bgl, &write_view, &write_sampler);
let (read_mirror_texture, read_mirror_view) =
create_read_mirror_texture(device, READ_MIRROR_INITIAL_DIM, READ_MIRROR_INITIAL_DIM);
let read_mirror_bind_group = build_read_mirror_bind_group(
device,
canvas_copy_bgl,
&read_mirror_view,
canvas_copy_sampler,
);
Self {
write_texture,
write_view,
write_bind_group,
write_w: layer_w,
write_h: layer_h,
read_mirror_texture,
read_mirror_view,
read_mirror_bind_group,
read_w: READ_MIRROR_INITIAL_DIM,
read_h: READ_MIRROR_INITIAL_DIM,
read_origin_cache: None,
canvas_copy_bgl: canvas_copy_bgl.clone(),
read_mirror_sampler,
write_sampler,
}
}
pub fn write_texture(&self) -> &wgpu::Texture {
&self.write_texture
}
pub fn clear_to_transparent(&self, encoder: &mut wgpu::CommandEncoder) {
let _ = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("scratch-clear-transparent"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &self.write_view,
resolve_target: None,
depth_slice: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
store: wgpu::StoreOp::Store,
},
})],
..Default::default()
});
}
pub fn seed_from_pre_stroke(
&self,
encoder: &mut wgpu::CommandEncoder,
pre_stroke: &wgpu::Texture,
) {
encoder.copy_texture_to_texture(
wgpu::TexelCopyTextureInfo {
texture: pre_stroke,
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
aspect: wgpu::TextureAspect::All,
},
wgpu::TexelCopyTextureInfo {
texture: &self.write_texture,
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
aspect: wgpu::TextureAspect::All,
},
wgpu::Extent3d {
width: self.write_w,
height: self.write_h,
depth_or_array_layers: 1,
},
);
}
pub fn write_view(&self) -> &wgpu::TextureView {
&self.write_view
}
pub fn write_bind_group(&self) -> &wgpu::BindGroup {
&self.write_bind_group
}
pub fn read_mirror_bind_group(&self) -> &wgpu::BindGroup {
&self.read_mirror_bind_group
}
pub fn read_mirror_texture(&self) -> &wgpu::Texture {
&self.read_mirror_texture
}
pub fn write_dimensions(&self) -> (u32, u32) {
(self.write_w, self.write_h)
}
pub fn reset_read_origin_cache(&mut self) {
self.read_origin_cache = None;
}
pub fn sync_read_mirror(
&mut self,
device: &wgpu::Device,
encoder: &mut wgpu::CommandEncoder,
origin_x: u32,
origin_y: u32,
w: u32,
h: u32,
) {
if w == 0 || h == 0 {
return;
}
if w > self.read_w || h > self.read_h {
self.grow_read_mirror(device, w.max(self.read_w), h.max(self.read_h));
}
if self.read_origin_cache == Some([origin_x, origin_y]) {
return;
}
encoder.copy_texture_to_texture(
wgpu::TexelCopyTextureInfo {
texture: &self.write_texture,
mip_level: 0,
origin: wgpu::Origin3d {
x: origin_x,
y: origin_y,
z: 0,
},
aspect: wgpu::TextureAspect::All,
},
wgpu::TexelCopyTextureInfo {
texture: &self.read_mirror_texture,
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
aspect: wgpu::TextureAspect::All,
},
wgpu::Extent3d {
width: w,
height: h,
depth_or_array_layers: 1,
},
);
self.read_origin_cache = Some([origin_x, origin_y]);
}
pub fn grow_write(
&mut self,
device: &wgpu::Device,
encoder: &mut wgpu::CommandEncoder,
new_w: u32,
new_h: u32,
dst_offset_x: u32,
dst_offset_y: u32,
) {
if new_w == self.write_w && new_h == self.write_h && dst_offset_x == 0 && dst_offset_y == 0
{
return;
}
let target_w = new_w.max(self.write_w);
let target_h = new_h.max(self.write_h);
let (new_texture, new_view) = create_write_texture(device, target_w, target_h);
if self.write_w > 0 && self.write_h > 0 {
encoder.copy_texture_to_texture(
wgpu::TexelCopyTextureInfo {
texture: &self.write_texture,
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
aspect: wgpu::TextureAspect::All,
},
wgpu::TexelCopyTextureInfo {
texture: &new_texture,
mip_level: 0,
origin: wgpu::Origin3d {
x: dst_offset_x,
y: dst_offset_y,
z: 0,
},
aspect: wgpu::TextureAspect::All,
},
wgpu::Extent3d {
width: self.write_w,
height: self.write_h,
depth_or_array_layers: 1,
},
);
}
let new_bind_group = build_write_bind_group(
device,
&self.canvas_copy_bgl,
&new_view,
&self.write_sampler,
);
self.write_texture = new_texture;
self.write_view = new_view;
self.write_bind_group = new_bind_group;
self.write_w = target_w;
self.write_h = target_h;
self.read_origin_cache = None;
}
fn grow_read_mirror(&mut self, device: &wgpu::Device, new_w: u32, new_h: u32) {
let (new_texture, new_view) = create_read_mirror_texture(device, new_w, new_h);
let new_read_bg = build_read_mirror_bind_group(
device,
&self.canvas_copy_bgl,
&new_view,
&self.read_mirror_sampler,
);
self.read_mirror_texture = new_texture;
self.read_mirror_view = new_view;
self.read_mirror_bind_group = new_read_bg;
self.read_w = new_w;
self.read_h = new_h;
self.read_origin_cache = None;
}
}
fn create_write_texture(
device: &wgpu::Device,
width: u32,
height: u32,
) -> (wgpu::Texture, wgpu::TextureView) {
let texture = device.create_texture(&wgpu::TextureDescriptor {
label: Some("scratch-write"),
size: wgpu::Extent3d {
width,
height,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8Unorm,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT
| wgpu::TextureUsages::COPY_SRC
| wgpu::TextureUsages::COPY_DST
| wgpu::TextureUsages::TEXTURE_BINDING,
view_formats: &[],
});
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
(texture, view)
}
fn create_read_mirror_texture(
device: &wgpu::Device,
width: u32,
height: u32,
) -> (wgpu::Texture, wgpu::TextureView) {
let texture = device.create_texture(&wgpu::TextureDescriptor {
label: Some("scratch-read-mirror"),
size: wgpu::Extent3d {
width,
height,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8Unorm,
usage: wgpu::TextureUsages::COPY_DST | wgpu::TextureUsages::TEXTURE_BINDING,
view_formats: &[],
});
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
(texture, view)
}
fn build_write_bind_group(
device: &wgpu::Device,
canvas_copy_bgl: &wgpu::BindGroupLayout,
view: &wgpu::TextureView,
sampler: &wgpu::Sampler,
) -> wgpu::BindGroup {
device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("scratch-write-bg"),
layout: canvas_copy_bgl,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(view),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(sampler),
},
],
})
}
fn build_read_mirror_bind_group(
device: &wgpu::Device,
canvas_copy_bgl: &wgpu::BindGroupLayout,
view: &wgpu::TextureView,
sampler: &wgpu::Sampler,
) -> wgpu::BindGroup {
device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("scratch-read-mirror-bg"),
layout: canvas_copy_bgl,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(view),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(sampler),
},
],
})
}