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},
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, PartialEq, Eq, Hash)]
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}
33
34pub struct BindingInstance<T> {
39 target: Handle<T>,
40 params: Vec<(&'static str, BindingInstanceEntry)>,
41 _marker: PhantomData<fn() -> T>,
42}
43
44impl<T> BindingInstance<T>
45where
46 T: Asset<Backend>,
47 T::Processed: BindGroupTarget,
48{
49 pub fn new(target: Handle<T>) -> Self {
50 Self { target, params: Vec::new(), _marker: PhantomData }
51 }
52
53 pub fn with_texture(mut self, name: &'static str, handle: Handle<Texture>) -> Self {
54 self.params.push((name, BindingInstanceEntry::Texture(handle)));
55 self
56 }
57
58 pub fn with_texture_array(mut self, name: &'static str, handle: Handle<TextureArray>) -> Self {
59 self.params.push((name, BindingInstanceEntry::TextureArray(handle)));
60 self
61 }
62
63 pub fn with_cubemap(mut self, name: &'static str, handle: Handle<Cubemap>) -> Self {
64 self.params.push((name, BindingInstanceEntry::Cubemap(handle)));
65 self
66 }
67
68 pub fn with_sampler(mut self, name: &'static str, kind: SamplerKind) -> Self {
69 self.params.push((name, BindingInstanceEntry::Sampler(kind)));
70 self
71 }
72
73 pub fn with_uniform(mut self, name: &'static str, data: Vec<u8>) -> Self {
74 self.params.push((name, BindingInstanceEntry::Uniform(data)));
75 self
76 }
77
78 pub fn with_storage(mut self, name: &'static str, data: Vec<u8>) -> Self {
79 self.params.push((name, BindingInstanceEntry::Storage(data)));
80 self
81 }
82
83 pub fn with_param(mut self, name: &'static str, entry: BindingInstanceEntry) -> Self {
84 self.params.push((name, entry));
85 self
86 }
87
88 fn validate(&self) {
89 if self.params.is_empty() {
90 tracing::warn!(
91 "BindingInstance::new(): no params — this instance won't bind anything \
92 against its target; did you forget to chain .with_texture(...)/.with_sampler(...)/etc.?"
93 );
94 }
95 }
96
97 pub fn build_asset(self, name: &str, assets: &mut Assets<BindingInstance<T>>) -> Handle<BindingInstance<T>>
98 where
99 BindingInstance<T>: AssetSource,
100 {
101 self.validate();
102 assets.insert(name, self)
103 }
104}
105
106pub fn binding_index(entries: &[BindingEntry], name: &str) -> Option<u32> {
108 entries.iter().find(|e| e.name == name).map(|e| e.binding)
109}
110
111pub struct GPUBindingInstance<T> {
113 pub target: Handle<T>,
114 pub bind_group: BindGroup,
115 buffers: Vec<(&'static str, Buffer)>,
116 _marker: PhantomData<fn() -> T>,
117}
118
119impl<T> GPUBindingInstance<T> {
120 pub fn update(&self, name: &str, data: &[u8]) {
123 match self.buffer(name) {
124 Some(buf) => buf.write(data),
125 None => tracing::warn!(
126 "GPUBindingInstance::update: no bound buffer named '{name}' — check for a typo \
127 against the entries in this instance's BindingInstance"
128 ),
129 }
130 }
131
132 pub fn buffer(&self, name: &str) -> Option<&Buffer> {
133 self.buffers.iter().find(|(n, _)| *n == name).map(|(_, buf)| buf)
134 }
135}
136
137impl<T> AssetSource for BindingInstance<T>
138where
139 T: Asset<Backend>,
140 T::Processed: BindGroupTarget,
141{
142 type Processed = GPUBindingInstance<T>;
143}
144
145impl<T> Asset<Backend> for BindingInstance<T>
146where
147 T: Asset<Backend>,
148 T::Processed: BindGroupTarget,
149{
150 type Deps<'a> = (
151 Read<'a, Assets<T>>,
152 Read<'a, Assets<Texture>>,
153 Read<'a, Assets<TextureArray>>,
154 Read<'a, Assets<Cubemap>>,
155 Read<'a, GlobalSamplers>,
156 );
157
158 fn upload<'a>(&self, backend: &Backend, deps: &Self::Deps<'a>) -> Option<GPUBindingInstance<T>> {
159 let (targets, textures, texture_arrays, cubemaps, samplers) = deps;
160 let target = targets.get(self.target)?;
161
162 let owned_buffers: Vec<(&'static str, Buffer)> = self
163 .params
164 .iter()
165 .filter_map(|(name, entry)| match entry {
166 BindingInstanceEntry::Uniform(bytes) => Some((
167 *name,
168 BufferBuilder::with_data(bytes)
169 .with_usage(BufferUsages::UNIFORM | BufferUsages::COPY_DST | BufferUsages::COPY_SRC)
170 .build(backend),
171 )),
172 BindingInstanceEntry::Storage(bytes) => Some((
173 *name,
174 BufferBuilder::with_data(bytes)
175 .with_usage(BufferUsages::STORAGE | BufferUsages::COPY_DST | BufferUsages::COPY_SRC)
176 .build(backend),
177 )),
178 _ => None,
179 })
180 .collect();
181
182 let mut builder = BindGroupBuilder::new(target.bind_group_layout());
183 for (name, entry) in &self.params {
184 let binding = binding_index(target.binding_entries(), name)?;
185 builder = match entry {
186 BindingInstanceEntry::Texture(handle) => builder.with_texture_2d_at(binding, textures.get(*handle)?),
187 BindingInstanceEntry::TextureArray(handle) => {
188 builder.with_texture_array_at(binding, texture_arrays.get(*handle)?)
189 }
190 BindingInstanceEntry::Cubemap(handle) => builder.with_texture_cubemap_at(binding, cubemaps.get(*handle)?),
191 BindingInstanceEntry::Sampler(kind) => builder.with_sampler_at(binding, samplers.get(*kind)),
192 BindingInstanceEntry::Uniform(_) | BindingInstanceEntry::Storage(_) => {
193 let buf = &owned_buffers.iter().find(|(n, _)| n == name)?.1;
194 builder.with_buffer_at(binding, buf)
195 }
196 };
197 }
198 let bind_group = builder.build(backend);
199
200 Some(GPUBindingInstance { target: self.target, bind_group, buffers: owned_buffers, _marker: PhantomData })
201 }
202}
203
204pub type GPUMaterialInstance = GPUBindingInstance<Material>;
205pub type MaterialInstance = BindingInstance<Material>;
208
209pub type GPUComputeInstance = GPUBindingInstance<Compute>;
210pub type ComputeInstance = BindingInstance<Compute>;