Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve Performance of Hidden Entities #190

Closed
ifletsomeclaire opened this issue Aug 14, 2020 · 8 comments
Closed

Improve Performance of Hidden Entities #190

ifletsomeclaire opened this issue Aug 14, 2020 · 8 comments
Labels
C-Performance A change motivated by improving speed, memory usage or compile times

Comments

@ifletsomeclaire
Copy link
Contributor

We currently have 2 options to "hide" entities (stop them from rendering - i.e. if off-screen/out of camera field of view):

  1. set is_visible field in the Draw component to false
  2. remove Draw component from the entity

I did some testing with a system to check if the entity was within my camera's view (2D camera with center origin):

pub fn camera_view_check(
    mut commands: Commands,
    time: Res<Time>,
    mut camera_query: Query<(&Camera, &Transform, &OrthographicProjection)>,
    mut draw_query: Query<(Entity, &mut Draw)>,
    draw_transform_query: Query<(&Draw, &Transform)>,
) {
    for (camera, camera_transform, ortho_proj) in &mut camera_query.iter() {
        if time.seconds_since_startup > 2.0 {
            for (entity, mut draw) in &mut draw_query.iter() {
                if let Ok(transform) = draw_transform_query.get::<Transform>(entity) {
                    let visual_check = camera.projection_matrix.transform_point3(Vec3::from(
                        camera_transform.value.w_axis().truncate()
                            - transform.value.w_axis().truncate(),
                    ));
                    match ortho_proj.window_origin {
                        WindowOrigin::Center => {
                            if visual_check[0].abs() > 1.0 || visual_check[1].abs() > 1.0 {
                                // commands.remove_one::<Draw>(entity);
                                // draw.is_visible = false;
                            }
                        }
                        WindowOrigin::BottomLeft => {}
                    }
                }
            }
        }
    }
}

I had to add a time check because the program panics if you delete Draw too early (it's a failed assert within wgpu related to removing abandoned assets).

My Tests

  • Spawn 5000 entities with SpriteComponents
  • Default values, other than adding a material and setting translation x and y values to random numbers so that entities spread out beyond camera view
  • Default plugins + FrameTimeDiagnosticsPlugin and PrintDiagnosticsPlugin

Baseline: Rendering Everything

Running the system with those two lines commented out (exactly as shown above):

Diagnostics:
---------------------------------------------------------------------------------------------
fps                                                              : 11.652735   (avg 11.539860)

frame_time                                                       : 0.077002    (avg 0.085608)

Test 1: Set is_visible to false

Running the system with the draw.is_visible = false; line active

Diagnostics:
---------------------------------------------------------------------------------------------
fps                                                              : 17.670094   (avg 17.110662)

frame_time                                                       : 0.057192    (avg 0.060759)

Setting is_visible to false upon creation provides the same results

Test 2: Remove Draw component entirely

Running the system with the commands.remove_one::<Draw>(entity); line active

Diagnostics:
---------------------------------------------------------------------------------------------
fps                                                              : 51.589498   (avg 52.723448)

frame_time                                                       : 0.018147    (avg 0.019235)

Removing other render-related components did not appear to change the fps at all.

@cart
Copy link
Member

cart commented Aug 15, 2020

Yeah theres definitely a lot of work to do on the performance front. I think Transform gpu buffer construction also plays a role in this.

Off the top of my head, the clear improvements to be made are:

  1. Make "is_visible: false" perf as close as possible to fully removing the Draw component. I'm not quite sure what the core issue is here, but that should be pretty easy to nail down.
  2. Implement instancing: GPU Instancing #89
  3. Implement batching
  4. Cut down on redundant work using the new Changed api: Now that we have Changed<T>, optimize all the things #63
  5. Optimize hash functions used: Start using aHash where it makes sense. #157
  6. Reduce the amount of hashing in general
  7. Cull objects not visible by the camera (as you're doing in your example above)

I think it makes sense to scope this issue to (1).

@GabCampbell GabCampbell added the C-Performance A change motivated by improving speed, memory usage or compile times label Aug 15, 2020
@ifletsomeclaire
Copy link
Contributor Author

I added a PR to resolve item 1 on @cart's list. A couple checks for is_visible in the render crate improved performance to be on par with removing the Draw component:

Diagnostics:
---------------------------------------------------------------------------------------------
fps                                                              : 54.102264   (avg 53.840505)

frame_time                                                       : 0.018435    (avg 0.018541)

However, there does appear to be a larger issue to resolve here - on a whim, I despawned the entities instead of setting is_visible to false and still got the same fps as above, even though I only had 2 entities remaining. I know there is already an issue with despawning entities #135, but I didn't get a panic, just removed 4998 entities (printed entity counts to be sure, and they are actually gone) and kept 2 at 53/54 fps.

This is the same as what occurs when you remove the Draw component, and is significantly slower than what you can get just using the ECS features without rendering - I was able to do mutating Transform/Translation/Scale/Rotation on 1 million entities without dropping below 60 fps on my machine. I think there might be something sticking around when components/entities are removed, which could be causing this performance hit.

Will keep looking into this to see if I can find the root cause, but I'm not totally confident in my ability to do so ;) Any tips or assistance welcome!

@cart
Copy link
Member

cart commented Aug 15, 2020

Fantastic! The remaining perf drop is almost certainly the GPU buffers not resizing when entities are removed.

@cart
Copy link
Member

cart commented Aug 16, 2020

We dont want to resize them every time an entity is removed as that would be expensive. So we should probably use array management techniques similar to Vec implementations.

@profan
Copy link

profan commented Sep 24, 2020

You don't necessarily need to resize the buffers unless you're short of GPU memory, can just make sure next time you upload data you only upload as much data into the buffer as you have entities for, should definitely work out the problem, though I imagine this is what you're planning already probably?

@alice-i-cecile
Copy link
Member

alice-i-cecile commented Jan 16, 2021

A user ran into this issue again today while testing hiding entities to perform tile map chunking: https://discord.com/channels/691052431525675048/743664267856838736/799944389023629322

Spawning and despawning the entities completely resulted in a 3x performance gain over toggling their visibility, which was very surprising to me.

@alice-i-cecile
Copy link
Member

This should be greatly improved by #1492 and related PRs.

@alice-i-cecile
Copy link
Member

Closing as frustum culling is now merged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-Performance A change motivated by improving speed, memory usage or compile times
Projects
None yet
Development

No branches or pull requests

5 participants