fyrox_impl/renderer/
mod.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//! Renderer is a "workhorse" of the engine, it draws scenes (both 3D and 2D), user interface,
22//! debug geometry and can add user-defined render passes. Current renderer implementation is not
23//! very flexible, but should cover 95% of use cases.
24
25#![warn(missing_docs)]
26
27pub mod framework;
28
29pub mod bundle;
30pub mod cache;
31pub mod debug_renderer;
32pub mod observer;
33pub mod resources;
34pub mod stats;
35pub mod storage;
36pub mod ui_renderer;
37pub mod utils;
38pub mod visibility;
39
40mod bloom;
41mod convolution;
42mod fxaa;
43mod gbuffer;
44mod hdr;
45mod light;
46mod light_volume;
47mod occlusion;
48mod settings;
49mod shadow;
50mod ssao;
51
52use crate::{
53    asset::{event::ResourceEvent, manager::ResourceManager},
54    core::{
55        algebra::{Matrix4, Vector2, Vector3},
56        color::Color,
57        info,
58        log::{Log, MessageKind},
59        math::Rect,
60        pool::Handle,
61        sstorage::ImmutableString,
62    },
63    engine::error::EngineError,
64    graphics::{
65        error::FrameworkError,
66        framebuffer::{Attachment, DrawCallStatistics, GpuFrameBuffer},
67        gpu_texture::{GpuTexture, GpuTextureDescriptor, GpuTextureKind, PixelKind},
68        server::{GraphicsServer, SharedGraphicsServer},
69        PolygonFace, PolygonFillMode,
70    },
71    material::shader::Shader,
72    renderer::{
73        bloom::BloomRenderer,
74        bundle::{BundleRenderContext, RenderDataBundleStorage, RenderDataBundleStorageOptions},
75        cache::texture::convert_pixel_kind,
76        cache::{
77            geometry::GeometryCache,
78            shader::{
79                binding, property, PropertyGroup, RenderMaterial, RenderPassContainer, ShaderCache,
80            },
81            texture::TextureCache,
82            uniform::{UniformBufferCache, UniformMemoryAllocator},
83        },
84        convolution::{EnvironmentMapIrradianceConvolution, EnvironmentMapSpecularConvolution},
85        debug_renderer::DebugRenderer,
86        fxaa::FxaaRenderer,
87        gbuffer::{GBuffer, GBufferRenderContext},
88        hdr::HighDynamicRangeRenderer,
89        light::{DeferredLightRenderer, DeferredRendererContext},
90        ssao::ScreenSpaceAmbientOcclusionRenderer,
91        ui_renderer::UiRenderInfo,
92        ui_renderer::{UiRenderContext, UiRenderer},
93        visibility::VisibilityCache,
94    },
95    resource::texture::{Texture, TextureKind, TextureResource},
96    scene::{mesh::RenderPath, node::Node, Scene, SceneContainer},
97};
98use cache::DynamicSurfaceCache;
99use fxhash::FxHashMap;
100use fyrox_graph::BaseSceneGraph;
101use lazy_static::lazy_static;
102use observer::{Observer, ObserversCollection};
103use resources::RendererResources;
104pub use settings::*;
105pub use stats::*;
106use std::{
107    any::{Any, TypeId},
108    cell::RefCell,
109    collections::hash_map::Entry,
110    rc::Rc,
111    sync::mpsc::Receiver,
112};
113use winit::window::Window;
114
115lazy_static! {
116    static ref GBUFFER_PASS_NAME: ImmutableString = ImmutableString::new("GBuffer");
117    static ref DIRECTIONAL_SHADOW_PASS_NAME: ImmutableString =
118        ImmutableString::new("DirectionalShadow");
119    static ref SPOT_SHADOW_PASS_NAME: ImmutableString = ImmutableString::new("SpotShadow");
120    static ref POINT_SHADOW_PASS_NAME: ImmutableString = ImmutableString::new("PointShadow");
121}
122
123/// Checks whether the provided render pass name is one of the names of built-in shadow render passes.
124pub fn is_shadow_pass(render_pass_name: &str) -> bool {
125    render_pass_name == &**DIRECTIONAL_SHADOW_PASS_NAME
126        || render_pass_name == &**SPOT_SHADOW_PASS_NAME
127        || render_pass_name == &**POINT_SHADOW_PASS_NAME
128}
129
130/// A set of frame buffers, renderers, that contains scene-specific data.
131pub struct SceneRenderData {
132    /// A set of render data containers associated with cameras.
133    pub camera_data: FxHashMap<Handle<Node>, RenderDataContainer>,
134    /// Scene-specific render data.
135    pub scene_data: RenderDataContainer,
136}
137
138impl SceneRenderData {
139    /// Creates new scene-specific render data.
140    pub fn new(
141        server: &dyn GraphicsServer,
142        frame_size: Vector2<f32>,
143        final_frame_texture: FrameTextureKind,
144    ) -> Result<Self, FrameworkError> {
145        Ok(Self {
146            camera_data: Default::default(),
147            scene_data: RenderDataContainer::new(server, frame_size, final_frame_texture)?,
148        })
149    }
150
151    /// Sets the new quality settings.
152    pub fn set_quality_settings(&mut self, settings: &QualitySettings) {
153        for camera_data in self.camera_data.values_mut() {
154            camera_data.set_quality_settings(settings);
155        }
156        self.scene_data.set_quality_settings(settings);
157    }
158}
159
160fn recreate_render_data_if_needed<T: Any>(
161    parent: Handle<T>,
162    server: &dyn GraphicsServer,
163    data: &mut RenderDataContainer,
164    frame_size: Vector2<f32>,
165    final_frame_texture: FrameTextureKind,
166) -> Result<(), FrameworkError> {
167    if data.gbuffer.width != frame_size.x as i32 || data.gbuffer.height != frame_size.y as i32 {
168        Log::info(format!(
169            "Associated scene rendering data was re-created for {} ({}), because render \
170                 frame size was changed. Old is {}x{}, new {}x{}!",
171            parent,
172            std::any::type_name::<T>(),
173            data.gbuffer.width,
174            data.gbuffer.height,
175            frame_size.x,
176            frame_size.y
177        ));
178
179        *data = RenderDataContainer::new(server, frame_size, final_frame_texture)?;
180    }
181
182    Ok(())
183}
184
185/// A set of frame buffers and renderers that can be used to render to.
186pub struct RenderDataContainer {
187    /// Prefiltered environment map that contains a specular component of the environment map with
188    /// roughness encoded in mip levels.
189    pub environment_map_specular_convolution: Option<EnvironmentMapSpecularConvolution>,
190
191    /// Irradiance cube map. Contains computed the irradiance computed from the environment map.
192    pub environment_map_irradiance_convolution: EnvironmentMapIrradianceConvolution,
193
194    /// A flag that defines whether the renderer must recalculate specular/irradiance convolution
195    /// for environment maps.
196    pub need_recalculate_convolution: bool,
197
198    /// Screen space ambient occlusion renderer.
199    pub ssao_renderer: ScreenSpaceAmbientOcclusionRenderer,
200
201    /// G-Buffer of the container.
202    pub gbuffer: GBuffer,
203
204    /// Intermediate high dynamic range frame buffer.
205    pub hdr_scene_framebuffer: GpuFrameBuffer,
206
207    /// Final frame of the container. Tone mapped + gamma corrected.
208    pub ldr_scene_framebuffer: GpuFrameBuffer,
209
210    /// Additional frame buffer for post-processing.
211    pub ldr_temp_framebuffer: [GpuFrameBuffer; 2],
212
213    /// HDR renderer has to be created per container, because it contains
214    /// scene luminance.
215    pub hdr_renderer: HighDynamicRangeRenderer,
216
217    /// Bloom contains only overly bright pixels that create light
218    /// bleeding effect (glow effect).
219    pub bloom_renderer: BloomRenderer,
220
221    /// Rendering statistics for a container.
222    pub statistics: SceneStatistics,
223}
224
225/// Texture kind that will be used to store final frame image.
226#[derive(Default)]
227pub enum FrameTextureKind {
228    /// Rectangular texture.
229    #[default]
230    Rectangle,
231    /// Cube texture (six square textures). Used primarily for reflection probes.
232    Cube,
233}
234
235impl RenderDataContainer {
236    /// Creates a new container.
237    pub fn new(
238        server: &dyn GraphicsServer,
239        frame_size: Vector2<f32>,
240        final_frame_texture: FrameTextureKind,
241    ) -> Result<Self, FrameworkError> {
242        let width = frame_size.x as usize;
243        let height = frame_size.y as usize;
244
245        if matches!(final_frame_texture, FrameTextureKind::Cube) {
246            assert_eq!(width, height);
247        }
248
249        let depth_stencil = server.create_2d_render_target(
250            "ObserverDepthStencil",
251            PixelKind::D24S8,
252            width,
253            height,
254        )?;
255        // Intermediate scene frame will be rendered in HDR render target.
256        let hdr_frame_texture = server.create_2d_render_target(
257            "ObserverHdrFrame",
258            PixelKind::RGBA16F,
259            width,
260            height,
261        )?;
262
263        let hdr_scene_framebuffer = server.create_frame_buffer(
264            Some(Attachment::depth_stencil(depth_stencil.clone())),
265            vec![Attachment::color(hdr_frame_texture)],
266        )?;
267
268        let ldr_frame_texture = server.create_texture(GpuTextureDescriptor {
269            name: "LdrFrameTexture",
270            kind: match final_frame_texture {
271                FrameTextureKind::Rectangle => GpuTextureKind::Rectangle { width, height },
272                FrameTextureKind::Cube => GpuTextureKind::Cube { size: width },
273            },
274            // Final scene frame is in standard sRGB space.
275            pixel_kind: PixelKind::RGBA8,
276            ..Default::default()
277        })?;
278
279        let ldr_scene_framebuffer = server.create_frame_buffer(
280            Some(Attachment::depth_stencil(depth_stencil.clone())),
281            vec![Attachment::color(ldr_frame_texture)],
282        )?;
283
284        fn make_ldr_temp_frame_buffer(
285            server: &dyn GraphicsServer,
286            width: usize,
287            height: usize,
288            depth_stencil: GpuTexture,
289        ) -> Result<GpuFrameBuffer, FrameworkError> {
290            let ldr_temp_texture = server.create_texture(GpuTextureDescriptor {
291                name: "LdrTempTexture",
292                kind: GpuTextureKind::Rectangle { width, height },
293                // Final scene frame is in standard sRGB space.
294                pixel_kind: PixelKind::RGBA8,
295                ..Default::default()
296            })?;
297
298            server.create_frame_buffer(
299                Some(Attachment::depth_stencil(depth_stencil)),
300                vec![Attachment::color(ldr_temp_texture)],
301            )
302        }
303
304        Ok(Self {
305            need_recalculate_convolution: true,
306            environment_map_specular_convolution: Default::default(),
307            environment_map_irradiance_convolution: EnvironmentMapIrradianceConvolution::new(
308                server, 32,
309            )?,
310            ssao_renderer: ScreenSpaceAmbientOcclusionRenderer::new(server, width, height)?,
311            gbuffer: GBuffer::new(server, width, height)?,
312            hdr_renderer: HighDynamicRangeRenderer::new(server)?,
313            bloom_renderer: BloomRenderer::new(server, width, height)?,
314            hdr_scene_framebuffer,
315            ldr_scene_framebuffer,
316            ldr_temp_framebuffer: [
317                make_ldr_temp_frame_buffer(server, width, height, depth_stencil.clone())?,
318                make_ldr_temp_frame_buffer(server, width, height, depth_stencil.clone())?,
319            ],
320            statistics: Default::default(),
321        })
322    }
323
324    fn copy_depth_stencil_to_scene_framebuffer(&mut self) {
325        self.gbuffer.framebuffer().blit_to(
326            &self.hdr_scene_framebuffer,
327            0,
328            0,
329            self.gbuffer.width,
330            self.gbuffer.height,
331            0,
332            0,
333            self.gbuffer.width,
334            self.gbuffer.height,
335            false,
336            true,
337            true,
338        );
339    }
340
341    /// Returns high-dynamic range frame buffer texture.
342    pub fn hdr_scene_frame_texture(&self) -> &GpuTexture {
343        &self.hdr_scene_framebuffer.color_attachments()[0].texture
344    }
345
346    /// Returns low-dynamic range frame buffer texture (final frame).
347    pub fn ldr_scene_frame_texture(&self) -> &GpuTexture {
348        &self.ldr_scene_framebuffer.color_attachments()[0].texture
349    }
350
351    /// Returns low-dynamic range frame buffer texture (accumulation frame).
352    pub fn ldr_temp_frame_texture(&self, i: usize) -> &GpuTexture {
353        &self.ldr_temp_framebuffer[i].color_attachments()[0].texture
354    }
355
356    /// Sets the new quality settings.
357    pub fn set_quality_settings(&mut self, settings: &QualitySettings) {
358        self.ssao_renderer.set_radius(settings.ssao_radius);
359    }
360}
361
362/// Creates a view-projection matrix that projects unit quad a screen with the specified viewport.
363pub fn make_viewport_matrix(viewport: Rect<i32>) -> Matrix4<f32> {
364    Matrix4::new_orthographic(
365        0.0,
366        viewport.w() as f32,
367        viewport.h() as f32,
368        0.0,
369        -1.0,
370        1.0,
371    ) * Matrix4::new_nonuniform_scaling(&Vector3::new(
372        viewport.w() as f32,
373        viewport.h() as f32,
374        0.0,
375    ))
376}
377
378/// See module docs.
379pub struct Renderer {
380    backbuffer: GpuFrameBuffer,
381    scene_render_passes: Vec<Rc<RefCell<dyn SceneRenderPass>>>,
382    deferred_light_renderer: DeferredLightRenderer,
383    /// A set of textures of certain kinds that could be used as a stub in cases when you don't have
384    /// your own texture of this kind.
385    pub renderer_resources: RendererResources,
386    /// User interface renderer.
387    pub ui_renderer: UiRenderer,
388    statistics: Statistics,
389    frame_size: (u32, u32),
390    quality_settings: QualitySettings,
391    /// Debug renderer instance can be used for debugging purposes
392    pub debug_renderer: DebugRenderer,
393    /// Screen space debug renderer instance can be used for debugging purposes to draw lines directly
394    /// on screen. It is useful to debug some rendering algorithms.
395    pub screen_space_debug_renderer: DebugRenderer,
396    /// A set of associated data for each scene that was rendered.
397    pub scene_data_map: FxHashMap<Handle<Scene>, SceneRenderData>,
398    backbuffer_clear_color: Color,
399    /// Texture cache with GPU textures.
400    pub texture_cache: TextureCache,
401    /// Uniform buffer cache.
402    pub uniform_buffer_cache: UniformBufferCache,
403    shader_cache: ShaderCache,
404    geometry_cache: GeometryCache,
405    fxaa_renderer: FxaaRenderer,
406    texture_event_receiver: Receiver<ResourceEvent>,
407    shader_event_receiver: Receiver<ResourceEvent>,
408    /// TextureId -> FrameBuffer mapping. This mapping is used for temporal frame buffers
409    /// like ones used to render UI instances.
410    pub ui_frame_buffers: FxHashMap<u64, GpuFrameBuffer>,
411    uniform_memory_allocator: UniformMemoryAllocator,
412    /// Dynamic surface cache. See [`DynamicSurfaceCache`] docs for more info.
413    pub dynamic_surface_cache: DynamicSurfaceCache,
414    /// Visibility cache based on occlusion query.
415    pub visibility_cache: VisibilityCache,
416    /// Graphics server.
417    pub server: SharedGraphicsServer,
418}
419
420fn make_ui_frame_buffer(
421    frame_size: Vector2<f32>,
422    server: &dyn GraphicsServer,
423    pixel_kind: PixelKind,
424) -> Result<GpuFrameBuffer, FrameworkError> {
425    let color_texture = server.create_texture(GpuTextureDescriptor {
426        name: "UiFbTexture",
427        kind: GpuTextureKind::Rectangle {
428            width: frame_size.x as usize,
429            height: frame_size.y as usize,
430        },
431        pixel_kind,
432        ..Default::default()
433    })?;
434
435    let depth_stencil = server.create_2d_render_target(
436        "UiDepthStencil",
437        PixelKind::D24S8,
438        frame_size.x as usize,
439        frame_size.y as usize,
440    )?;
441
442    server.create_frame_buffer(
443        Some(Attachment::depth_stencil(depth_stencil)),
444        vec![Attachment::color(color_texture)],
445    )
446}
447
448/// A context for custom scene render passes.
449pub struct SceneRenderPassContext<'a, 'b> {
450    /// Amount of time (in seconds) that passed from creation of the engine. Keep in mind, that
451    /// this value is **not** guaranteed to match real time. A user can change delta time with
452    /// which the engine "ticks" and this delta time affects elapsed time.
453    pub elapsed_time: f32,
454    /// A graphics server that is used as a wrapper to underlying graphics API.
455    pub server: &'a dyn GraphicsServer,
456
457    /// A texture cache that uploads engine's `Texture` as internal `GpuTexture` to GPU.
458    /// Use this to get a corresponding GPU texture by an instance of a `Texture`.
459    pub texture_cache: &'a mut TextureCache,
460
461    /// A geometry cache that uploads engine's `SurfaceData` as internal `GeometryBuffer` to GPU.
462    /// Use this to get a corresponding GPU geometry buffer (essentially it is just a VAO) by an
463    /// instance of a `SurfaceData`.
464    pub geometry_cache: &'a mut GeometryCache,
465
466    /// A cache that stores all native shaders associated with a shader resource. You can use it
467    /// to get a ready-to-use set of shaders for your shader resource, which could be obtained
468    /// from a material.
469    pub shader_cache: &'a mut ShaderCache,
470
471    /// A storage that contains "pre-compiled" groups of render data (batches).
472    pub bundle_storage: &'a RenderDataBundleStorage,
473
474    /// Current quality settings of the renderer.
475    pub quality_settings: &'a QualitySettings,
476
477    /// Current framebuffer to which scene is being rendered to.
478    pub framebuffer: &'a GpuFrameBuffer,
479
480    /// A scene being rendered.
481    pub scene: &'b Scene,
482
483    /// A camera from the scene that is used as "eyes".
484    pub observer: &'b Observer,
485
486    /// A handle of the scene being rendered.
487    pub scene_handle: Handle<Scene>,
488
489    /// A set of textures of certain kinds that could be used as a stub in cases when you don't have
490    /// your own texture of this kind.
491    pub renderer_resources: &'a RendererResources,
492
493    /// A texture with depth values from G-Buffer.
494    ///
495    /// # Important notes
496    ///
497    /// Keep in mind that G-Buffer cannot be modified in custom render passes, so you don't
498    /// have an ability to write to this texture. However, you can still write to depth of
499    /// the frame buffer as you'd normally do.
500    pub depth_texture: &'a GpuTexture,
501
502    /// A texture with world-space normals from G-Buffer.
503    ///
504    /// # Important notes
505    ///
506    /// Keep in mind that G-Buffer cannot be modified in custom render passes, so you don't
507    /// have an ability to write to this texture.
508    pub normal_texture: &'a GpuTexture,
509
510    /// A texture with ambient lighting values from G-Buffer.
511    ///
512    /// # Important notes
513    ///
514    /// Keep in mind that G-Buffer cannot be modified in custom render passes, so you don't
515    /// have an ability to write to this texture.
516    pub ambient_texture: &'a GpuTexture,
517
518    /// User interface renderer.
519    pub ui_renderer: &'a mut UiRenderer,
520
521    /// A cache of uniform buffers.
522    pub uniform_buffer_cache: &'a mut UniformBufferCache,
523
524    /// Memory allocator for uniform buffers that tries to pack uniforms densely into large uniform
525    /// buffers, giving you offsets to the data.
526    pub uniform_memory_allocator: &'a mut UniformMemoryAllocator,
527
528    /// Dynamic surface cache. See [`DynamicSurfaceCache`] docs for more info.
529    pub dynamic_surface_cache: &'a mut DynamicSurfaceCache,
530
531    /// A reference to the resource manager.
532    pub resource_manager: &'a ResourceManager,
533}
534
535/// A trait for custom scene rendering pass. It could be used to add your own rendering techniques.
536pub trait SceneRenderPass {
537    /// Renders scene into high dynamic range target. It will be called for **each** scene
538    /// registered in the engine, but you are able to filter out scene by its handle.
539    fn on_hdr_render(
540        &mut self,
541        _ctx: SceneRenderPassContext,
542    ) -> Result<RenderPassStatistics, FrameworkError> {
543        Ok(RenderPassStatistics::default())
544    }
545
546    /// Renders scene into low dynamic range target. It will be called for **each** scene
547    /// registered in the engine, but you are able to filter out scene by its handle.
548    fn on_ldr_render(
549        &mut self,
550        _ctx: SceneRenderPassContext,
551    ) -> Result<RenderPassStatistics, FrameworkError> {
552        Ok(RenderPassStatistics::default())
553    }
554
555    /// Should return type id of a plugin, that holds this render pass. **WARNING:** Setting incorrect
556    /// (anything else, than a real plugin's type id) value here will result in hard crash with happy
557    /// debugging times.
558    fn source_type_id(&self) -> TypeId;
559}
560
561fn blit_pixels(
562    uniform_buffer_cache: &mut UniformBufferCache,
563    framebuffer: &GpuFrameBuffer,
564    texture: &GpuTexture,
565    blit_shader: &RenderPassContainer,
566    viewport: Rect<i32>,
567    renderer_resources: &RendererResources,
568) -> Result<DrawCallStatistics, FrameworkError> {
569    let wvp = make_viewport_matrix(viewport);
570    let properties = PropertyGroup::from([property("worldViewProjection", &wvp)]);
571    let material = RenderMaterial::from([
572        binding(
573            "diffuseTexture",
574            (texture, &renderer_resources.linear_clamp_sampler),
575        ),
576        binding("properties", &properties),
577    ]);
578    blit_shader.run_pass(
579        1,
580        &ImmutableString::new("Primary"),
581        framebuffer,
582        &renderer_resources.quad,
583        viewport,
584        &material,
585        uniform_buffer_cache,
586        Default::default(),
587        None,
588    )
589}
590
591fn render_target_size(
592    render_target: &TextureResource,
593) -> Result<(Vector2<f32>, FrameTextureKind), FrameworkError> {
594    render_target
595        .data_ref()
596        .as_loaded_ref()
597        .and_then(|rt| match rt.kind() {
598            TextureKind::Rectangle { width, height } => Some((
599                Vector2::new(width as f32, height as f32),
600                FrameTextureKind::Rectangle,
601            )),
602            TextureKind::Cube { size } => Some((
603                Vector2::new(size as f32, size as f32),
604                FrameTextureKind::Cube,
605            )),
606            _ => None,
607        })
608        .ok_or_else(|| {
609            FrameworkError::Custom(
610                "Render target must be a valid rectangle or cube texture!".to_string(),
611            )
612        })
613}
614
615impl Renderer {
616    /// Creates a new renderer with the given graphics server.
617    pub fn new(
618        server: Rc<dyn GraphicsServer>,
619        frame_size: (u32, u32),
620        resource_manager: &ResourceManager,
621    ) -> Result<Self, EngineError> {
622        let settings = QualitySettings::default();
623
624        let (texture_event_sender, texture_event_receiver) = std::sync::mpsc::channel();
625
626        resource_manager
627            .state()
628            .event_broadcaster
629            .add(texture_event_sender);
630
631        let (shader_event_sender, shader_event_receiver) = std::sync::mpsc::channel();
632
633        resource_manager
634            .state()
635            .event_broadcaster
636            .add(shader_event_sender);
637
638        let caps = server.capabilities();
639        Log::info(format!("Graphics Server Capabilities\n{caps:?}",));
640
641        let shader_cache = ShaderCache::default();
642
643        let one_megabyte = 1024 * 1024;
644        let uniform_memory_allocator = UniformMemoryAllocator::new(
645            // Clamp max uniform block size from the upper bound, to prevent allocating huge
646            // uniform buffers when GPU supports it. Some AMD GPUs are able to allocate ~500 Mb
647            // uniform buffers, which will lead to ridiculous VRAM consumption.
648            caps.max_uniform_block_size.min(one_megabyte),
649            caps.uniform_buffer_offset_alignment,
650        );
651
652        Ok(Self {
653            backbuffer: server.back_buffer(),
654            frame_size,
655            deferred_light_renderer: DeferredLightRenderer::new(&*server, &settings)?,
656            renderer_resources: RendererResources::new(&*server)?,
657            ui_renderer: UiRenderer::new(&*server)?,
658            quality_settings: settings,
659            debug_renderer: DebugRenderer::new(&*server)?,
660            screen_space_debug_renderer: DebugRenderer::new(&*server)?,
661            scene_data_map: Default::default(),
662            backbuffer_clear_color: Color::BLACK,
663            texture_cache: Default::default(),
664            geometry_cache: Default::default(),
665            ui_frame_buffers: Default::default(),
666            fxaa_renderer: FxaaRenderer::default(),
667            statistics: Statistics::default(),
668            shader_event_receiver,
669            texture_event_receiver,
670            shader_cache,
671            scene_render_passes: Default::default(),
672            uniform_buffer_cache: UniformBufferCache::new(server.clone()),
673            server,
674            visibility_cache: Default::default(),
675            uniform_memory_allocator,
676            dynamic_surface_cache: DynamicSurfaceCache::new(),
677        })
678    }
679
680    /// Adds a custom render pass.
681    pub fn add_render_pass(&mut self, pass: Rc<RefCell<dyn SceneRenderPass>>) {
682        self.scene_render_passes.push(pass);
683    }
684
685    /// Removes specified render pass.
686    pub fn remove_render_pass(&mut self, pass: Rc<RefCell<dyn SceneRenderPass>>) {
687        if let Some(index) = self
688            .scene_render_passes
689            .iter()
690            .position(|p| Rc::ptr_eq(p, &pass))
691        {
692            self.scene_render_passes.remove(index);
693        }
694    }
695
696    /// Returns a slice with every registered render passes.
697    pub fn render_passes(&self) -> &[Rc<RefCell<dyn SceneRenderPass>>] {
698        &self.scene_render_passes
699    }
700
701    /// Removes all render passes from the renderer.
702    pub fn clear_render_passes(&mut self) {
703        self.scene_render_passes.clear()
704    }
705
706    /// Returns statistics for last frame.
707    pub fn get_statistics(&self) -> Statistics {
708        self.statistics
709    }
710
711    /// Unloads texture from GPU memory.
712    pub fn unload_texture(&mut self, texture: &TextureResource) {
713        self.texture_cache.unload(texture)
714    }
715
716    /// Sets color which will be used to fill screen when there is nothing to render.
717    pub fn set_backbuffer_clear_color(&mut self, color: Color) {
718        self.backbuffer_clear_color = color;
719    }
720
721    /// Returns a reference to current graphics server.
722    pub fn graphics_server(&self) -> &dyn GraphicsServer {
723        &*self.server
724    }
725
726    /// Sets new frame size. You should call the same method on [`crate::engine::Engine`]
727    /// instead, which will update the size for the user interface and rendering context
728    /// as well as this one.
729    ///
730    /// # Notes
731    ///
732    /// Input values will be set to 1 pixel if new size is 0. Rendering cannot
733    /// be performed into 0x0 texture.
734    pub(crate) fn set_frame_size(&mut self, new_size: (u32, u32)) -> Result<(), FrameworkError> {
735        self.frame_size.0 = new_size.0.max(1);
736        self.frame_size.1 = new_size.1.max(1);
737
738        self.graphics_server().set_frame_size(new_size);
739
740        Ok(())
741    }
742
743    /// Returns current (width, height) pair of back buffer size.
744    pub fn get_frame_size(&self) -> (u32, u32) {
745        self.frame_size
746    }
747
748    /// Returns current bounds of back buffer.
749    pub fn get_frame_bounds(&self) -> Vector2<f32> {
750        Vector2::new(self.frame_size.0 as f32, self.frame_size.1 as f32)
751    }
752
753    /// Sets new quality settings for renderer. Never call this method in a loop, otherwise
754    /// you may get **significant** lags. Always check if current quality setting differs
755    /// from new!
756    pub fn set_quality_settings(
757        &mut self,
758        settings: &QualitySettings,
759    ) -> Result<(), FrameworkError> {
760        self.quality_settings = *settings;
761        for data in self.scene_data_map.values_mut() {
762            data.set_quality_settings(settings);
763        }
764        self.deferred_light_renderer
765            .set_quality_settings(&*self.server, settings)
766    }
767
768    /// Returns current quality settings.
769    pub fn get_quality_settings(&self) -> QualitySettings {
770        self.quality_settings
771    }
772
773    /// Removes all cached GPU data, forces renderer to re-upload data to GPU.
774    /// Do not call this method until you absolutely need! It may cause **significant**
775    /// performance lag!
776    pub fn flush(&mut self) {
777        self.texture_cache.clear();
778        self.geometry_cache.clear();
779    }
780
781    /// Renders the given UI into specified render target. This method is especially useful if you need
782    /// to have off-screen UIs. This method will render the specified UI to the given render target.
783    /// If the render target is not specified (set to [`None`]), the UI will be rendered directly on
784    /// screen. Keep in mind that ideally, the resolution of the render target should match the screen
785    /// size of the UI. Otherwise, the rendered image will have some sort of aliasing issues.
786    pub fn render_ui(&mut self, render_info: UiRenderInfo) -> Result<(), FrameworkError> {
787        let (frame_buffer, rt_size) = if let Some(render_target) =
788            render_info.render_target.as_ref()
789        {
790            let (rt_size, rt_pixel_kind) = render_target
791                .data_ref()
792                .as_loaded_ref()
793                .and_then(|rt| {
794                    rt.kind()
795                        .rectangle_size()
796                        .map(|s| (s.cast::<f32>(), convert_pixel_kind(rt.pixel_kind())))
797                })
798                .ok_or_else(|| FrameworkError::Custom("invalid render target state".to_string()))?;
799
800            // Create or reuse existing frame buffer.
801            let frame_buffer = match self.ui_frame_buffers.entry(render_target.key()) {
802                Entry::Occupied(entry) => {
803                    let frame_buffer = entry.into_mut();
804                    let frame = frame_buffer.color_attachments().first().unwrap();
805                    let color_texture_kind = frame.texture.kind();
806                    if let GpuTextureKind::Rectangle { width, height } = color_texture_kind {
807                        if width != rt_size.x as usize
808                            || height != rt_size.y as usize
809                            || frame.texture.pixel_kind() != rt_pixel_kind
810                        {
811                            *frame_buffer =
812                                make_ui_frame_buffer(rt_size, &*self.server, rt_pixel_kind)?;
813                        }
814                    } else {
815                        return Err(FrameworkError::Custom(
816                            "ui can be rendered only in rectangle texture!".to_string(),
817                        ));
818                    }
819                    frame_buffer
820                }
821                Entry::Vacant(entry) => {
822                    entry.insert(make_ui_frame_buffer(rt_size, &*self.server, rt_pixel_kind)?)
823                }
824            };
825
826            let viewport = Rect::new(0, 0, rt_size.x as i32, rt_size.y as i32);
827            frame_buffer.clear(viewport, Some(render_info.clear_color), Some(0.0), Some(0));
828
829            (frame_buffer, rt_size)
830        } else {
831            (
832                &mut self.backbuffer,
833                Vector2::new(self.frame_size.0 as f32, self.frame_size.1 as f32),
834            )
835        };
836
837        self.statistics += self.ui_renderer.render(UiRenderContext {
838            server: &*self.server,
839            viewport: Rect::new(0, 0, rt_size.x as i32, rt_size.y as i32),
840            frame_buffer,
841            frame_width: rt_size.x,
842            frame_height: rt_size.y,
843            drawing_context: &render_info.ui.drawing_context,
844            renderer_resources: &self.renderer_resources,
845            texture_cache: &mut self.texture_cache,
846            uniform_buffer_cache: &mut self.uniform_buffer_cache,
847            render_pass_cache: &mut self.shader_cache,
848            uniform_memory_allocator: &mut self.uniform_memory_allocator,
849            resource_manager: render_info.resource_manager,
850        })?;
851
852        if let Some(render_target) = render_info.render_target.as_ref() {
853            // Finally, register texture in the cache so it will become available as texture in
854            // deferred/forward renderer.
855            self.texture_cache.try_register(
856                &*self.server,
857                render_target,
858                frame_buffer
859                    .color_attachments()
860                    .first()
861                    .unwrap()
862                    .texture
863                    .clone(),
864            )?;
865        }
866
867        Ok(())
868    }
869
870    fn update_texture_cache(&mut self, resource_manager: &ResourceManager, dt: f32) {
871        // Maximum amount of textures uploaded to GPU per frame. This defines throughput **only** for
872        // requests from resource manager. This is needed to prevent huge lag when there are tons of
873        // requests, so this is some kind of work load balancer.
874        const THROUGHPUT: usize = 5;
875
876        let mut uploaded = 0;
877        while let Ok(event) = self.texture_event_receiver.try_recv() {
878            if let ResourceEvent::Loaded(resource) | ResourceEvent::Reloaded(resource) = event {
879                if let Some(texture) = resource.try_cast::<Texture>() {
880                    match self
881                        .texture_cache
882                        .upload(&*self.server, resource_manager, &texture)
883                    {
884                        Ok(_) => {
885                            uploaded += 1;
886                            if uploaded >= THROUGHPUT {
887                                break;
888                            }
889                        }
890                        Err(e) => {
891                            Log::writeln(
892                                MessageKind::Error,
893                                format!("Failed to upload texture to GPU. Reason: {e:?}"),
894                            );
895                        }
896                    }
897                }
898            }
899        }
900
901        self.texture_cache.update(dt);
902    }
903
904    fn update_shader_cache(&mut self, dt: f32) {
905        while let Ok(event) = self.shader_event_receiver.try_recv() {
906            if let ResourceEvent::Loaded(resource) | ResourceEvent::Reloaded(resource) = event {
907                if let Some(shader) = resource.try_cast::<Shader>() {
908                    // Remove and immediately "touch" the shader cache to force upload shader.
909                    self.shader_cache.remove(&shader);
910                    let _ = self.shader_cache.get(&*self.server, &shader);
911                }
912            }
913        }
914
915        self.shader_cache.update(dt)
916    }
917
918    /// Update caches - this will remove timed out resources.
919    ///
920    /// Normally, this is called from `Engine::update()`.
921    /// You should only call this manually if you don't use that method.
922    pub fn update_caches(&mut self, resource_manager: &ResourceManager, dt: f32) {
923        self.update_texture_cache(resource_manager, dt);
924        self.update_shader_cache(dt);
925        self.geometry_cache.update(dt);
926    }
927
928    fn render_scene_observer(
929        &mut self,
930        observer: &Observer,
931        scene_handle: Handle<Scene>,
932        scene: &Scene,
933        elapsed_time: f32,
934        dt: f32,
935        resource_manager: &ResourceManager,
936        need_recalculate_convolution: bool,
937    ) -> Result<&mut RenderDataContainer, FrameworkError> {
938        let server = &*self.server;
939
940        let scene_render_data = self.scene_data_map.get_mut(&scene_handle).ok_or_else(|| {
941            FrameworkError::Custom(format!(
942                "No assocated render data for {scene_handle} scene!"
943            ))
944        })?;
945        let render_data = if let Some(render_target) = observer.render_target.as_ref() {
946            let (rt_size, final_frame_texture) = render_target_size(render_target)?;
947            let observer_render_data = match scene_render_data.camera_data.entry(observer.handle) {
948                Entry::Occupied(entry) => {
949                    let observer_render_data = entry.into_mut();
950                    recreate_render_data_if_needed(
951                        scene_handle,
952                        server,
953                        observer_render_data,
954                        rt_size,
955                        final_frame_texture,
956                    )?;
957                    observer_render_data
958                }
959                Entry::Vacant(entry) => {
960                    let render_data = entry.insert(RenderDataContainer::new(
961                        server,
962                        rt_size,
963                        final_frame_texture,
964                    )?);
965                    info!(
966                        "A new associated scene rendering data was created for observer {}!",
967                        observer.handle
968                    );
969                    render_data
970                }
971            };
972
973            if let Some(probe_data) = observer.reflection_probe_data.as_ref() {
974                observer_render_data.ldr_scene_framebuffer.set_cubemap_face(
975                    0,
976                    probe_data.cube_map_face,
977                    0,
978                );
979            }
980
981            self.texture_cache.try_register(
982                server,
983                render_target,
984                observer_render_data.ldr_scene_frame_texture().clone(),
985            )?;
986
987            observer_render_data
988        } else {
989            &mut scene_render_data.scene_data
990        };
991
992        render_data.need_recalculate_convolution |= need_recalculate_convolution;
993
994        let visibility_cache = self
995            .visibility_cache
996            .get_or_register(&scene.graph, observer.handle);
997
998        let mut bundle_storage = RenderDataBundleStorage::from_graph(
999            &scene.graph,
1000            observer.render_mask,
1001            elapsed_time,
1002            &observer.position,
1003            GBUFFER_PASS_NAME.clone(),
1004            RenderDataBundleStorageOptions {
1005                collect_lights: true,
1006            },
1007            &mut self.dynamic_surface_cache,
1008        );
1009        if observer.reflection_probe_data.is_some() {
1010            bundle_storage.environment_map = None;
1011        }
1012
1013        server.set_polygon_fill_mode(
1014            PolygonFace::FrontAndBack,
1015            scene.rendering_options.polygon_rasterization_mode,
1016        );
1017
1018        render_data.statistics += render_data.gbuffer.fill(GBufferRenderContext {
1019            server,
1020            observer,
1021            geom_cache: &mut self.geometry_cache,
1022            bundle_storage: &bundle_storage,
1023            texture_cache: &mut self.texture_cache,
1024            shader_cache: &mut self.shader_cache,
1025            quality_settings: &self.quality_settings,
1026            renderer_resources: &self.renderer_resources,
1027            graph: &scene.graph,
1028            uniform_buffer_cache: &mut self.uniform_buffer_cache,
1029            uniform_memory_allocator: &mut self.uniform_memory_allocator,
1030            screen_space_debug_renderer: &mut self.screen_space_debug_renderer,
1031            resource_manager,
1032        })?;
1033
1034        server.set_polygon_fill_mode(PolygonFace::FrontAndBack, PolygonFillMode::Fill);
1035
1036        render_data.copy_depth_stencil_to_scene_framebuffer();
1037
1038        render_data.hdr_scene_framebuffer.clear(
1039            observer.viewport,
1040            Some(
1041                scene
1042                    .rendering_options
1043                    .clear_color
1044                    .unwrap_or(self.backbuffer_clear_color),
1045            ),
1046            None, // Keep depth, we've just copied valid data in it.
1047            Some(0),
1048        );
1049
1050        let (pass_stats, light_stats) =
1051            self.deferred_light_renderer
1052                .render(DeferredRendererContext {
1053                    elapsed_time,
1054                    server,
1055                    scene,
1056                    observer,
1057                    gbuffer: &mut render_data.gbuffer,
1058                    ambient_color: match observer.reflection_probe_data.as_ref() {
1059                        None => scene.rendering_options.ambient_lighting_color,
1060                        Some(probe_data) => probe_data.ambient_lighting_color,
1061                    },
1062                    environment_lighting_source: match observer.reflection_probe_data.as_ref() {
1063                        None => scene.rendering_options.environment_lighting_source,
1064                        Some(probe_data) => probe_data.environment_lighting_source,
1065                    },
1066                    render_data_bundle: &bundle_storage,
1067                    settings: &self.quality_settings,
1068                    textures: &mut self.texture_cache,
1069                    geometry_cache: &mut self.geometry_cache,
1070                    frame_buffer: &render_data.hdr_scene_framebuffer,
1071                    shader_cache: &mut self.shader_cache,
1072                    renderer_resources: &self.renderer_resources,
1073                    uniform_buffer_cache: &mut self.uniform_buffer_cache,
1074                    visibility_cache,
1075                    uniform_memory_allocator: &mut self.uniform_memory_allocator,
1076                    dynamic_surface_cache: &mut self.dynamic_surface_cache,
1077                    ssao_renderer: &render_data.ssao_renderer,
1078                    resource_manager,
1079                    environment_map_specular_convolution: &mut render_data
1080                        .environment_map_specular_convolution,
1081                    environment_map_irradiance_convolution: &render_data
1082                        .environment_map_irradiance_convolution,
1083                    need_recalculate_convolution: &mut render_data.need_recalculate_convolution,
1084                })?;
1085
1086        render_data.statistics += light_stats;
1087        render_data.statistics += pass_stats;
1088
1089        let depth = render_data.gbuffer.depth();
1090
1091        render_data.statistics += bundle_storage.render_to_frame_buffer(
1092            server,
1093            &mut self.geometry_cache,
1094            &mut self.shader_cache,
1095            |bundle| bundle.render_path == RenderPath::Forward,
1096            |_| true,
1097            BundleRenderContext {
1098                texture_cache: &mut self.texture_cache,
1099                render_pass_name: &ImmutableString::new("Forward"),
1100                frame_buffer: &render_data.hdr_scene_framebuffer,
1101                viewport: observer.viewport,
1102                uniform_memory_allocator: &mut self.uniform_memory_allocator,
1103                resource_manager,
1104                use_pom: self.quality_settings.use_parallax_mapping,
1105                light_position: &Default::default(),
1106                renderer_resources: &self.renderer_resources,
1107                ambient_light: scene.rendering_options.ambient_lighting_color,
1108                scene_depth: Some(depth),
1109            },
1110        )?;
1111
1112        for render_pass in self.scene_render_passes.iter() {
1113            render_data.statistics +=
1114                render_pass
1115                    .borrow_mut()
1116                    .on_hdr_render(SceneRenderPassContext {
1117                        elapsed_time,
1118                        server,
1119                        texture_cache: &mut self.texture_cache,
1120                        geometry_cache: &mut self.geometry_cache,
1121                        shader_cache: &mut self.shader_cache,
1122                        quality_settings: &self.quality_settings,
1123                        bundle_storage: &bundle_storage,
1124                        scene,
1125                        observer,
1126                        scene_handle,
1127                        renderer_resources: &self.renderer_resources,
1128                        depth_texture: render_data.gbuffer.depth(),
1129                        normal_texture: render_data.gbuffer.normal_texture(),
1130                        ambient_texture: render_data.gbuffer.ambient_texture(),
1131                        framebuffer: &render_data.hdr_scene_framebuffer,
1132                        ui_renderer: &mut self.ui_renderer,
1133                        uniform_buffer_cache: &mut self.uniform_buffer_cache,
1134                        uniform_memory_allocator: &mut self.uniform_memory_allocator,
1135                        dynamic_surface_cache: &mut self.dynamic_surface_cache,
1136                        resource_manager,
1137                    })?;
1138        }
1139
1140        // Prepare glow map.
1141        render_data.statistics += render_data.bloom_renderer.render(
1142            render_data.hdr_scene_frame_texture(),
1143            &mut self.uniform_buffer_cache,
1144            &self.renderer_resources,
1145        )?;
1146
1147        // Convert high dynamic range frame to low dynamic range (sRGB) with tone mapping and gamma correction.
1148        let mut dest_buf = 0;
1149        let mut src_buf = 1;
1150        render_data.statistics += render_data.hdr_renderer.render(
1151            server,
1152            render_data.hdr_scene_frame_texture(),
1153            render_data.bloom_renderer.result(),
1154            &render_data.ldr_temp_framebuffer[dest_buf],
1155            observer.viewport,
1156            dt,
1157            observer.exposure,
1158            observer.color_grading_lut.as_ref(),
1159            observer.color_grading_enabled,
1160            &mut self.texture_cache,
1161            &mut self.uniform_buffer_cache,
1162            &self.renderer_resources,
1163            resource_manager,
1164        )?;
1165        std::mem::swap(&mut dest_buf, &mut src_buf);
1166
1167        // Apply FXAA if needed.
1168        if self.quality_settings.fxaa {
1169            render_data.statistics += self.fxaa_renderer.render(
1170                observer.viewport,
1171                render_data.ldr_temp_frame_texture(src_buf),
1172                &render_data.ldr_temp_framebuffer[dest_buf],
1173                &mut self.uniform_buffer_cache,
1174                &self.renderer_resources,
1175            )?;
1176            std::mem::swap(&mut dest_buf, &mut src_buf);
1177        }
1178
1179        render_data.statistics += blit_pixels(
1180            &mut self.uniform_buffer_cache,
1181            &render_data.ldr_scene_framebuffer,
1182            render_data.ldr_temp_frame_texture(src_buf),
1183            &self.renderer_resources.shaders.blit,
1184            observer.viewport,
1185            &self.renderer_resources,
1186        )?;
1187
1188        // Render debug geometry in the LDR frame buffer.
1189        self.debug_renderer.set_lines(&scene.drawing_context.lines);
1190        render_data.statistics += self.debug_renderer.render(
1191            &mut self.uniform_buffer_cache,
1192            observer.viewport,
1193            &render_data.ldr_scene_framebuffer,
1194            observer.position.view_projection_matrix,
1195            &self.renderer_resources,
1196        )?;
1197
1198        for render_pass in self.scene_render_passes.iter() {
1199            render_data.statistics +=
1200                render_pass
1201                    .borrow_mut()
1202                    .on_ldr_render(SceneRenderPassContext {
1203                        elapsed_time,
1204                        server,
1205                        texture_cache: &mut self.texture_cache,
1206                        geometry_cache: &mut self.geometry_cache,
1207                        shader_cache: &mut self.shader_cache,
1208                        quality_settings: &self.quality_settings,
1209                        bundle_storage: &bundle_storage,
1210                        scene,
1211                        observer,
1212                        scene_handle,
1213                        renderer_resources: &self.renderer_resources,
1214                        depth_texture: render_data.gbuffer.depth(),
1215                        normal_texture: render_data.gbuffer.normal_texture(),
1216                        ambient_texture: render_data.gbuffer.ambient_texture(),
1217                        framebuffer: &render_data.ldr_scene_framebuffer,
1218                        ui_renderer: &mut self.ui_renderer,
1219                        uniform_buffer_cache: &mut self.uniform_buffer_cache,
1220                        uniform_memory_allocator: &mut self.uniform_memory_allocator,
1221                        dynamic_surface_cache: &mut self.dynamic_surface_cache,
1222                        resource_manager,
1223                    })?;
1224        }
1225
1226        Ok(render_data)
1227    }
1228
1229    /// Unconditionally renders a scene and returns a reference to a [`RenderDataContainer`] instance
1230    /// that contains rendered data (including intermediate data, such as G-Buffer content, etc.).
1231    pub fn render_scene(
1232        &mut self,
1233        scene_handle: Handle<Scene>,
1234        scene: &Scene,
1235        elapsed_time: f32,
1236        dt: f32,
1237        resource_manager: &ResourceManager,
1238    ) -> Result<&SceneRenderData, FrameworkError> {
1239        let graph = &scene.graph;
1240
1241        let backbuffer_width = self.frame_size.0 as f32;
1242        let backbuffer_height = self.frame_size.1 as f32;
1243
1244        let window_viewport = Rect::new(0, 0, self.frame_size.0 as i32, self.frame_size.1 as i32);
1245
1246        let frame_size = scene
1247            .rendering_options
1248            .render_target
1249            .as_ref()
1250            .map_or_else(
1251                // Use either backbuffer size
1252                || Vector2::new(backbuffer_width, backbuffer_height),
1253                // Or framebuffer size
1254                |rt| {
1255                    if let TextureKind::Rectangle { width, height } = rt.data_ref().kind() {
1256                        Vector2::new(width as f32, height as f32)
1257                    } else {
1258                        panic!("only rectangle textures can be used as render target!")
1259                    }
1260                },
1261            )
1262            // Clamp to [1.0; infinity] range.
1263            .sup(&Vector2::new(1.0, 1.0));
1264
1265        let scene_render_data = match self.scene_data_map.entry(scene_handle) {
1266            Entry::Occupied(entry) => {
1267                let render_data = entry.into_mut();
1268                recreate_render_data_if_needed(
1269                    scene_handle,
1270                    &*self.server,
1271                    &mut render_data.scene_data,
1272                    frame_size,
1273                    FrameTextureKind::Rectangle,
1274                )?;
1275                render_data
1276            }
1277            Entry::Vacant(entry) => {
1278                let render_data = entry.insert(SceneRenderData::new(
1279                    &*self.server,
1280                    frame_size,
1281                    FrameTextureKind::Rectangle,
1282                )?);
1283                info!(
1284                    "A new associated scene rendering data was created for scene {scene_handle}!"
1285                );
1286                render_data
1287            }
1288        };
1289
1290        let pipeline_stats = &self.server.pipeline_statistics();
1291        scene_render_data.scene_data.statistics = Default::default();
1292
1293        // If we specified a texture to draw to, we have to register it in texture cache
1294        // so it can be used in later on as texture. This is useful in case if you need
1295        // to draw something on offscreen and then draw it on some mesh.
1296        if let Some(rt) = scene.rendering_options.render_target.clone() {
1297            self.texture_cache.try_register(
1298                &*self.server,
1299                &rt,
1300                scene_render_data
1301                    .scene_data
1302                    .ldr_scene_frame_texture()
1303                    .clone(),
1304            )?;
1305        }
1306
1307        scene_render_data
1308            .camera_data
1309            .retain(|h, _| graph.is_valid_handle(*h));
1310
1311        let observers = ObserversCollection::from_scene(scene, frame_size);
1312
1313        // At first, render the reflection probes to off-screen render target.
1314        let mut need_recalculate_convolution = false;
1315        for observer in observers.reflection_probes.iter() {
1316            self.render_scene_observer(
1317                observer,
1318                scene_handle,
1319                scene,
1320                elapsed_time,
1321                dt,
1322                resource_manager,
1323                // There's no need to recalculate convolution for the environment map more than once
1324                // when rendering reflection probes, because it does not use a dynamic environment map.
1325                false,
1326            )?;
1327            need_recalculate_convolution = true;
1328        }
1329
1330        // Then render everything else.
1331        for observer in observers.cameras.iter() {
1332            self.render_scene_observer(
1333                observer,
1334                scene_handle,
1335                scene,
1336                elapsed_time,
1337                dt,
1338                resource_manager,
1339                need_recalculate_convolution,
1340            )?;
1341        }
1342
1343        self.visibility_cache.update(graph);
1344
1345        let scene_render_data = self.scene_data_map.get_mut(&scene_handle).unwrap();
1346
1347        // Optionally render everything into back buffer.
1348        if scene.rendering_options.render_target.is_none() {
1349            scene_render_data.scene_data.statistics += blit_pixels(
1350                &mut self.uniform_buffer_cache,
1351                &self.backbuffer,
1352                scene_render_data.scene_data.ldr_scene_frame_texture(),
1353                &self.renderer_resources.shaders.blit,
1354                window_viewport,
1355                &self.renderer_resources,
1356            )?;
1357        }
1358
1359        self.statistics += scene_render_data.scene_data.statistics;
1360        scene_render_data.scene_data.statistics.pipeline =
1361            self.server.pipeline_statistics() - *pipeline_stats;
1362
1363        Ok(scene_render_data)
1364    }
1365
1366    fn render_frame<'a>(
1367        &mut self,
1368        scenes: &SceneContainer,
1369        elapsed_time: f32,
1370        resource_manager: &ResourceManager,
1371        ui_render_info: impl Iterator<Item = UiRenderInfo<'a>>,
1372    ) -> Result<(), FrameworkError> {
1373        if self.frame_size.0 == 0 || self.frame_size.1 == 0 {
1374            return Ok(());
1375        }
1376
1377        self.uniform_buffer_cache.mark_all_unused();
1378        self.uniform_memory_allocator.clear();
1379        self.dynamic_surface_cache.clear();
1380
1381        // Make sure to drop associated data for destroyed scenes.
1382        self.scene_data_map
1383            .retain(|h, _| scenes.is_valid_handle(*h));
1384
1385        // We have to invalidate resource bindings cache because some textures or programs,
1386        // or other GL resources can be destroyed and then on their "names" some new resource
1387        // are created, but cache still thinks that resource is correctly bound, but it is different
1388        // object have same name.
1389        self.server.invalidate_resource_bindings_cache();
1390        let dt = self.statistics.capped_frame_time;
1391        self.statistics.begin_frame();
1392
1393        let window_viewport = Rect::new(0, 0, self.frame_size.0 as i32, self.frame_size.1 as i32);
1394        self.backbuffer.clear(
1395            window_viewport,
1396            Some(self.backbuffer_clear_color),
1397            Some(1.0),
1398            Some(0),
1399        );
1400
1401        let backbuffer_width = self.frame_size.0 as f32;
1402        let backbuffer_height = self.frame_size.1 as f32;
1403
1404        for (scene_handle, scene) in scenes.pair_iter().filter(|(_, s)| *s.enabled) {
1405            self.render_scene(scene_handle, scene, elapsed_time, dt, resource_manager)?;
1406        }
1407
1408        self.graphics_server()
1409            .set_polygon_fill_mode(PolygonFace::FrontAndBack, PolygonFillMode::Fill);
1410
1411        // Render UI on top of everything without gamma correction.
1412        for info in ui_render_info {
1413            self.render_ui(info)?;
1414        }
1415
1416        let screen_matrix =
1417            Matrix4::new_orthographic(0.0, backbuffer_width, backbuffer_height, 0.0, -1.0, 1.0);
1418        self.screen_space_debug_renderer.render(
1419            &mut self.uniform_buffer_cache,
1420            window_viewport,
1421            &self.backbuffer,
1422            screen_matrix,
1423            &self.renderer_resources,
1424        )?;
1425
1426        self.statistics.geometry_cache_size = self.geometry_cache.alive_count();
1427        self.statistics.texture_cache_size = self.texture_cache.alive_count();
1428        self.statistics.shader_cache_size = self.shader_cache.alive_count();
1429        self.statistics.uniform_buffer_cache_size = self.uniform_buffer_cache.alive_count();
1430
1431        Ok(())
1432    }
1433
1434    pub(crate) fn render_and_swap_buffers<'a>(
1435        &mut self,
1436        scenes: &SceneContainer,
1437        elapsed_time: f32,
1438        ui_info: impl Iterator<Item = UiRenderInfo<'a>>,
1439        window: &Window,
1440        resource_manager: &ResourceManager,
1441    ) -> Result<(), FrameworkError> {
1442        self.render_frame(scenes, elapsed_time, resource_manager, ui_info)?;
1443        self.statistics.end_frame();
1444        window.pre_present_notify();
1445        self.graphics_server().swap_buffers()?;
1446        self.statistics.finalize();
1447        self.statistics.pipeline = self.server.pipeline_statistics();
1448        Ok(())
1449    }
1450}