Skip to content

Fix false positive GPU frustum culling#17939

Merged
superdump merged 1 commit intobevyengine:mainfrom
mate-h:frustum-cull-fix
Feb 20, 2025
Merged

Fix false positive GPU frustum culling#17939
superdump merged 1 commit intobevyengine:mainfrom
mate-h:frustum-cull-fix

Conversation

@mate-h
Copy link
Copy Markdown
Contributor

@mate-h mate-h commented Feb 19, 2025

Objective

Fix incorrect mesh culling where objects (particularly directional shadows) were being incorrectly culled during the early preprocessing phase. The issue manifested specifically on Apple M1 GPUs but not on newer devices like the M4. The bug was in the view_frustum_intersects_obb function, where including the w component (plane distance) in the dot product calculations led to false positive culling results. This caused objects to be incorrectly culled before shadow casting could begin.

Issue Details

The problem of missing shadows is reproducible on Apple M1 GPUs as of this commit (bisected):

00722b8d0 Make indirect drawing opt-out instead of opt-in, enabling multidraw by default. (#16757)

and as recent as this commit:

c818c9214 Add option to animate materials in many_cubes (#17927)
  • The frustum culling calculation incorrectly included the w component (plane distance) when transforming basis vectors
  • The relative radius calculation should only consider directional transformation (xyz), not positional information (w)
  • This caused false positive culling specifically on M1 devices likely due to different device-specific floating-point behavior
  • When objects were incorrectly culled, early_instance_count never incremented, leading to missing geometry in the shadow pass

Testing

  • Tested on M1 and M4 devices to verify the fix
  • Verified shadows and geometry render correctly on both platforms
  • Confirmed the solution matches the existing Rust implementation's behavior for calculating the relative radius:
    pub fn relative_radius(&self, p_normal: &Vec3A, world_from_local: &Mat3A) -> f32 {
    // NOTE: dot products on Vec3A use SIMD and even with the overhead of conversion are net faster than Vec3
    let half_extents = self.half_extents;
    Vec3A::new(
    p_normal.dot(world_from_local.x_axis),
    p_normal.dot(world_from_local.y_axis),
    p_normal.dot(world_from_local.z_axis),
    )
    .abs()
    .dot(half_extents)
    }
  • The fix resolves a mathematical error in the frustum culling calculation while maintaining correct culling behavior across all platforms.

Showcase

c818c9214
c818c9214

mate-h/frustum-cull-fix
frustum-cull-fix

@JMS55 JMS55 added C-Bug An unexpected or incorrect behavior A-Rendering Drawing game state to the screen S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it labels Feb 20, 2025
@superdump superdump added this pull request to the merge queue Feb 20, 2025
Merged via the queue into bevyengine:main with commit 9e11e96 Feb 20, 2025
33 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Rendering Drawing game state to the screen C-Bug An unexpected or incorrect behavior S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it

Projects

No open projects
Status: Done

Development

Successfully merging this pull request may close these issues.

4 participants