wde_renderer/assets/bindings/
render_data.rs

1use wde_logger::prelude::*;
2
3use crate::prelude::*;
4use bevy::{
5    ecs::system::{
6        StaticSystemParam, SystemParam, SystemParamItem, SystemState,
7        lifetimeless::{SRes, SResMut}
8    },
9    prelude::*
10};
11use std::{any::TypeId, collections::HashMap};
12
13#[derive(Message)]
14pub(crate) struct RenderDataRecreated(pub TypeId);
15
16// ============= RENDER DATA PLUGIN REGISTER =============
17/// A plugin to register a render data type.
18///
19/// This should be added to the app to initialize the [`RenderData`] and create its
20/// corresponding [`GpuRenderData`]. If [`RenderData::recreate`] is implemented and returns
21/// `Some(true)`, the data are recreated and dependencies are notified.
22///
23/// The GPU asset can then be retrieved using [`GpuRenderData`], and the binding index
24/// specified in the description can be used to get the corresponding buffer or texture handle.
25///
26/// The render data type must implement [`RenderData`] and derive at least [`Asset`],
27/// [`TypePath`] and [`Clone`].
28pub struct RenderDataRegisterPlugin<R: RenderData>(std::marker::PhantomData<R>);
29impl<R: RenderData> Default for RenderDataRegisterPlugin<R> {
30    fn default() -> Self {
31        RenderDataRegisterPlugin(std::marker::PhantomData)
32    }
33}
34impl<R: RenderData + TypePath + Sync + Send + Clone + Asset> Plugin
35    for RenderDataRegisterPlugin<R>
36{
37    fn build(&self, app: &mut App) {
38        app.add_message::<RenderDataRecreated>();
39
40        // Register asset
41        app.init_asset::<RenderDataHolder<R>>();
42
43        // Register render asset
44        app.add_plugins(RenderAssetsPlugin::<GpuRenderData<R>>::default());
45    }
46
47    fn finish(&self, app: &mut App) {
48        // Build the render data, add it to the asset server, and insert the handle as a resource for retrieval in the gpu creation stage
49        let builder = {
50            let mut builder = RenderDataBuilder::default();
51            let mut state: SystemState<R::Params> = SystemState::new(app.world_mut());
52            let mut params = state.get_mut(app.world_mut());
53            R::describe(&mut params, &mut builder);
54            builder
55        };
56        let handle = app
57            .world_mut()
58            .resource::<AssetServer>()
59            .add(RenderDataHolder::<R> {
60                _phantom: std::marker::PhantomData,
61                builder
62            });
63        app.world_mut()
64            .commands()
65            .insert_resource(RenderDataHolderHandle(handle));
66
67        // Add the recreate system if necessary
68        let recreate = {
69            let mut state: SystemState<R::Params> = SystemState::new(app.world_mut());
70            let params = state.get_mut(app.world_mut());
71            R::recreate(&params).is_some()
72        };
73        if recreate {
74            app.add_systems(Update, recreate_system::<R>);
75        }
76    }
77}
78
79fn recreate_system<R: RenderData + TypePath + Sync + Send + 'static>(
80    mut params: StaticSystemParam<R::Params>,
81    asset_server: Res<AssetServer>,
82    mut commands: Commands,
83    mut recreate_version: MessageWriter<RenderDataRecreated>
84) {
85    if let Some(true) = R::recreate(&params) {
86        debug!("Recreating render data {}.", std::any::type_name::<R>());
87
88        // Create new render data and update the asset
89        let builder = {
90            let mut builder = RenderDataBuilder::default();
91            R::describe(&mut params, &mut builder);
92            builder
93        };
94        let new_handle = asset_server.add(RenderDataHolder::<R> {
95            _phantom: std::marker::PhantomData,
96            builder
97        });
98        commands.insert_resource(RenderDataHolderHandle(new_handle));
99
100        // Add that the render data have been recreated for this type
101        recreate_version.write(RenderDataRecreated(TypeId::of::<R>()));
102    }
103}
104
105// ============= RENDER DATA AND BUILDER METHODS =============
106/// A builder for render data. This is used to describe the buffers and textures that should be created on the gpu for a render data.
107/// After each data have been described, buffers and textures will be created on the gpu according to the description, and can be retrieved on the GPU side (see [`GpuRenderData`](GpuRenderData)).
108#[derive(Default, Clone)]
109pub struct RenderDataBuilder {
110    binding_to_index: HashMap<u32, (usize, usize)>, // binding index, (binding type (0 for buffer, 1 for texture), idx in array)
111    buffers: Vec<Buffer>,
112    textures: Vec<Texture>
113}
114impl RenderDataBuilder {
115    /// Adds a buffer to the render data. The binding index is used to retrieve the buffer later when using this render data.
116    pub fn add_buffer(&mut self, binding: u32, buffer: Buffer) -> &mut Self {
117        self.buffers.push(buffer);
118        self.binding_to_index
119            .insert(binding, (0, self.buffers.len() - 1));
120        self
121    }
122    /// Adds a texture to the render data. The binding index is used to retrieve the texture later when using this render data.
123    pub fn add_texture(&mut self, binding: u32, texture: Texture) -> &mut Self {
124        self.textures.push(texture);
125        self.binding_to_index
126            .insert(binding, (1, self.textures.len() - 1));
127        self
128    }
129}
130
131/// A trait for describing render data.
132///
133/// This trait is used to declare buffers and textures that should be created on the GPU.
134/// The struct implementing this trait should usually derive [`Asset`], [`TypePath`] and
135/// [`Clone`] so it can be registered through [`RenderDataRegisterPlugin`].
136///
137/// The prepared GPU side can then be retrieved in render systems through [`GpuRenderData`],
138/// using the binding indices specified in [`RenderData::describe`].
139pub trait RenderData {
140    type Params: SystemParam;
141
142    /// Describes the render data by adding buffers and textures to the builder.
143    ///
144    /// This is called during initialization, and when [`recreate`](Self::recreate)
145    /// returns `Some(true)`.
146    fn describe(params: &mut SystemParamItem<Self::Params>, builder: &mut RenderDataBuilder);
147    /// Whether the gpu asset should be recreated. This is called every frame. The default is to never recreate (None).
148    fn recreate(_params: &SystemParamItem<Self::Params>) -> Option<bool> {
149        None
150    }
151}
152
153// ============= RENDER DATA BUILDER TEMPORARY STORAGE =============
154#[allow(unused)]
155#[derive(Resource)]
156struct RenderDataHolderHandle<R: RenderData + TypePath + Sync + Send>(Handle<RenderDataHolder<R>>);
157
158#[derive(Asset, Clone, TypePath)]
159pub struct RenderDataHolder<R: RenderData + TypePath + Sync + Send> {
160    _phantom: std::marker::PhantomData<R>,
161    builder: RenderDataBuilder
162}
163
164// ============= RENDER DATA RENDER ASSET GPU CREATION =============
165/// The gpu asset that is created from the render data holder. This is the asset that is actually used in the render pass.
166pub type SRenderData<T> = SRes<RenderAssets<GpuRenderData<T>>>;
167/// The gpu asset that is created from the render data holder. This is the asset that is actually used in the render pass.
168pub type SRenderDataMut<T> = SResMut<RenderAssets<GpuRenderData<T>>>;
169
170/// Alias for a `Res<RenderAssets<GpuRenderData<M>>>`.
171pub type ResRenderData<'w, M> = Res<'w, RenderAssets<GpuRenderData<M>>>;
172/// Alias for a `ResMut<RenderAssets<GpuRenderData<M>>>`.
173pub type ResMutRenderData<'w, M> = ResMut<'w, RenderAssets<GpuRenderData<M>>>;
174
175/// The gpu asset that is created from the render data holder. This is the asset that is actually used in the render pass.
176/// It contains the gpu buffers and textures that are created according to the description in the [`RenderData`](RenderData) implementation, and can be retrieved using the binding index specified in the description.
177pub struct GpuRenderData<R: RenderData> {
178    _phantom: std::marker::PhantomData<R>,
179    builder: RenderDataBuilder,
180    buffers: Vec<Handle<Buffer>>,
181    textures: Vec<Handle<Texture>>
182}
183impl<R: RenderData> GpuRenderData<R> {
184    /// Retrieves the buffer handle for the given binding index.
185    pub fn get_buffer(&self, binding: u32) -> Option<Handle<Buffer>> {
186        self.builder
187            .binding_to_index
188            .get(&binding)
189            .and_then(|(ty, idx)| {
190                debug_assert!(
191                    *ty == 0,
192                    "Tried to get buffer for binding {}, but it was a texture",
193                    binding
194                );
195                self.buffers.get(*idx).cloned()
196            })
197    }
198    /// Retrieves the texture handle for the given binding index.
199    pub fn get_texture(&self, binding: u32) -> Option<Handle<Texture>> {
200        self.builder
201            .binding_to_index
202            .get(&binding)
203            .and_then(|(ty, idx)| {
204                debug_assert!(
205                    *ty == 1,
206                    "Tried to get texture for binding {}, but it was a buffer",
207                    binding
208                );
209                self.textures.get(*idx).cloned()
210            })
211    }
212}
213impl<R: RenderData + Clone + Asset> RenderAsset for GpuRenderData<R> {
214    type SourceAsset = RenderDataHolder<R>;
215    type Params = SRes<AssetServer>;
216
217    fn prepare(
218        asset: Self::SourceAsset,
219        asset_server: &mut SystemParamItem<Self::Params>
220    ) -> Result<Self, PrepareAssetError<Self::SourceAsset>> {
221        debug!(
222            "Preparing render binding {} GPU resources.",
223            std::any::type_name::<R>()
224        );
225        let mut buffers = Vec::new();
226        let mut textures = Vec::new();
227        let mut updated_builder = RenderDataBuilder::default();
228        for (binding, (ty, idx)) in asset.builder.binding_to_index.iter() {
229            match ty {
230                0 => {
231                    // buffer
232                    let mut buffer = asset.builder.buffers.get(*idx).unwrap().clone();
233                    let handle = asset_server.add(buffer.clone());
234                    buffers.push(handle);
235
236                    buffer.content = None; // free cpu side content as it's now on gpu, and we won't need it anymore
237                    updated_builder.add_buffer(*binding, buffer);
238                }
239                1 => {
240                    // texture
241                    let mut texture = asset.builder.textures.get(*idx).unwrap().clone();
242                    let handle = asset_server.add(texture.clone());
243                    textures.push(handle);
244
245                    texture.data = vec![]; // free cpu side content as it's now on gpu, and we won't need it anymore
246                    updated_builder.add_texture(*binding, texture);
247                }
248                _ => unreachable!()
249            }
250        }
251        Ok(GpuRenderData {
252            _phantom: std::marker::PhantomData,
253            builder: updated_builder,
254            buffers,
255            textures
256        })
257    }
258}