1use crate::{
22 core::{arrayvec::ArrayVec, log::Log, math::Rect, sstorage::ImmutableString},
23 graphics::{
24 error::FrameworkError,
25 framebuffer::{DrawCallStatistics, GpuFrameBuffer, ResourceBindGroup, ResourceBinding},
26 geometry_buffer::GpuGeometryBuffer,
27 gpu_program::{GpuProgram, ShaderResourceDefinition, ShaderResourceKind},
28 gpu_texture::GpuTexture,
29 sampler::GpuSampler,
30 server::GraphicsServer,
31 uniform::StaticUniformBuffer,
32 DrawParameters, ElementRange,
33 },
34 material::{
35 shader::{Shader, ShaderResource},
36 MaterialPropertyRef,
37 },
38 renderer::{
39 bundle,
40 cache::{uniform::UniformBufferCache, TemporaryCache},
41 },
42};
43use fxhash::FxHashMap;
44use std::ops::Deref;
45
46pub struct NamedValue<T> {
47 pub name: ImmutableString,
48 pub value: T,
49}
50
51impl<T> NamedValue<T> {
52 pub fn new(name: impl Into<ImmutableString>, value: T) -> Self {
53 Self {
54 name: name.into(),
55 value,
56 }
57 }
58}
59
60pub struct NamedValuesContainer<T, const N: usize> {
61 properties: [NamedValue<T>; N],
62}
63
64fn search<'a, T>(slice: &'a [NamedValue<T>], name: &ImmutableString) -> Option<&'a NamedValue<T>> {
65 slice
66 .binary_search_by(|prop| prop.name.cached_hash().cmp(&name.cached_hash()))
67 .ok()
68 .and_then(|idx| slice.get(idx))
69}
70
71impl<T, const N: usize> NamedValuesContainer<T, N> {
72 pub fn property_ref(&self, name: &ImmutableString) -> Option<&NamedValue<T>> {
73 search(&self.properties, name)
74 }
75
76 pub fn data_ref(&self) -> NamedValuesContainerRef<'_, T> {
77 NamedValuesContainerRef {
78 properties: &self.properties,
79 }
80 }
81}
82
83impl<T, const N: usize> From<[NamedValue<T>; N]> for NamedValuesContainer<T, N> {
84 fn from(mut value: [NamedValue<T>; N]) -> Self {
85 value.sort_unstable_by_key(|prop| prop.name.cached_hash());
86 Self { properties: value }
87 }
88}
89
90impl<T, const N: usize> Deref for NamedValuesContainer<T, N> {
91 type Target = [NamedValue<T>];
92
93 fn deref(&self) -> &Self::Target {
94 &self.properties
95 }
96}
97
98pub struct NamedValuesContainerRef<'a, T> {
99 properties: &'a [NamedValue<T>],
100}
101
102impl<T> NamedValuesContainerRef<'_, T> {
103 pub fn property_ref(&self, name: &ImmutableString) -> Option<&NamedValue<T>> {
104 search(self.properties, name)
105 }
106}
107
108pub struct PropertyGroup<'a, const N: usize> {
109 pub properties: NamedValuesContainer<MaterialPropertyRef<'a>, N>,
110}
111
112pub type NamedPropertyRef<'a> = NamedValue<MaterialPropertyRef<'a>>;
113
114pub fn property<'a>(
115 name: impl Into<ImmutableString>,
116 value: impl Into<MaterialPropertyRef<'a>>,
117) -> NamedPropertyRef<'a> {
118 NamedValue::new(name, value.into())
119}
120
121pub fn binding<'a, 'b>(
122 name: impl Into<ImmutableString>,
123 value: impl Into<GpuResourceBinding<'a, 'b>>,
124) -> NamedValue<GpuResourceBinding<'a, 'b>> {
125 NamedValue::new(name, value.into())
126}
127
128impl<'a> From<(&'a GpuTexture, &'a GpuSampler)> for GpuResourceBinding<'a, '_> {
129 fn from(value: (&'a GpuTexture, &'a GpuSampler)) -> Self {
130 GpuResourceBinding::Texture {
131 texture: value.0,
132 sampler: value.1,
133 }
134 }
135}
136
137impl<'a, 'b, const N: usize> From<&'a PropertyGroup<'b, N>> for GpuResourceBinding<'a, 'b> {
138 fn from(value: &'a PropertyGroup<'b, N>) -> Self {
139 GpuResourceBinding::PropertyGroup {
140 properties: value.properties.data_ref(),
141 }
142 }
143}
144
145impl<'a, const N: usize> From<[NamedPropertyRef<'a>; N]> for PropertyGroup<'a, N> {
146 fn from(value: [NamedPropertyRef<'a>; N]) -> Self {
147 Self {
148 properties: value.into(),
149 }
150 }
151}
152
153impl<'a, const N: usize> Deref for PropertyGroup<'a, N> {
154 type Target = [NamedValue<MaterialPropertyRef<'a>>];
155
156 fn deref(&self) -> &Self::Target {
157 &self.properties
158 }
159}
160
161pub enum GpuResourceBinding<'a, 'b> {
162 Texture {
163 texture: &'a GpuTexture,
164 sampler: &'a GpuSampler,
165 },
166 PropertyGroup {
167 properties: NamedValuesContainerRef<'a, MaterialPropertyRef<'b>>,
168 },
169}
170
171impl<'a, 'b> GpuResourceBinding<'a, 'b> {
172 pub fn texture(texture: &'a GpuTexture, sampler: &'a GpuSampler) -> Self {
173 Self::Texture { texture, sampler }
174 }
175
176 pub fn property_group<const N: usize>(properties: &'a PropertyGroup<'b, N>) -> Self {
177 Self::PropertyGroup {
178 properties: properties.properties.data_ref(),
179 }
180 }
181}
182
183pub struct RenderMaterial<'a, 'b, const N: usize> {
184 pub bindings: NamedValuesContainer<GpuResourceBinding<'a, 'b>, N>,
185}
186
187impl<'a, 'b, const N: usize> From<[NamedValue<GpuResourceBinding<'a, 'b>>; N]>
188 for RenderMaterial<'a, 'b, N>
189{
190 fn from(value: [NamedValue<GpuResourceBinding<'a, 'b>>; N]) -> Self {
191 Self {
192 bindings: value.into(),
193 }
194 }
195}
196
197pub struct RenderPassData {
198 pub program: GpuProgram,
199 pub draw_params: DrawParameters,
200}
201
202pub struct RenderPassContainer {
203 pub resources: Vec<ShaderResourceDefinition>,
204 pub render_passes: FxHashMap<ImmutableString, RenderPassData>,
205}
206
207impl RenderPassContainer {
208 pub fn from_str(server: &dyn GraphicsServer, str: &str) -> Result<Self, FrameworkError> {
209 let shader = Shader::from_string(str).map_err(|e| FrameworkError::Custom(e.to_string()))?;
210 Self::new(server, &shader)
211 }
212
213 pub fn new(server: &dyn GraphicsServer, shader: &Shader) -> Result<Self, FrameworkError> {
214 let mut render_passes = FxHashMap::default();
215
216 for render_pass in shader.definition.passes.iter() {
217 let program_name = format!("{}_{}", shader.definition.name, render_pass.name);
218 match server.create_program(
219 &program_name,
220 render_pass.vertex_shader.0.clone(),
221 render_pass.vertex_shader_line,
222 render_pass.fragment_shader.0.clone(),
223 render_pass.fragment_shader_line,
224 &shader.definition.resources,
225 ) {
226 Ok(gpu_program) => {
227 render_passes.insert(
228 ImmutableString::new(&render_pass.name),
229 RenderPassData {
230 program: gpu_program,
231 draw_params: render_pass.draw_parameters.clone(),
232 },
233 );
234 }
235 Err(e) => {
236 return Err(FrameworkError::Custom(format!(
237 "Failed to create {program_name} shader' GPU program. Reason: {e}"
238 )));
239 }
240 };
241 }
242
243 Ok(Self {
244 render_passes,
245 resources: shader.definition.resources.clone(),
246 })
247 }
248
249 pub fn get(
250 &self,
251 render_pass_name: &ImmutableString,
252 ) -> Result<&RenderPassData, FrameworkError> {
253 self.render_passes.get(render_pass_name).ok_or_else(|| {
254 FrameworkError::Custom(format!("No render pass with name {render_pass_name}!"))
255 })
256 }
257
258 pub fn run_pass<const N: usize>(
259 &self,
260 instance_count: usize,
261 render_pass_name: &ImmutableString,
262 framebuffer: &GpuFrameBuffer,
263 geometry: &GpuGeometryBuffer,
264 viewport: Rect<i32>,
265 material: &RenderMaterial<'_, '_, N>,
266 uniform_buffer_cache: &mut UniformBufferCache,
267 element_range: ElementRange,
268 override_params: Option<&DrawParameters>,
269 ) -> Result<DrawCallStatistics, FrameworkError> {
270 if instance_count == 0 {
271 return Ok(Default::default());
272 }
273
274 let render_pass = self.get(render_pass_name)?;
275
276 let mut resource_bindings = ArrayVec::<ResourceBinding, 32>::new();
277
278 for resource in self.resources.iter() {
279 if resource.is_built_in() {
281 continue;
282 }
283
284 match resource.kind {
285 ShaderResourceKind::Texture { .. } => {
286 if let Some((tex, sampler)) = material
287 .bindings
288 .property_ref(&resource.name)
289 .and_then(|p| {
290 if let GpuResourceBinding::Texture { texture, sampler } = p.value {
291 Some((texture, sampler))
292 } else {
293 None
294 }
295 })
296 {
297 resource_bindings.push(ResourceBinding::texture(
298 tex,
299 sampler,
300 resource.binding,
301 ));
302 } else {
303 return Err(FrameworkError::Custom(format!(
304 "No texture bound to {} resource binding!",
305 resource.name
306 )));
307 }
308 }
309 ShaderResourceKind::PropertyGroup(ref shader_property_group) => {
310 let mut buf = StaticUniformBuffer::<16384>::new();
311
312 if let Some(material_property_group) = material
313 .bindings
314 .property_ref(&resource.name)
315 .and_then(|p| {
316 if let GpuResourceBinding::PropertyGroup { ref properties } = p.value {
317 Some(properties)
318 } else {
319 None
320 }
321 })
322 {
323 bundle::write_with_material(
324 shader_property_group,
325 material_property_group,
326 |c: &NamedValuesContainerRef<MaterialPropertyRef>, n| {
327 c.property_ref(n).map(|v| v.value)
328 },
329 &mut buf,
330 );
331 } else {
332 bundle::write_shader_values(shader_property_group, &mut buf)
336 }
337
338 resource_bindings.push(ResourceBinding::buffer(
339 &uniform_buffer_cache.write(buf)?,
340 resource.binding,
341 Default::default(),
342 ));
343 }
344 }
345 }
346
347 let resources = [ResourceBindGroup {
348 bindings: &resource_bindings,
349 }];
350
351 if instance_count == 1 {
352 framebuffer.draw(
353 geometry,
354 viewport,
355 &render_pass.program,
356 override_params.unwrap_or(&render_pass.draw_params),
357 &resources,
358 element_range,
359 )
360 } else {
361 framebuffer.draw_instances(
362 instance_count,
363 geometry,
364 viewport,
365 &render_pass.program,
366 override_params.unwrap_or(&render_pass.draw_params),
367 &resources,
368 element_range,
369 )
370 }
371 }
372}
373
374#[derive(Default)]
375pub struct ShaderCache {
376 pub(super) cache: TemporaryCache<RenderPassContainer>,
377}
378
379impl ShaderCache {
380 pub fn remove(&mut self, shader: &ShaderResource) {
381 let mut state = shader.state();
382 if let Some(shader_state) = state.data() {
383 self.cache.remove(&shader_state.cache_index);
384 }
385 }
386
387 pub fn get(
388 &mut self,
389 server: &dyn GraphicsServer,
390 shader: &ShaderResource,
391 ) -> Option<&RenderPassContainer> {
392 let mut shader_state = shader.state();
393
394 if let Some(shader_state) = shader_state.data() {
395 match self.cache.get_or_insert_with(
396 &shader_state.cache_index,
397 Default::default(),
398 || RenderPassContainer::new(server, shader_state),
399 ) {
400 Ok(shader_set) => Some(shader_set),
401 Err(error) => {
402 Log::err(format!("{error}"));
403 None
404 }
405 }
406 } else {
407 None
408 }
409 }
410
411 pub fn update(&mut self, dt: f32) {
412 self.cache.update(dt)
413 }
414
415 pub fn clear(&mut self) {
416 self.cache.clear();
417 }
418
419 pub fn alive_count(&self) -> usize {
420 self.cache.alive_count()
421 }
422}