use crate::brush::composite_pipeline::{CompositePipeline, CompositeUniforms};
use crate::brush::pipeline::BrushPipelines;
use crate::gpu::paint_target::GpuPaintTarget;
pub trait BrushPaintTargetExt {
fn commit_brush_dab(
&self,
encoder: &mut wgpu::CommandEncoder,
brush_pipelines: &BrushPipelines,
queue: &wgpu::Queue,
scratch_bg: &wgpu::BindGroup,
selection_bg: &wgpu::BindGroup,
pre_stroke_bg: &wgpu::BindGroup,
opacity: f32,
blend_mode: u32,
fg_premultiplied: bool,
);
fn save_pre_stroke_snapshot(
&self,
device: &wgpu::Device,
encoder: &mut wgpu::CommandEncoder,
brush_pipelines: &BrushPipelines,
snapshot_view: &wgpu::TextureView,
snapshot_texture: &wgpu::Texture,
);
fn commit_scratch_blit(
&self,
device: &wgpu::Device,
encoder: &mut wgpu::CommandEncoder,
brush_pipelines: &BrushPipelines,
scratch_view: &wgpu::TextureView,
scratch_texture: &wgpu::Texture,
);
}
impl BrushPaintTargetExt for GpuPaintTarget<'_> {
fn commit_brush_dab(
&self,
encoder: &mut wgpu::CommandEncoder,
brush_pipelines: &BrushPipelines,
queue: &wgpu::Queue,
scratch_bg: &wgpu::BindGroup,
selection_bg: &wgpu::BindGroup,
pre_stroke_bg: &wgpu::BindGroup,
opacity: f32,
blend_mode: u32,
fg_premultiplied: bool,
) {
let canvas_ext = self.canvas_extent();
let layer_w = canvas_ext.width as f32;
let layer_h = canvas_ext.height as f32;
let layer_off_x = canvas_ext.x0() as f32;
let layer_off_y = canvas_ext.y0() as f32;
let (cw, ch) = self.canvas_size();
let (cox, coy) = self.canvas_origin();
let uniforms = CompositeUniforms {
origin: [layer_off_x, layer_off_y],
size: [layer_w, layer_h],
target_offset: [layer_off_x, layer_off_y],
target_size: [layer_w, layer_h],
canvas_size: [cw as f32, ch as f32],
canvas_origin: [cox as f32, coy as f32],
uv_min: [0.0, 0.0],
uv_max: [1.0, 1.0],
blend_mode,
fg_premultiplied: u32::from(fg_premultiplied),
stroke_opacity: opacity,
apply_selection: 0,
};
let composite = brush_pipelines.get::<CompositePipeline>("composite");
let offset = composite.write_uniforms(queue, &uniforms);
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("paint-target-commit-brush-dab"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: self.view(),
resolve_target: None,
depth_slice: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Load,
store: wgpu::StoreOp::Store,
},
})],
..Default::default()
});
pass.set_viewport(0.0, 0.0, layer_w, layer_h, 0.0, 1.0);
pass.set_pipeline(composite.pipeline(self.format()));
pass.set_bind_group(0, composite.uniform_bind_group(), &[offset]);
pass.set_bind_group(1, scratch_bg, &[]);
pass.set_bind_group(2, selection_bg, &[]);
pass.set_bind_group(3, pre_stroke_bg, &[]);
pass.draw(0..6, 0..1);
}
fn save_pre_stroke_snapshot(
&self,
device: &wgpu::Device,
encoder: &mut wgpu::CommandEncoder,
brush_pipelines: &BrushPipelines,
snapshot_view: &wgpu::TextureView,
snapshot_texture: &wgpu::Texture,
) {
let extent = self.layer_extent();
if self.format() != wgpu::TextureFormat::R8Unorm {
encoder.copy_texture_to_texture(
wgpu::TexelCopyTextureInfo {
texture: self.texture(),
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
aspect: wgpu::TextureAspect::All,
},
wgpu::TexelCopyTextureInfo {
texture: snapshot_texture,
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
aspect: wgpu::TextureAspect::All,
},
wgpu::Extent3d {
width: extent.width,
height: extent.height,
depth_or_array_layers: 1,
},
);
return;
}
let _ = snapshot_texture; let source_bg = brush_pipelines.create_blit_source_bind_group(device, self.view());
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("paint-target-save-pre-stroke-r8"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: snapshot_view,
resolve_target: None,
depth_slice: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
store: wgpu::StoreOp::Store,
},
})],
..Default::default()
});
pass.set_viewport(
0.0,
0.0,
extent.width as f32,
extent.height as f32,
0.0,
1.0,
);
pass.set_pipeline(brush_pipelines.mask_blit_pipeline());
pass.set_bind_group(0, &source_bg, &[]);
pass.draw(0..3, 0..1);
}
fn commit_scratch_blit(
&self,
device: &wgpu::Device,
encoder: &mut wgpu::CommandEncoder,
brush_pipelines: &BrushPipelines,
scratch_view: &wgpu::TextureView,
scratch_texture: &wgpu::Texture,
) {
let extent = self.layer_extent();
if self.format() != wgpu::TextureFormat::R8Unorm {
encoder.copy_texture_to_texture(
wgpu::TexelCopyTextureInfo {
texture: scratch_texture,
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
aspect: wgpu::TextureAspect::All,
},
wgpu::TexelCopyTextureInfo {
texture: self.texture(),
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
aspect: wgpu::TextureAspect::All,
},
wgpu::Extent3d {
width: extent.width,
height: extent.height,
depth_or_array_layers: 1,
},
);
return;
}
let _ = scratch_texture; let source_bg = brush_pipelines.create_blit_source_bind_group(device, scratch_view);
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("paint-target-commit-scratch-blit-r8"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: self.view(),
resolve_target: None,
depth_slice: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
store: wgpu::StoreOp::Store,
},
})],
..Default::default()
});
pass.set_viewport(
0.0,
0.0,
extent.width as f32,
extent.height as f32,
0.0,
1.0,
);
pass.set_pipeline(brush_pipelines.scratch_blit_r8_pipeline());
pass.set_bind_group(0, &source_bg, &[]);
pass.draw(0..3, 0..1);
}
}