fyrox_impl/renderer/
bundle.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21//! The module responsible for bundle generation for rendering optimizations.
22
23#![allow(missing_docs)] // TODO
24
25use crate::{
26    asset::untyped::ResourceKind,
27    core::{
28        algebra::{Matrix4, Vector3, Vector4},
29        arrayvec::ArrayVec,
30        color,
31        color::Color,
32        log::Log,
33        math::{frustum::Frustum, Rect},
34        pool::Handle,
35        sstorage::ImmutableString,
36    },
37    graph::BaseSceneGraph,
38    material::{
39        self, shader::ShaderDefinition, MaterialProperty, MaterialPropertyGroup, MaterialResource,
40    },
41    renderer::{
42        cache::{
43            geometry::GeometryCache,
44            shader::ShaderCache,
45            texture::TextureCache,
46            uniform::{UniformBlockLocation, UniformMemoryAllocator},
47            TimeToLive,
48        },
49        framework::{
50            error::FrameworkError,
51            framebuffer::{BufferLocation, FrameBuffer, ResourceBindGroup, ResourceBinding},
52            gpu_program::{ShaderProperty, ShaderPropertyKind, ShaderResourceKind},
53            gpu_texture::GpuTexture,
54            server::GraphicsServer,
55            uniform::StaticUniformBuffer,
56            uniform::{ByteStorage, UniformBuffer},
57            ElementRange,
58        },
59        FallbackResources, LightData, RenderPassStatistics,
60    },
61    resource::texture::TextureResource,
62    scene::{
63        graph::Graph,
64        light::{
65            directional::{CsmOptions, DirectionalLight},
66            point::PointLight,
67            spot::SpotLight,
68            BaseLight,
69        },
70        mesh::{
71            buffer::{
72                BytesStorage, TriangleBuffer, TriangleBufferRefMut, VertexAttributeDescriptor,
73                VertexBuffer, VertexBufferRefMut,
74            },
75            surface::{SurfaceData, SurfaceResource},
76            RenderPath,
77        },
78        node::{Node, RdcControlFlow},
79    },
80};
81use fxhash::{FxBuildHasher, FxHashMap, FxHasher};
82use fyrox_core::math::Matrix4Ext;
83use fyrox_graph::{SceneGraph, SceneGraphNode};
84use std::{
85    cell::RefCell,
86    fmt::{Debug, Formatter},
87    hash::{Hash, Hasher},
88    rc::Rc,
89};
90
91/// Observer info contains all the data, that describes an observer. It could be a real camera, light source's
92/// "virtual camera" that is used for shadow mapping, etc.
93#[derive(Clone, Default)]
94pub struct ObserverInfo {
95    /// World-space position of the observer.
96    pub observer_position: Vector3<f32>,
97    /// Location of the near clipping plane.
98    pub z_near: f32,
99    /// Location of the far clipping plane.
100    pub z_far: f32,
101    /// View matrix of the observer.
102    pub view_matrix: Matrix4<f32>,
103    /// Projection matrix of the observer.
104    pub projection_matrix: Matrix4<f32>,
105}
106
107/// Render context is used to collect render data from the scene nodes. It provides all required information about
108/// the observer (camera, light source virtual camera, etc.), that could be used for culling.
109pub struct RenderContext<'a> {
110    /// Amount of time (in seconds) that passed from creation of the engine. Keep in mind, that
111    /// this value is **not** guaranteed to match real time. A user can change delta time with
112    /// which the engine "ticks" and this delta time affects elapsed time.
113    pub elapsed_time: f32,
114    pub observer_info: &'a ObserverInfo,
115    /// Frustum of the observer, it is built using observer's view and projection matrix. Use the frustum to do
116    /// frustum culling.
117    pub frustum: Option<&'a Frustum>,
118    /// Render data bundle storage. Your scene node must write at least one surface instance here for the node to
119    /// be rendered.
120    pub storage: &'a mut dyn RenderDataBundleStorageTrait,
121    /// A reference to the graph that is being rendered. Allows you to get access to other scene nodes to do
122    /// some useful job.
123    pub graph: &'a Graph,
124    /// A name of the render pass for which the context was created for.
125    pub render_pass_name: &'a ImmutableString,
126}
127
128impl RenderContext<'_> {
129    /// Calculates sorting index using of the given point by transforming it in the view space and
130    /// using Z coordinate. This index could be used for back-to-front sorting to prevent blending
131    /// issues.
132    pub fn calculate_sorting_index(&self, global_position: Vector3<f32>) -> u64 {
133        let granularity = 1000.0;
134        u64::MAX
135            - (self
136                .observer_info
137                .view_matrix
138                .transform_point(&(global_position.into()))
139                .z
140                * granularity) as u64
141    }
142}
143
144#[allow(missing_docs)] // TODO
145pub struct BundleRenderContext<'a> {
146    pub texture_cache: &'a mut TextureCache,
147    pub render_pass_name: &'a ImmutableString,
148    pub frame_buffer: &'a mut dyn FrameBuffer,
149    pub viewport: Rect<i32>,
150    pub uniform_memory_allocator: &'a mut UniformMemoryAllocator,
151
152    // Built-in uniforms.
153    pub use_pom: bool,
154    pub light_position: &'a Vector3<f32>,
155    pub ambient_light: Color,
156    // TODO: Add depth pre-pass to remove Option here. Current architecture allows only forward
157    // renderer to have access to depth buffer that is available from G-Buffer.
158    pub scene_depth: Option<&'a Rc<RefCell<dyn GpuTexture>>>,
159    pub fallback_resources: &'a FallbackResources,
160}
161
162/// A set of data of a surface for rendering.
163pub struct SurfaceInstanceData {
164    /// A world matrix.
165    pub world_transform: Matrix4<f32>,
166    /// A set of bone matrices.
167    pub bone_matrices: Vec<Matrix4<f32>>,
168    /// A set of weights for each blend shape in the surface.
169    pub blend_shapes_weights: Vec<f32>,
170    /// A range of elements of the instance. Allows you to draw either the full range ([`ElementRange::Full`])
171    /// of the graphics primitives from the surface data or just a part of it ([`ElementRange::Specific`]).
172    pub element_range: ElementRange,
173    /// A handle of a node that emitted this surface data. Could be none, if there's no info about scene node.
174    pub node_handle: Handle<Node>,
175}
176
177/// A set of surface instances that share the same vertex/index data and a material.
178pub struct RenderDataBundle {
179    /// A pointer to shared surface data.
180    pub data: SurfaceResource,
181    /// Amount of time (in seconds) for GPU geometry buffer (vertex + index buffers) generated for
182    /// the `data`.
183    pub time_to_live: TimeToLive,
184    /// A set of instances.
185    pub instances: Vec<SurfaceInstanceData>,
186    /// A material that is shared across all instances.
187    pub material: MaterialResource,
188    /// A render path of the bundle.
189    pub render_path: RenderPath,
190    sort_index: u64,
191}
192
193impl Debug for RenderDataBundle {
194    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
195        write!(
196            f,
197            "Bundle {}: {} instances",
198            self.data.key(),
199            self.instances.len()
200        )
201    }
202}
203
204/// Describes where to the actual uniform data is located in the memory backed by the uniform
205/// memory allocator on per-instance basis.
206pub struct InstanceUniformData {
207    /// Instance info block location.
208    pub instance_block: UniformBlockLocation,
209    /// Bone matrices block location. Could be [`None`], if there's no bone matrices.
210    pub bone_matrices_block: Option<UniformBlockLocation>,
211}
212
213/// Describes where to the actual uniform data is located in the memory backed by the uniform
214/// memory allocator on per-bundle basis.
215pub struct BundleUniformData {
216    /// Material info block location.
217    pub material_property_group_blocks: Vec<(usize, UniformBlockLocation)>,
218    /// Lights info block location.
219    pub light_data_block: UniformBlockLocation,
220    /// Block locations for each instance in a bundle.
221    pub instance_blocks: Vec<InstanceUniformData>,
222}
223
224pub struct GlobalUniformData {
225    /// Camera info block location.
226    pub camera_block: UniformBlockLocation,
227    /// Light source data info block location.
228    pub lights_block: UniformBlockLocation,
229    /// Graphics settings block location.
230    pub graphics_settings_block: UniformBlockLocation,
231}
232
233fn write_with_material<T: ByteStorage>(
234    shader_property_group: &[ShaderProperty],
235    material_property_group: &MaterialPropertyGroup,
236    buf: &mut UniformBuffer<T>,
237) {
238    // The order of fields is strictly defined in shader, so we must iterate over shader definition
239    // of a structure and look for respective values in the material.
240    for shader_property in shader_property_group {
241        let material_property = material_property_group.property_ref(shader_property.name.clone());
242
243        macro_rules! push_value {
244            ($variant:ident, $shader_value:ident) => {
245                if let Some(property) = material_property {
246                    if let MaterialProperty::$variant(material_value) = property {
247                        buf.push(material_value);
248                    } else {
249                        buf.push($shader_value);
250                        Log::err(format!(
251                            "Unable to use material property {} because of mismatching types.\
252                            Expected {:?} got {:?}. Fallback to shader default value.",
253                            shader_property.name, shader_property, property
254                        ));
255                    }
256                } else {
257                    buf.push($shader_value);
258                }
259            };
260        }
261
262        macro_rules! push_slice {
263            ($variant:ident, $shader_value:ident, $max_size:ident) => {
264                if let Some(property) = material_property {
265                    if let MaterialProperty::$variant(material_value) = property {
266                        buf.push_slice_with_max_size(material_value, *$max_size);
267                    } else {
268                        buf.push_slice_with_max_size($shader_value, *$max_size);
269                        Log::err(format!(
270                            "Unable to use material property {} because of mismatching types.\
271                            Expected {:?} got {:?}. Fallback to shader default value.",
272                            shader_property.name, shader_property, property
273                        ))
274                    }
275                } else {
276                    buf.push_slice_with_max_size($shader_value, *$max_size);
277                }
278            };
279        }
280
281        use ShaderPropertyKind::*;
282        match &shader_property.kind {
283            Float(value) => push_value!(Float, value),
284            FloatArray { value, max_len } => push_slice!(FloatArray, value, max_len),
285            Int(value) => push_value!(Int, value),
286            IntArray { value, max_len } => push_slice!(IntArray, value, max_len),
287            UInt(value) => push_value!(UInt, value),
288            UIntArray { value, max_len } => push_slice!(UIntArray, value, max_len),
289            Vector2(value) => push_value!(Vector2, value),
290            Vector2Array { value, max_len } => push_slice!(Vector2Array, value, max_len),
291            Vector3(value) => push_value!(Vector3, value),
292            Vector3Array { value, max_len } => push_slice!(Vector3Array, value, max_len),
293            Vector4(value) => push_value!(Vector4, value),
294            Vector4Array { value, max_len } => push_slice!(Vector4Array, value, max_len),
295            Matrix2(value) => push_value!(Matrix2, value),
296            Matrix2Array { value, max_len } => push_slice!(Matrix2Array, value, max_len),
297            Matrix3(value) => push_value!(Matrix3, value),
298            Matrix3Array { value, max_len } => push_slice!(Matrix3Array, value, max_len),
299            Matrix4(value) => push_value!(Matrix4, value),
300            Matrix4Array { value, max_len } => push_slice!(Matrix4Array, value, max_len),
301            Bool(value) => push_value!(Bool, value),
302            Color { r, g, b, a } => {
303                let value = &color::Color::from_rgba(*r, *g, *b, *a);
304                push_value!(Color, value)
305            }
306        };
307    }
308}
309
310fn write_shader_values<T: ByteStorage>(
311    shader_property_group: &[ShaderProperty],
312    buf: &mut UniformBuffer<T>,
313) {
314    for property in shader_property_group {
315        use ShaderPropertyKind::*;
316        match &property.kind {
317            Float(value) => buf.push(value),
318            FloatArray { value, max_len } => buf.push_slice_with_max_size(value, *max_len),
319            Int(value) => buf.push(value),
320            IntArray { value, max_len } => buf.push_slice_with_max_size(value, *max_len),
321            UInt(value) => buf.push(value),
322            UIntArray { value, max_len } => buf.push_slice_with_max_size(value, *max_len),
323            Vector2(value) => buf.push(value),
324            Vector2Array { value, max_len } => buf.push_slice_with_max_size(value, *max_len),
325            Vector3(value) => buf.push(value),
326            Vector3Array { value, max_len } => buf.push_slice_with_max_size(value, *max_len),
327            Vector4(value) => buf.push(value),
328            Vector4Array { value, max_len } => buf.push_slice_with_max_size(value, *max_len),
329            Matrix2(value) => buf.push(value),
330            Matrix2Array { value, max_len } => buf.push_slice_with_max_size(value, *max_len),
331            Matrix3(value) => buf.push(value),
332            Matrix3Array { value, max_len } => buf.push_slice_with_max_size(value, *max_len),
333            Matrix4(value) => buf.push(value),
334            Matrix4Array { value, max_len } => buf.push_slice_with_max_size(value, *max_len),
335            Bool(value) => buf.push(value),
336            Color { r, g, b, a } => buf.push(&color::Color::from_rgba(*r, *g, *b, *a)),
337        };
338    }
339}
340
341impl RenderDataBundle {
342    /// Writes all the required uniform data of the bundle to uniform memory allocator.
343    pub fn write_uniforms(
344        &self,
345        view_projection_matrix: &Matrix4<f32>,
346        render_context: &mut BundleRenderContext,
347    ) -> Option<BundleUniformData> {
348        let mut material_state = self.material.state();
349
350        let material = material_state.data()?;
351
352        // Upload material property groups.
353        let mut material_property_group_blocks = Vec::new();
354        let shader = material.shader().data_ref();
355        for resource_definition in shader.definition.resources.iter() {
356            // Ignore built-in groups.
357            if resource_definition.is_built_in() {
358                continue;
359            }
360
361            let ShaderResourceKind::PropertyGroup(ref shader_property_group) =
362                resource_definition.kind
363            else {
364                continue;
365            };
366
367            let mut buf = StaticUniformBuffer::<16384>::new();
368
369            if let Some(material_property_group) =
370                material.property_group_ref(resource_definition.name.clone())
371            {
372                write_with_material(shader_property_group, material_property_group, &mut buf);
373            } else {
374                // No respective resource bound in the material, use shader defaults. This is very
375                // important, because some drivers will crash if uniform buffer has insufficient
376                // data.
377                write_shader_values(shader_property_group, &mut buf)
378            }
379
380            material_property_group_blocks.push((
381                resource_definition.binding,
382                render_context.uniform_memory_allocator.allocate(buf),
383            ))
384        }
385
386        let light_data = StaticUniformBuffer::<256>::new()
387            .with(render_context.light_position)
388            .with(&render_context.ambient_light.as_frgba());
389        let light_data_block = render_context.uniform_memory_allocator.allocate(light_data);
390
391        // Upload instance uniforms.
392        let mut instance_blocks = Vec::with_capacity(self.instances.len());
393        for instance in self.instances.iter() {
394            let mut packed_blend_shape_weights =
395                [Vector4::<f32>::default(); ShaderDefinition::MAX_BLEND_SHAPE_WEIGHT_GROUPS];
396
397            for (i, blend_shape_weight) in instance.blend_shapes_weights.iter().enumerate() {
398                let n = i / 4;
399                let c = i % 4;
400                packed_blend_shape_weights[n][c] = *blend_shape_weight;
401            }
402
403            let instance_buffer = StaticUniformBuffer::<1024>::new()
404                .with(&instance.world_transform)
405                .with(&(view_projection_matrix * instance.world_transform))
406                .with(&(instance.blend_shapes_weights.len() as i32))
407                .with(&(!instance.bone_matrices.is_empty()))
408                .with_slice_with_max_size(
409                    &packed_blend_shape_weights,
410                    ShaderDefinition::MAX_BLEND_SHAPE_WEIGHT_GROUPS,
411                );
412
413            let mut instance_uniform_data = InstanceUniformData {
414                instance_block: render_context
415                    .uniform_memory_allocator
416                    .allocate(instance_buffer),
417                bone_matrices_block: None,
418            };
419
420            if !instance.bone_matrices.is_empty() {
421                const INIT: Matrix4<f32> = Matrix4::new(
422                    0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
423                );
424                let mut matrices = [INIT; ShaderDefinition::MAX_BONE_MATRICES];
425                const SIZE: usize = ShaderDefinition::MAX_BONE_MATRICES * size_of::<Matrix4<f32>>();
426                matrices[0..instance.bone_matrices.len()].copy_from_slice(&instance.bone_matrices);
427
428                let bone_matrices_block = render_context
429                    .uniform_memory_allocator
430                    .allocate(StaticUniformBuffer::<SIZE>::new().with_slice(&matrices));
431                instance_uniform_data.bone_matrices_block = Some(bone_matrices_block);
432            }
433
434            instance_blocks.push(instance_uniform_data);
435        }
436
437        Some(BundleUniformData {
438            material_property_group_blocks,
439            light_data_block,
440            instance_blocks,
441        })
442    }
443
444    /// Draws the entire bundle to the specified frame buffer with the specified rendering environment.
445    pub fn render_to_frame_buffer<F>(
446        &self,
447        server: &dyn GraphicsServer,
448        geometry_cache: &mut GeometryCache,
449        shader_cache: &mut ShaderCache,
450        instance_filter: &mut F,
451        render_context: &mut BundleRenderContext,
452        bundle_uniform_data: BundleUniformData,
453        global_uniform_data: &GlobalUniformData,
454    ) -> Result<RenderPassStatistics, FrameworkError>
455    where
456        F: FnMut(&SurfaceInstanceData) -> bool,
457    {
458        let mut stats = RenderPassStatistics::default();
459
460        let mut material_state = self.material.state();
461
462        let Some(material) = material_state.data() else {
463            return Ok(stats);
464        };
465
466        let Some(geometry) = geometry_cache.get(server, &self.data, self.time_to_live) else {
467            return Ok(stats);
468        };
469
470        let Some(render_pass) =
471            shader_cache
472                .get(server, material.shader())
473                .and_then(|shader_set| {
474                    shader_set
475                        .render_passes
476                        .get(render_context.render_pass_name)
477                })
478        else {
479            return Ok(stats);
480        };
481
482        let mut material_bindings = ArrayVec::<ResourceBinding, 32>::new();
483        let shader = material.shader().data_ref();
484        for resource_definition in shader.definition.resources.iter() {
485            let name = resource_definition.name.as_str();
486
487            match name {
488                "fyrox_sceneDepth" => {
489                    material_bindings.push(ResourceBinding::texture_with_binding(
490                        if let Some(scene_depth) = render_context.scene_depth.as_ref() {
491                            scene_depth
492                        } else {
493                            &render_context.fallback_resources.black_dummy
494                        },
495                        resource_definition.binding,
496                    ));
497                }
498                "fyrox_cameraData" => {
499                    material_bindings.push(
500                        render_context.uniform_memory_allocator.block_to_binding(
501                            global_uniform_data.camera_block,
502                            resource_definition.binding,
503                        ),
504                    );
505                }
506                "fyrox_lightData" => {
507                    material_bindings.push(
508                        render_context.uniform_memory_allocator.block_to_binding(
509                            bundle_uniform_data.light_data_block,
510                            resource_definition.binding,
511                        ),
512                    );
513                }
514                "fyrox_graphicsSettings" => {
515                    material_bindings.push(
516                        render_context.uniform_memory_allocator.block_to_binding(
517                            global_uniform_data.graphics_settings_block,
518                            resource_definition.binding,
519                        ),
520                    );
521                }
522                "fyrox_lightsBlock" => {
523                    material_bindings.push(
524                        render_context.uniform_memory_allocator.block_to_binding(
525                            global_uniform_data.lights_block,
526                            resource_definition.binding,
527                        ),
528                    );
529                }
530                _ => match resource_definition.kind {
531                    ShaderResourceKind::Texture { fallback, .. } => {
532                        let fallback = render_context.fallback_resources.sampler_fallback(fallback);
533
534                        let texture = if let Some(binding) =
535                            material.binding_ref(resource_definition.name.clone())
536                        {
537                            if let material::MaterialResourceBinding::Texture(binding) = binding {
538                                binding
539                                    .value
540                                    .as_ref()
541                                    .and_then(|t| render_context.texture_cache.get(server, t))
542                                    .unwrap_or(fallback)
543                            } else {
544                                Log::err(format!(
545                                    "Unable to use texture binding {}, types mismatch! Expected \
546                                {:?} got {:?}",
547                                    resource_definition.name, resource_definition.kind, binding
548                                ));
549
550                                fallback
551                            }
552                        } else {
553                            fallback
554                        };
555
556                        material_bindings.push(ResourceBinding::texture_with_binding(
557                            texture,
558                            resource_definition.binding,
559                        ));
560                    }
561                    ShaderResourceKind::PropertyGroup(_) => {
562                        // No validation here, it is done in uniform variables collection step.
563                        if let Some((_, block_location)) = bundle_uniform_data
564                            .material_property_group_blocks
565                            .iter()
566                            .find(|(binding, _)| *binding == resource_definition.binding)
567                        {
568                            material_bindings.push(
569                                render_context
570                                    .uniform_memory_allocator
571                                    .block_to_binding(*block_location, resource_definition.binding),
572                            );
573                        }
574                    }
575                },
576            }
577        }
578
579        for (instance, uniform_data) in self
580            .instances
581            .iter()
582            .zip(bundle_uniform_data.instance_blocks)
583        {
584            if !instance_filter(instance) {
585                continue;
586            }
587            let mut instance_bindings = ArrayVec::<ResourceBinding, 32>::new();
588
589            for resource_definition in shader.definition.resources.iter() {
590                let name = resource_definition.name.as_str();
591                match name {
592                    "fyrox_instanceData" => {
593                        instance_bindings.push(
594                            render_context.uniform_memory_allocator.block_to_binding(
595                                uniform_data.instance_block,
596                                resource_definition.binding,
597                            ),
598                        );
599                    }
600                    "fyrox_boneMatrices" => {
601                        match uniform_data.bone_matrices_block {
602                            Some(block) => {
603                                instance_bindings.push(
604                                    render_context
605                                        .uniform_memory_allocator
606                                        .block_to_binding(block, resource_definition.binding),
607                                );
608                            }
609                            None => {
610                                // Bind stub buffer, instead of creating and uploading 16kb with zeros per draw
611                                // call.
612                                instance_bindings.push(ResourceBinding::Buffer {
613                                    buffer: &*render_context
614                                        .fallback_resources
615                                        .bone_matrices_stub_uniform_buffer,
616                                    binding: BufferLocation::Explicit {
617                                        binding: resource_definition.binding,
618                                    },
619                                    data_usage: Default::default(),
620                                });
621                            }
622                        }
623                    }
624                    _ => (),
625                };
626            }
627
628            stats += render_context.frame_buffer.draw(
629                geometry,
630                render_context.viewport,
631                &*render_pass.program,
632                &render_pass.draw_params,
633                &[
634                    ResourceBindGroup {
635                        bindings: &material_bindings,
636                    },
637                    ResourceBindGroup {
638                        bindings: &instance_bindings,
639                    },
640                ],
641                instance.element_range,
642            )?;
643        }
644
645        Ok(stats)
646    }
647}
648
649/// A trait for an entity that can collect render data.
650pub trait RenderDataBundleStorageTrait {
651    /// Adds a new mesh to the bundle storage using the given set of vertices and triangles. This
652    /// method automatically creates a render bundle according to a hash of the following parameters:
653    ///
654    /// - Material
655    /// - Vertex Type
656    /// - Render Path
657    ///
658    /// If one of these parameters is different, then a new bundle will be created and used to store
659    /// the given vertices and indices. If an appropriate bundle exists, the method will store
660    /// the given vertices and the triangles in it.
661    ///
662    /// ## When to use
663    ///
664    /// This method is used to reduce amount of draw calls of underlying GAPI, by merging small
665    /// portions of data into one big block that shares drawing parameters and can be rendered in
666    /// a single draw call. The vertices in this case should be pre-processed by applying world
667    /// transform to them. This is so-called dynamic batching.
668    ///
669    /// Do not use this method if you have a mesh with lots of vertices and triangles, because
670    /// pre-processing them on CPU could take more time than rendering them directly on GPU one-by-one.
671    fn push_triangles(
672        &mut self,
673        layout: &[VertexAttributeDescriptor],
674        material: &MaterialResource,
675        render_path: RenderPath,
676        sort_index: u64,
677        node_handle: Handle<Node>,
678        func: &mut dyn FnMut(VertexBufferRefMut, TriangleBufferRefMut),
679    );
680
681    /// Adds a new surface instance to the storage. The method will automatically put the instance
682    /// in the appropriate bundle. Bundle selection is done using the material, surface data, render
683    /// path. If only one of these parameters is different, then the surface instance will be put
684    /// in a separate bundle.
685    fn push(
686        &mut self,
687        data: &SurfaceResource,
688        material: &MaterialResource,
689        render_path: RenderPath,
690        sort_index: u64,
691        instance_data: SurfaceInstanceData,
692    );
693}
694
695pub enum LightSourceKind {
696    Spot {
697        full_cone_angle: f32,
698        hotspot_cone_angle: f32,
699        distance: f32,
700        shadow_bias: f32,
701        cookie_texture: Option<TextureResource>,
702    },
703    Point {
704        radius: f32,
705        shadow_bias: f32,
706    },
707    Directional {
708        csm_options: CsmOptions,
709    },
710    Unknown,
711}
712
713pub struct LightSource {
714    pub handle: Handle<Node>,
715    pub global_transform: Matrix4<f32>,
716    pub kind: LightSourceKind,
717    pub position: Vector3<f32>,
718    pub up_vector: Vector3<f32>,
719    pub side_vector: Vector3<f32>,
720    pub look_vector: Vector3<f32>,
721    pub cast_shadows: bool,
722    pub local_scale: Vector3<f32>,
723    pub color: Color,
724    pub intensity: f32,
725    pub scatter_enabled: bool,
726    pub scatter: Vector3<f32>,
727}
728
729/// Bundle storage handles bundle generation for a scene before rendering. It is used to optimize
730/// rendering by reducing amount of state changes of OpenGL context.
731pub struct RenderDataBundleStorage {
732    bundle_map: FxHashMap<u64, usize>,
733    pub observer_info: ObserverInfo,
734    /// A sorted list of bundles.
735    pub bundles: Vec<RenderDataBundle>,
736    pub light_sources: Vec<LightSource>,
737}
738
739pub struct RenderDataBundleStorageOptions {
740    pub collect_lights: bool,
741}
742
743impl Default for RenderDataBundleStorageOptions {
744    fn default() -> Self {
745        Self {
746            collect_lights: true,
747        }
748    }
749}
750
751impl RenderDataBundleStorage {
752    pub fn new_empty(observer_info: ObserverInfo) -> Self {
753        Self {
754            bundle_map: Default::default(),
755            observer_info,
756            bundles: Default::default(),
757            light_sources: Default::default(),
758        }
759    }
760
761    /// Creates a new render bundle storage from the given graph and observer info. It "asks" every node in the
762    /// graph one-by-one to give render data which is then put in the storage, sorted and ready for rendering.
763    /// Frustum culling is done on scene node side ([`crate::scene::node::NodeTrait::collect_render_data`]).
764    pub fn from_graph(
765        graph: &Graph,
766        elapsed_time: f32,
767        observer_info: ObserverInfo,
768        render_pass_name: ImmutableString,
769        options: RenderDataBundleStorageOptions,
770    ) -> Self {
771        // Aim for the worst-case scenario when every node has unique render data.
772        let capacity = graph.node_count() as usize;
773        let mut storage = Self {
774            bundle_map: FxHashMap::with_capacity_and_hasher(capacity, FxBuildHasher::default()),
775            observer_info: observer_info.clone(),
776            bundles: Vec::with_capacity(capacity),
777            light_sources: Default::default(),
778        };
779
780        let frustum = Frustum::from_view_projection_matrix(
781            observer_info.projection_matrix * observer_info.view_matrix,
782        )
783        .unwrap_or_default();
784
785        let mut lod_filter = vec![true; graph.capacity() as usize];
786        for (node_handle, node) in graph.pair_iter() {
787            if let Some(lod_group) = node.lod_group() {
788                for level in lod_group.levels.iter() {
789                    for &object in level.objects.iter() {
790                        if let Some(object_ref) = graph.try_get(object) {
791                            let distance = observer_info
792                                .observer_position
793                                .metric_distance(&object_ref.global_position());
794                            let z_range = observer_info.z_far - observer_info.z_near;
795                            let normalized_distance = (distance - observer_info.z_near) / z_range;
796                            let visible = normalized_distance >= level.begin()
797                                && normalized_distance <= level.end();
798                            lod_filter[object.index() as usize] = visible;
799                        }
800                    }
801                }
802            }
803
804            if options.collect_lights {
805                if let Some(base_light) = node.component_ref::<BaseLight>() {
806                    if frustum.is_intersects_aabb(&node.world_bounding_box())
807                        && base_light.global_visibility()
808                        && base_light.is_globally_enabled()
809                    {
810                        let kind = if let Some(spot_light) = node.cast::<SpotLight>() {
811                            LightSourceKind::Spot {
812                                full_cone_angle: spot_light.full_cone_angle(),
813                                hotspot_cone_angle: spot_light.hotspot_cone_angle(),
814                                distance: spot_light.distance(),
815                                shadow_bias: spot_light.shadow_bias(),
816                                cookie_texture: spot_light.cookie_texture(),
817                            }
818                        } else if let Some(point_light) = node.cast::<PointLight>() {
819                            LightSourceKind::Point {
820                                radius: point_light.radius(),
821                                shadow_bias: point_light.shadow_bias(),
822                            }
823                        } else if let Some(directional_light) = node.cast::<DirectionalLight>() {
824                            LightSourceKind::Directional {
825                                csm_options: (*directional_light.csm_options).clone(),
826                            }
827                        } else {
828                            LightSourceKind::Unknown
829                        };
830
831                        let source = LightSource {
832                            handle: node_handle,
833                            global_transform: base_light.global_transform(),
834                            kind,
835                            position: base_light.global_position(),
836                            up_vector: base_light.up_vector(),
837                            side_vector: base_light.side_vector(),
838                            look_vector: base_light.look_vector(),
839                            cast_shadows: base_light.cast_shadows(),
840                            local_scale: **base_light.local_transform().scale(),
841                            color: base_light.color(),
842                            intensity: base_light.intensity(),
843                            scatter_enabled: base_light.is_scatter_enabled(),
844                            scatter: base_light.scatter(),
845                        };
846
847                        storage.light_sources.push(source);
848                    }
849                }
850            }
851        }
852
853        let mut ctx = RenderContext {
854            elapsed_time,
855            observer_info: &observer_info,
856            frustum: Some(&frustum),
857            storage: &mut storage,
858            graph,
859            render_pass_name: &render_pass_name,
860        };
861
862        #[inline(always)]
863        fn iterate_recursive(
864            node_handle: Handle<Node>,
865            graph: &Graph,
866            lod_filter: &[bool],
867            ctx: &mut RenderContext,
868        ) {
869            if lod_filter[node_handle.index() as usize] {
870                let node = graph.node(node_handle);
871                if let RdcControlFlow::Continue = node.collect_render_data(ctx) {
872                    for child in node.children() {
873                        iterate_recursive(*child, graph, lod_filter, ctx);
874                    }
875                }
876            }
877        }
878
879        iterate_recursive(graph.root(), graph, &lod_filter, &mut ctx);
880
881        storage.sort();
882
883        storage
884    }
885
886    /// Sorts the bundles by their respective sort index.
887    pub fn sort(&mut self) {
888        self.bundles.sort_unstable_by_key(|b| b.sort_index);
889    }
890
891    pub fn write_global_uniform_blocks(
892        &self,
893        render_context: &mut BundleRenderContext,
894    ) -> GlobalUniformData {
895        let mut light_data = LightData::<{ ShaderDefinition::MAX_LIGHTS }>::default();
896
897        for (i, light) in self
898            .light_sources
899            .iter()
900            .enumerate()
901            .take(ShaderDefinition::MAX_LIGHTS)
902        {
903            let color = light.color.as_frgb();
904
905            light_data.color_radius[i] = Vector4::new(color.x, color.y, color.z, 0.0);
906            light_data.position[i] = light.position;
907            light_data.direction[i] = light.up_vector;
908
909            match light.kind {
910                LightSourceKind::Spot {
911                    full_cone_angle,
912                    hotspot_cone_angle,
913                    distance,
914                    ..
915                } => {
916                    light_data.color_radius[i].w = distance;
917                    light_data.parameters[i].x = (hotspot_cone_angle * 0.5).cos();
918                    light_data.parameters[i].y = (full_cone_angle * 0.5).cos();
919                }
920                LightSourceKind::Point { radius, .. } => {
921                    light_data.color_radius[i].w = radius;
922                    light_data.parameters[i].x = std::f32::consts::PI.cos();
923                    light_data.parameters[i].y = std::f32::consts::PI.cos();
924                }
925                LightSourceKind::Directional { .. } => {
926                    light_data.color_radius[i].w = f32::INFINITY;
927                    light_data.parameters[i].x = std::f32::consts::PI.cos();
928                    light_data.parameters[i].y = std::f32::consts::PI.cos();
929                }
930                LightSourceKind::Unknown => {}
931            }
932
933            light_data.count += 1;
934        }
935
936        let lights_data = StaticUniformBuffer::<2048>::new()
937            .with(&(light_data.count as i32))
938            .with_slice(&light_data.color_radius)
939            .with_slice(&light_data.parameters)
940            .with_slice(&light_data.position)
941            .with_slice(&light_data.direction);
942        let lights_block = render_context
943            .uniform_memory_allocator
944            .allocate(lights_data);
945
946        // Upload camera uniforms.
947        let inv_view = self
948            .observer_info
949            .view_matrix
950            .try_inverse()
951            .unwrap_or_default();
952        let view_projection = self.observer_info.projection_matrix * self.observer_info.view_matrix;
953        let camera_up = inv_view.up();
954        let camera_side = inv_view.side();
955        let camera_uniforms = StaticUniformBuffer::<512>::new()
956            .with(&view_projection)
957            .with(&self.observer_info.observer_position)
958            .with(&camera_up)
959            .with(&camera_side)
960            .with(&self.observer_info.z_near)
961            .with(&self.observer_info.z_far)
962            .with(&(self.observer_info.z_far - self.observer_info.z_near));
963        let camera_block = render_context
964            .uniform_memory_allocator
965            .allocate(camera_uniforms);
966
967        let graphics_settings = StaticUniformBuffer::<256>::new().with(&render_context.use_pom);
968        let graphics_settings_block = render_context
969            .uniform_memory_allocator
970            .allocate(graphics_settings);
971
972        GlobalUniformData {
973            camera_block,
974            lights_block,
975            graphics_settings_block,
976        }
977    }
978
979    /// Draws the entire bundle set to the specified frame buffer with the specified rendering environment.
980    pub fn render_to_frame_buffer<BundleFilter, InstanceFilter>(
981        &self,
982        server: &dyn GraphicsServer,
983        geometry_cache: &mut GeometryCache,
984        shader_cache: &mut ShaderCache,
985        mut bundle_filter: BundleFilter,
986        mut instance_filter: InstanceFilter,
987        mut render_context: BundleRenderContext,
988    ) -> Result<RenderPassStatistics, FrameworkError>
989    where
990        BundleFilter: FnMut(&RenderDataBundle) -> bool,
991        InstanceFilter: FnMut(&SurfaceInstanceData) -> bool,
992    {
993        let global_uniforms = self.write_global_uniform_blocks(&mut render_context);
994
995        let view_projection = self.observer_info.projection_matrix * self.observer_info.view_matrix;
996        let mut bundle_uniform_data_set = Vec::with_capacity(self.bundles.len());
997        for bundle in self.bundles.iter() {
998            if !bundle_filter(bundle) {
999                continue;
1000            }
1001            bundle_uniform_data_set
1002                .push(bundle.write_uniforms(&view_projection, &mut render_context));
1003        }
1004        render_context.uniform_memory_allocator.upload(server)?;
1005
1006        let mut stats = RenderPassStatistics::default();
1007        for (bundle, bundle_uniform_data) in self
1008            .bundles
1009            .iter()
1010            .filter(|bundle| bundle_filter(bundle))
1011            .zip(bundle_uniform_data_set)
1012        {
1013            if let Some(bundle_uniform_data) = bundle_uniform_data {
1014                stats += bundle.render_to_frame_buffer(
1015                    server,
1016                    geometry_cache,
1017                    shader_cache,
1018                    &mut instance_filter,
1019                    &mut render_context,
1020                    bundle_uniform_data,
1021                    &global_uniforms,
1022                )?
1023            }
1024        }
1025        Ok(stats)
1026    }
1027}
1028
1029impl RenderDataBundleStorageTrait for RenderDataBundleStorage {
1030    /// Adds a new mesh to the bundle storage using the given set of vertices and triangles. This
1031    /// method automatically creates a render bundle according to a hash of the following parameters:
1032    ///
1033    /// - Material
1034    /// - Vertex Type
1035    /// - Render Path
1036    ///
1037    /// If one of these parameters is different, then a new bundle will be created and used to store
1038    /// the given vertices and indices. If an appropriate bundle exists, the method will store the
1039    /// given vertices and the triangles in it.
1040    ///
1041    /// ## When to use
1042    ///
1043    /// This method is used to reduce amount of draw calls of underlying GAPI, by merging small
1044    /// portions of data into one big block that shares drawing parameters and can be rendered in
1045    /// a single draw call. The vertices in this case should be pre-processed by applying world
1046    /// transform to them.
1047    ///
1048    /// Do not use this method if you have a mesh with lots of vertices and triangles, because
1049    /// pre-processing them on CPU could take more time than rendering them directly on GPU one-by-one.
1050    fn push_triangles(
1051        &mut self,
1052        layout: &[VertexAttributeDescriptor],
1053        material: &MaterialResource,
1054        render_path: RenderPath,
1055        sort_index: u64,
1056        node_handle: Handle<Node>,
1057        func: &mut dyn FnMut(VertexBufferRefMut, TriangleBufferRefMut),
1058    ) {
1059        let mut hasher = FxHasher::default();
1060        hasher.write_u64(material.key());
1061        layout.hash(&mut hasher);
1062        hasher.write_u32(render_path as u32);
1063        let key = hasher.finish();
1064
1065        let bundle = if let Some(&bundle_index) = self.bundle_map.get(&key) {
1066            self.bundles.get_mut(bundle_index).unwrap()
1067        } else {
1068            let default_capacity = 4096;
1069
1070            // Initialize empty vertex buffer.
1071            let vertex_buffer = VertexBuffer::new_with_layout(
1072                layout,
1073                0,
1074                BytesStorage::with_capacity(default_capacity),
1075            )
1076            .unwrap();
1077
1078            // Initialize empty triangle buffer.
1079            let triangle_buffer = TriangleBuffer::new(Vec::with_capacity(default_capacity * 3));
1080
1081            // Create temporary surface data (valid for one frame).
1082            let data = SurfaceResource::new_ok(
1083                ResourceKind::Embedded,
1084                SurfaceData::new(vertex_buffer, triangle_buffer),
1085            );
1086
1087            self.bundle_map.insert(key, self.bundles.len());
1088            self.bundles.push(RenderDataBundle {
1089                data,
1090                sort_index,
1091                instances: vec![
1092                    // Each bundle must have at least one instance to be rendered.
1093                    SurfaceInstanceData {
1094                        world_transform: Matrix4::identity(),
1095                        bone_matrices: Default::default(),
1096                        blend_shapes_weights: Default::default(),
1097                        element_range: Default::default(),
1098                        node_handle,
1099                    },
1100                ],
1101                material: material.clone(),
1102                render_path,
1103                // Temporary buffer lives one frame.
1104                time_to_live: TimeToLive(0.0),
1105            });
1106            self.bundles.last_mut().unwrap()
1107        };
1108
1109        let mut data = bundle.data.data_ref();
1110        let data = &mut *data;
1111
1112        let vertex_buffer = data.vertex_buffer.modify();
1113        let triangle_buffer = data.geometry_buffer.modify();
1114
1115        func(vertex_buffer, triangle_buffer);
1116    }
1117
1118    /// Adds a new surface instance to the storage. The method will automatically put the instance in the appropriate
1119    /// bundle. Bundle selection is done using the material, surface data, render path. If only one
1120    /// of these parameters is different, then the surface instance will be put in a separate bundle.
1121    fn push(
1122        &mut self,
1123        data: &SurfaceResource,
1124        material: &MaterialResource,
1125        render_path: RenderPath,
1126        sort_index: u64,
1127        instance_data: SurfaceInstanceData,
1128    ) {
1129        let mut hasher = FxHasher::default();
1130        hasher.write_u64(material.key());
1131        hasher.write_u64(data.key());
1132        hasher.write_u32(render_path as u32);
1133        let key = hasher.finish();
1134
1135        let bundle = if let Some(&bundle_index) = self.bundle_map.get(&key) {
1136            self.bundles.get_mut(bundle_index).unwrap()
1137        } else {
1138            self.bundle_map.insert(key, self.bundles.len());
1139            self.bundles.push(RenderDataBundle {
1140                data: data.clone(),
1141                sort_index,
1142                instances: Default::default(),
1143                material: material.clone(),
1144                render_path,
1145                time_to_live: Default::default(),
1146            });
1147            self.bundles.last_mut().unwrap()
1148        };
1149
1150        bundle.instances.push(instance_data)
1151    }
1152}