use std::sync::Arc;
use super::internal::{DeviceTextures, LiveImageRead};
use super::mips::{mip_footprint, mip_level_size, mip_levels};
use super::pass::{
LivePass, LivePassMode, LiveTextureBackend, LiveTextureStats, QuadDecision, TextureOutOfMemory,
};
use super::{LiveImageId, LiveImageSource, PixelRect, ScalingFilter};
use crate::RenderFrame;
pub(crate) struct CpuTexture {
width: u32,
height: u32,
pixels: Arc<Vec<u8>>,
levels: Vec<(u32, u32, Vec<u8>)>,
}
impl CpuTexture {
fn level(&self, k: u32) -> Option<(u32, u32, &[u8])> {
match k {
0 => Some((self.width, self.height, self.pixels.as_slice())),
k => self
.levels
.get(k as usize - 1)
.map(|(w, h, px)| (*w, *h, px.as_slice())),
}
}
}
pub(crate) struct CpuBackend {
max_dimension: u32,
fail_next_create: bool,
lost: bool,
writes: Vec<PixelRect>,
mip_rebuilds: Vec<Option<PixelRect>>,
}
impl LiveTextureBackend for CpuBackend {
type Texture = CpuTexture;
type Shared = Arc<Vec<u8>>;
fn max_dimension(&self) -> u32 {
self.max_dimension
}
fn create_texture(
&mut self,
width: u32,
height: u32,
mipped: bool,
_label: &str,
) -> Result<CpuTexture, TextureOutOfMemory> {
if std::mem::take(&mut self.fail_next_create) {
return Err(TextureOutOfMemory);
}
let levels = if mipped {
(1..mip_levels(width, height))
.map(|k| {
let (w, h) = mip_level_size(width, height, k);
(w, h, vec![0; w as usize * h as usize * 4])
})
.collect()
} else {
Vec::new()
};
Ok(CpuTexture {
width,
height,
pixels: Arc::new(vec![0; width as usize * height as usize * 4]),
levels,
})
}
fn staged_row_bytes(&self, width: u32) -> u64 {
u64::from(width) * 4
}
fn write(&mut self, texture: &mut CpuTexture, read: &LiveImageRead<'_>, band: PixelRect) {
let stride = read.stride();
let row = band.width as usize * 4;
let pitch = texture.width as usize * 4;
let pixels = read.pixels();
let level0 = Arc::make_mut(&mut texture.pixels);
for y in band.y..band.y + band.height {
let from = y as usize * stride + band.x as usize * 4;
let to = y as usize * pitch + band.x as usize * 4;
level0[to..to + row].copy_from_slice(&pixels[from..from + row]);
}
self.writes.push(band);
}
fn prepare_filter(&mut self, _texture: &mut CpuTexture, _filter: ScalingFilter) {}
fn texture_bytes(&self, texture: &CpuTexture) -> u64 {
u64::from(texture.width) * u64::from(texture.height) * 4
+ texture
.levels
.iter()
.map(|(w, h, _)| u64::from(*w) * u64::from(*h) * 4)
.sum::<u64>()
}
fn rebuild_mips(&mut self, texture: &mut CpuTexture, region: Option<PixelRect>, opaque: bool) {
for k in 1..=texture.levels.len() as u32 {
let (below, rest) = texture.levels.split_at_mut(k as usize - 1);
let (sw, sh, src): (u32, u32, &[u8]) = match below.last() {
Some((w, h, px)) => (*w, *h, px),
None => (texture.width, texture.height, texture.pixels.as_slice()),
};
let (w, h, dst) = &mut rest[0];
let area = match region {
Some(changed) => mip_footprint(changed, k, (*w, *h)),
None => PixelRect::full(*w, *h),
};
let not_alpha = opaque && k == 1;
for y in area.y..area.y + area.height {
for x in area.x..area.x + area.width {
let texel =
crate::resample::downsample_half_texel(src, sw, sh, x, y, not_alpha);
let at = (y as usize * *w as usize + x as usize) * 4;
dst[at..at + 4].copy_from_slice(&texel);
}
}
}
self.mip_rebuilds.push(region);
}
fn share(&self, texture: &CpuTexture) -> Arc<Vec<u8>> {
Arc::clone(&texture.pixels)
}
fn copy(&mut self, from: &Arc<Vec<u8>>, into: &mut CpuTexture) {
debug_assert_eq!(from.len(), into.pixels.len(), "copies are of one size");
into.pixels = Arc::clone(from);
}
fn device_lost(&self) -> bool {
self.lost
}
}
pub struct LiveImageMirror {
pass: LivePass<CpuBackend>,
}
#[derive(Debug, Default, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct MirrorReport {
pub full_uploads: u32,
pub partial_uploads: u32,
pub rects: Vec<PixelRect>,
pub bytes: u64,
pub deferred: u32,
pub contended: u32,
pub pruned: u32,
pub paused: u32,
pub mip_rebuilds: Vec<Option<PixelRect>>,
}
impl Default for LiveImageMirror {
fn default() -> Self {
Self::new()
}
}
impl LiveImageMirror {
pub fn new() -> Self {
Self {
pass: LivePass::new(CpuBackend {
max_dimension: LiveImageSource::MAX_DIMENSION,
fail_next_create: false,
lost: false,
writes: Vec::new(),
mip_rebuilds: Vec::new(),
}),
}
}
pub fn consume(&mut self, frame: &RenderFrame) -> MirrorReport {
self.run(frame, LivePassMode::Present)
}
pub fn capture(&mut self, frame: &RenderFrame) -> MirrorReport {
self.run(frame, LivePassMode::Capture)
}
fn run(&mut self, frame: &RenderFrame, mode: LivePassMode) -> MirrorReport {
self.run_at(frame, mode, std::time::Instant::now())
}
fn run_at(
&mut self,
frame: &RenderFrame,
mode: LivePassMode,
now: std::time::Instant,
) -> MirrorReport {
let before = self.pass.stats();
self.pass.backend_mut().writes.clear();
self.pass.backend_mut().mip_rebuilds.clear();
self.pass.prepare_at(&frame.live_images, mode, now);
let after = self.pass.stats();
let counts = self.pass.last_counts();
MirrorReport {
full_uploads: (after.uploads_full - before.uploads_full) as u32,
partial_uploads: (after.uploads_partial - before.uploads_partial) as u32,
rects: std::mem::take(&mut self.pass.backend_mut().writes),
bytes: after.bytes_uploaded - before.bytes_uploaded,
deferred: self.pass.decisions().iter().filter(|d| d.deferred).count() as u32,
contended: (after.contended - before.contended) as u32,
pruned: counts.pruned,
paused: counts.kept,
mip_rebuilds: std::mem::take(&mut self.pass.backend_mut().mip_rebuilds),
}
}
pub fn pixels(&self, id: LiveImageId) -> Option<(u32, u32, &[u8])> {
self.pass
.texture(id)
.map(|t| (t.width, t.height, t.pixels.as_slice()))
}
pub fn mip_level(&self, id: LiveImageId, k: u32) -> Option<(u32, u32, &[u8])> {
self.pass.texture(id)?.level(k)
}
pub fn texture_count(&self) -> usize {
self.pass.stats().textures
}
pub fn decisions(&self) -> &[QuadDecision] {
self.pass.decisions()
}
pub fn stats(&self) -> LiveTextureStats {
self.pass.stats()
}
#[doc(hidden)]
pub fn last_timings(&self) -> &super::internal::PassTimings {
self.pass.last_timings()
}
pub fn set_max_dimension(&mut self, max: u32) {
self.pass.backend_mut().max_dimension = max;
}
#[doc(hidden)]
pub fn fail_next_texture(&mut self) {
self.pass.backend_mut().fail_next_create = true;
}
#[doc(hidden)]
pub fn set_device_lost(&mut self, lost: bool) {
self.pass.backend_mut().lost = lost;
}
#[doc(hidden)]
pub fn set_staging_budget(&mut self, bytes: u64) {
self.pass.set_staging_budget(bytes);
}
#[doc(hidden)]
pub fn set_band_bytes(&mut self, bytes: u64) {
self.pass.set_band_bytes(bytes);
}
#[doc(hidden)]
pub fn set_park_budget(&mut self, bytes: u64) {
self.pass.set_park_budget(bytes);
}
#[doc(hidden)]
pub fn set_refresh_interval(&mut self, refresh: std::time::Duration) {
self.pass.set_refresh_interval(refresh);
}
#[doc(hidden)]
pub fn on_same_device(&mut self) -> LiveImageMirror {
let joined = self.pass.device().cloned();
let textures = joined.unwrap_or_else(|| {
let textures = DeviceTextures::new();
self.pass.join_device(&textures);
textures
});
let mut other = LiveImageMirror::new();
other.pass.join_device(&textures);
other
}
#[doc(hidden)]
pub fn consume_at(&mut self, frame: &RenderFrame, now: std::time::Instant) -> MirrorReport {
self.run_at(frame, LivePassMode::Present, now)
}
}