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