mirror of
https://github.com/FyroxEngine/Fyrox.git
synced 2026-10-12 02:02:20 +00:00
prevent redundant occlusion queries for visible objects
This commit is contained in:
@@ -596,32 +596,35 @@ impl DeferredLightRenderer {
|
||||
// for now we'll skip it, since this optimization could be useful only for scenes with
|
||||
// mixed indoor/outdoor environment.
|
||||
if light.cast::<DirectionalLight>().is_none() {
|
||||
// Draw full screen quad, that will be used to count pixels that passed the stencil test
|
||||
// on the stencil buffer's content generated by two previous drawing commands.
|
||||
visibility_cache.begin_query(state, camera_global_position, light_handle)?;
|
||||
frame_buffer.draw(
|
||||
&self.quad,
|
||||
state,
|
||||
viewport,
|
||||
&self.flat_shader.program,
|
||||
&DrawParameters {
|
||||
cull_face: None,
|
||||
color_write: ColorMask::all(false),
|
||||
depth_write: false,
|
||||
stencil_test: Some(StencilFunc {
|
||||
func: CompareFunc::NotEqual,
|
||||
..Default::default()
|
||||
}),
|
||||
depth_test: true,
|
||||
blend: None,
|
||||
stencil_op: Default::default(),
|
||||
},
|
||||
ElementRange::Full,
|
||||
|mut program_binding| {
|
||||
program_binding.set_matrix4(&self.flat_shader.wvp_matrix, &frame_matrix);
|
||||
},
|
||||
)?;
|
||||
visibility_cache.end_query();
|
||||
if visibility_cache.needs_occlusion_query(camera_global_position, light_handle) {
|
||||
// Draw full screen quad, that will be used to count pixels that passed the stencil test
|
||||
// on the stencil buffer's content generated by two previous drawing commands.
|
||||
visibility_cache.begin_query(state, camera_global_position, light_handle)?;
|
||||
frame_buffer.draw(
|
||||
&self.quad,
|
||||
state,
|
||||
viewport,
|
||||
&self.flat_shader.program,
|
||||
&DrawParameters {
|
||||
cull_face: None,
|
||||
color_write: ColorMask::all(false),
|
||||
depth_write: false,
|
||||
stencil_test: Some(StencilFunc {
|
||||
func: CompareFunc::NotEqual,
|
||||
..Default::default()
|
||||
}),
|
||||
depth_test: true,
|
||||
blend: None,
|
||||
stencil_op: Default::default(),
|
||||
},
|
||||
ElementRange::Full,
|
||||
|mut program_binding| {
|
||||
program_binding
|
||||
.set_matrix4(&self.flat_shader.wvp_matrix, &frame_matrix);
|
||||
},
|
||||
)?;
|
||||
visibility_cache.end_query();
|
||||
}
|
||||
|
||||
if !visibility_cache.is_visible(camera_global_position, light_handle) {
|
||||
continue;
|
||||
|
||||
@@ -36,7 +36,14 @@ struct PendingQuery {
|
||||
node: Handle<Node>,
|
||||
}
|
||||
|
||||
type NodeVisibilityMap = FxHashMap<Handle<Node>, Option<bool>>;
|
||||
#[derive(Debug)]
|
||||
enum Visibility {
|
||||
Undefined,
|
||||
Invisible,
|
||||
Visible,
|
||||
}
|
||||
|
||||
type NodeVisibilityMap = FxHashMap<Handle<Node>, Visibility>;
|
||||
|
||||
/// Volumetric visibility cache based on occlusion query.
|
||||
pub struct VisibilityCache {
|
||||
@@ -66,20 +73,59 @@ impl VisibilityCache {
|
||||
world_to_grid(world_position, self.granularity)
|
||||
}
|
||||
|
||||
pub fn is_visible(&self, observer_position: Vector3<f32>, node: Handle<Node>) -> bool {
|
||||
fn visibility_info(
|
||||
&self,
|
||||
observer_position: Vector3<f32>,
|
||||
node: Handle<Node>,
|
||||
) -> Option<&Visibility> {
|
||||
let grid_position = self.world_to_grid(observer_position);
|
||||
|
||||
let Some(cell) = self.cells.get(&grid_position) else {
|
||||
self.cells
|
||||
.get(&grid_position)
|
||||
.and_then(|cell| cell.get(&node))
|
||||
}
|
||||
|
||||
pub fn needs_occlusion_query(
|
||||
&self,
|
||||
observer_position: Vector3<f32>,
|
||||
node: Handle<Node>,
|
||||
) -> bool {
|
||||
let Some(visibility) = self.visibility_info(observer_position, node) else {
|
||||
// There's no data about the visibility, so the occlusion query is needed.
|
||||
return true;
|
||||
};
|
||||
|
||||
match visibility {
|
||||
Visibility::Undefined => {
|
||||
// There's already an occlusion query on GPU.
|
||||
false
|
||||
}
|
||||
Visibility::Invisible => {
|
||||
// The object could be invisible from one angle at the observer position, but visible
|
||||
// from another. Since we're using only position of the observer, we cannot be 100%
|
||||
// sure, that the object is invisible even if a previous query told us so.
|
||||
true
|
||||
}
|
||||
Visibility::Visible => {
|
||||
// Some pixels of the object is visible from the given observer position, so we don't
|
||||
// need a new occlusion query.
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_visible(&self, observer_position: Vector3<f32>, node: Handle<Node>) -> bool {
|
||||
let Some(visibility_info) = self.visibility_info(observer_position, node) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let Some(visibility_info) = cell.get(&node) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
// Undefined visibility is treated like the object is visible, this is needed because
|
||||
// GPU queries are async.
|
||||
visibility_info.unwrap_or(true)
|
||||
match *visibility_info {
|
||||
Visibility::Visible
|
||||
// Undefined visibility is treated like the object is visible, this is needed because
|
||||
// GPU queries are async, and we must still render the object to prevent popping light.
|
||||
| Visibility::Undefined => true,
|
||||
Visibility::Invisible => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn begin_query(
|
||||
@@ -101,7 +147,7 @@ impl VisibilityCache {
|
||||
.entry(grid_position)
|
||||
.or_default()
|
||||
.entry(node)
|
||||
.or_default();
|
||||
.or_insert(Visibility::Undefined);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -130,15 +176,23 @@ impl VisibilityCache {
|
||||
.expect("object visibility must be predefined!");
|
||||
|
||||
match visibility {
|
||||
None => {
|
||||
*visibility = Some(query_result);
|
||||
}
|
||||
Some(visibility) => {
|
||||
// Override "invisibility" - if any fragment of an object is visible, then
|
||||
// it will remain visible forever. This is ok for non-moving objects only.
|
||||
if !*visibility && query_result {
|
||||
*visibility = true;
|
||||
Visibility::Undefined => match query_result {
|
||||
true => {
|
||||
*visibility = Visibility::Visible;
|
||||
}
|
||||
false => {
|
||||
*visibility = Visibility::Invisible;
|
||||
}
|
||||
},
|
||||
Visibility::Invisible => {
|
||||
if query_result {
|
||||
// Override "invisibility" - if any fragment of an object is visible, then
|
||||
// it will remain visible forever. This is ok for non-moving objects only.
|
||||
*visibility = Visibility::Visible;
|
||||
}
|
||||
}
|
||||
Visibility::Visible => {
|
||||
// Ignore the query result and keep the visibility.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user