use std::sync::Arc;
use super::View;
use super::kept::KeptTable;
use super::marks::{Blocks, Shown};
use super::passes::{dispatch_blocks, offset};
use super::resources::step;
use super::words::Kept;
use crate::data::Version;
use crate::expr::compile::EndUse;
use crate::gpu::Inner;
use crate::gpu::pipelines::Variant;
use crate::gpu::shaders as wgsl;
use crate::gpu::work::Count;
#[derive(Clone, PartialEq, Debug)]
pub(crate) struct End {
pub(crate) mark: Option<usize>,
pub(crate) state: Arc<[u32]>,
pub(crate) stride: u32,
pub(crate) normals: Option<Arc<[u32]>>,
pub(crate) order: u64,
pub(crate) pages: Option<Arc<[u32]>>,
pub(crate) kept: Option<KeptTable>,
pub(crate) moved: Option<(Version, Arc<[u32]>)>,
}
impl End {
pub(crate) fn unattached() -> End {
End {
mark: None,
state: Arc::new([]),
stride: 0,
normals: None,
order: 0,
pages: None,
kept: None,
moved: None,
}
}
fn moved_whole(&self, before: &End) -> bool {
self.mark != before.mark || self.order != before.order
}
fn moved_since(&self, before: &End) -> &[u32] {
match (&self.moved, &before.moved) {
(Some((version, places)), Some((was, _))) if version != was => places,
_ => &[],
}
}
fn tables(&self) -> usize {
let kept = |k: &KeptTable| 2 * k.buckets().len();
self.state.len()
+ self.pages.as_ref().map_or(0, |p| 2 + p.len() + self.kept.as_ref().map_or(0, kept))
+ self.normals.as_ref().map_or(0, |n| n.len())
}
}
fn shared(ends: &[End], c: usize) -> Option<usize> {
ends[..c].iter().position(|e| *e == ends[c])
}
pub(super) fn words(ends: &[End]) -> usize {
(0..ends.len())
.map(|c| wgsl::END_WORDS as usize + if shared(ends, c).is_some() { 0 } else { ends[c].tables() })
.sum()
}
pub(super) fn pack(ends: &[End], uses: &[EndUse], at: u32, out: &mut Vec<u32>) {
let mut heads: Vec<[u32; wgsl::END_WORDS as usize]> = Vec::with_capacity(ends.len());
let mut table = at + wgsl::END_WORDS * ends.len() as u32;
let mut take = |len: usize| {
let at = table;
table += len as u32;
at
};
for (c, (end, used)) in ends.iter().zip(uses).enumerate() {
let mut head = if let Some(earlier) = shared(ends, c) {
heads[earlier]
} else {
let state = if end.mark.is_some() { take(end.state.len()) } else { wgsl::NONE };
let kept = end.kept.as_ref().map_or(0, |k| 2 * k.buckets().len());
let slots = end.pages.as_ref().map_or(wgsl::NONE, |p| take(2 + p.len() + kept));
let normals = end.normals.as_ref().map_or(wgsl::NONE, |n| take(n.len()));
[state, end.stride, slots, normals, wgsl::NONE]
};
head[wgsl::END_COLUMN as usize] = used.column;
heads.push(head);
out.extend(head);
}
for (c, end) in ends.iter().enumerate() {
if shared(ends, c).is_some() {
continue;
}
if end.mark.is_some() {
out.extend(end.state.iter());
}
if let Some(pages) = &end.pages {
out.push(pages.len() as u32);
out.extend(pages.iter());
let kept = end.kept.as_ref().map_or(&[][..], KeptTable::buckets);
out.push(kept.len() as u32);
out.extend(kept.iter().flatten());
}
if let Some(normals) = &end.normals {
out.extend(normals.iter());
}
}
}
impl View {
pub(super) fn attach_ends(&mut self) {
for i in 0..self.marks.len() {
let ends = ends(&self.marks, i);
let was = &self.marks[i].ends;
if ends == *was {
continue;
}
let whole = ends.len() != was.len() || ends.iter().zip(was).any(|(end, before)| end.moved_whole(before));
let mut places: Vec<u32> =
ends.iter().zip(was).flat_map(|(end, before)| end.moved_since(before).iter().copied()).collect();
places.sort_unstable();
places.dedup();
let blocks = match (whole, places.is_empty()) {
(true, _) => None,
(false, true) => Some(Vec::new()),
(false, false) => self.naming(i, &places),
};
let shown = &mut self.marks[i];
match blocks {
Some(blocks) => shown.unresolved.add(Some(&blocks)),
None => shown.unresolved = Blocks::All,
}
shown.ends = ends;
shown.packed = false;
}
}
fn naming(&self, i: usize, places: &[u32]) -> Option<Vec<u32>> {
let shown = &self.marks[i];
let style = shown.mark.style();
let drawn = self.drawings.get(&(style.data.clone(), style.table.clone()))?;
Some(drawn.naming(shown.drawing()?, places))
}
pub(super) fn resolve_ends(
&mut self,
pass: &mut wgpu::ComputePass<'_>,
inner: &mut Inner,
mark: &wgpu::BindGroup,
group: &wgpu::BindGroup,
marks: &[usize],
) {
if marks.is_empty() {
return;
}
let device = &self.gpu.0.device;
pass.set_bind_group(2, group, &[step(wgsl::STEP_ENDS)]);
for &i in marks {
let shown = &mut self.marks[i];
let rows = shown.live();
pass.set_pipeline(&inner.pipelines.compute(device, Variant::channels(shown.reads())));
pass.set_bind_group(1, mark, &[offset(i)]);
dispatch_blocks(pass, rows, shown.end_block_count);
let blocks = shown.unresolved.of(rows);
let resolved = blocks.iter().map(|&b| u64::from((rows - b * wgsl::PAGE_ROWS).min(wgsl::PAGE_ROWS)));
self.gpu.0.counters.add(Count::Resolved, resolved.sum());
shown.unresolved = Blocks::default();
shown.version += 1;
shown.place_version += 1;
}
pass.set_bind_group(2, group, &[step(wgsl::STEP_CHANNELS)]);
}
}
fn ends(marks: &[Shown], i: usize) -> Vec<End> {
let Some(compiled) = &marks[i].compiled else { return Vec::new() };
let data = &marks[i].mark.style().data;
compiled
.program
.ends
.iter()
.map(|end| match super::graph::end_mark(marks, data, &end.table) {
Some(m) if marks[m].pages_held() => {
let (state, normals) = (marks[m].words.pages(Kept::State), marks[m].words.pages(Kept::Normals));
End {
mark: Some(m),
state: state.blocks.clone(),
stride: state.width,
normals: (normals.width > 0).then(|| normals.blocks.clone()),
order: marks[m].drawing().map_or(0, |d| d.order.id()),
pages: marks[m].drawing().map(|d| d.pages.clone()),
kept: marks[m].drawing().map(|d| d.kept_table.clone()),
moved: marks[m].drawing().map(|d| d.moved.clone()),
}
}
_ => End::unattached(),
})
.collect()
}