Compare commits
No commits in common. "63a8080e60254281662802c3087c228057f517ec" and "e500914fa0da9f21ba337d3448f742ebfe0837c3" have entirely different histories.
63a8080e60
...
e500914fa0
|
|
@ -10,22 +10,22 @@ const BUNDLED_MODELS: &[BundledModel] = &[
|
||||||
BundledModel {
|
BundledModel {
|
||||||
name: "BossSD1",
|
name: "BossSD1",
|
||||||
filename: "BossSD1-WaveNet.nam",
|
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 {
|
BundledModel {
|
||||||
name: "DeluxeReverb",
|
name: "DeluxeReverb",
|
||||||
filename: "DeluxeReverb.nam",
|
filename: "DeluxeReverb.nam",
|
||||||
data: include_bytes!("../../../../../src/assets/nam_models/DeluxeReverb.nam"),
|
data: include_bytes!("../../../../../vendor/NeuralAudio/Utils/Models/DeluxeReverb.nam"),
|
||||||
},
|
},
|
||||||
BundledModel {
|
BundledModel {
|
||||||
name: "DingwallBass",
|
name: "DingwallBass",
|
||||||
filename: "DingwallBass.nam",
|
filename: "DingwallBass.nam",
|
||||||
data: include_bytes!("../../../../../src/assets/nam_models/DingwallBass.nam"),
|
data: include_bytes!("../../../../../vendor/NeuralAudio/Utils/Models/DingwallBass.nam"),
|
||||||
},
|
},
|
||||||
BundledModel {
|
BundledModel {
|
||||||
name: "Rhythm",
|
name: "Rhythm",
|
||||||
filename: "Rhythm.nam",
|
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 stroke.points.len() < 2 {
|
||||||
if let Some(pt) = stroke.points.first() {
|
if let Some(pt) = stroke.points.first() {
|
||||||
let r = stroke.brush_settings.radius_at_pressure(pt.pressure);
|
let r = stroke.brush_settings.radius_at_pressure(pt.pressure);
|
||||||
let raw_o = stroke.brush_settings.opacity_at_pressure(pt.pressure);
|
let 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);
|
|
||||||
// Single-tap smudge has no direction — skip (same as CPU engine)
|
// Single-tap smudge has no direction — skip (same as CPU engine)
|
||||||
if !matches!(stroke.blend_mode, RasterBlendMode::Smudge) {
|
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);
|
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 y2 = p0.y + t * dy;
|
||||||
let pressure2 = p0.pressure + t * (p1.pressure - p0.pressure);
|
let pressure2 = p0.pressure + t * (p1.pressure - p0.pressure);
|
||||||
let radius2 = stroke.brush_settings.radius_at_pressure(pressure2);
|
let radius2 = stroke.brush_settings.radius_at_pressure(pressure2);
|
||||||
let raw_opacity = stroke.brush_settings.opacity_at_pressure(pressure2);
|
let opacity2 = 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);
|
|
||||||
|
|
||||||
if matches!(stroke.blend_mode, RasterBlendMode::Smudge) {
|
if matches!(stroke.blend_mode, RasterBlendMode::Smudge) {
|
||||||
let ndx = dx / seg_len;
|
let ndx = dx / seg_len;
|
||||||
|
|
|
||||||
|
|
@ -276,10 +276,12 @@ impl GpuBrushEngine {
|
||||||
|
|
||||||
/// Dispatch the brush compute shader for `dabs` onto the canvas of `keyframe_id`.
|
/// 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,
|
/// Each dab is dispatched as a separate copy+compute+swap so that every dab
|
||||||
/// dispatch the compute shader, then swap. The bbox-only copy is safe because
|
/// reads the result of the previous one. This is required for the smudge tool:
|
||||||
/// neither normal/erase nor smudge reads outside the current dab's radius.
|
/// 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.
|
/// If `dabs` is empty, does nothing.
|
||||||
pub fn render_dabs(
|
pub fn render_dabs(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|
@ -292,21 +294,35 @@ impl GpuBrushEngine {
|
||||||
canvas_h: u32,
|
canvas_h: u32,
|
||||||
) {
|
) {
|
||||||
if dabs.is_empty() { return; }
|
if dabs.is_empty() { return; }
|
||||||
|
|
||||||
if !self.canvases.contains_key(&keyframe_id) { return; }
|
if !self.canvases.contains_key(&keyframe_id) { return; }
|
||||||
|
|
||||||
for dab in dabs {
|
let full_extent = wgpu::Extent3d {
|
||||||
let r_fringe = dab.radius + 1.0;
|
width: self.canvases[&keyframe_id].width,
|
||||||
let x0 = ((dab.x - r_fringe).floor() as i32).max(0) as u32;
|
height: self.canvases[&keyframe_id].height,
|
||||||
let y0 = ((dab.y - r_fringe).floor() as i32).max(0) as u32;
|
depth_or_array_layers: 1,
|
||||||
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 bbox_w = x1 - x0;
|
for dab in dabs {
|
||||||
let bbox_h = y1 - y0;
|
// 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();
|
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(
|
let mut copy_enc = device.create_command_encoder(
|
||||||
&wgpu::CommandEncoderDescriptor { label: Some("canvas_copy_encoder") },
|
&wgpu::CommandEncoderDescriptor { label: Some("canvas_copy_encoder") },
|
||||||
);
|
);
|
||||||
|
|
@ -314,19 +330,20 @@ impl GpuBrushEngine {
|
||||||
wgpu::TexelCopyTextureInfo {
|
wgpu::TexelCopyTextureInfo {
|
||||||
texture: canvas.src(),
|
texture: canvas.src(),
|
||||||
mip_level: 0,
|
mip_level: 0,
|
||||||
origin: wgpu::Origin3d { x: x0, y: y0, z: 0 },
|
origin: wgpu::Origin3d::ZERO,
|
||||||
aspect: wgpu::TextureAspect::All,
|
aspect: wgpu::TextureAspect::All,
|
||||||
},
|
},
|
||||||
wgpu::TexelCopyTextureInfo {
|
wgpu::TexelCopyTextureInfo {
|
||||||
texture: canvas.dst(),
|
texture: canvas.dst(),
|
||||||
mip_level: 0,
|
mip_level: 0,
|
||||||
origin: wgpu::Origin3d { x: x0, y: y0, z: 0 },
|
origin: wgpu::Origin3d::ZERO,
|
||||||
aspect: wgpu::TextureAspect::All,
|
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()));
|
queue.submit(Some(copy_enc.finish()));
|
||||||
|
|
||||||
|
// Upload single-dab buffer and params
|
||||||
let dab_bytes = bytemuck::bytes_of(dab);
|
let dab_bytes = bytemuck::bytes_of(dab);
|
||||||
let dab_buf = device.create_buffer(&wgpu::BufferDescriptor {
|
let dab_buf = device.create_buffer(&wgpu::BufferDescriptor {
|
||||||
label: Some("dab_storage_buf"),
|
label: Some("dab_storage_buf"),
|
||||||
|
|
@ -358,10 +375,22 @@ impl GpuBrushEngine {
|
||||||
label: Some("brush_dab_bg"),
|
label: Some("brush_dab_bg"),
|
||||||
layout: &self.compute_bg_layout,
|
layout: &self.compute_bg_layout,
|
||||||
entries: &[
|
entries: &[
|
||||||
wgpu::BindGroupEntry { binding: 0, resource: dab_buf.as_entire_binding() },
|
wgpu::BindGroupEntry {
|
||||||
wgpu::BindGroupEntry { binding: 1, resource: params_buf.as_entire_binding() },
|
binding: 0,
|
||||||
wgpu::BindGroupEntry { binding: 2, resource: wgpu::BindingResource::TextureView(canvas.src_view()) },
|
resource: dab_buf.as_entire_binding(),
|
||||||
wgpu::BindGroupEntry { binding: 3, resource: wgpu::BindingResource::TextureView(canvas.dst_view()) },
|
},
|
||||||
|
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(
|
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_pipeline(&self.compute_pipeline);
|
||||||
pass.set_bind_group(0, &bg, &[]);
|
pass.set_bind_group(0, &bg, &[]);
|
||||||
pass.dispatch_workgroups(bbox_w.div_ceil(8), bbox_h.div_ceil(8), 1);
|
pass.dispatch_workgroups(bbox_w.div_ceil(8), bbox_h.div_ceil(8), 1);
|
||||||
}
|
}
|
||||||
queue.submit(Some(compute_enc.finish()));
|
queue.submit(Some(compute_enc.finish()));
|
||||||
|
|
||||||
|
// Swap: the just-written dst becomes src for the next dab.
|
||||||
canvas.swap();
|
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