From bf4a9a21339c04c81d5bd34a054ad1633a3525ed Mon Sep 17 00:00:00 2001 From: Dmitry Stepanov Date: Tue, 10 Sep 2024 14:20:34 +0300 Subject: [PATCH] minimized amount of changing shader uniforms per draw call - only change those that actually change, material properties now set once per bundle - improves performance by ~5-10% depending on video driver - reduced boilerplate code by hiding it in `RenderDataBundle::render_to_frame_buffer` helper method --- editor/src/highlight.rs | 101 ++--- fyrox-impl/src/renderer/bundle.rs | 357 +++++++++++++++++- fyrox-impl/src/renderer/forward_renderer.rs | 97 ++--- .../src/renderer/framework/gpu_program.rs | 16 + fyrox-impl/src/renderer/gbuffer/mod.rs | 109 ++---- fyrox-impl/src/renderer/mod.rs | 249 +----------- fyrox-impl/src/renderer/shadow/csm.rs | 115 ++---- fyrox-impl/src/renderer/shadow/point.rs | 99 ++--- fyrox-impl/src/renderer/shadow/spot.rs | 110 ++---- 9 files changed, 561 insertions(+), 692 deletions(-) diff --git a/editor/src/highlight.rs b/editor/src/highlight.rs index 167300b1c..f06297cb9 100644 --- a/editor/src/highlight.rs +++ b/editor/src/highlight.rs @@ -30,8 +30,7 @@ use crate::{ fxhash::FxHashSet, graph::{BaseSceneGraph, SceneGraph}, renderer::{ - apply_material, - bundle::{RenderContext, RenderDataBundleStorage}, + bundle::{BundleRenderContext, RenderContext, RenderDataBundleStorage}, framework::{ error::FrameworkError, framebuffer::{ @@ -45,7 +44,7 @@ use crate::{ }, state::{BlendFactor, BlendFunc, PipelineState}, }, - MaterialContext, RenderPassStatistics, SceneRenderPass, SceneRenderPassContext, + RenderPassStatistics, SceneRenderPass, SceneRenderPassContext, }, scene::{mesh::surface::SurfaceData, node::Node, Scene}, }, @@ -256,74 +255,34 @@ impl SceneRenderPass for HighlightRenderPass { let camera_side = inv_view.side(); for bundle in render_bundle_storage.bundles.iter() { - let mut material_state = bundle.material.state(); - - let Some(material) = material_state.data() else { - continue; - }; - - let Some(geometry) = - ctx.geometry_cache - .get(ctx.pipeline_state, &bundle.data, bundle.time_to_live) - else { - continue; - }; - - let blend_shapes_storage = bundle - .data - .data_ref() - .blend_shapes_container - .as_ref() - .and_then(|c| c.blend_shape_storage.clone()); - - let Some(render_pass) = ctx - .shader_cache - .get(ctx.pipeline_state, material.shader()) - .and_then(|shader_set| shader_set.render_passes.get(&render_pass_name)) - else { - continue; - }; - - for instance in bundle.instances.iter() { - self.framebuffer.draw( - geometry, - ctx.pipeline_state, - ctx.viewport, - &render_pass.program, - &render_pass.draw_params, - instance.element_range, - |mut program_binding| { - apply_material(MaterialContext { - material, - program_binding: &mut program_binding, - texture_cache: ctx.texture_cache, - world_matrix: &instance.world_transform, - view_projection_matrix: &view_projection, - wvp_matrix: &(view_projection * instance.world_transform), - bone_matrices: &instance.bone_matrices, - use_skeletal_animation: !instance.bone_matrices.is_empty(), - camera_position: &ctx.camera.global_position(), - camera_up_vector: &camera_up, - camera_side_vector: &camera_side, - z_near: ctx.camera.projection().z_near(), - z_far: ctx.camera.projection().z_far(), - use_pom: false, - light_position: &Default::default(), - blend_shapes_storage: blend_shapes_storage.as_ref(), - blend_shapes_weights: &instance.blend_shapes_weights, - normal_dummy: &ctx.normal_dummy, - white_dummy: &ctx.white_dummy, - black_dummy: &ctx.black_dummy, - volume_dummy: &ctx.volume_dummy, - matrix_storage: ctx.matrix_storage, - persistent_identifier: instance.persistent_identifier, - light_data: None, - ambient_light: Default::default(), - scene_depth: Some(&ctx.depth_texture), - }); - }, - )?; - } + bundle.render_to_frame_buffer( + ctx.pipeline_state, + ctx.geometry_cache, + ctx.shader_cache, + |_| true, + BundleRenderContext { + texture_cache: ctx.texture_cache, + render_pass_name: &render_pass_name, + frame_buffer: &self.framebuffer, + view_projection_matrix: &view_projection, + camera_position: &ctx.camera.global_position(), + camera_up_vector: &camera_up, + camera_side_vector: &camera_side, + z_near: ctx.camera.projection().z_near(), + z_far: ctx.camera.projection().z_far(), + use_pom: false, + light_position: &Default::default(), + normal_dummy: &ctx.normal_dummy, + white_dummy: &ctx.white_dummy, + black_dummy: &ctx.black_dummy, + volume_dummy: &ctx.volume_dummy, + matrix_storage: ctx.matrix_storage, + light_data: None, + ambient_light: Default::default(), + scene_depth: Some(&ctx.depth_texture), + viewport: ctx.viewport, + }, + )?; } } diff --git a/fyrox-impl/src/renderer/bundle.rs b/fyrox-impl/src/renderer/bundle.rs index 701e88fd4..5e16915c5 100644 --- a/fyrox-impl/src/renderer/bundle.rs +++ b/fyrox-impl/src/renderer/bundle.rs @@ -24,13 +24,27 @@ use crate::{ asset::untyped::ResourceKind, core::{ algebra::{Matrix4, Vector3}, - math::frustum::Frustum, + color::Color, + math::{frustum::Frustum, Rect}, pool::Handle, sstorage::ImmutableString, }, graph::BaseSceneGraph, - material::MaterialResource, - renderer::{cache::TimeToLive, framework::geometry_buffer::ElementRange}, + material::{shader::SamplerFallback, Material, MaterialResource, PropertyValue}, + renderer::{ + cache::{geometry::GeometryCache, shader::ShaderCache, texture::TextureCache, TimeToLive}, + framework::{ + error::FrameworkError, + framebuffer::FrameBuffer, + geometry_buffer::ElementRange, + gpu_program::{BuiltInUniform, GpuProgramBinding}, + gpu_texture::GpuTexture, + state::PipelineState, + }, + storage::MatrixStorageCache, + LightData, RenderPassStatistics, + }, + resource::texture::TextureResource, scene::{ graph::Graph, mesh::{ @@ -46,9 +60,11 @@ use crate::{ }; use fxhash::{FxBuildHasher, FxHashMap, FxHasher}; use std::{ + cell::RefCell, collections::hash_map::DefaultHasher, fmt::{Debug, Formatter}, hash::{Hash, Hasher}, + rc::Rc, }; /// Observer info contains all the data, that describes an observer. It could be a real camera, light source's @@ -107,6 +123,266 @@ impl<'a> RenderContext<'a> { } } +#[allow(missing_docs)] // TODO +pub struct InstanceContext<'a> { + pub matrix_storage: &'a mut MatrixStorageCache, + pub persistent_identifier: PersistentIdentifier, + + pub world_matrix: &'a Matrix4, + pub wvp_matrix: &'a Matrix4, + pub bone_matrices: &'a [Matrix4], + pub use_skeletal_animation: bool, + pub blend_shapes_weights: &'a [f32], +} + +impl<'a> InstanceContext<'a> { + #[allow(missing_docs)] // TODO + pub fn apply_to(self, program_binding: &mut GpuProgramBinding) { + let InstanceContext { + matrix_storage, + persistent_identifier, + world_matrix, + wvp_matrix, + bone_matrices, + use_skeletal_animation, + blend_shapes_weights, + } = self; + + let built_in_uniforms = &program_binding.program.built_in_uniform_locations; + + // Apply values for built-in uniforms. + if let Some(location) = &built_in_uniforms[BuiltInUniform::WorldMatrix as usize] { + program_binding.set_matrix4(location, world_matrix); + } + if let Some(location) = + &built_in_uniforms[BuiltInUniform::WorldViewProjectionMatrix as usize] + { + program_binding.set_matrix4(location, wvp_matrix); + } + if let Some(location) = &built_in_uniforms[BuiltInUniform::BoneMatrices as usize] { + let active_sampler = program_binding.active_sampler(); + + let storage = matrix_storage + .try_bind_and_upload( + program_binding.state, + persistent_identifier, + bone_matrices, + active_sampler, + ) + .expect("Failed to upload bone matrices!"); + + program_binding.set_texture_to_sampler(location, storage.texture(), active_sampler); + } + if let Some(location) = &built_in_uniforms[BuiltInUniform::UseSkeletalAnimation as usize] { + program_binding.set_bool(location, use_skeletal_animation); + } + + if let Some(location) = &built_in_uniforms[BuiltInUniform::BlendShapesWeights as usize] { + program_binding.set_f32_slice(location, blend_shapes_weights); + } + if let Some(location) = &built_in_uniforms[BuiltInUniform::BlendShapesCount as usize] { + program_binding.set_i32(location, blend_shapes_weights.len() as i32); + } + } +} + +#[allow(missing_docs)] // TODO +pub struct BundleRenderContext<'a> { + pub texture_cache: &'a mut TextureCache, + pub render_pass_name: &'a ImmutableString, + pub frame_buffer: &'a FrameBuffer, + pub viewport: Rect, + pub matrix_storage: &'a mut MatrixStorageCache, + + // Built-in uniforms. + pub view_projection_matrix: &'a Matrix4, + pub use_pom: bool, + pub light_position: &'a Vector3, + pub light_data: Option<&'a LightData>, + pub ambient_light: Color, + // TODO: Add depth pre-pass to remove Option here. Current architecture allows only forward + // renderer to have access to depth buffer that is available from G-Buffer. + pub scene_depth: Option<&'a Rc>>, + + pub camera_position: &'a Vector3, + pub camera_up_vector: &'a Vector3, + pub camera_side_vector: &'a Vector3, + pub z_near: f32, + pub z_far: f32, + + // Fallback textures. + pub normal_dummy: &'a Rc>, + pub white_dummy: &'a Rc>, + pub black_dummy: &'a Rc>, + pub volume_dummy: &'a Rc>, +} + +impl<'a> BundleRenderContext<'a> { + #[allow(missing_docs)] // TODO + pub fn apply_material( + &mut self, + material: &Material, + program_binding: &mut GpuProgramBinding, + blend_shapes_storage: Option, + ) { + let built_in_uniforms = &program_binding.program.built_in_uniform_locations; + + // Apply values for built-in uniforms. + if let Some(location) = &built_in_uniforms[BuiltInUniform::ViewProjectionMatrix as usize] { + program_binding.set_matrix4(location, self.view_projection_matrix); + } + if let Some(location) = &built_in_uniforms[BuiltInUniform::CameraPosition as usize] { + program_binding.set_vector3(location, self.camera_position); + } + + if let Some(location) = &built_in_uniforms[BuiltInUniform::CameraUpVector as usize] { + program_binding.set_vector3(location, self.camera_up_vector); + } + if let Some(location) = &built_in_uniforms[BuiltInUniform::CameraSideVector as usize] { + program_binding.set_vector3(location, self.camera_side_vector); + } + if let Some(location) = &built_in_uniforms[BuiltInUniform::ZNear as usize] { + program_binding.set_f32(location, self.z_near); + } + if let Some(location) = &built_in_uniforms[BuiltInUniform::ZFar as usize] { + program_binding.set_f32(location, self.z_far); + } + + if let Some(location) = &built_in_uniforms[BuiltInUniform::SceneDepth as usize] { + if let Some(scene_depth) = self.scene_depth.as_ref() { + program_binding.set_texture(location, scene_depth); + } + } + + if let Some(location) = &built_in_uniforms[BuiltInUniform::UsePOM as usize] { + program_binding.set_bool(location, self.use_pom); + } + if let Some(location) = &built_in_uniforms[BuiltInUniform::LightPosition as usize] { + program_binding.set_vector3(location, self.light_position); + } + + if let Some(light_data) = self.light_data { + if let Some(location) = &built_in_uniforms[BuiltInUniform::LightCount as usize] { + program_binding.set_i32(location, light_data.count as i32); + } + + if let Some(location) = &built_in_uniforms[BuiltInUniform::LightsColorRadius as usize] { + program_binding.set_vector4_slice(location, &light_data.color_radius); + } + + if let Some(location) = &built_in_uniforms[BuiltInUniform::LightsPosition as usize] { + program_binding.set_vector3_slice(location, &light_data.position); + } + + if let Some(location) = &built_in_uniforms[BuiltInUniform::LightsDirection as usize] { + program_binding.set_vector3_slice(location, &light_data.direction); + } + + if let Some(location) = &built_in_uniforms[BuiltInUniform::LightsParameters as usize] { + program_binding.set_vector2_slice(location, &light_data.parameters); + } + } + + if let Some(location) = &built_in_uniforms[BuiltInUniform::AmbientLight as usize] { + program_binding.set_srgb_color(location, &self.ambient_light); + } + + if let Some(location) = &built_in_uniforms[BuiltInUniform::BlendShapesStorage as usize] { + if let Some(texture) = blend_shapes_storage + .as_ref() + .and_then(|blend_shapes_storage| { + self.texture_cache + .get(program_binding.state, blend_shapes_storage) + }) + { + program_binding.set_texture(location, texture); + } else { + program_binding.set_texture(location, self.volume_dummy); + } + } + + // Apply material properties. + for (name, value) in material.properties() { + if let Some(uniform) = program_binding.uniform_location(name) { + match value { + PropertyValue::Float(v) => { + program_binding.set_f32(&uniform, *v); + } + PropertyValue::Int(v) => { + program_binding.set_i32(&uniform, *v); + } + PropertyValue::UInt(v) => { + program_binding.set_u32(&uniform, *v); + } + PropertyValue::Vector2(v) => { + program_binding.set_vector2(&uniform, v); + } + PropertyValue::Vector3(v) => { + program_binding.set_vector3(&uniform, v); + } + PropertyValue::Vector4(v) => { + program_binding.set_vector4(&uniform, v); + } + PropertyValue::Matrix2(v) => { + program_binding.set_matrix2(&uniform, v); + } + PropertyValue::Matrix3(v) => { + program_binding.set_matrix3(&uniform, v); + } + PropertyValue::Matrix4(v) => { + program_binding.set_matrix4(&uniform, v); + } + PropertyValue::Color(v) => { + program_binding.set_srgb_color(&uniform, v); + } + PropertyValue::Bool(v) => { + program_binding.set_bool(&uniform, *v); + } + PropertyValue::Sampler { value, fallback } => { + let texture = value + .as_ref() + .and_then(|t| self.texture_cache.get(program_binding.state, t)) + .unwrap_or(match fallback { + SamplerFallback::White => self.white_dummy, + SamplerFallback::Normal => self.normal_dummy, + SamplerFallback::Black => self.black_dummy, + }); + + program_binding.set_texture(&uniform, texture); + } + PropertyValue::FloatArray(v) => { + program_binding.set_f32_slice(&uniform, v); + } + PropertyValue::IntArray(v) => { + program_binding.set_i32_slice(&uniform, v); + } + PropertyValue::UIntArray(v) => { + program_binding.set_u32_slice(&uniform, v); + } + PropertyValue::Vector2Array(v) => { + program_binding.set_vector2_slice(&uniform, v); + } + PropertyValue::Vector3Array(v) => { + program_binding.set_vector3_slice(&uniform, v); + } + PropertyValue::Vector4Array(v) => { + program_binding.set_vector4_slice(&uniform, v); + } + PropertyValue::Matrix2Array(v) => { + program_binding.set_matrix2_array(&uniform, v); + } + PropertyValue::Matrix3Array(v) => { + program_binding.set_matrix3_array(&uniform, v); + } + PropertyValue::Matrix4Array(v) => { + program_binding.set_matrix4_array(&uniform, v); + } + } + } + } + } +} + /// Persistent identifier marks drawing data, telling the renderer that the data is the same, no matter from which /// render bundle it came from. It is used by the renderer to create associated GPU resources. #[derive(Copy, Clone, Hash, Debug, PartialEq, Eq)] @@ -172,6 +448,81 @@ impl Debug for RenderDataBundle { } } +impl RenderDataBundle { + /// Draws the entire bundle to the specified frame buffer with the specified rendering environment. + pub fn render_to_frame_buffer( + &self, + state: &PipelineState, + geometry_cache: &mut GeometryCache, + shader_cache: &mut ShaderCache, + mut instance_filter: F, + mut render_context: BundleRenderContext, + ) -> Result + where + F: FnMut(&SurfaceInstanceData) -> bool, + { + let mut stats = RenderPassStatistics::default(); + + let mut material_state = self.material.state(); + + let Some(material) = material_state.data() else { + return Ok(stats); + }; + + let Some(geometry) = geometry_cache.get(state, &self.data, self.time_to_live) else { + return Ok(stats); + }; + + let blend_shapes_storage = self + .data + .data_ref() + .blend_shapes_container + .as_ref() + .and_then(|c| c.blend_shape_storage.clone()); + + let Some(render_pass) = shader_cache + .get(state, material.shader()) + .and_then(|shader_set| { + shader_set + .render_passes + .get(render_context.render_pass_name) + }) + else { + return Ok(stats); + }; + + state.set_framebuffer(render_context.frame_buffer.id()); + state.set_viewport(render_context.viewport); + state.apply_draw_parameters(&render_pass.draw_params); + + let mut program_binding = render_pass.program.bind(state); + render_context.apply_material(material, &mut program_binding, blend_shapes_storage); + + let geometry_binding = geometry.bind(state); + + for instance in self.instances.iter() { + if !instance_filter(instance) { + continue; + } + + InstanceContext { + world_matrix: &instance.world_transform, + wvp_matrix: &(render_context.view_projection_matrix * instance.world_transform), + bone_matrices: &instance.bone_matrices, + use_skeletal_animation: !instance.bone_matrices.is_empty(), + blend_shapes_weights: &instance.blend_shapes_weights, + matrix_storage: render_context.matrix_storage, + persistent_identifier: instance.persistent_identifier, + } + .apply_to(&mut program_binding); + + stats += geometry_binding.draw(instance.element_range)?; + } + + Ok(stats) + } +} + /// A trait for an entity that can collect render data. pub trait RenderDataBundleStorageTrait { /// Adds a new mesh to the bundle storage using the given set of vertices and triangles. This diff --git a/fyrox-impl/src/renderer/forward_renderer.rs b/fyrox-impl/src/renderer/forward_renderer.rs index f8d96e9ee..61f64cac1 100644 --- a/fyrox-impl/src/renderer/forward_renderer.rs +++ b/fyrox-impl/src/renderer/forward_renderer.rs @@ -26,16 +26,16 @@ //! For now it is used **only** to render transparent meshes (or any other mesh that has Forward render //! path). +use crate::renderer::bundle::BundleRenderContext; use crate::{ core::{ algebra::{Vector2, Vector4}, color::Color, - math::{frustum::Frustum, Rect}, + math::{frustum::Frustum, Matrix4Ext, Rect}, scope_profile, sstorage::ImmutableString, }, renderer::{ - apply_material, bundle::RenderDataBundleStorage, cache::{shader::ShaderCache, texture::TextureCache}, framework::{ @@ -43,7 +43,7 @@ use crate::{ state::PipelineState, }, storage::MatrixStorageCache, - GeometryCache, LightData, MaterialContext, QualitySettings, RenderPassStatistics, + GeometryCache, LightData, QualitySettings, RenderPassStatistics, }, scene::{ camera::Camera, @@ -52,7 +52,6 @@ use crate::{ mesh::RenderPath, }, }; -use fyrox_core::math::Matrix4Ext; use std::{cell::RefCell, rc::Rc}; pub(crate) struct ForwardRenderer { @@ -175,70 +174,34 @@ impl ForwardRenderer { .iter() .filter(|b| b.render_path == RenderPath::Forward) { - let mut material_state = bundle.material.state(); - - let Some(material) = material_state.data() else { - continue; - }; - - let Some(geometry) = geom_cache.get(state, &bundle.data, bundle.time_to_live) else { - continue; - }; - - let blend_shapes_storage = bundle - .data - .data_ref() - .blend_shapes_container - .as_ref() - .and_then(|c| c.blend_shape_storage.clone()); - - let Some(render_pass) = shader_cache - .get(state, material.shader()) - .and_then(|shader_set| shader_set.render_passes.get(&self.render_pass_name)) - else { - continue; - }; - - for instance in bundle.instances.iter() { - statistics += framebuffer.draw( - geometry, - state, + statistics += bundle.render_to_frame_buffer( + state, + geom_cache, + shader_cache, + |_| true, + BundleRenderContext { + texture_cache, + render_pass_name: &self.render_pass_name, + frame_buffer: framebuffer, viewport, - &render_pass.program, - &render_pass.draw_params, - instance.element_range, - |mut program_binding| { - apply_material(MaterialContext { - material, - program_binding: &mut program_binding, - texture_cache, - world_matrix: &instance.world_transform, - view_projection_matrix: &view_projection, - wvp_matrix: &(view_projection * instance.world_transform), - bone_matrices: &instance.bone_matrices, - use_skeletal_animation: !instance.bone_matrices.is_empty(), - camera_position: &camera.global_position(), - camera_up_vector: &camera_up, - camera_side_vector: &camera_side, - z_near: camera.projection().z_near(), - z_far: camera.projection().z_far(), - use_pom: quality_settings.use_parallax_mapping, - light_position: &Default::default(), - blend_shapes_storage: blend_shapes_storage.as_ref(), - blend_shapes_weights: &instance.blend_shapes_weights, - normal_dummy: &normal_dummy, - white_dummy: &white_dummy, - black_dummy: &black_dummy, - volume_dummy: &volume_dummy, - matrix_storage, - persistent_identifier: instance.persistent_identifier, - light_data: Some(&light_data), - ambient_light, - scene_depth: Some(&scene_depth), - }); - }, - )?; - } + matrix_storage, + view_projection_matrix: &view_projection, + camera_position: &camera.global_position(), + camera_up_vector: &camera_up, + camera_side_vector: &camera_side, + z_near: camera.projection().z_near(), + z_far: camera.projection().z_far(), + use_pom: quality_settings.use_parallax_mapping, + light_position: &Default::default(), + normal_dummy: &normal_dummy, + white_dummy: &white_dummy, + black_dummy: &black_dummy, + volume_dummy: &volume_dummy, + light_data: Some(&light_data), + ambient_light, + scene_depth: Some(&scene_depth), + }, + )?; } Ok(statistics) diff --git a/fyrox-impl/src/renderer/framework/gpu_program.rs b/fyrox-impl/src/renderer/framework/gpu_program.rs index 8b74e82e1..f3fe65f3b 100644 --- a/fyrox-impl/src/renderer/framework/gpu_program.rs +++ b/fyrox-impl/src/renderer/framework/gpu_program.rs @@ -174,6 +174,22 @@ impl<'a, 'b> GpuProgramBinding<'a, 'b> { self } + #[inline(always)] + pub fn set_texture_to_sampler( + &mut self, + location: &UniformLocation, + texture: &Rc>, + sampler: u32, + ) -> &mut Self { + unsafe { + self.state + .gl + .uniform_1_i32(Some(&location.id), sampler as i32) + }; + texture.borrow().bind(self.state, sampler); + self + } + #[inline(always)] pub fn set_bool(&mut self, location: &UniformLocation, value: bool) -> &mut Self { unsafe { diff --git a/fyrox-impl/src/renderer/gbuffer/mod.rs b/fyrox-impl/src/renderer/gbuffer/mod.rs index 911c47cf6..a6dc31367 100644 --- a/fyrox-impl/src/renderer/gbuffer/mod.rs +++ b/fyrox-impl/src/renderer/gbuffer/mod.rs @@ -38,8 +38,7 @@ use crate::{ sstorage::ImmutableString, }, renderer::{ - apply_material, - bundle::RenderDataBundleStorage, + bundle::{BundleRenderContext, RenderDataBundleStorage, SurfaceInstanceData}, cache::shader::ShaderCache, debug_renderer::DebugRenderer, framework::{ @@ -48,7 +47,6 @@ use crate::{ Attachment, AttachmentKind, BlendParameters, DrawParameters, FrameBuffer, }, geometry_buffer::{ElementRange, GeometryBuffer, GeometryBufferKind}, - gpu_program::GpuProgramBinding, gpu_texture::{ Coordinate, GpuTexture, GpuTextureKind, MagnificationFilter, MinificationFilter, PixelKind, WrapMode, @@ -58,7 +56,7 @@ use crate::{ gbuffer::decal::DecalShader, occlusion::OcclusionTester, storage::MatrixStorageCache, - GeometryCache, MaterialContext, QualitySettings, RenderPassStatistics, TextureCache, + GeometryCache, QualitySettings, RenderPassStatistics, TextureCache, }, scene::{ camera::Camera, @@ -335,83 +333,44 @@ impl GBuffer { .grid_cache .cell(camera.global_position()); + let instance_filter = |instance: &SurfaceInstanceData| { + !quality_settings.use_occlusion_culling + || grid_cell.map_or(true, |cell| cell.is_visible(instance.node_handle)) + }; + for bundle in bundle_storage .bundles .iter() .filter(|b| b.render_path == RenderPath::Deferred) { - let mut material_state = bundle.material.state(); - - let Some(material) = material_state.data() else { - continue; - }; - - let Some(geometry) = geom_cache.get(state, &bundle.data, bundle.time_to_live) else { - continue; - }; - - let blend_shapes_storage = bundle - .data - .data_ref() - .blend_shapes_container - .as_ref() - .and_then(|c| c.blend_shape_storage.clone()); - - let Some(render_pass) = shader_cache - .get(state, material.shader()) - .and_then(|shader_set| shader_set.render_passes.get(&self.render_pass_name)) - else { - continue; - }; - - for instance in bundle.instances.iter() { - if quality_settings.use_occlusion_culling - && !grid_cell.map_or(true, |cell| cell.is_visible(instance.node_handle)) - { - continue; - } - - let apply_uniforms = |mut program_binding: GpuProgramBinding| { - apply_material(MaterialContext { - material, - program_binding: &mut program_binding, - texture_cache, - matrix_storage, - world_matrix: &instance.world_transform, - view_projection_matrix: &view_projection, - wvp_matrix: &(view_projection * instance.world_transform), - bone_matrices: &instance.bone_matrices, - use_skeletal_animation: !instance.bone_matrices.is_empty(), - camera_position: &camera.global_position(), - camera_up_vector: &camera_up, - camera_side_vector: &camera_side, - z_near: camera.projection().z_near(), - use_pom: quality_settings.use_parallax_mapping, - light_position: &Default::default(), - blend_shapes_storage: blend_shapes_storage.as_ref(), - blend_shapes_weights: &instance.blend_shapes_weights, - normal_dummy: &normal_dummy, - white_dummy: &white_dummy, - black_dummy: &black_dummy, - volume_dummy: &volume_dummy, - persistent_identifier: instance.persistent_identifier, - light_data: None, - ambient_light: Color::WHITE, // TODO - scene_depth: None, // TODO. Add z-pre-pass. - z_far: camera.projection().z_far(), - }); - }; - - statistics += self.framebuffer.draw( - geometry, - state, + statistics += bundle.render_to_frame_buffer( + state, + geom_cache, + shader_cache, + instance_filter, + BundleRenderContext { + texture_cache, + render_pass_name: &self.render_pass_name, + frame_buffer: &mut self.framebuffer, viewport, - &render_pass.program, - &render_pass.draw_params, - instance.element_range, - apply_uniforms, - )?; - } + matrix_storage, + view_projection_matrix: &view_projection, + camera_position: &camera.global_position(), + camera_up_vector: &camera_up, + camera_side_vector: &camera_side, + z_near: camera.projection().z_near(), + use_pom: quality_settings.use_parallax_mapping, + light_position: &Default::default(), + normal_dummy: &normal_dummy, + white_dummy: &white_dummy, + black_dummy: &black_dummy, + volume_dummy: &volume_dummy, + light_data: None, + ambient_light: Color::WHITE, // TODO + scene_depth: None, // TODO. Add z-pre-pass. + z_far: camera.projection().z_far(), + }, + )?; } if quality_settings.use_occlusion_culling { diff --git a/fyrox-impl/src/renderer/mod.rs b/fyrox-impl/src/renderer/mod.rs index b3b7615ae..958df58e1 100644 --- a/fyrox-impl/src/renderer/mod.rs +++ b/fyrox-impl/src/renderer/mod.rs @@ -70,13 +70,10 @@ use crate::{ }, graph::SceneGraph, gui::draw::DrawingContext, - material::{ - shader::{SamplerFallback, Shader, ShaderResource, ShaderResourceExtension}, - Material, PropertyValue, - }, + material::shader::{Shader, ShaderResource, ShaderResourceExtension}, renderer::{ bloom::BloomRenderer, - bundle::{ObserverInfo, PersistentIdentifier, RenderDataBundleStorage}, + bundle::{ObserverInfo, RenderDataBundleStorage}, cache::{geometry::GeometryCache, shader::ShaderCache, texture::TextureCache}, debug_renderer::DebugRenderer, flat_shader::FlatShader, @@ -87,7 +84,6 @@ use crate::{ geometry_buffer::{ DrawCallStatistics, ElementRange, GeometryBuffer, GeometryBufferKind, }, - gpu_program::{BuiltInUniform, GpuProgramBinding}, gpu_texture::{ Coordinate, GpuTexture, GpuTextureKind, MagnificationFilter, MinificationFilter, PixelKind, WrapMode, @@ -929,247 +925,6 @@ impl Default for LightData { } } -#[allow(missing_docs)] // TODO -pub struct MaterialContext<'a, 'b, 'c> { - pub material: &'a Material, - pub program_binding: &'a mut GpuProgramBinding<'b, 'c>, - pub texture_cache: &'a mut TextureCache, - pub matrix_storage: &'a mut MatrixStorageCache, - pub persistent_identifier: PersistentIdentifier, - - // Built-in uniforms. - pub world_matrix: &'a Matrix4, - pub view_projection_matrix: &'a Matrix4, - pub wvp_matrix: &'a Matrix4, - pub bone_matrices: &'a [Matrix4], - pub use_skeletal_animation: bool, - pub use_pom: bool, - pub light_position: &'a Vector3, - pub blend_shapes_storage: Option<&'a TextureResource>, - pub blend_shapes_weights: &'a [f32], - pub light_data: Option<&'a LightData>, - pub ambient_light: Color, - // TODO: Add depth pre-pass to remove Option here. Current architecture allows only forward - // renderer to have access to depth buffer that is available from G-Buffer. - pub scene_depth: Option<&'a Rc>>, - - pub camera_position: &'a Vector3, - pub camera_up_vector: &'a Vector3, - pub camera_side_vector: &'a Vector3, - pub z_near: f32, - pub z_far: f32, - - // Fallback samplers. - pub normal_dummy: &'a Rc>, - pub white_dummy: &'a Rc>, - pub black_dummy: &'a Rc>, - pub volume_dummy: &'a Rc>, -} - -#[allow(missing_docs)] // TODO -pub fn apply_material(ctx: MaterialContext) { - let built_in_uniforms = &ctx.program_binding.program.built_in_uniform_locations; - - // Apply values for built-in uniforms. - if let Some(location) = &built_in_uniforms[BuiltInUniform::WorldMatrix as usize] { - ctx.program_binding.set_matrix4(location, ctx.world_matrix); - } - if let Some(location) = &built_in_uniforms[BuiltInUniform::ViewProjectionMatrix as usize] { - ctx.program_binding - .set_matrix4(location, ctx.view_projection_matrix); - } - if let Some(location) = &built_in_uniforms[BuiltInUniform::WorldViewProjectionMatrix as usize] { - ctx.program_binding.set_matrix4(location, ctx.wvp_matrix); - } - if let Some(location) = &built_in_uniforms[BuiltInUniform::BoneMatrices as usize] { - let active_sampler = ctx.program_binding.active_sampler(); - - let storage = ctx - .matrix_storage - .try_bind_and_upload( - ctx.program_binding.state, - ctx.persistent_identifier, - ctx.bone_matrices, - active_sampler, - ) - .expect("Failed to upload bone matrices!"); - - ctx.program_binding.set_texture(location, storage.texture()); - } - if let Some(location) = &built_in_uniforms[BuiltInUniform::UseSkeletalAnimation as usize] { - ctx.program_binding - .set_bool(location, ctx.use_skeletal_animation); - } - if let Some(location) = &built_in_uniforms[BuiltInUniform::CameraPosition as usize] { - ctx.program_binding - .set_vector3(location, ctx.camera_position); - } - - if let Some(location) = &built_in_uniforms[BuiltInUniform::CameraUpVector as usize] { - ctx.program_binding - .set_vector3(location, ctx.camera_up_vector); - } - if let Some(location) = &built_in_uniforms[BuiltInUniform::CameraSideVector as usize] { - ctx.program_binding - .set_vector3(location, ctx.camera_side_vector); - } - if let Some(location) = &built_in_uniforms[BuiltInUniform::ZNear as usize] { - ctx.program_binding.set_f32(location, ctx.z_near); - } - if let Some(location) = &built_in_uniforms[BuiltInUniform::ZFar as usize] { - ctx.program_binding.set_f32(location, ctx.z_far); - } - - if let Some(location) = &built_in_uniforms[BuiltInUniform::SceneDepth as usize] { - if let Some(scene_depth) = ctx.scene_depth.as_ref() { - ctx.program_binding.set_texture(location, scene_depth); - } - } - - if let Some(location) = &built_in_uniforms[BuiltInUniform::UsePOM as usize] { - ctx.program_binding.set_bool(location, ctx.use_pom); - } - if let Some(location) = &built_in_uniforms[BuiltInUniform::LightPosition as usize] { - ctx.program_binding - .set_vector3(location, ctx.light_position); - } - - if let Some(light_data) = ctx.light_data { - if let Some(location) = &built_in_uniforms[BuiltInUniform::LightCount as usize] { - ctx.program_binding - .set_i32(location, light_data.count as i32); - } - - if let Some(location) = &built_in_uniforms[BuiltInUniform::LightsColorRadius as usize] { - ctx.program_binding - .set_vector4_slice(location, &light_data.color_radius); - } - - if let Some(location) = &built_in_uniforms[BuiltInUniform::LightsPosition as usize] { - ctx.program_binding - .set_vector3_slice(location, &light_data.position); - } - - if let Some(location) = &built_in_uniforms[BuiltInUniform::LightsDirection as usize] { - ctx.program_binding - .set_vector3_slice(location, &light_data.direction); - } - - if let Some(location) = &built_in_uniforms[BuiltInUniform::LightsParameters as usize] { - ctx.program_binding - .set_vector2_slice(location, &light_data.parameters); - } - } - - if let Some(location) = &built_in_uniforms[BuiltInUniform::AmbientLight as usize] { - ctx.program_binding - .set_srgb_color(location, &ctx.ambient_light); - } - - if let Some(location) = &built_in_uniforms[BuiltInUniform::BlendShapesStorage as usize] { - if let Some(texture) = ctx - .blend_shapes_storage - .as_ref() - .and_then(|blend_shapes_storage| { - ctx.texture_cache - .get(ctx.program_binding.state, blend_shapes_storage) - }) - { - ctx.program_binding.set_texture(location, texture); - } else { - ctx.program_binding.set_texture(location, ctx.volume_dummy); - } - } - if let Some(location) = &built_in_uniforms[BuiltInUniform::BlendShapesWeights as usize] { - ctx.program_binding - .set_f32_slice(location, ctx.blend_shapes_weights); - } - if let Some(location) = &built_in_uniforms[BuiltInUniform::BlendShapesCount as usize] { - ctx.program_binding - .set_i32(location, ctx.blend_shapes_weights.len() as i32); - } - - // Apply material properties. - for (name, value) in ctx.material.properties() { - if let Some(uniform) = ctx.program_binding.uniform_location(name) { - match value { - PropertyValue::Float(v) => { - ctx.program_binding.set_f32(&uniform, *v); - } - PropertyValue::Int(v) => { - ctx.program_binding.set_i32(&uniform, *v); - } - PropertyValue::UInt(v) => { - ctx.program_binding.set_u32(&uniform, *v); - } - PropertyValue::Vector2(v) => { - ctx.program_binding.set_vector2(&uniform, v); - } - PropertyValue::Vector3(v) => { - ctx.program_binding.set_vector3(&uniform, v); - } - PropertyValue::Vector4(v) => { - ctx.program_binding.set_vector4(&uniform, v); - } - PropertyValue::Matrix2(v) => { - ctx.program_binding.set_matrix2(&uniform, v); - } - PropertyValue::Matrix3(v) => { - ctx.program_binding.set_matrix3(&uniform, v); - } - PropertyValue::Matrix4(v) => { - ctx.program_binding.set_matrix4(&uniform, v); - } - PropertyValue::Color(v) => { - ctx.program_binding.set_srgb_color(&uniform, v); - } - PropertyValue::Bool(v) => { - ctx.program_binding.set_bool(&uniform, *v); - } - PropertyValue::Sampler { value, fallback } => { - let texture = value - .as_ref() - .and_then(|t| ctx.texture_cache.get(ctx.program_binding.state, t)) - .unwrap_or(match fallback { - SamplerFallback::White => ctx.white_dummy, - SamplerFallback::Normal => ctx.normal_dummy, - SamplerFallback::Black => ctx.black_dummy, - }); - - ctx.program_binding.set_texture(&uniform, texture); - } - PropertyValue::FloatArray(v) => { - ctx.program_binding.set_f32_slice(&uniform, v); - } - PropertyValue::IntArray(v) => { - ctx.program_binding.set_i32_slice(&uniform, v); - } - PropertyValue::UIntArray(v) => { - ctx.program_binding.set_u32_slice(&uniform, v); - } - PropertyValue::Vector2Array(v) => { - ctx.program_binding.set_vector2_slice(&uniform, v); - } - PropertyValue::Vector3Array(v) => { - ctx.program_binding.set_vector3_slice(&uniform, v); - } - PropertyValue::Vector4Array(v) => { - ctx.program_binding.set_vector4_slice(&uniform, v); - } - PropertyValue::Matrix2Array(v) => { - ctx.program_binding.set_matrix2_array(&uniform, v); - } - PropertyValue::Matrix3Array(v) => { - ctx.program_binding.set_matrix3_array(&uniform, v); - } - PropertyValue::Matrix4Array(v) => { - ctx.program_binding.set_matrix4_array(&uniform, v); - } - } - } - } -} - impl Renderer { pub(crate) fn new( context: glow::Context, diff --git a/fyrox-impl/src/renderer/shadow/csm.rs b/fyrox-impl/src/renderer/shadow/csm.rs index 86392802d..fb39d17e9 100644 --- a/fyrox-impl/src/renderer/shadow/csm.rs +++ b/fyrox-impl/src/renderer/shadow/csm.rs @@ -21,23 +21,23 @@ use crate::{ core::{ algebra::{Matrix4, Point3, Vector2, Vector3}, - math::{aabb::AxisAlignedBoundingBox, frustum::Frustum, Rect}, + color::Color, + math::{aabb::AxisAlignedBoundingBox, frustum::Frustum, Matrix4Ext, Rect}, }, renderer::{ - apply_material, - bundle::{ObserverInfo, RenderDataBundleStorage}, + bundle::{BundleRenderContext, ObserverInfo, RenderDataBundleStorage}, cache::{geometry::GeometryCache, shader::ShaderCache, texture::TextureCache}, framework::{ error::FrameworkError, - framebuffer::{Attachment, AttachmentKind, CullFace, DrawParameters, FrameBuffer}, + framebuffer::{Attachment, AttachmentKind, FrameBuffer}, gpu_texture::{ Coordinate, GpuTexture, GpuTextureKind, MagnificationFilter, MinificationFilter, PixelKind, WrapMode, }, - state::{ColorMask, PipelineState}, + state::PipelineState, }, storage::MatrixStorageCache, - MaterialContext, RenderPassStatistics, ShadowMapPrecision, DIRECTIONAL_SHADOW_PASS_NAME, + RenderPassStatistics, ShadowMapPrecision, DIRECTIONAL_SHADOW_PASS_NAME, }, scene::{ camera::Camera, @@ -45,8 +45,6 @@ use crate::{ light::directional::{DirectionalLight, FrustumSplitOptions, CSM_NUM_CASCADES}, }, }; -use fyrox_core::color::Color; -use fyrox_core::math::Matrix4Ext; use std::{cell::RefCell, rc::Rc}; pub struct Cascade { @@ -282,81 +280,34 @@ impl CsmRenderer { ); for bundle in bundle_storage.bundles.iter() { - let mut material_state = bundle.material.state(); - let Some(material) = material_state.data() else { - continue; - }; - - let Some(geometry) = geom_cache.get(state, &bundle.data, bundle.time_to_live) - else { - continue; - }; - - let blend_shapes_storage = bundle - .data - .data_ref() - .blend_shapes_container - .as_ref() - .and_then(|c| c.blend_shape_storage.clone()); - - let Some(render_pass) = - shader_cache - .get(state, material.shader()) - .and_then(|shader_set| { - shader_set.render_passes.get(&DIRECTIONAL_SHADOW_PASS_NAME) - }) - else { - continue; - }; - - for instance in bundle.instances.iter() { - stats += framebuffer.draw( - geometry, - state, + stats += bundle.render_to_frame_buffer( + state, + geom_cache, + shader_cache, + |_| true, + BundleRenderContext { + texture_cache, + render_pass_name: &DIRECTIONAL_SHADOW_PASS_NAME, + frame_buffer: framebuffer, viewport, - &render_pass.program, - &DrawParameters { - cull_face: Some(CullFace::Back), - color_write: ColorMask::all(false), - depth_write: true, - stencil_test: None, - depth_test: true, - blend: None, - stencil_op: Default::default(), - }, - instance.element_range, - |mut program_binding| { - apply_material(MaterialContext { - material, - program_binding: &mut program_binding, - texture_cache, - matrix_storage, - world_matrix: &instance.world_transform, - view_projection_matrix: &light_view_projection, - wvp_matrix: &(light_view_projection * instance.world_transform), - bone_matrices: &instance.bone_matrices, - use_skeletal_animation: !instance.bone_matrices.is_empty(), - camera_position: &camera.global_position(), - camera_up_vector: &camera_up, - camera_side_vector: &camera_side, - z_near, - use_pom: false, - light_position: &Default::default(), - blend_shapes_storage: blend_shapes_storage.as_ref(), - blend_shapes_weights: &instance.blend_shapes_weights, - normal_dummy: &normal_dummy, - white_dummy: &white_dummy, - black_dummy: &black_dummy, - volume_dummy: &volume_dummy, - persistent_identifier: instance.persistent_identifier, - light_data: None, // TODO - ambient_light: Color::WHITE, // TODO - scene_depth: None, - z_far, - }); - }, - )?; - } + matrix_storage, + view_projection_matrix: &light_view_projection, + camera_position: &camera.global_position(), + camera_up_vector: &camera_up, + camera_side_vector: &camera_side, + z_near, + use_pom: false, + light_position: &Default::default(), + normal_dummy: &normal_dummy, + white_dummy: &white_dummy, + black_dummy: &black_dummy, + volume_dummy: &volume_dummy, + light_data: None, // TODO + ambient_light: Color::WHITE, // TODO + scene_depth: None, + z_far, + }, + )?; } } diff --git a/fyrox-impl/src/renderer/shadow/point.rs b/fyrox-impl/src/renderer/shadow/point.rs index 8fd248619..73ee761d2 100644 --- a/fyrox-impl/src/renderer/shadow/point.rs +++ b/fyrox-impl/src/renderer/shadow/point.rs @@ -22,12 +22,11 @@ use crate::{ core::{ algebra::{Matrix4, Point3, Vector3}, color::Color, - math::Rect, + math::{Matrix4Ext, Rect}, scope_profile, }, renderer::{ - apply_material, - bundle::{ObserverInfo, RenderDataBundleStorage}, + bundle::{BundleRenderContext, ObserverInfo, RenderDataBundleStorage}, cache::{shader::ShaderCache, texture::TextureCache}, framework::{ error::FrameworkError, @@ -40,12 +39,10 @@ use crate::{ }, shadow::cascade_size, storage::MatrixStorageCache, - GeometryCache, MaterialContext, RenderPassStatistics, ShadowMapPrecision, - POINT_SHADOW_PASS_NAME, + GeometryCache, RenderPassStatistics, ShadowMapPrecision, POINT_SHADOW_PASS_NAME, }, scene::graph::Graph, }; -use fyrox_core::math::Matrix4Ext; use std::{cell::RefCell, rc::Rc}; pub struct PointShadowMapRenderer { @@ -274,70 +271,34 @@ impl PointShadowMapRenderer { ); for bundle in bundle_storage.bundles.iter() { - let mut material_state = bundle.material.state(); - let Some(material) = material_state.data() else { - continue; - }; - let Some(geometry) = geom_cache.get(state, &bundle.data, bundle.time_to_live) - else { - continue; - }; - - let blend_shapes_storage = bundle - .data - .data_ref() - .blend_shapes_container - .as_ref() - .and_then(|c| c.blend_shape_storage.clone()); - - let Some(render_pass) = shader_cache - .get(state, material.shader()) - .and_then(|shader_set| shader_set.render_passes.get(&POINT_SHADOW_PASS_NAME)) - else { - continue; - }; - - for instance in bundle.instances.iter() { - statistics += framebuffer.draw( - geometry, - state, + statistics += bundle.render_to_frame_buffer( + state, + geom_cache, + shader_cache, + |_| true, + BundleRenderContext { + texture_cache, + render_pass_name: &POINT_SHADOW_PASS_NAME, + frame_buffer: framebuffer, viewport, - &render_pass.program, - &render_pass.draw_params, - instance.element_range, - |mut program_binding| { - apply_material(MaterialContext { - material, - program_binding: &mut program_binding, - texture_cache, - matrix_storage, - world_matrix: &instance.world_transform, - view_projection_matrix: &light_view_projection_matrix, - wvp_matrix: &(light_view_projection_matrix - * instance.world_transform), - bone_matrices: &instance.bone_matrices, - use_skeletal_animation: !instance.bone_matrices.is_empty(), - camera_position: &Default::default(), - camera_up_vector: &camera_up, - camera_side_vector: &camera_side, - z_near, - use_pom: false, - light_position: &light_pos, - blend_shapes_storage: blend_shapes_storage.as_ref(), - blend_shapes_weights: &instance.blend_shapes_weights, - normal_dummy: &normal_dummy, - white_dummy: &white_dummy, - black_dummy: &black_dummy, - volume_dummy: &volume_dummy, - persistent_identifier: instance.persistent_identifier, - light_data: None, // TODO - ambient_light: Color::WHITE, // TODO - scene_depth: None, - z_far, - }); - }, - )?; - } + matrix_storage, + view_projection_matrix: &light_view_projection_matrix, + camera_position: &Default::default(), + camera_up_vector: &camera_up, + camera_side_vector: &camera_side, + z_near, + use_pom: false, + light_position: &light_pos, + normal_dummy: &normal_dummy, + white_dummy: &white_dummy, + black_dummy: &black_dummy, + volume_dummy: &volume_dummy, + light_data: None, // TODO + ambient_light: Color::WHITE, // TODO + scene_depth: None, + z_far, + }, + )?; } } diff --git a/fyrox-impl/src/renderer/shadow/spot.rs b/fyrox-impl/src/renderer/shadow/spot.rs index 9391a3162..e4ed3d4ac 100644 --- a/fyrox-impl/src/renderer/shadow/spot.rs +++ b/fyrox-impl/src/renderer/shadow/spot.rs @@ -22,30 +22,27 @@ use crate::{ core::{ algebra::{Matrix4, Vector3}, color::Color, - math::Rect, + math::{Matrix4Ext, Rect}, scope_profile, }, renderer::{ - apply_material, - bundle::{ObserverInfo, RenderDataBundleStorage}, + bundle::{BundleRenderContext, ObserverInfo, RenderDataBundleStorage}, cache::{shader::ShaderCache, texture::TextureCache}, framework::{ error::FrameworkError, - framebuffer::{Attachment, AttachmentKind, CullFace, DrawParameters, FrameBuffer}, + framebuffer::{Attachment, AttachmentKind, FrameBuffer}, gpu_texture::{ Coordinate, GpuTexture, GpuTextureKind, MagnificationFilter, MinificationFilter, PixelKind, WrapMode, }, - state::{ColorMask, PipelineState}, + state::PipelineState, }, shadow::cascade_size, storage::MatrixStorageCache, - GeometryCache, MaterialContext, RenderPassStatistics, ShadowMapPrecision, - SPOT_SHADOW_PASS_NAME, + GeometryCache, RenderPassStatistics, ShadowMapPrecision, SPOT_SHADOW_PASS_NAME, }, scene::graph::Graph, }; -use fyrox_core::math::Matrix4Ext; use std::{cell::RefCell, rc::Rc}; pub struct SpotShadowMapRenderer { @@ -184,77 +181,34 @@ impl SpotShadowMapRenderer { let camera_side = inv_view.side(); for bundle in bundle_storage.bundles.iter() { - let mut material_state = bundle.material.state(); - let Some(material) = material_state.data() else { - continue; - }; - - let Some(geometry) = geom_cache.get(state, &bundle.data, bundle.time_to_live) else { - continue; - }; - - let blend_shapes_storage = bundle - .data - .data_ref() - .blend_shapes_container - .as_ref() - .and_then(|c| c.blend_shape_storage.clone()); - - let Some(render_pass) = shader_cache - .get(state, material.shader()) - .and_then(|shader_set| shader_set.render_passes.get(&SPOT_SHADOW_PASS_NAME)) - else { - continue; - }; - - for instance in bundle.instances.iter() { - statistics += framebuffer.draw( - geometry, - state, + statistics += bundle.render_to_frame_buffer( + state, + geom_cache, + shader_cache, + |_| true, + BundleRenderContext { + texture_cache, + render_pass_name: &SPOT_SHADOW_PASS_NAME, + frame_buffer: framebuffer, viewport, - &render_pass.program, - &DrawParameters { - cull_face: Some(CullFace::Back), - color_write: ColorMask::all(false), - depth_write: true, - stencil_test: None, - depth_test: true, - blend: None, - stencil_op: Default::default(), - }, - instance.element_range, - |mut program_binding| { - apply_material(MaterialContext { - material, - program_binding: &mut program_binding, - texture_cache, - matrix_storage, - world_matrix: &instance.world_transform, - view_projection_matrix: &light_view_projection, - wvp_matrix: &(light_view_projection * instance.world_transform), - bone_matrices: &instance.bone_matrices, - use_skeletal_animation: !instance.bone_matrices.is_empty(), - camera_position: &Default::default(), - camera_up_vector: &camera_up, - camera_side_vector: &camera_side, - z_near, - use_pom: false, - light_position: &Default::default(), - blend_shapes_storage: blend_shapes_storage.as_ref(), - blend_shapes_weights: &instance.blend_shapes_weights, - normal_dummy: &normal_dummy, - white_dummy: &white_dummy, - black_dummy: &black_dummy, - volume_dummy: &volume_dummy, - persistent_identifier: instance.persistent_identifier, - light_data: None, // TODO - ambient_light: Color::WHITE, // TODO - scene_depth: None, - z_far, - }); - }, - )?; - } + matrix_storage, + view_projection_matrix: &light_view_projection, + camera_position: &Default::default(), + camera_up_vector: &camera_up, + camera_side_vector: &camera_side, + z_near, + use_pom: false, + light_position: &Default::default(), + normal_dummy: &normal_dummy, + white_dummy: &white_dummy, + black_dummy: &black_dummy, + volume_dummy: &volume_dummy, + light_data: None, // TODO + ambient_light: Color::WHITE, // TODO + scene_depth: None, + z_far, + }, + )?; } Ok(statistics)