egui/crates/epaint/src/textures.rs

232 lines
7.4 KiB
Rust
Raw Normal View History

2022-01-22 10:23:12 +00:00
use crate::{ImageData, ImageDelta, TextureId};
// ----------------------------------------------------------------------------
/// Low-level manager for allocating textures.
///
/// Communicates with the painting subsystem using [`Self::take_delta`].
#[derive(Default)]
pub struct TextureManager {
/// We allocate texture id:s linearly.
next_id: u64,
/// Information about currently allocated textures.
Update dependencies (#1933) * Update ahash from 0.7 to 0.8 Opt to use ahash::HashMap over ahash::AHashMap * Fix ahash compilation for web * Update ron to 0.8 * Add note about why we cannot update tiny-skia * cargo update Updating crates.io index Updating android_system_properties v0.1.2 -> v0.1.4 Updating anyhow v1.0.58 -> v1.0.62 Updating async-broadcast v0.4.0 -> v0.4.1 Updating async-channel v1.6.1 -> v1.7.1 Updating async-io v1.7.0 -> v1.8.0 Updating async-task v4.2.0 -> v4.3.0 Updating async-trait v0.1.56 -> v0.1.57 Updating backtrace v0.3.65 -> v0.3.66 Updating bit-set v0.5.2 -> v0.5.3 Updating bumpalo v3.10.0 -> v3.11.0 Updating bytemuck v1.10.0 -> v1.12.1 Updating bytemuck_derive v1.1.0 -> v1.2.1 Updating bytes v1.1.0 -> v1.2.1 Updating cast v0.2.7 -> v0.3.0 Updating chrono v0.4.19 -> v0.4.22 Updating clap v3.2.8 -> v3.2.17 Updating clipboard-win v4.4.1 -> v4.4.2 Updating combine v4.6.4 -> v4.6.6 Updating concurrent-queue v1.2.2 -> v1.2.4 Updating criterion v0.3.5 -> v0.3.6 Updating criterion-plot v0.4.4 -> v0.4.5 Updating crossbeam-channel v0.5.5 -> v0.5.6 Updating crossbeam-deque v0.8.1 -> v0.8.2 Updating crossbeam-epoch v0.9.9 -> v0.9.10 Updating crossbeam-utils v0.8.10 -> v0.8.11 Updating document-features v0.2.1 -> v0.2.3 Updating dyn-clone v1.0.6 -> v1.0.9 Removing easy-parallel v3.2.0 Updating either v1.7.0 -> v1.8.0 Updating enum-map v2.1.0 -> v2.4.1 Updating enum-map-derive v0.8.0 -> v0.10.0 Updating event-listener v2.5.2 -> v2.5.3 Updating fastrand v1.7.0 -> v1.8.0 Updating futures-core v0.3.21 -> v0.3.23 Updating futures-io v0.3.21 -> v0.3.23 Updating futures-sink v0.3.21 -> v0.3.23 Updating futures-task v0.3.21 -> v0.3.23 Updating futures-util v0.3.21 -> v0.3.23 Updating gimli v0.26.1 -> v0.26.2 Updating gpu-descriptor v0.2.2 -> v0.2.3 Removing hashbrown v0.11.2 Removing hashbrown v0.12.1 Adding hashbrown v0.12.3 Adding iana-time-zone v0.1.46 Updating image v0.24.2 -> v0.24.3 Updating inplace_it v0.3.3 -> v0.3.4 Updating itoa v1.0.2 -> v1.0.3 Updating js-sys v0.3.58 -> v0.3.59 Updating libc v0.2.126 -> v0.2.132 Updating libm v0.2.2 -> v0.2.5 Removing memmap2 v0.3.1 Removing memmap2 v0.5.4 Adding memmap2 v0.5.7 Removing num-iter v0.1.43 Updating object v0.28.4 -> v0.29.0 Updating once_cell v1.13.0 -> v1.13.1 Updating os_str_bytes v6.1.0 -> v6.3.0 Updating owned_ttf_parser v0.15.0 -> v0.15.1 Removing parking_lot v0.11.2 Removing parking_lot_core v0.8.5 Updating plotters v0.3.1 -> v0.3.3 Updating plotters-backend v0.3.2 -> v0.3.4 Updating plotters-svg v0.3.1 -> v0.3.3 Updating proc-macro-crate v1.1.3 -> v1.2.1 Updating proc-macro2 v1.0.40 -> v1.0.43 Updating quote v1.0.20 -> v1.0.21 Updating redox_syscall v0.2.13 -> v0.2.16 Updating regex v1.5.6 -> v1.6.0 Updating regex-syntax v0.6.26 -> v0.6.27 Updating rfd v0.8.0 -> v0.8.4 Removing rustc_version v0.4.0 Updating ryu v1.0.10 -> v1.0.11 Updating sctk-adwaita v0.4.1 -> v0.4.2 Removing semver v1.0.12 Updating serde v1.0.138 -> v1.0.143 Updating serde_derive v1.0.138 -> v1.0.143 Updating serde_json v1.0.82 -> v1.0.83 Updating serde_repr v0.1.8 -> v0.1.9 Updating slab v0.4.6 -> v0.4.7 Removing smithay-client-toolkit v0.15.4 Updating smithay-clipboard v0.6.5 -> v0.6.6 Updating syn v1.0.98 -> v1.0.99 Updating thiserror v1.0.31 -> v1.0.32 Updating thiserror-impl v1.0.31 -> v1.0.32 Updating time v0.3.11 -> v0.3.13 Adding tiny-skia v0.7.0 Adding tiny-skia-path v0.7.0 Updating tracing v0.1.35 -> v0.1.36 Updating tracing-core v0.1.28 -> v0.1.29 Updating tracing-subscriber v0.3.14 -> v0.3.15 Updating unicode-ident v1.0.1 -> v1.0.3 Updating unicode_names2 v0.5.0 -> v0.5.1 Updating ureq v2.4.0 -> v2.5.0 Updating wasm-bindgen-futures v0.4.31 -> v0.4.32 Updating web-sys v0.3.58 -> v0.3.59 Updating webpki-roots v0.22.3 -> v0.22.4 Updating weezl v0.1.6 -> v0.1.7 Updating wgpu-core v0.13.1 -> v0.13.2 Updating wgpu-hal v0.13.1 -> v0.13.2 Updating wgpu-types v0.13.0 -> v0.13.2 Updating windows v0.32.0 -> v0.37.0 Updating windows_aarch64_msvc v0.32.0 -> v0.37.0 Updating windows_i686_gnu v0.32.0 -> v0.37.0 Updating windows_i686_msvc v0.32.0 -> v0.37.0 Updating windows_x86_64_gnu v0.32.0 -> v0.37.0 Updating windows_x86_64_msvc v0.32.0 -> v0.37.0 Updating x11-dl v2.19.1 -> v2.20.0 Updating zbus_names v2.1.0 -> v2.2.0 Updating zvariant v3.4.1 -> v3.6.0 Updating zvariant_derive v3.4.1 -> v3.6.0 * Add "Unicode-DFS-2016" to deny.toml whitelist
2022-08-19 09:46:38 +00:00
metas: ahash::HashMap<TextureId, TextureMeta>,
delta: TexturesDelta,
}
impl TextureManager {
/// Allocate a new texture.
///
/// The given name can be useful for later debugging.
///
/// The returned [`TextureId`] will be [`TextureId::Managed`], with an index
/// starting from zero and increasing with each call to [`Self::alloc`].
///
/// The first texture you allocate will be `TextureId::Managed(0) == TexureId::default()` and
/// MUST have a white pixel at (0,0) ([`crate::WHITE_UV`]).
///
/// The texture is given a retain-count of `1`, requiring one call to [`Self::free`] to free it.
pub fn alloc(&mut self, name: String, image: ImageData, filter: TextureFilter) -> 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,
filter,
});
self.delta.set.push((id, ImageDelta::full(image, filter)));
id
}
2022-01-22 10:23:12 +00:00
/// Assign a new image to an existing texture,
/// or update a region of it.
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 {
// whole update
2022-01-22 10:23:12 +00:00
meta.size = delta.image.size();
meta.bytes_per_pixel = delta.image.bytes_per_pixel();
// since we update the whole image, we can discard all old enqueued deltas
self.delta.set.retain(|(x, _)| x != &id);
2022-01-22 10:23:12 +00:00
}
self.delta.set.push((id, delta));
} else {
crate::epaint_assert!(false, "Tried setting texture {id:?} which is not allocated");
}
}
/// Free an existing texture.
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");
}
}
/// Increase the retain-count of the given texture.
///
/// For each time you call [`Self::retain`] you must call [`Self::free`] on additional time.
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",
);
}
}
/// Take and reset changes since last frame.
///
/// These should be applied to the painting subsystem each frame.
pub fn take_delta(&mut self) -> TexturesDelta {
std::mem::take(&mut self.delta)
}
/// Get meta-data about a specific texture.
pub fn meta(&self, id: TextureId) -> Option<&TextureMeta> {
self.metas.get(&id)
}
/// Get meta-data about all allocated textures in some arbitrary order.
pub fn allocated(&self) -> impl ExactSizeIterator<Item = (&TextureId, &TextureMeta)> {
self.metas.iter()
}
/// Total number of allocated textures.
pub fn num_allocated(&self) -> usize {
self.metas.len()
}
}
/// Meta-data about an allocated texture.
#[derive(Clone, Debug, PartialEq)]
pub struct TextureMeta {
/// A human-readable name useful for debugging.
pub name: String,
/// width x height
pub size: [usize; 2],
/// 4 or 1
pub bytes_per_pixel: usize,
/// Free when this reaches zero.
pub retain_count: usize,
/// The texture filtering mode to use when rendering
pub filter: TextureFilter,
}
/// How the texture texels are filtered.
#[derive(Copy, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub enum TextureFilter {
/// Show the nearest pixel value.
///
/// When zooming in you will get sharp, square pixels/texels.
/// When zooming out you will get a very crisp (and aliased) look.
Nearest,
/// Linearly interpolate the nearest neighbors, creating a smoother look when zooming in and out.
///
/// This is the default.
Linear,
}
impl Default for TextureFilter {
fn default() -> Self {
Self::Linear
}
}
impl TextureMeta {
/// Size in bytes.
/// width x height x [`Self::bytes_per_pixel`].
pub fn bytes_used(&self) -> usize {
self.size[0] * self.size[1] * self.bytes_per_pixel
}
}
// ----------------------------------------------------------------------------
/// What has been allocated and freed during the last period.
///
/// These are commands given to the integration painter.
#[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 {
/// New or changed textures. Apply before painting.
pub set: Vec<(TextureId, ImageDelta)>,
/// Textures to free after painting.
pub free: Vec<TextureId>,
}
impl TexturesDelta {
2022-02-22 12:21:41 +00:00
pub fn is_empty(&self) -> bool {
self.set.is_empty() && self.free.is_empty()
}
pub fn append(&mut self, mut newer: TexturesDelta) {
self.set.extend(newer.set.into_iter());
self.free.append(&mut newer.free);
}
2022-02-21 20:51:21 +00:00
pub fn clear(&mut self) {
self.set.clear();
self.free.clear();
}
}
2022-02-22 12:21:41 +00:00
impl std::fmt::Debug for TexturesDelta {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use std::fmt::Write as _;
2022-02-22 12:21:41 +00:00
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,
2022-02-22 12:21:41 +00:00
"{:?} partial ([{} {}] - [{} {}]), ",
tex_id,
pos[0],
pos[1],
pos[0] + size[0],
pos[1] + size[1]
)
.ok();
2022-02-22 12:21:41 +00:00
} else {
write!(string, "{:?} full {}x{}, ", tex_id, size[0], size[1]).ok();
2022-02-22 12:21:41 +00:00
}
}
debug_struct.field("set", &string);
}
if !self.free.is_empty() {
debug_struct.field("free", &self.free);
}
debug_struct.finish()
}
}