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 textures::Texture,
16 },
17 render::Backend,
18 types::flags::BufferUsages,
19 },
20};
21
22#[derive(Clone)]
25pub enum BindingInstanceEntry {
26 Texture(Handle<Texture>),
27 TextureArray(Handle<TextureArray>),
28 Cubemap(Handle<Cubemap>),
29 Sampler(SamplerKind),
30 Uniform(Vec<u8>),
31 Storage(Vec<u8>),
32 Buffer(Buffer),
33 DynamicBuffer(DynamicBuffer),
34}
35
36pub struct BindingInstance<T> {
41 target: Handle<T>,
42 params: Vec<(&'static str, BindingInstanceEntry)>,
43 _marker: PhantomData<fn() -> T>,
44}
45
46impl<T> BindingInstance<T>
47where
48 T: Asset<Backend>,
49 T::Processed: BindGroupTarget,
50{
51 pub fn new(target: Handle<T>) -> Self {
52 Self { target, params: Vec::new(), _marker: PhantomData }
53 }
54
55 pub fn with_texture(mut self, name: &'static str, handle: Handle<Texture>) -> Self {
56 self.params.push((name, BindingInstanceEntry::Texture(handle)));
57 self
58 }
59
60 pub fn with_texture_array(mut self, name: &'static str, handle: Handle<TextureArray>) -> Self {
61 self.params.push((name, BindingInstanceEntry::TextureArray(handle)));
62 self
63 }
64
65 pub fn with_cubemap(mut self, name: &'static str, handle: Handle<Cubemap>) -> Self {
66 self.params.push((name, BindingInstanceEntry::Cubemap(handle)));
67 self
68 }
69
70 pub fn with_sampler(mut self, name: &'static str, kind: SamplerKind) -> Self {
71 self.params.push((name, BindingInstanceEntry::Sampler(kind)));
72 self
73 }
74
75 pub fn with_uniform(mut self, name: &'static str, data: Vec<u8>) -> Self {
76 self.params.push((name, BindingInstanceEntry::Uniform(data)));
77 self
78 }
79
80 pub fn with_storage(mut self, name: &'static str, data: Vec<u8>) -> Self {
81 self.params.push((name, BindingInstanceEntry::Storage(data)));
82 self
83 }
84
85 pub fn with_buffer(mut self, name: &'static str, buffer: Buffer) -> Self {
92 self.params.push((name, BindingInstanceEntry::Buffer(buffer)));
93 self
94 }
95
96 pub fn with_dynamic_buffer(mut self, name: &'static str, buffer: DynamicBuffer) -> Self {
102 self.params.push((name, BindingInstanceEntry::DynamicBuffer(buffer)));
103 self
104 }
105
106 pub fn with_param(mut self, name: &'static str, entry: BindingInstanceEntry) -> Self {
107 self.params.push((name, entry));
108 self
109 }
110
111 fn validate(&self) {
112 if self.params.is_empty() {
113 tracing::warn!(
114 "BindingInstance::new(): no params — this instance won't bind anything \
115 against its target; did you forget to chain .with_texture(...)/.with_sampler(...)/etc.?"
116 );
117 }
118 }
119
120 pub fn build_asset(self, name: &str, assets: &mut Assets<BindingInstance<T>>) -> Handle<BindingInstance<T>>
121 where
122 BindingInstance<T>: AssetSource,
123 {
124 self.validate();
125 assets.insert(name, self)
126 }
127}
128
129pub fn binding_index(entries: &[BindingEntry], name: &str) -> Option<u32> {
131 entries.iter().find(|e| e.name == name).map(|e| e.binding)
132}
133
134pub struct GPUBindingInstance<T> {
136 pub target: Handle<T>,
137 pub bind_group: BindGroup,
138 buffers: Vec<(&'static str, Buffer)>,
139 dynamic_buffers: Vec<(&'static str, DynamicBuffer)>,
140 _marker: PhantomData<fn() -> T>,
141}
142
143impl<T> GPUBindingInstance<T> {
144 pub fn update(&self, name: &str, data: &[u8]) {
147 match self.buffer(name) {
148 Some(buf) => buf.write(data),
149 None => tracing::warn!(
150 "GPUBindingInstance::update: no bound buffer named '{name}' — check for a typo \
151 against the entries in this instance's BindingInstance"
152 ),
153 }
154 }
155
156 pub fn buffer(&self, name: &str) -> Option<&Buffer> {
157 self.buffers.iter().find(|(n, _)| *n == name).map(|(_, buf)| buf)
158 }
159
160 pub fn dynamic_buffer(&self, name: &str) -> Option<&DynamicBuffer> {
164 self.dynamic_buffers.iter().find(|(n, _)| *n == name).map(|(_, buf)| buf)
165 }
166}
167
168impl<T> AssetSource for BindingInstance<T>
169where
170 T: Asset<Backend>,
171 T::Processed: BindGroupTarget,
172{
173 type Processed = GPUBindingInstance<T>;
174}
175
176impl<T> Asset<Backend> for BindingInstance<T>
177where
178 T: Asset<Backend>,
179 T::Processed: BindGroupTarget,
180{
181 type Deps<'a> = (
182 Read<'a, Assets<T>>,
183 Read<'a, Assets<Texture>>,
184 Read<'a, Assets<TextureArray>>,
185 Read<'a, Assets<Cubemap>>,
186 Read<'a, GlobalSamplers>,
187 );
188
189 fn upload<'a>(&self, backend: &Backend, deps: &Self::Deps<'a>) -> Option<GPUBindingInstance<T>> {
190 let (targets, textures, texture_arrays, cubemaps, samplers) = deps;
191 let target = targets.get(self.target)?;
192
193 let owned_buffers: Vec<(&'static str, Buffer)> = self
198 .params
199 .iter()
200 .filter_map(|(name, entry)| match entry {
201 BindingInstanceEntry::Uniform(bytes) => Some((
202 *name,
203 BufferBuilder::with_data(bytes)
204 .with_usage(BufferUsages::UNIFORM | BufferUsages::COPY_DST | BufferUsages::COPY_SRC)
205 .build(backend),
206 )),
207 BindingInstanceEntry::Storage(bytes) => Some((
208 *name,
209 BufferBuilder::with_data(bytes)
210 .with_usage(BufferUsages::STORAGE | BufferUsages::COPY_DST | BufferUsages::COPY_SRC)
211 .build(backend),
212 )),
213 BindingInstanceEntry::Buffer(buffer) => Some((*name, buffer.clone())),
214 _ => None,
215 })
216 .collect();
217
218 let owned_dynamic_buffers: Vec<(&'static str, DynamicBuffer)> = self
222 .params
223 .iter()
224 .filter_map(|(name, entry)| match entry {
225 BindingInstanceEntry::DynamicBuffer(buffer) => Some((*name, buffer.clone())),
226 _ => None,
227 })
228 .collect();
229
230 let mut builder = BindGroupBuilder::new(target.bind_group_layout());
231 for (name, entry) in &self.params {
232 let binding = binding_index(target.binding_entries(), name)?;
233 builder = match entry {
234 BindingInstanceEntry::Texture(handle) => builder.with_texture_2d_at(binding, textures.get(*handle)?),
235 BindingInstanceEntry::TextureArray(handle) => {
236 builder.with_texture_array_at(binding, texture_arrays.get(*handle)?)
237 }
238 BindingInstanceEntry::Cubemap(handle) => builder.with_texture_cubemap_at(binding, cubemaps.get(*handle)?),
239 BindingInstanceEntry::Sampler(kind) => builder.with_sampler_at(binding, samplers.get(*kind)),
240 BindingInstanceEntry::Uniform(_) | BindingInstanceEntry::Storage(_) | BindingInstanceEntry::Buffer(_) => {
241 let buf = &owned_buffers.iter().find(|(n, _)| n == name)?.1;
242 builder.with_buffer_at(binding, buf)
243 }
244 BindingInstanceEntry::DynamicBuffer(_) => {
245 let buf = &owned_dynamic_buffers.iter().find(|(n, _)| n == name)?.1;
246 builder.with_dynamic_buffer_at(binding, buf)
247 }
248 };
249 }
250 let bind_group = builder.build(backend);
251
252 Some(GPUBindingInstance {
253 target: self.target,
254 bind_group,
255 buffers: owned_buffers,
256 dynamic_buffers: owned_dynamic_buffers,
257 _marker: PhantomData,
258 })
259 }
260}
261
262pub type GPUMaterialInstance = GPUBindingInstance<Material>;
263pub type MaterialInstance = BindingInstance<Material>;
266
267pub type GPUComputeInstance = GPUBindingInstance<Compute>;
268pub type ComputeInstance = BindingInstance<Compute>;