Skip to main content

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