hephae_render/
image_bind.rs

1//! Utilities to store and keep track of [`Image`]s as [`BindGroup`]s in the render world.
2//!
3//! See `examples/atlas.rs` for example usage.
4
5use bevy_asset::prelude::*;
6use bevy_ecs::{
7    event::EventReader,
8    prelude::{Resource, *},
9};
10use bevy_image::prelude::*;
11use bevy_render::{
12    Extract,
13    render_resource::{BindGroup, BindGroupEntry, BindGroupLayout},
14    renderer::RenderDevice,
15};
16use bevy_utils::{Entry, HashMap};
17
18/// Extracts [`AssetEvent<Image>`]s from the main world to the render world.
19#[derive(Resource, Default)]
20pub struct ImageAssetEvents(Vec<AssetEvent<Image>>);
21
22/// Stores [`BindGroup`]s for each [`Image`] on-demand via [`create`](ImageBindGroups::create).
23#[derive(Resource, Default)]
24pub struct ImageBindGroups(HashMap<AssetId<Image>, BindGroup>);
25impl ImageBindGroups {
26    /// Ensures a [`BindGroup`] for a given [`Image`] is created, returning `true` if it exists
27    /// already. Should work in concert with
28    /// [`Vertex::create_batch`](crate::vertex::Vertex::create_batch).
29    #[inline]
30    pub fn create(
31        &mut self,
32        id: impl Into<AssetId<Image>>,
33        device: &RenderDevice,
34        layout: &BindGroupLayout,
35        entries: &[BindGroupEntry],
36    ) -> bool {
37        match self.0.entry(id.into()) {
38            Entry::Vacant(e) => {
39                e.insert(device.create_bind_group("hephae_atlas_page", layout, entries));
40                true
41            }
42            Entry::Occupied(..) => false,
43        }
44    }
45
46    /// Gets the [`BindGroup`] previously created with [`create`](Self::create). Should work in
47    /// concert with [`RenderCommand::render`](bevy_render::render_phase::RenderCommand::render).
48    #[inline]
49    pub fn get(&self, id: impl Into<AssetId<Image>>) -> Option<&BindGroup> {
50        self.0.get(&id.into())
51    }
52}
53
54/// Populates [`ImageAssetEvents`].
55pub fn extract_image_events(
56    mut events: ResMut<ImageAssetEvents>,
57    mut image_events: Extract<EventReader<AssetEvent<Image>>>,
58) {
59    let images = &mut events.0;
60    images.extend(image_events.read());
61}
62
63/// For each removed [`Image`], remove the [`BindGroup`] in [`ImageBindGroups`] too.
64pub fn validate_image_bind_groups(mut image_bind_groups: ResMut<ImageBindGroups>, mut events: ResMut<ImageAssetEvents>) {
65    for event in events.0.drain(..) {
66        match event {
67            AssetEvent::Added { .. } | AssetEvent::LoadedWithDependencies { .. } => {}
68            AssetEvent::Modified { id } | AssetEvent::Removed { id } | AssetEvent::Unused { id } => {
69                image_bind_groups.0.remove(&id);
70            }
71        }
72    }
73}