use crate::{ImageData, ImageDelta, TextureId};
#[derive(Default)]
pub struct TextureManager {
next_id: u64,
metas: ahash::HashMap<TextureId, TextureMeta>,
delta: TexturesDelta,
}
impl TextureManager {
pub fn alloc(&mut self, name: String, image: ImageData, options: TextureOptions) -> TextureId {
let id = TextureId::Managed(self.next_id);
self.next_id += 1;
self.metas.entry(id).or_insert_with(|| TextureMeta {
name,
size: image.size(),
bytes_per_pixel: image.bytes_per_pixel(),
retain_count: 1,
options,
});
self.delta.set.push((id, ImageDelta::full(image, options)));
id
}
pub fn set(&mut self, id: TextureId, delta: ImageDelta) {
if let Some(meta) = self.metas.get_mut(&id) {
if let Some(pos) = delta.pos {
crate::epaint_assert!(
pos[0] + delta.image.width() <= meta.size[0]
&& pos[1] + delta.image.height() <= meta.size[1],
"Partial texture update is outside the bounds of texture {id:?}",
);
} else {
meta.size = delta.image.size();
meta.bytes_per_pixel = delta.image.bytes_per_pixel();
self.delta.set.retain(|(x, _)| x != &id);
}
self.delta.set.push((id, delta));
} else {
crate::epaint_assert!(false, "Tried setting texture {id:?} which is not allocated");
}
}
pub fn free(&mut self, id: TextureId) {
if let std::collections::hash_map::Entry::Occupied(mut entry) = self.metas.entry(id) {
let meta = entry.get_mut();
meta.retain_count -= 1;
if meta.retain_count == 0 {
entry.remove();
self.delta.free.push(id);
}
} else {
crate::epaint_assert!(false, "Tried freeing texture {id:?} which is not allocated");
}
}
pub fn retain(&mut self, id: TextureId) {
if let Some(meta) = self.metas.get_mut(&id) {
meta.retain_count += 1;
} else {
crate::epaint_assert!(
false,
"Tried retaining texture {id:?} which is not allocated",
);
}
}
pub fn take_delta(&mut self) -> TexturesDelta {
std::mem::take(&mut self.delta)
}
pub fn meta(&self, id: TextureId) -> Option<&TextureMeta> {
self.metas.get(&id)
}
pub fn allocated(&self) -> impl ExactSizeIterator<Item = (&TextureId, &TextureMeta)> {
self.metas.iter()
}
pub fn num_allocated(&self) -> usize {
self.metas.len()
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TextureMeta {
pub name: String,
pub size: [usize; 2],
pub bytes_per_pixel: usize,
pub retain_count: usize,
pub options: TextureOptions,
}
impl TextureMeta {
pub fn bytes_used(&self) -> usize {
self.size[0] * self.size[1] * self.bytes_per_pixel
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct TextureOptions {
pub magnification: TextureFilter,
pub minification: TextureFilter,
pub wrap_mode: TextureWrapMode,
}
impl TextureOptions {
pub const LINEAR: Self = Self {
magnification: TextureFilter::Linear,
minification: TextureFilter::Linear,
wrap_mode: TextureWrapMode::ClampToEdge,
};
pub const NEAREST: Self = Self {
magnification: TextureFilter::Nearest,
minification: TextureFilter::Nearest,
wrap_mode: TextureWrapMode::ClampToEdge,
};
pub const LINEAR_REPEAT: Self = Self {
magnification: TextureFilter::Linear,
minification: TextureFilter::Linear,
wrap_mode: TextureWrapMode::Repeat,
};
pub const LINEAR_MIRRORED_REPEAT: Self = Self {
magnification: TextureFilter::Linear,
minification: TextureFilter::Linear,
wrap_mode: TextureWrapMode::MirroredRepeat,
};
pub const NEAREST_REPEAT: Self = Self {
magnification: TextureFilter::Nearest,
minification: TextureFilter::Nearest,
wrap_mode: TextureWrapMode::Repeat,
};
pub const NEAREST_MIRRORED_REPEAT: Self = Self {
magnification: TextureFilter::Nearest,
minification: TextureFilter::Nearest,
wrap_mode: TextureWrapMode::MirroredRepeat,
};
}
impl Default for TextureOptions {
fn default() -> Self {
Self::LINEAR
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub enum TextureFilter {
Nearest,
Linear,
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub enum TextureWrapMode {
#[default]
ClampToEdge,
Repeat,
MirroredRepeat,
}
#[derive(Clone, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[must_use = "The painter must take care of this"]
pub struct TexturesDelta {
pub set: Vec<(TextureId, ImageDelta)>,
pub free: Vec<TextureId>,
}
impl TexturesDelta {
pub fn is_empty(&self) -> bool {
self.set.is_empty() && self.free.is_empty()
}
pub fn append(&mut self, mut newer: Self) {
self.set.extend(newer.set);
self.free.append(&mut newer.free);
}
pub fn clear(&mut self) {
self.set.clear();
self.free.clear();
}
}
impl std::fmt::Debug for TexturesDelta {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use std::fmt::Write as _;
let mut debug_struct = f.debug_struct("TexturesDelta");
if !self.set.is_empty() {
let mut string = String::new();
for (tex_id, delta) in &self.set {
let size = delta.image.size();
if let Some(pos) = delta.pos {
write!(
string,
"{:?} partial ([{} {}] - [{} {}]), ",
tex_id,
pos[0],
pos[1],
pos[0] + size[0],
pos[1] + size[1]
)
.ok();
} else {
write!(string, "{:?} full {}x{}, ", tex_id, size[0], size[1]).ok();
}
}
debug_struct.field("set", &string);
}
if !self.free.is_empty() {
debug_struct.field("free", &self.free);
}
debug_struct.finish()
}
}