#![warn(missing_docs)]
pub mod framework;
pub mod bundle;
pub mod cache;
pub mod debug_renderer;
pub mod observer;
pub mod resources;
pub mod stats;
pub mod storage;
pub mod ui_renderer;
pub mod utils;
pub mod visibility;
mod bloom;
mod convolution;
mod fxaa;
mod gbuffer;
mod hdr;
mod light;
mod light_volume;
mod occlusion;
mod settings;
mod shadow;
mod ssao;
use crate::renderer::hdr::HdrRendererArgs;
use crate::{
asset::{event::ResourceEvent, manager::ResourceManager},
core::{
algebra::{Matrix4, Vector2, Vector3},
color::Color,
info,
log::{Log, MessageKind},
math::Rect,
pool::Handle,
sstorage::ImmutableString,
},
engine::error::EngineError,
graphics::{
error::FrameworkError,
framebuffer::{Attachment, DrawCallStatistics, GpuFrameBuffer},
gpu_texture::{GpuTexture, GpuTextureDescriptor, GpuTextureKind, PixelKind},
server::{GraphicsServer, SharedGraphicsServer},
PolygonFace, PolygonFillMode,
},
material::shader::Shader,
renderer::{
bundle::{BundleRenderContext, RenderDataBundleStorage, RenderDataBundleStorageOptions},
cache::texture::convert_pixel_kind,
cache::{
geometry::GeometryCache,
shader::{
binding, property, PropertyGroup, RenderMaterial, RenderPassContainer, ShaderCache,
},
texture::TextureCache,
uniform::{UniformBufferCache, UniformMemoryAllocator},
},
convolution::{EnvironmentMapIrradianceConvolution, EnvironmentMapSpecularConvolution},
debug_renderer::DebugRenderer,
fxaa::FxaaRenderer,
gbuffer::{GBuffer, GBufferRenderContext},
hdr::HighDynamicRangeRenderer,
light::{DeferredLightRenderer, DeferredRendererContext},
ssao::ScreenSpaceAmbientOcclusionRenderer,
ui_renderer::UiRenderInfo,
ui_renderer::{UiRenderContext, UiRenderer},
visibility::VisibilityCache,
},
resource::texture::{Texture, TextureKind, TextureResource},
scene::{mesh::RenderPath, node::Node, Scene, SceneContainer},
};
use cache::DynamicSurfaceCache;
use fxhash::FxHashMap;
use fyrox_graph::SceneGraph;
use observer::{Observer, ObserversCollection};
use resources::RendererResources;
pub use settings::*;
pub use stats::*;
use std::sync::LazyLock;
use std::{
any::{Any, TypeId},
cell::RefCell,
collections::hash_map::Entry,
rc::Rc,
sync::mpsc::Receiver,
};
use winit::window::Window;
static GBUFFER_PASS_NAME: LazyLock<ImmutableString> =
LazyLock::new(|| ImmutableString::new("GBuffer"));
static DIRECTIONAL_SHADOW_PASS_NAME: LazyLock<ImmutableString> =
LazyLock::new(|| ImmutableString::new("DirectionalShadow"));
static SPOT_SHADOW_PASS_NAME: LazyLock<ImmutableString> =
LazyLock::new(|| ImmutableString::new("SpotShadow"));
static POINT_SHADOW_PASS_NAME: LazyLock<ImmutableString> =
LazyLock::new(|| ImmutableString::new("PointShadow"));
pub fn is_shadow_pass(render_pass_name: &str) -> bool {
render_pass_name == &**DIRECTIONAL_SHADOW_PASS_NAME
|| render_pass_name == &**SPOT_SHADOW_PASS_NAME
|| render_pass_name == &**POINT_SHADOW_PASS_NAME
}
pub struct SceneRenderData {
pub camera_data: FxHashMap<Handle<Node>, RenderDataContainer>,
pub scene_data: RenderDataContainer,
}
impl SceneRenderData {
pub fn new(
server: &dyn GraphicsServer,
frame_size: Vector2<f32>,
final_frame_texture: FrameTextureKind,
) -> Result<Self, FrameworkError> {
Ok(Self {
camera_data: Default::default(),
scene_data: RenderDataContainer::new(server, frame_size, final_frame_texture)?,
})
}
pub fn set_quality_settings(&mut self, settings: &QualitySettings) {
for camera_data in self.camera_data.values_mut() {
camera_data.set_quality_settings(settings);
}
self.scene_data.set_quality_settings(settings);
}
}
fn recreate_render_data_if_needed<T: Any>(
parent: Handle<T>,
server: &dyn GraphicsServer,
data: &mut RenderDataContainer,
frame_size: Vector2<f32>,
final_frame_texture: FrameTextureKind,
) -> Result<(), FrameworkError> {
if data.gbuffer.width != frame_size.x as i32 || data.gbuffer.height != frame_size.y as i32 {
Log::info(format!(
"Associated scene rendering data was re-created for {} ({}), because render \
frame size was changed. Old is {}x{}, new {}x{}!",
parent,
std::any::type_name::<T>(),
data.gbuffer.width,
data.gbuffer.height,
frame_size.x,
frame_size.y
));
*data = RenderDataContainer::new(server, frame_size, final_frame_texture)?;
}
Ok(())
}
pub struct RenderDataContainer {
pub environment_map_specular_convolution: Option<EnvironmentMapSpecularConvolution>,
pub environment_map_irradiance_convolution: EnvironmentMapIrradianceConvolution,
pub need_recalculate_convolution: bool,
pub ssao_renderer: ScreenSpaceAmbientOcclusionRenderer,
pub gbuffer: GBuffer,
pub hdr_scene_framebuffer: GpuFrameBuffer,
pub ldr_scene_framebuffer: GpuFrameBuffer,
pub ldr_temp_framebuffer: [GpuFrameBuffer; 2],
pub hdr_renderer: HighDynamicRangeRenderer,
pub statistics: SceneStatistics,
}
#[derive(Default)]
pub enum FrameTextureKind {
#[default]
Rectangle,
Cube,
}
impl RenderDataContainer {
pub fn new(
server: &dyn GraphicsServer,
frame_size: Vector2<f32>,
final_frame_texture: FrameTextureKind,
) -> Result<Self, FrameworkError> {
let width = frame_size.x as usize;
let height = frame_size.y as usize;
if matches!(final_frame_texture, FrameTextureKind::Cube) {
assert_eq!(width, height);
}
let depth_stencil = server.create_2d_render_target(
"ObserverDepthStencil",
PixelKind::D24S8,
width,
height,
)?;
let hdr_frame_texture = server.create_2d_render_target(
"ObserverHdrFrame",
PixelKind::RGB10A2,
width,
height,
)?;
let hdr_scene_framebuffer = server.create_frame_buffer(
Some(Attachment::depth_stencil(depth_stencil.clone())),
vec![Attachment::color(hdr_frame_texture)],
)?;
let ldr_frame_texture = server.create_texture(GpuTextureDescriptor {
name: "LdrFrameTexture",
kind: match final_frame_texture {
FrameTextureKind::Rectangle => GpuTextureKind::Rectangle { width, height },
FrameTextureKind::Cube => GpuTextureKind::Cube { size: width },
},
pixel_kind: PixelKind::RGBA8,
..Default::default()
})?;
let ldr_scene_framebuffer = server.create_frame_buffer(
Some(Attachment::depth_stencil(depth_stencil.clone())),
vec![Attachment::color(ldr_frame_texture)],
)?;
fn make_ldr_temp_frame_buffer(
server: &dyn GraphicsServer,
width: usize,
height: usize,
depth_stencil: GpuTexture,
) -> Result<GpuFrameBuffer, FrameworkError> {
let ldr_temp_texture = server.create_texture(GpuTextureDescriptor {
name: "LdrTempTexture",
kind: GpuTextureKind::Rectangle { width, height },
pixel_kind: PixelKind::RGBA8,
..Default::default()
})?;
server.create_frame_buffer(
Some(Attachment::depth_stencil(depth_stencil)),
vec![Attachment::color(ldr_temp_texture)],
)
}
Ok(Self {
need_recalculate_convolution: true,
environment_map_specular_convolution: Default::default(),
environment_map_irradiance_convolution: EnvironmentMapIrradianceConvolution::new(
server, 32,
)?,
ssao_renderer: ScreenSpaceAmbientOcclusionRenderer::new(server, width, height)?,
gbuffer: GBuffer::new(server, width, height)?,
hdr_renderer: HighDynamicRangeRenderer::new(width, height, server)?,
hdr_scene_framebuffer,
ldr_scene_framebuffer,
ldr_temp_framebuffer: [
make_ldr_temp_frame_buffer(server, width, height, depth_stencil.clone())?,
make_ldr_temp_frame_buffer(server, width, height, depth_stencil.clone())?,
],
statistics: Default::default(),
})
}
fn copy_depth_stencil_to_scene_framebuffer(&mut self) {
self.gbuffer.framebuffer().blit_to(
&self.hdr_scene_framebuffer,
0,
0,
self.gbuffer.width,
self.gbuffer.height,
0,
0,
self.gbuffer.width,
self.gbuffer.height,
false,
true,
true,
);
}
pub fn hdr_scene_frame_texture(&self) -> &GpuTexture {
&self.hdr_scene_framebuffer.color_attachments()[0].texture
}
pub fn ldr_scene_frame_texture(&self) -> &GpuTexture {
&self.ldr_scene_framebuffer.color_attachments()[0].texture
}
pub fn ldr_temp_frame_texture(&self, i: usize) -> &GpuTexture {
&self.ldr_temp_framebuffer[i].color_attachments()[0].texture
}
pub fn set_quality_settings(&mut self, settings: &QualitySettings) {
self.ssao_renderer.set_radius(settings.ssao_radius);
}
}
pub fn make_viewport_matrix(viewport: Rect<i32>) -> Matrix4<f32> {
Matrix4::new_orthographic(
0.0,
viewport.w() as f32,
viewport.h() as f32,
0.0,
-1.0,
1.0,
) * Matrix4::new_nonuniform_scaling(&Vector3::new(
viewport.w() as f32,
viewport.h() as f32,
0.0,
))
}
pub struct Renderer {
backbuffer: GpuFrameBuffer,
scene_render_passes: Vec<Rc<RefCell<dyn SceneRenderPass>>>,
deferred_light_renderer: DeferredLightRenderer,
pub renderer_resources: RendererResources,
pub ui_renderer: UiRenderer,
statistics: Statistics,
frame_size: (u32, u32),
quality_settings: QualitySettings,
pub debug_renderer: DebugRenderer,
pub screen_space_debug_renderer: DebugRenderer,
pub scene_data_map: FxHashMap<Handle<Scene>, SceneRenderData>,
backbuffer_clear_color: Color,
pub texture_cache: TextureCache,
pub uniform_buffer_cache: UniformBufferCache,
shader_cache: ShaderCache,
geometry_cache: GeometryCache,
fxaa_renderer: FxaaRenderer,
texture_event_receiver: Receiver<ResourceEvent>,
shader_event_receiver: Receiver<ResourceEvent>,
pub ui_frame_buffers: FxHashMap<u64, GpuFrameBuffer>,
uniform_memory_allocator: UniformMemoryAllocator,
pub dynamic_surface_cache: DynamicSurfaceCache,
pub visibility_cache: VisibilityCache,
pub server: SharedGraphicsServer,
}
fn make_ui_frame_buffer(
frame_size: Vector2<f32>,
server: &dyn GraphicsServer,
pixel_kind: PixelKind,
) -> Result<GpuFrameBuffer, FrameworkError> {
let color_texture = server.create_texture(GpuTextureDescriptor {
name: "UiFbTexture",
kind: GpuTextureKind::Rectangle {
width: frame_size.x as usize,
height: frame_size.y as usize,
},
pixel_kind,
..Default::default()
})?;
let depth_stencil = server.create_2d_render_target(
"UiDepthStencil",
PixelKind::D24S8,
frame_size.x as usize,
frame_size.y as usize,
)?;
server.create_frame_buffer(
Some(Attachment::depth_stencil(depth_stencil)),
vec![Attachment::color(color_texture)],
)
}
pub struct SceneRenderPassContext<'a, 'b> {
pub elapsed_time: f32,
pub server: &'a dyn GraphicsServer,
pub texture_cache: &'a mut TextureCache,
pub geometry_cache: &'a mut GeometryCache,
pub shader_cache: &'a mut ShaderCache,
pub bundle_storage: &'a RenderDataBundleStorage,
pub quality_settings: &'a QualitySettings,
pub framebuffer: &'a GpuFrameBuffer,
pub scene: &'b Scene,
pub observer: &'b Observer,
pub scene_handle: Handle<Scene>,
pub renderer_resources: &'a RendererResources,
pub depth_texture: &'a GpuTexture,
pub normal_texture: &'a GpuTexture,
pub ambient_texture: &'a GpuTexture,
pub ui_renderer: &'a mut UiRenderer,
pub uniform_buffer_cache: &'a mut UniformBufferCache,
pub uniform_memory_allocator: &'a mut UniformMemoryAllocator,
pub dynamic_surface_cache: &'a mut DynamicSurfaceCache,
pub resource_manager: &'a ResourceManager,
}
pub trait SceneRenderPass {
fn on_hdr_render(
&mut self,
_ctx: SceneRenderPassContext,
) -> Result<RenderPassStatistics, FrameworkError> {
Ok(RenderPassStatistics::default())
}
fn on_ldr_render(
&mut self,
_ctx: SceneRenderPassContext,
) -> Result<RenderPassStatistics, FrameworkError> {
Ok(RenderPassStatistics::default())
}
fn source_type_id(&self) -> TypeId;
}
fn blit_pixels(
uniform_buffer_cache: &mut UniformBufferCache,
framebuffer: &GpuFrameBuffer,
texture: &GpuTexture,
blit_shader: &RenderPassContainer,
viewport: Rect<i32>,
renderer_resources: &RendererResources,
) -> Result<DrawCallStatistics, FrameworkError> {
let wvp = make_viewport_matrix(viewport);
let properties = PropertyGroup::from([property("worldViewProjection", &wvp)]);
let material = RenderMaterial::from([
binding(
"diffuseTexture",
(texture, &renderer_resources.linear_clamp_sampler),
),
binding("properties", &properties),
]);
blit_shader.run_pass(
1,
&ImmutableString::new("Primary"),
framebuffer,
&renderer_resources.quad,
viewport,
&material,
uniform_buffer_cache,
Default::default(),
None,
)
}
fn render_target_size(
render_target: &TextureResource,
) -> Result<(Vector2<f32>, FrameTextureKind), FrameworkError> {
render_target
.data_ref()
.as_loaded_ref()
.and_then(|rt| match rt.kind() {
TextureKind::Rectangle { width, height } => Some((
Vector2::new(width as f32, height as f32),
FrameTextureKind::Rectangle,
)),
TextureKind::Cube { size } => Some((
Vector2::new(size as f32, size as f32),
FrameTextureKind::Cube,
)),
_ => None,
})
.ok_or_else(|| {
FrameworkError::Custom(
"Render target must be a valid rectangle or cube texture!".to_string(),
)
})
}
impl Renderer {
pub fn new(
server: Rc<dyn GraphicsServer>,
frame_size: (u32, u32),
resource_manager: &ResourceManager,
) -> Result<Self, EngineError> {
let settings = QualitySettings::default();
let (texture_event_sender, texture_event_receiver) = std::sync::mpsc::channel();
resource_manager
.state()
.event_broadcaster
.add(texture_event_sender);
let (shader_event_sender, shader_event_receiver) = std::sync::mpsc::channel();
resource_manager
.state()
.event_broadcaster
.add(shader_event_sender);
let caps = server.capabilities();
Log::info(format!("Graphics Server Capabilities\n{caps:?}",));
let shader_cache = ShaderCache::default();
let one_megabyte = 1024 * 1024;
let uniform_memory_allocator = UniformMemoryAllocator::new(
caps.max_uniform_block_size.min(one_megabyte),
caps.uniform_buffer_offset_alignment,
);
Ok(Self {
backbuffer: server.back_buffer(),
frame_size,
deferred_light_renderer: DeferredLightRenderer::new(&*server, &settings)?,
renderer_resources: RendererResources::new(&*server)?,
ui_renderer: UiRenderer::new(&*server)?,
quality_settings: settings,
debug_renderer: DebugRenderer::new(&*server)?,
screen_space_debug_renderer: DebugRenderer::new(&*server)?,
scene_data_map: Default::default(),
backbuffer_clear_color: Color::BLACK,
texture_cache: Default::default(),
geometry_cache: Default::default(),
ui_frame_buffers: Default::default(),
fxaa_renderer: FxaaRenderer::default(),
statistics: Statistics::default(),
shader_event_receiver,
texture_event_receiver,
shader_cache,
scene_render_passes: Default::default(),
uniform_buffer_cache: UniformBufferCache::new(server.clone()),
server,
visibility_cache: Default::default(),
uniform_memory_allocator,
dynamic_surface_cache: DynamicSurfaceCache::new(),
})
}
pub fn add_render_pass(&mut self, pass: Rc<RefCell<dyn SceneRenderPass>>) {
self.scene_render_passes.push(pass);
}
pub fn remove_render_pass(&mut self, pass: Rc<RefCell<dyn SceneRenderPass>>) {
if let Some(index) = self
.scene_render_passes
.iter()
.position(|p| Rc::ptr_eq(p, &pass))
{
self.scene_render_passes.remove(index);
}
}
pub fn render_passes(&self) -> &[Rc<RefCell<dyn SceneRenderPass>>] {
&self.scene_render_passes
}
pub fn clear_render_passes(&mut self) {
self.scene_render_passes.clear()
}
pub fn get_statistics(&self) -> Statistics {
self.statistics
}
pub fn unload_texture(&mut self, texture: &TextureResource) {
self.texture_cache.unload(texture)
}
pub fn set_backbuffer_clear_color(&mut self, color: Color) {
self.backbuffer_clear_color = color;
}
pub fn graphics_server(&self) -> &dyn GraphicsServer {
&*self.server
}
pub(crate) fn set_frame_size(&mut self, new_size: (u32, u32)) -> Result<(), FrameworkError> {
self.frame_size.0 = new_size.0.max(1);
self.frame_size.1 = new_size.1.max(1);
self.graphics_server().set_frame_size(new_size);
Ok(())
}
pub fn get_frame_size(&self) -> (u32, u32) {
self.frame_size
}
pub fn get_frame_bounds(&self) -> Vector2<f32> {
Vector2::new(self.frame_size.0 as f32, self.frame_size.1 as f32)
}
pub fn set_quality_settings(
&mut self,
settings: &QualitySettings,
) -> Result<(), FrameworkError> {
self.quality_settings = *settings;
for data in self.scene_data_map.values_mut() {
data.set_quality_settings(settings);
}
self.deferred_light_renderer
.set_quality_settings(&*self.server, settings)
}
pub fn get_quality_settings(&self) -> QualitySettings {
self.quality_settings
}
pub fn flush(&mut self) {
self.texture_cache.clear();
self.geometry_cache.clear();
}
pub fn render_ui(&mut self, render_info: UiRenderInfo) -> Result<(), FrameworkError> {
let _debug_scope = self.server.begin_scope(&format!("UI {:p}", render_info.ui));
let (frame_buffer, rt_size) = if let Some(render_target) =
render_info.render_target.as_ref()
{
let (rt_size, rt_pixel_kind) = render_target
.data_ref()
.as_loaded_ref()
.and_then(|rt| {
rt.kind()
.rectangle_size()
.map(|s| (s.cast::<f32>(), convert_pixel_kind(rt.pixel_kind())))
})
.ok_or_else(|| FrameworkError::Custom("invalid render target state".to_string()))?;
let frame_buffer = match self.ui_frame_buffers.entry(render_target.key()) {
Entry::Occupied(entry) => {
let frame_buffer = entry.into_mut();
let frame = frame_buffer.color_attachments().first().unwrap();
let color_texture_kind = frame.texture.kind();
if let GpuTextureKind::Rectangle { width, height } = color_texture_kind {
if width != rt_size.x as usize
|| height != rt_size.y as usize
|| frame.texture.pixel_kind() != rt_pixel_kind
{
*frame_buffer =
make_ui_frame_buffer(rt_size, &*self.server, rt_pixel_kind)?;
}
} else {
return Err(FrameworkError::Custom(
"ui can be rendered only in rectangle texture!".to_string(),
));
}
frame_buffer
}
Entry::Vacant(entry) => {
entry.insert(make_ui_frame_buffer(rt_size, &*self.server, rt_pixel_kind)?)
}
};
let viewport = Rect::new(0, 0, rt_size.x as i32, rt_size.y as i32);
frame_buffer.clear(viewport, Some(render_info.clear_color), Some(0.0), Some(0));
(frame_buffer, rt_size)
} else {
(
&mut self.backbuffer,
Vector2::new(self.frame_size.0 as f32, self.frame_size.1 as f32),
)
};
self.statistics += self.ui_renderer.render(UiRenderContext {
server: &*self.server,
viewport: Rect::new(0, 0, rt_size.x as i32, rt_size.y as i32),
frame_buffer,
frame_width: rt_size.x,
frame_height: rt_size.y,
drawing_context: &render_info.ui.drawing_context,
renderer_resources: &self.renderer_resources,
texture_cache: &mut self.texture_cache,
uniform_buffer_cache: &mut self.uniform_buffer_cache,
render_pass_cache: &mut self.shader_cache,
uniform_memory_allocator: &mut self.uniform_memory_allocator,
resource_manager: render_info.resource_manager,
})?;
if let Some(render_target) = render_info.render_target.as_ref() {
self.texture_cache.try_register(
&*self.server,
render_target,
frame_buffer
.color_attachments()
.first()
.unwrap()
.texture
.clone(),
)?;
}
Ok(())
}
fn update_texture_cache(&mut self, resource_manager: &ResourceManager, dt: f32) {
const THROUGHPUT: usize = 5;
let mut uploaded = 0;
while let Ok(event) = self.texture_event_receiver.try_recv() {
if let ResourceEvent::Loaded(resource) | ResourceEvent::Reloaded(resource) = event {
if let Some(texture) = resource.try_cast::<Texture>() {
match self
.texture_cache
.upload(&*self.server, resource_manager, &texture)
{
Ok(_) => {
uploaded += 1;
if uploaded >= THROUGHPUT {
break;
}
}
Err(e) => {
Log::writeln(
MessageKind::Error,
format!("Failed to upload texture to GPU. Reason: {e:?}"),
);
}
}
}
}
}
self.texture_cache.update(dt);
}
fn update_shader_cache(&mut self, dt: f32) {
while let Ok(event) = self.shader_event_receiver.try_recv() {
if let ResourceEvent::Loaded(resource) | ResourceEvent::Reloaded(resource) = event {
if let Some(shader) = resource.try_cast::<Shader>() {
self.shader_cache.remove(&shader);
let _ = self.shader_cache.get(&*self.server, &shader);
}
}
}
self.shader_cache.update(dt)
}
pub fn update_caches(&mut self, resource_manager: &ResourceManager, dt: f32) {
self.update_texture_cache(resource_manager, dt);
self.update_shader_cache(dt);
self.geometry_cache.update(dt);
}
fn render_scene_observer(
&mut self,
observer: &Observer,
scene_handle: Handle<Scene>,
scene: &Scene,
elapsed_time: f32,
dt: f32,
resource_manager: &ResourceManager,
need_recalculate_convolution: bool,
) -> Result<&mut RenderDataContainer, FrameworkError> {
let server = &*self.server;
let scene_render_data = self.scene_data_map.get_mut(&scene_handle).ok_or_else(|| {
FrameworkError::Custom(format!(
"No associated render data for {scene_handle} scene!"
))
})?;
let render_data = if let Some(render_target) = observer.render_target.as_ref() {
let (rt_size, final_frame_texture) = render_target_size(render_target)?;
let observer_render_data = match scene_render_data.camera_data.entry(observer.handle) {
Entry::Occupied(entry) => {
let observer_render_data = entry.into_mut();
recreate_render_data_if_needed(
scene_handle,
server,
observer_render_data,
rt_size,
final_frame_texture,
)?;
observer_render_data
}
Entry::Vacant(entry) => {
let render_data = entry.insert(RenderDataContainer::new(
server,
rt_size,
final_frame_texture,
)?);
info!(
"A new associated scene rendering data was created for observer {}!",
observer.handle
);
render_data
}
};
if let Some(probe_data) = observer.reflection_probe_data.as_ref() {
observer_render_data.ldr_scene_framebuffer.set_cubemap_face(
0,
probe_data.cube_map_face,
0,
);
}
self.texture_cache.try_register(
server,
render_target,
observer_render_data.ldr_scene_frame_texture().clone(),
)?;
observer_render_data
} else {
&mut scene_render_data.scene_data
};
render_data.need_recalculate_convolution |= need_recalculate_convolution;
let visibility_cache = self
.visibility_cache
.get_or_register(&scene.graph, observer.handle);
let mut bundle_storage = RenderDataBundleStorage::from_graph(
&scene.graph,
observer.render_mask,
elapsed_time,
&observer.position,
GBUFFER_PASS_NAME.clone(),
RenderDataBundleStorageOptions {
collect_lights: true,
},
&mut self.dynamic_surface_cache,
);
if observer.reflection_probe_data.is_some() {
bundle_storage.environment_map = None;
}
server.set_polygon_fill_mode(
PolygonFace::FrontAndBack,
scene.rendering_options.polygon_rasterization_mode,
);
render_data.statistics += render_data.gbuffer.fill(GBufferRenderContext {
server,
observer,
geom_cache: &mut self.geometry_cache,
bundle_storage: &bundle_storage,
texture_cache: &mut self.texture_cache,
shader_cache: &mut self.shader_cache,
quality_settings: &self.quality_settings,
renderer_resources: &self.renderer_resources,
graph: &scene.graph,
uniform_buffer_cache: &mut self.uniform_buffer_cache,
uniform_memory_allocator: &mut self.uniform_memory_allocator,
screen_space_debug_renderer: &mut self.screen_space_debug_renderer,
resource_manager,
})?;
server.set_polygon_fill_mode(PolygonFace::FrontAndBack, PolygonFillMode::Fill);
render_data.copy_depth_stencil_to_scene_framebuffer();
render_data.hdr_scene_framebuffer.clear(
observer.viewport,
Some(
scene
.rendering_options
.clear_color
.unwrap_or(self.backbuffer_clear_color),
),
None, Some(0),
);
let (pass_stats, light_stats) =
self.deferred_light_renderer
.render(DeferredRendererContext {
elapsed_time,
server,
scene,
observer,
gbuffer: &mut render_data.gbuffer,
ambient_color: match observer.reflection_probe_data.as_ref() {
None => scene.rendering_options.ambient_lighting_color,
Some(probe_data) => probe_data.ambient_lighting_color,
},
environment_lighting_source: match observer.reflection_probe_data.as_ref() {
None => scene.rendering_options.environment_lighting_source,
Some(probe_data) => probe_data.environment_lighting_source,
},
render_data_bundle: &bundle_storage,
settings: &self.quality_settings,
textures: &mut self.texture_cache,
geometry_cache: &mut self.geometry_cache,
frame_buffer: &render_data.hdr_scene_framebuffer,
shader_cache: &mut self.shader_cache,
renderer_resources: &self.renderer_resources,
uniform_buffer_cache: &mut self.uniform_buffer_cache,
visibility_cache,
uniform_memory_allocator: &mut self.uniform_memory_allocator,
dynamic_surface_cache: &mut self.dynamic_surface_cache,
ssao_renderer: &render_data.ssao_renderer,
resource_manager,
environment_map_specular_convolution: &mut render_data
.environment_map_specular_convolution,
environment_map_irradiance_convolution: &render_data
.environment_map_irradiance_convolution,
need_recalculate_convolution: &mut render_data.need_recalculate_convolution,
})?;
render_data.statistics += light_stats;
render_data.statistics += pass_stats;
let depth = render_data.gbuffer.depth();
{
let _debug_scope = server.begin_scope("ForwardRendering");
render_data.statistics += bundle_storage.render_to_frame_buffer(
server,
&mut self.geometry_cache,
&mut self.shader_cache,
|bundle| bundle.render_path == RenderPath::Forward,
|_| true,
BundleRenderContext {
texture_cache: &mut self.texture_cache,
render_pass_name: &ImmutableString::new("Forward"),
frame_buffer: &render_data.hdr_scene_framebuffer,
viewport: observer.viewport,
uniform_memory_allocator: &mut self.uniform_memory_allocator,
resource_manager,
use_pom: self.quality_settings.use_parallax_mapping,
light_position: &Default::default(),
renderer_resources: &self.renderer_resources,
ambient_light: scene.rendering_options.ambient_lighting_color,
scene_depth: Some(depth),
},
)?;
}
for render_pass in self.scene_render_passes.iter() {
let _debug_scope = server.begin_scope(&format!(
"UserRenderPass::on_hdr_render {:p}",
render_pass.as_ptr()
));
render_data.statistics +=
render_pass
.borrow_mut()
.on_hdr_render(SceneRenderPassContext {
elapsed_time,
server,
texture_cache: &mut self.texture_cache,
geometry_cache: &mut self.geometry_cache,
shader_cache: &mut self.shader_cache,
quality_settings: &self.quality_settings,
bundle_storage: &bundle_storage,
scene,
observer,
scene_handle,
renderer_resources: &self.renderer_resources,
depth_texture: render_data.gbuffer.depth(),
normal_texture: render_data.gbuffer.normal_texture(),
ambient_texture: render_data.gbuffer.ambient_texture(),
framebuffer: &render_data.hdr_scene_framebuffer,
ui_renderer: &mut self.ui_renderer,
uniform_buffer_cache: &mut self.uniform_buffer_cache,
uniform_memory_allocator: &mut self.uniform_memory_allocator,
dynamic_surface_cache: &mut self.dynamic_surface_cache,
resource_manager,
})?;
}
let mut dest_buf = 0;
let mut src_buf = 1;
render_data.statistics += render_data.hdr_renderer.render(HdrRendererArgs {
server,
hdr_scene_frame: render_data.hdr_scene_frame_texture(),
ldr_framebuffer: &render_data.ldr_temp_framebuffer[dest_buf],
viewport: observer.viewport,
speed: observer.hdr_adaptation_speed * dt,
exposure: observer.exposure,
color_grading_lut: observer.color_grading_lut.as_ref(),
use_color_grading: observer.color_grading_enabled,
texture_cache: &mut self.texture_cache,
uniform_buffer_cache: &mut self.uniform_buffer_cache,
renderer_resources: &self.renderer_resources,
resource_manager,
settings: &self.quality_settings,
})?;
std::mem::swap(&mut dest_buf, &mut src_buf);
if self.quality_settings.fxaa {
render_data.statistics += self.fxaa_renderer.render(
server,
observer.viewport,
render_data.ldr_temp_frame_texture(src_buf),
&render_data.ldr_temp_framebuffer[dest_buf],
&mut self.uniform_buffer_cache,
&self.renderer_resources,
)?;
std::mem::swap(&mut dest_buf, &mut src_buf);
}
render_data.statistics += blit_pixels(
&mut self.uniform_buffer_cache,
&render_data.ldr_scene_framebuffer,
render_data.ldr_temp_frame_texture(src_buf),
&self.renderer_resources.shaders.blit,
observer.viewport,
&self.renderer_resources,
)?;
self.debug_renderer.set_lines(&scene.drawing_context.lines);
render_data.statistics += self.debug_renderer.render(
server,
&mut self.uniform_buffer_cache,
observer.viewport,
&render_data.ldr_scene_framebuffer,
observer.position.view_projection_matrix,
&self.renderer_resources,
)?;
for render_pass in self.scene_render_passes.iter() {
let _debug_scope = server.begin_scope(&format!(
"UserRenderPass::on_ldr_render {:p}",
render_pass.as_ptr()
));
render_data.statistics +=
render_pass
.borrow_mut()
.on_ldr_render(SceneRenderPassContext {
elapsed_time,
server,
texture_cache: &mut self.texture_cache,
geometry_cache: &mut self.geometry_cache,
shader_cache: &mut self.shader_cache,
quality_settings: &self.quality_settings,
bundle_storage: &bundle_storage,
scene,
observer,
scene_handle,
renderer_resources: &self.renderer_resources,
depth_texture: render_data.gbuffer.depth(),
normal_texture: render_data.gbuffer.normal_texture(),
ambient_texture: render_data.gbuffer.ambient_texture(),
framebuffer: &render_data.ldr_scene_framebuffer,
ui_renderer: &mut self.ui_renderer,
uniform_buffer_cache: &mut self.uniform_buffer_cache,
uniform_memory_allocator: &mut self.uniform_memory_allocator,
dynamic_surface_cache: &mut self.dynamic_surface_cache,
resource_manager,
})?;
}
Ok(render_data)
}
pub fn render_scene(
&mut self,
scene_handle: Handle<Scene>,
scene: &Scene,
elapsed_time: f32,
dt: f32,
resource_manager: &ResourceManager,
) -> Result<&SceneRenderData, FrameworkError> {
let graph = &scene.graph;
let _debug_scope = self.server.begin_scope(&format!("Scene {:p}", scene));
let backbuffer_width = self.frame_size.0 as f32;
let backbuffer_height = self.frame_size.1 as f32;
let window_viewport = Rect::new(0, 0, self.frame_size.0 as i32, self.frame_size.1 as i32);
let frame_size = scene
.rendering_options
.render_target
.as_ref()
.map_or_else(
|| Vector2::new(backbuffer_width, backbuffer_height),
|rt| {
if let TextureKind::Rectangle { width, height } = rt.data_ref().kind() {
Vector2::new(width as f32, height as f32)
} else {
panic!("only rectangle textures can be used as render target!")
}
},
)
.sup(&Vector2::new(1.0, 1.0));
let scene_render_data = match self.scene_data_map.entry(scene_handle) {
Entry::Occupied(entry) => {
let render_data = entry.into_mut();
recreate_render_data_if_needed(
scene_handle,
&*self.server,
&mut render_data.scene_data,
frame_size,
FrameTextureKind::Rectangle,
)?;
render_data
}
Entry::Vacant(entry) => {
let render_data = entry.insert(SceneRenderData::new(
&*self.server,
frame_size,
FrameTextureKind::Rectangle,
)?);
info!(
"A new associated scene rendering data was created for scene {scene_handle}!"
);
render_data
}
};
let pipeline_stats = &self.server.pipeline_statistics();
scene_render_data.scene_data.statistics = Default::default();
if let Some(rt) = scene.rendering_options.render_target.clone() {
self.texture_cache.try_register(
&*self.server,
&rt,
scene_render_data
.scene_data
.ldr_scene_frame_texture()
.clone(),
)?;
}
scene_render_data
.camera_data
.retain(|h, _| graph.is_valid_handle(*h));
let observers = ObserversCollection::from_scene(scene, frame_size);
let mut need_recalculate_convolution = false;
for observer in observers.reflection_probes.iter() {
let _debug_scope = self
.server
.begin_scope(&format!("Reflection Probe {:p}", observer));
self.render_scene_observer(
observer,
scene_handle,
scene,
elapsed_time,
dt,
resource_manager,
false,
)?;
need_recalculate_convolution = true;
}
for observer in observers.cameras.iter() {
let _debug_scope = self.server.begin_scope(&format!("Camera {:p}", observer));
self.render_scene_observer(
observer,
scene_handle,
scene,
elapsed_time,
dt,
resource_manager,
need_recalculate_convolution,
)?;
}
self.visibility_cache.update(graph);
let scene_render_data = self.scene_data_map.get_mut(&scene_handle).unwrap();
if scene.rendering_options.render_target.is_none() {
scene_render_data.scene_data.statistics += blit_pixels(
&mut self.uniform_buffer_cache,
&self.backbuffer,
scene_render_data.scene_data.ldr_scene_frame_texture(),
&self.renderer_resources.shaders.blit,
window_viewport,
&self.renderer_resources,
)?;
}
self.statistics += scene_render_data.scene_data.statistics;
scene_render_data.scene_data.statistics.pipeline =
self.server.pipeline_statistics() - *pipeline_stats;
Ok(scene_render_data)
}
fn render_frame<'a>(
&mut self,
scenes: &SceneContainer,
elapsed_time: f32,
resource_manager: &ResourceManager,
ui_render_info: impl Iterator<Item = UiRenderInfo<'a>>,
) -> Result<(), FrameworkError> {
if self.frame_size.0 == 0 || self.frame_size.1 == 0 {
return Ok(());
}
self.uniform_buffer_cache.mark_all_unused();
self.uniform_memory_allocator.clear();
self.dynamic_surface_cache.clear();
self.scene_data_map
.retain(|h, _| scenes.is_valid_handle(*h));
self.server.invalidate_resource_bindings_cache();
let dt = self.statistics.capped_frame_time;
self.statistics.begin_frame();
let window_viewport = Rect::new(0, 0, self.frame_size.0 as i32, self.frame_size.1 as i32);
self.backbuffer.clear(
window_viewport,
Some(self.backbuffer_clear_color),
Some(1.0),
Some(0),
);
let backbuffer_width = self.frame_size.0 as f32;
let backbuffer_height = self.frame_size.1 as f32;
for (scene_handle, scene) in scenes.pair_iter().filter(|(_, s)| *s.enabled) {
self.render_scene(scene_handle, scene, elapsed_time, dt, resource_manager)?;
}
self.graphics_server()
.set_polygon_fill_mode(PolygonFace::FrontAndBack, PolygonFillMode::Fill);
for info in ui_render_info {
self.render_ui(info)?;
}
let screen_matrix =
Matrix4::new_orthographic(0.0, backbuffer_width, backbuffer_height, 0.0, -1.0, 1.0);
self.screen_space_debug_renderer.render(
&*self.server,
&mut self.uniform_buffer_cache,
window_viewport,
&self.backbuffer,
screen_matrix,
&self.renderer_resources,
)?;
self.statistics.geometry_cache_size = self.geometry_cache.alive_count();
self.statistics.texture_cache_size = self.texture_cache.alive_count();
self.statistics.shader_cache_size = self.shader_cache.alive_count();
self.statistics.uniform_buffer_cache_size = self.uniform_buffer_cache.alive_count();
Ok(())
}
pub(crate) fn render_and_swap_buffers<'a>(
&mut self,
scenes: &SceneContainer,
elapsed_time: f32,
ui_info: impl Iterator<Item = UiRenderInfo<'a>>,
window: &Window,
resource_manager: &ResourceManager,
) -> Result<(), FrameworkError> {
self.render_frame(scenes, elapsed_time, resource_manager, ui_info)?;
self.statistics.end_frame();
window.pre_present_notify();
self.graphics_server().swap_buffers()?;
self.statistics.finalize();
self.statistics.pipeline = self.server.pipeline_statistics();
Ok(())
}
}