pebble/graphics/pipeline/
instance.rs1use std::marker::PhantomData;
2
3use crate::{
4 assets::{handle::Handle, storage::Assets, upload::{Asset, AssetSource}},
5 ecs::resources::Read,
6 graphics::{
7 pipeline::{
8 binding::{BindGroupTarget, BindingEntry},
9 buffers::{BindGroup, BindGroupBuilder, Buffer, BufferBuilder, DynamicBuffer},
10 compute::Compute,
11 cubemap::Cubemap,
12 material::Material,
13 samplers::{GlobalSamplers, SamplerKind},
14 texture_array::TextureArray,
15 texture_view::TextureView,
16 textures::Texture,
17 },
18 render::Backend,
19 types::flags::BufferUsages,
20 },
21};
22
23#[derive(Clone)]
26pub enum BindingInstanceEntry {
27 Texture(Handle<Texture>),
28 TextureArray(Handle<TextureArray>),
29 Cubemap(Handle<Cubemap>),
30 TextureView(TextureView),
31 Sampler(SamplerKind),
32 Uniform(Vec<u8>),
33 Storage(Vec<u8>),
34 Buffer(Buffer),
35 DynamicBuffer(DynamicBuffer),
36}
37
38pub struct BindingInstance<T> {
43 target: Handle<T>,
44 params: Vec<(&'static str, BindingInstanceEntry)>,
45 _marker: PhantomData<fn() -> T>,
46}
47
48impl<T> BindingInstance<T>
49where
50 T: Asset<Backend>,
51 T::Processed: BindGroupTarget,
52{
53 pub fn new(target: Handle<T>) -> Self {
54 Self { target, params: Vec::new(), _marker: PhantomData }
55 }
56
57 pub fn with_texture(mut self, name: &'static str, handle: Handle<Texture>) -> Self {
58 self.params.push((name, BindingInstanceEntry::Texture(handle)));
59 self
60 }
61
62 pub fn with_texture_array(mut self, name: &'static str, handle: Handle<TextureArray>) -> Self {
63 self.params.push((name, BindingInstanceEntry::TextureArray(handle)));
64 self
65 }
66
67 pub fn with_cubemap(mut self, name: &'static str, handle: Handle<Cubemap>) -> Self {
68 self.params.push((name, BindingInstanceEntry::Cubemap(handle)));
69 self
70 }
71
72 pub fn with_texture_view(mut self, name: &'static str, view: TextureView) -> Self {
78 self.params.push((name, BindingInstanceEntry::TextureView(view)));
79 self
80 }
81
82 pub fn with_sampler(mut self, name: &'static str, kind: SamplerKind) -> Self {
83 self.params.push((name, BindingInstanceEntry::Sampler(kind)));
84 self
85 }
86
87 pub fn with_uniform(mut self, name: &'static str, data: Vec<u8>) -> Self {
88 self.params.push((name, BindingInstanceEntry::Uniform(data)));
89 self
90 }
91
92 pub fn with_storage(mut self, name: &'static str, data: Vec<u8>) -> Self {
93 self.params.push((name, BindingInstanceEntry::Storage(data)));
94 self
95 }
96
97 pub fn with_buffer(mut self, name: &'static str, buffer: Buffer) -> Self {
104 self.params.push((name, BindingInstanceEntry::Buffer(buffer)));
105 self
106 }
107
108 pub fn with_dynamic_buffer(mut self, name: &'static str, buffer: DynamicBuffer) -> Self {
114 self.params.push((name, BindingInstanceEntry::DynamicBuffer(buffer)));
115 self
116 }
117
118 pub fn with_param(mut self, name: &'static str, entry: BindingInstanceEntry) -> Self {
119 self.params.push((name, entry));
120 self
121 }
122
123 fn validate(&self) {
124 if self.params.is_empty() {
125 tracing::warn!(
126 "BindingInstance::new(): no params — this instance won't bind anything \
127 against its target; did you forget to chain .with_texture(...)/.with_sampler(...)/etc.?"
128 );
129 }
130 }
131
132 pub fn build_asset(self, name: &str, assets: &mut Assets<BindingInstance<T>>) -> Handle<BindingInstance<T>>
133 where
134 BindingInstance<T>: AssetSource,
135 {
136 self.validate();
137 assets.insert(name, self)
138 }
139}
140
141pub fn binding_index(entries: &[BindingEntry], name: &str) -> Option<u32> {
143 entries.iter().find(|e| e.name == name).map(|e| e.binding)
144}
145
146pub struct GPUBindingInstance<T> {
148 pub target: Handle<T>,
149 pub bind_group: BindGroup,
150 buffers: Vec<(&'static str, Buffer)>,
151 dynamic_buffers: Vec<(&'static str, DynamicBuffer)>,
152 _marker: PhantomData<fn() -> T>,
153}
154
155impl<T> GPUBindingInstance<T> {
156 pub fn update(&self, name: &str, data: &[u8]) {
159 match self.buffer(name) {
160 Some(buf) => buf.write(data),
161 None => tracing::warn!(
162 "GPUBindingInstance::update: no bound buffer named '{name}' — check for a typo \
163 against the entries in this instance's BindingInstance"
164 ),
165 }
166 }
167
168 pub fn buffer(&self, name: &str) -> Option<&Buffer> {
169 self.buffers.iter().find(|(n, _)| *n == name).map(|(_, buf)| buf)
170 }
171
172 pub fn dynamic_buffer(&self, name: &str) -> Option<&DynamicBuffer> {
176 self.dynamic_buffers.iter().find(|(n, _)| *n == name).map(|(_, buf)| buf)
177 }
178}
179
180impl<T> AssetSource for BindingInstance<T>
181where
182 T: Asset<Backend>,
183 T::Processed: BindGroupTarget,
184{
185 type Processed = GPUBindingInstance<T>;
186}
187
188impl<T> Asset<Backend> for BindingInstance<T>
189where
190 T: Asset<Backend>,
191 T::Processed: BindGroupTarget,
192{
193 type Deps<'a> = (
194 Read<'a, Assets<T>>,
195 Read<'a, Assets<Texture>>,
196 Read<'a, Assets<TextureArray>>,
197 Read<'a, Assets<Cubemap>>,
198 Read<'a, GlobalSamplers>,
199 );
200
201 fn upload<'a>(&self, backend: &Backend, deps: &Self::Deps<'a>) -> Option<GPUBindingInstance<T>> {
202 let (targets, textures, texture_arrays, cubemaps, samplers) = deps;
203 let target = targets.get(self.target)?;
204
205 let owned_buffers: Vec<(&'static str, Buffer)> = self
210 .params
211 .iter()
212 .filter_map(|(name, entry)| match entry {
213 BindingInstanceEntry::Uniform(bytes) => Some((
214 *name,
215 BufferBuilder::with_data(bytes)
216 .with_usage(BufferUsages::UNIFORM | BufferUsages::COPY_DST | BufferUsages::COPY_SRC)
217 .build(backend),
218 )),
219 BindingInstanceEntry::Storage(bytes) => Some((
220 *name,
221 BufferBuilder::with_data(bytes)
222 .with_usage(BufferUsages::STORAGE | BufferUsages::COPY_DST | BufferUsages::COPY_SRC)
223 .build(backend),
224 )),
225 BindingInstanceEntry::Buffer(buffer) => Some((*name, buffer.clone())),
226 _ => None,
227 })
228 .collect();
229
230 let owned_dynamic_buffers: Vec<(&'static str, DynamicBuffer)> = self
234 .params
235 .iter()
236 .filter_map(|(name, entry)| match entry {
237 BindingInstanceEntry::DynamicBuffer(buffer) => Some((*name, buffer.clone())),
238 _ => None,
239 })
240 .collect();
241
242 let mut builder = BindGroupBuilder::new(target.bind_group_layout());
243 for (name, entry) in &self.params {
244 let binding = binding_index(target.binding_entries(), name)?;
245 builder = match entry {
246 BindingInstanceEntry::Texture(handle) => builder.with_texture_2d_at(binding, textures.get(*handle)?),
247 BindingInstanceEntry::TextureArray(handle) => {
248 builder.with_texture_array_at(binding, texture_arrays.get(*handle)?)
249 }
250 BindingInstanceEntry::Cubemap(handle) => builder.with_texture_cubemap_at(binding, cubemaps.get(*handle)?),
251 BindingInstanceEntry::TextureView(view) => builder.with_texture_view_at(binding, view),
252 BindingInstanceEntry::Sampler(kind) => builder.with_sampler_at(binding, samplers.get(*kind)),
253 BindingInstanceEntry::Uniform(_) | BindingInstanceEntry::Storage(_) | BindingInstanceEntry::Buffer(_) => {
254 let buf = &owned_buffers.iter().find(|(n, _)| n == name)?.1;
255 builder.with_buffer_at(binding, buf)
256 }
257 BindingInstanceEntry::DynamicBuffer(_) => {
258 let buf = &owned_dynamic_buffers.iter().find(|(n, _)| n == name)?.1;
259 builder.with_dynamic_buffer_at(binding, buf)
260 }
261 };
262 }
263 let bind_group = builder.build(backend);
264
265 Some(GPUBindingInstance {
266 target: self.target,
267 bind_group,
268 buffers: owned_buffers,
269 dynamic_buffers: owned_dynamic_buffers,
270 _marker: PhantomData,
271 })
272 }
273}
274
275pub type GPUMaterialInstance = GPUBindingInstance<Material>;
276pub type MaterialInstance = BindingInstance<Material>;
279
280pub type GPUComputeInstance = GPUBindingInstance<Compute>;
281pub type ComputeInstance = BindingInstance<Compute>;