use super::{SHADE, View};
use crate::gpu::Inner;
use crate::gpu::pipelines::{self, Asked, Urgency, Variant};
use crate::gpu::shaders::{PATH_ENDS, PATH_GEOMETRY, Shader};
use crate::{Mark, Marker, Scene, Space};
const SELECT: [&str; 4] = ["select_start", "select_count", "select_find", "select_cut"];
pub(super) const PLACING: [&str; 5] = ["claim", "decide", "occupy", "ask", "fade"];
pub(super) struct Asking {
target: (wgpu::TextureFormat, u32),
input: bool,
drawing: Vec<Asked>,
texts: Vec<Asked>,
answering: Vec<Asked>,
}
#[cfg(all(feature = "web", target_arch = "wasm32"))]
pub(crate) fn common(format: wgpu::TextureFormat, samples: u32) -> Vec<Asked> {
let draw = |shader| Variant::draw(shader, format, samples);
let mut needs = vec![
Variant::channels(pipelines::Reads { joined: false, timed: false, viewed: false }),
draw(Shader::Strokes).path(PATH_ENDS),
draw(Shader::Strokes).path(PATH_GEOMETRY),
draw(Shader::Points),
draw(Shader::Backdrop),
Variant::compute(Shader::Bounds, "points"),
];
for path in [PATH_ENDS, PATH_GEOMETRY] {
needs.push(Variant::compute(Shader::Bounds, "strokes").path(path));
needs.extend(["count_segments", "write_segments"].map(|e| Variant::compute(Shader::Lists, e).path(path)));
}
needs.extend(["scan_blocks", "scan_sums", "add_sums"].map(|e| Variant::compute(Shader::Lists, e)));
needs.extend([draw(Shader::PointsPorts), draw(Shader::Overlay), draw(Shader::Glyphs), draw(Shader::GlyphsPorts)]);
needs.push(Variant::compute(Shader::Candidates, "candidates"));
needs.extend(["columns", "rows"].map(|e| Variant::compute(Shader::Fields, e)));
needs.into_iter().map(Asked::Shader).collect()
}
impl View {
pub(super) fn ask(&mut self, inner: &mut Inner, target: (wgpu::TextureFormat, u32), input: bool) -> usize {
let device = &self.gpu.0.device;
let at = if let Some(at) = self.asked.iter().position(|a| a.target == target && a.input == input) {
at
} else {
let Stages { draw, text, changes, lit, input: inputs } = stages(&self.scene, target.0, target.1);
let (mut drawing, mut texts, mut answering) = if input {
(draw, text, [changes, lit, inputs].concat())
} else {
([draw, text, changes, lit].concat(), Vec::new(), Vec::new())
};
for asked in [&mut drawing, &mut texts, &mut answering] {
let mut seen = rustc_hash::FxHashSet::default();
asked.retain(|need| seen.insert(need.clone()));
}
inner.pipelines.ask(device, &drawing, Urgency::Draw, &self.waker);
inner.pipelines.ask(device, &texts, Urgency::Text, &self.waker);
inner.pipelines.ask(device, &answering, Urgency::Answer, &self.waker);
self.asked.push(Asking { target, input, drawing, texts, answering });
self.asked.len() - 1
};
inner.pipelines.ask(device, &self.channels(), Urgency::Draw, &self.waker);
at
}
fn channels(&self) -> Vec<Asked> {
let mut channels: Vec<Asked> = Vec::new();
for shown in &self.marks {
let need = Asked::Shader(Variant::channels(shown.reads()));
if !channels.contains(&need) {
channels.push(need);
}
}
channels
}
pub(super) fn pipelines_ready(
&mut self,
inner: &mut Inner,
format: wgpu::TextureFormat,
samples: u32,
input: bool,
) -> bool {
let at = self.ask(inner, (format, samples), input);
inner.pipelines.ready(&self.asked[at].drawing) && inner.pipelines.ready(&self.channels())
}
pub(super) fn texts_ready(&self, inner: &Inner) -> bool {
self.asked.iter().all(|a| inner.pipelines.ready(&a.texts))
}
pub(super) fn answers_ready(&self, inner: &Inner) -> bool {
self.asked.iter().filter(|a| a.input).all(|a| inner.pipelines.ready(&a.answering))
}
}
#[derive(Default)]
struct Stages {
draw: Vec<Asked>,
text: Vec<Asked>,
changes: Vec<Asked>,
lit: Vec<Asked>,
input: Vec<Asked>,
}
fn stages(scene: &Scene, format: wgpu::TextureFormat, samples: u32) -> Stages {
let any = |test: fn(&Mark) -> bool| scene.marks.iter().any(test);
let draw = |shader| Asked::Shader(Variant::draw(shader, format, samples));
let compute = |shader, entry| Asked::Shader(Variant::compute(shader, entry));
let mut stages = Stages {
draw: vec![draw(Shader::Backdrop)],
changes: vec![compute(Shader::Evaluate, "ease"), compute(Shader::Freeze, "gather")],
..Stages::default()
};
let controls = scene.controls;
let diagram = matches!(scene.space, Space::Diagram(_));
let legends = any(|m| m.style().legend.is_some() && m.style().color.is_some());
let written = matches!(scene.space, Space::Plot(_)) || scene.title.is_some() || scene.subtitle.is_some() || legends;
if written || controls.select_box || controls.connect {
stages.text.push(draw(Shader::Overlay));
}
let labeled = any(|m| {
m.style().label.is_some()
|| matches!(m, Mark::Vertices(v) if v.ports.iter().any(|(_, port)| port.label.is_some()))
});
if written || labeled {
stages.text.extend(["columns", "rows"].map(|e| compute(Shader::Fields, e)));
}
if any(|m| m.style().label.is_some()) && !diagram {
stages.text.extend([compute(Shader::Labels, "clear_cells"), compute(Shader::Labels, "reset_cells")]);
}
if any(|m| m.style().shadow) {
stages.draw.push(Asked::Shader(Variant::draw(Shader::Shade, SHADE, 1)));
}
if any(Mark::flows) {
stages.draw.push(compute(Shader::Evaluate, "travel"));
stages.changes.push(compute(Shader::Evaluate, "carry"));
}
if any(|m| matches!(m, Mark::Edges(_) | Mark::Lines(_) | Mark::Areas(_))) {
stages.draw.extend(["scan_blocks", "scan_sums", "add_sums"].map(|e| compute(Shader::Lists, e)));
}
for mark in &scene.marks {
mark_stages(mark, scene, format, samples, &mut stages);
}
stages
}
fn mark_stages(mark: &Mark, scene: &Scene, format: wgpu::TextureFormat, samples: u32, stages: &mut Stages) {
let draw = |shader| Variant::draw(shader, format, samples);
let compute = |shader, entry| Asked::Shader(Variant::compute(shader, entry));
let style = mark.style();
let primitive = mark.kind().primitive().code();
let path = mark.path(&scene.space).unwrap_or(PATH_ENDS);
let (shader, cast, lit) = match mark {
Mark::Vertices(v) => {
if let Marker::Wgsl { source, .. } = &v.marker {
let custom =
|shader, format, samples| Asked::Custom(source.clone(), Variant::draw(shader, format, samples));
stages.draw.push(custom(Shader::PointsCustom, format, samples));
stages.lit.push(custom(Shader::PointsCustomFocus, format, samples));
if style.shadow {
stages.draw.push(custom(Shader::PointsCustomCast, SHADE, 1));
}
(None, None, None)
} else {
(Some(Shader::Points), Some(Shader::PointsCast), Some(Shader::PointsFocus))
}
}
Mark::Faces(_) => (Some(Shader::Triangles), Some(Shader::TrianglesCast), None),
Mark::Areas(_) => (Some(Shader::Areas), None, Some(Shader::AreasFocus)),
Mark::Edges(_) | Mark::Lines(_) => {
(Some(Shader::Strokes), Some(Shader::StrokesCast), Some(Shader::StrokesFocus))
}
};
stages.draw.extend(shader.map(|s| Asked::Shader(draw(s).path(path))));
stages.lit.extend(lit.map(|s| Asked::Shader(draw(s).path(path))));
if style.shadow {
stages.draw.extend(cast.map(|s| Asked::Shader(Variant::draw(s, SHADE, 1).path(path))));
}
let bounds = match mark {
Mark::Vertices(_) => "points",
Mark::Areas(_) => "areas",
_ => "strokes",
};
stages.draw.push(Asked::Shader(Variant::compute(Shader::Bounds, bounds).path(path)));
if matches!(mark, Mark::Edges(_)) || path == PATH_GEOMETRY {
stages.draw.extend(
["count_segments", "write_segments"].map(|e| Asked::Shader(Variant::compute(Shader::Lists, e).path(path))),
);
}
stages.input.push(Asked::Shader(Variant::probe(pipelines::probe(samples), primitive).path(path)));
if style.label.is_some() {
stages.text.push(Asked::Shader(draw(Shader::Glyphs).path(path).primitive(primitive)));
stages
.text
.push(Asked::Shader(Variant::compute(Shader::Candidates, "candidates").path(path).primitive(primitive)));
if !matches!(scene.space, Space::Diagram(_)) {
stages.text.extend(PLACING.map(|e| Asked::Shader(Variant::compute(Shader::Labels, e).path(path))));
}
if style.label.as_ref().is_some_and(|l| l.max.is_some()) && !matches!(scene.space, Space::Diagram(_)) {
stages.text.extend(SELECT.map(|e| compute(Shader::Labels, e)));
}
}
match mark {
Mark::Faces(_) => stages.draw.push(compute(Shader::Normals, "accumulate")),
Mark::Vertices(vertices) => {
if !vertices.ports.is_empty() {
stages.draw.push(Asked::Shader(draw(Shader::PointsPorts)));
}
if vertices.ports.iter().any(|(_, port)| port.label.is_some()) {
stages.text.push(Asked::Shader(draw(Shader::GlyphsPorts)));
}
}
Mark::Edges(_) | Mark::Lines(_) | Mark::Areas(_) => {}
}
}