vello_cpu/
region.rs

1// Copyright 2025 the Vello Authors
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4//! Splitting a single mutable buffer into regions that can be accessed concurrently.
5
6use crate::fine::COLOR_COMPONENTS;
7use alloc::vec::Vec;
8use vello_common::coarse::WideTile;
9use vello_common::tile::Tile;
10
11#[derive(Debug)]
12pub struct Regions<'a> {
13    regions: Vec<Region<'a>>,
14}
15
16impl<'a> Regions<'a> {
17    pub fn new(width: u16, height: u16, mut buffer: &'a mut [u8]) -> Self {
18        let buf_width = usize::from(width);
19        let buf_height = usize::from(height);
20
21        let row_advance = buf_width * COLOR_COMPONENTS;
22
23        let height_regions = buf_height.div_ceil(usize::from(Tile::HEIGHT));
24        let width_regions = buf_width.div_ceil(usize::from(WideTile::WIDTH));
25
26        let mut regions = Vec::with_capacity(height_regions * width_regions);
27
28        let mut next_lines: [&'a mut [u8]; Tile::HEIGHT as usize] =
29            [&mut [], &mut [], &mut [], &mut []];
30
31        for y in 0..height_regions {
32            let base_y = y * usize::from(Tile::HEIGHT);
33            let region_height = usize::from(Tile::HEIGHT).min(buf_height - base_y);
34
35            for line in next_lines.iter_mut().take(region_height) {
36                let (head, tail) = buffer.split_at_mut(row_advance);
37                *line = head;
38                buffer = tail;
39            }
40
41            for x in 0..width_regions {
42                let mut areas: [&mut [u8]; Tile::HEIGHT as usize] =
43                    [&mut [], &mut [], &mut [], &mut []];
44
45                // All rows have the same width, so we can just take the first row.
46                let region_width =
47                    (usize::from(WideTile::WIDTH) * COLOR_COMPONENTS).min(next_lines[0].len());
48
49                for h in 0..region_height {
50                    let next = core::mem::take(&mut next_lines[h]);
51                    let (head, tail) = next.split_at_mut(region_width);
52                    areas[h] = head;
53                    next_lines[h] = tail;
54                }
55
56                regions.push(Region::new(
57                    areas,
58                    u16::try_from(x).unwrap(),
59                    u16::try_from(y).unwrap(),
60                    region_width as u16 / COLOR_COMPONENTS as u16,
61                    region_height as u16,
62                ));
63            }
64        }
65
66        Self { regions }
67    }
68
69    /// Apply the given function to each region. The functions will be applied
70    /// in parallel in the current threadpool.
71    #[cfg(feature = "multithreading")]
72    pub fn update_regions_par(&mut self, func: impl Fn(&mut Region<'_>) + Send + Sync) {
73        use rayon::iter::ParallelIterator;
74        use rayon::prelude::IntoParallelRefMutIterator;
75
76        self.regions.par_iter_mut().for_each(func);
77    }
78
79    /// Apply the given function to each region.
80    pub fn update_regions(&mut self, func: impl FnMut(&mut Region<'_>)) {
81        self.regions.iter_mut().for_each(func);
82    }
83}
84
85/// A rectangular region containing the pixels from one wide tile.
86///
87/// For wide tiles at the right/bottom edge, it might contain less pixels
88/// than the actual wide tile, if the pixmap width/height isn't a multiple of the
89/// tile width/height.
90#[derive(Default, Debug)]
91pub struct Region<'a> {
92    /// The x coordinate of the wide tile this region covers.
93    pub(crate) x: u16,
94    /// The y coordinate of the wide tile this region covers.
95    pub(crate) y: u16,
96    pub width: u16,
97    pub height: u16,
98    areas: [&'a mut [u8]; Tile::HEIGHT as usize],
99}
100
101impl<'a> Region<'a> {
102    pub(crate) fn new(
103        areas: [&'a mut [u8]; Tile::HEIGHT as usize],
104        x: u16,
105        y: u16,
106        width: u16,
107        height: u16,
108    ) -> Self {
109        Self {
110            areas,
111            x,
112            y,
113            width,
114            height,
115        }
116    }
117
118    pub(crate) fn row_mut(&mut self, y: u16) -> &mut [u8] {
119        self.areas[usize::from(y)]
120    }
121
122    pub fn areas(&mut self) -> &mut [&'a mut [u8]; Tile::HEIGHT as usize] {
123        &mut self.areas
124    }
125}