Compare commits
No commits in common. "63a8080e60254281662802c3087c228057f517ec" and "e500914fa0da9f21ba337d3448f742ebfe0837c3" have entirely different histories.
63a8080e60
...
e500914fa0
|
|
@ -10,22 +10,22 @@ const BUNDLED_MODELS: &[BundledModel] = &[
|
|||
BundledModel {
|
||||
name: "BossSD1",
|
||||
filename: "BossSD1-WaveNet.nam",
|
||||
data: include_bytes!("../../../../../src/assets/nam_models/BossSD1-WaveNet.nam"),
|
||||
data: include_bytes!("../../../../../vendor/NeuralAudio/Utils/Models/BossSD1-WaveNet.nam"),
|
||||
},
|
||||
BundledModel {
|
||||
name: "DeluxeReverb",
|
||||
filename: "DeluxeReverb.nam",
|
||||
data: include_bytes!("../../../../../src/assets/nam_models/DeluxeReverb.nam"),
|
||||
data: include_bytes!("../../../../../vendor/NeuralAudio/Utils/Models/DeluxeReverb.nam"),
|
||||
},
|
||||
BundledModel {
|
||||
name: "DingwallBass",
|
||||
filename: "DingwallBass.nam",
|
||||
data: include_bytes!("../../../../../src/assets/nam_models/DingwallBass.nam"),
|
||||
data: include_bytes!("../../../../../vendor/NeuralAudio/Utils/Models/DingwallBass.nam"),
|
||||
},
|
||||
BundledModel {
|
||||
name: "Rhythm",
|
||||
filename: "Rhythm.nam",
|
||||
data: include_bytes!("../../../../../src/assets/nam_models/Rhythm.nam"),
|
||||
data: include_bytes!("../../../../../vendor/NeuralAudio/Utils/Models/Rhythm.nam"),
|
||||
},
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -139,8 +139,7 @@ impl BrushEngine {
|
|||
if stroke.points.len() < 2 {
|
||||
if let Some(pt) = stroke.points.first() {
|
||||
let r = stroke.brush_settings.radius_at_pressure(pt.pressure);
|
||||
let raw_o = stroke.brush_settings.opacity_at_pressure(pt.pressure);
|
||||
let o = 1.0 - (1.0 - raw_o).powf(stroke.brush_settings.dabs_per_radius * 0.5);
|
||||
let o = stroke.brush_settings.opacity_at_pressure(pt.pressure);
|
||||
// Single-tap smudge has no direction — skip (same as CPU engine)
|
||||
if !matches!(stroke.blend_mode, RasterBlendMode::Smudge) {
|
||||
push_dab(&mut dabs, &mut bbox, pt.x, pt.y, r, o, 0.0, 0.0, 0.0);
|
||||
|
|
@ -178,12 +177,7 @@ impl BrushEngine {
|
|||
let y2 = p0.y + t * dy;
|
||||
let pressure2 = p0.pressure + t * (p1.pressure - p0.pressure);
|
||||
let radius2 = stroke.brush_settings.radius_at_pressure(pressure2);
|
||||
let raw_opacity = stroke.brush_settings.opacity_at_pressure(pressure2);
|
||||
// Normalize per-dab opacity so dense dabs don't saturate faster than sparse ones.
|
||||
// Formula: per_dab = 1 − (1 − raw)^(dabs_per_radius / 2)
|
||||
// Derivation: N = 2/dabs_per_radius dabs cover one full diameter at the centre;
|
||||
// accumulated = 1 − (1 − per_dab)^N = raw → per_dab = 1 − (1−raw)^(dabs_per_radius/2)
|
||||
let opacity2 = 1.0 - (1.0 - raw_opacity).powf(stroke.brush_settings.dabs_per_radius * 0.5);
|
||||
let opacity2 = stroke.brush_settings.opacity_at_pressure(pressure2);
|
||||
|
||||
if matches!(stroke.blend_mode, RasterBlendMode::Smudge) {
|
||||
let ndx = dx / seg_len;
|
||||
|
|
|
|||
|
|
@ -276,10 +276,12 @@ impl GpuBrushEngine {
|
|||
|
||||
/// Dispatch the brush compute shader for `dabs` onto the canvas of `keyframe_id`.
|
||||
///
|
||||
/// Each dab is dispatched serially: copy the dab's bounding box from src→dst,
|
||||
/// dispatch the compute shader, then swap. The bbox-only copy is safe because
|
||||
/// neither normal/erase nor smudge reads outside the current dab's radius.
|
||||
/// Each dab is dispatched as a separate copy+compute+swap so that every dab
|
||||
/// reads the result of the previous one. This is required for the smudge tool:
|
||||
/// if all dabs were batched into one dispatch they would all read the pre-batch
|
||||
/// canvas state, breaking the carry-forward that makes smudge drag pixels along.
|
||||
///
|
||||
/// `dab_bbox` is the union bounding box (unused here; kept for API compat).
|
||||
/// If `dabs` is empty, does nothing.
|
||||
pub fn render_dabs(
|
||||
&mut self,
|
||||
|
|
@ -292,41 +294,56 @@ impl GpuBrushEngine {
|
|||
canvas_h: u32,
|
||||
) {
|
||||
if dabs.is_empty() { return; }
|
||||
|
||||
if !self.canvases.contains_key(&keyframe_id) { return; }
|
||||
|
||||
for dab in dabs {
|
||||
let r_fringe = dab.radius + 1.0;
|
||||
let x0 = ((dab.x - r_fringe).floor() as i32).max(0) as u32;
|
||||
let y0 = ((dab.y - r_fringe).floor() as i32).max(0) as u32;
|
||||
let x1 = ((dab.x + r_fringe).ceil() as i32).min(canvas_w as i32) as u32;
|
||||
let y1 = ((dab.y + r_fringe).ceil() as i32).min(canvas_h as i32) as u32;
|
||||
if x1 <= x0 || y1 <= y0 { continue; }
|
||||
let full_extent = wgpu::Extent3d {
|
||||
width: self.canvases[&keyframe_id].width,
|
||||
height: self.canvases[&keyframe_id].height,
|
||||
depth_or_array_layers: 1,
|
||||
};
|
||||
|
||||
let bbox_w = x1 - x0;
|
||||
let bbox_h = y1 - y0;
|
||||
for dab in dabs {
|
||||
// Per-dab bounding box
|
||||
let r_fringe = dab.radius + 1.0;
|
||||
let dx0 = (dab.x - r_fringe).floor() as i32;
|
||||
let dy0 = (dab.y - r_fringe).floor() as i32;
|
||||
let dx1 = (dab.x + r_fringe).ceil() as i32;
|
||||
let dy1 = (dab.y + r_fringe).ceil() as i32;
|
||||
|
||||
let x0 = dx0.max(0) as u32;
|
||||
let y0 = dy0.max(0) as u32;
|
||||
let x1 = (dx1.min(canvas_w as i32 - 1)).max(0) as u32;
|
||||
let y1 = (dy1.min(canvas_h as i32 - 1)).max(0) as u32;
|
||||
if x1 < x0 || y1 < y0 { continue; }
|
||||
|
||||
let bbox_w = x1 - x0 + 1;
|
||||
let bbox_h = y1 - y0 + 1;
|
||||
|
||||
let canvas = self.canvases.get_mut(&keyframe_id).unwrap();
|
||||
|
||||
// Pre-fill dst from src so pixels outside this dab's bbox are preserved.
|
||||
let mut copy_enc = device.create_command_encoder(
|
||||
&wgpu::CommandEncoderDescriptor { label: Some("canvas_copy_encoder") },
|
||||
);
|
||||
copy_enc.copy_texture_to_texture(
|
||||
wgpu::TexelCopyTextureInfo {
|
||||
texture: canvas.src(),
|
||||
texture: canvas.src(),
|
||||
mip_level: 0,
|
||||
origin: wgpu::Origin3d { x: x0, y: y0, z: 0 },
|
||||
aspect: wgpu::TextureAspect::All,
|
||||
origin: wgpu::Origin3d::ZERO,
|
||||
aspect: wgpu::TextureAspect::All,
|
||||
},
|
||||
wgpu::TexelCopyTextureInfo {
|
||||
texture: canvas.dst(),
|
||||
texture: canvas.dst(),
|
||||
mip_level: 0,
|
||||
origin: wgpu::Origin3d { x: x0, y: y0, z: 0 },
|
||||
aspect: wgpu::TextureAspect::All,
|
||||
origin: wgpu::Origin3d::ZERO,
|
||||
aspect: wgpu::TextureAspect::All,
|
||||
},
|
||||
wgpu::Extent3d { width: bbox_w, height: bbox_h, depth_or_array_layers: 1 },
|
||||
full_extent,
|
||||
);
|
||||
queue.submit(Some(copy_enc.finish()));
|
||||
|
||||
// Upload single-dab buffer and params
|
||||
let dab_bytes = bytemuck::bytes_of(dab);
|
||||
let dab_buf = device.create_buffer(&wgpu::BufferDescriptor {
|
||||
label: Some("dab_storage_buf"),
|
||||
|
|
@ -337,8 +354,8 @@ impl GpuBrushEngine {
|
|||
queue.write_buffer(&dab_buf, 0, dab_bytes);
|
||||
|
||||
let params = DabParams {
|
||||
bbox_x0: x0 as i32,
|
||||
bbox_y0: y0 as i32,
|
||||
bbox_x0: x0 as i32,
|
||||
bbox_y0: y0 as i32,
|
||||
bbox_w,
|
||||
bbox_h,
|
||||
num_dabs: 1,
|
||||
|
|
@ -358,10 +375,22 @@ impl GpuBrushEngine {
|
|||
label: Some("brush_dab_bg"),
|
||||
layout: &self.compute_bg_layout,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry { binding: 0, resource: dab_buf.as_entire_binding() },
|
||||
wgpu::BindGroupEntry { binding: 1, resource: params_buf.as_entire_binding() },
|
||||
wgpu::BindGroupEntry { binding: 2, resource: wgpu::BindingResource::TextureView(canvas.src_view()) },
|
||||
wgpu::BindGroupEntry { binding: 3, resource: wgpu::BindingResource::TextureView(canvas.dst_view()) },
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: dab_buf.as_entire_binding(),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 1,
|
||||
resource: params_buf.as_entire_binding(),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 2,
|
||||
resource: wgpu::BindingResource::TextureView(canvas.src_view()),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 3,
|
||||
resource: wgpu::BindingResource::TextureView(canvas.dst_view()),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
|
|
@ -370,13 +399,18 @@ impl GpuBrushEngine {
|
|||
);
|
||||
{
|
||||
let mut pass = compute_enc.begin_compute_pass(
|
||||
&wgpu::ComputePassDescriptor { label: Some("brush_dab_pass"), timestamp_writes: None },
|
||||
&wgpu::ComputePassDescriptor {
|
||||
label: Some("brush_dab_pass"),
|
||||
timestamp_writes: None,
|
||||
},
|
||||
);
|
||||
pass.set_pipeline(&self.compute_pipeline);
|
||||
pass.set_bind_group(0, &bg, &[]);
|
||||
pass.dispatch_workgroups(bbox_w.div_ceil(8), bbox_h.div_ceil(8), 1);
|
||||
}
|
||||
queue.submit(Some(compute_enc.finish()));
|
||||
|
||||
// Swap: the just-written dst becomes src for the next dab.
|
||||
canvas.swap();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue