Skip to main content

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