#Need info about GPU/vertex animation

78 messages · Page 1 of 1 (latest)

regal sierra
#

Are there any examples of animation implementation via vertex shader? I will be glad of any helpful information

barren gorge
# regal sierra Are there any examples of animation implementation via vertex shader? I will be ...

Could you elaborate a bit more? This is awfully little detail about what you actually want to do. "animation" could be anything from: "simply offsetting the meshes vertices slightly based on some noise seeded with time" which is like, 4 lines of shader code and really simple, to "full skinned mesh animation with bones, IK, weight-painting, etc" and knowing which you mean dramatically changes the resources and examples that will be useful to you

regal sierra
barren gorge
regal sierra
barren gorge
#

what i mainly wonder is whether the animations themselves were performing badly, or some other factor related to the number of entities, especially moving entities

regal sierra
# barren gorge How did you profile it? What was slow?

I tried to use the methods from the examples, but they did not include the possibility of using custom materials and instancing. Maybe I don't need gpu animation, but a way to use animation by creating an entity not as a SceneBundle, but by adding everything individually. I hope I made myself clear.

barren gorge
regal sierra
#

I'm asking about animation because I haven't found any examples of animation without loading an object as a SceneBundle

barren gorge
barren gorge
regal sierra
barren gorge
# regal sierra I upload a mesh/scene to a resource, and every time I create an entity, I use an...

firstly, i highly recommend upgrading. we're on bevy 0.17 now, its getting close to 2 years since 0.14 came out. I dont remember the state of... most things from 0.14 so i can't help you with that.

If its slower when you copy a scene from a resource, i suspect the cause is that its somehow duplicating the mesh and material assets which leads to zero instancing. Bevys built-in instancing works primarily by instancing entities using the same mesh and material handles.

#

that last point also means that if you want to change something about each instance, doing so through changing the material will require separate material assets and therefore break instancing

#

there, an image is used to change the instances color, but you could of course use some other set of data or even just randomize colors for example

regal sierra
barren gorge
barren gorge
regal sierra
# barren gorge could you show the code which changes the color? mainly the CPU side where you s...
let material;

if let Some(mat) = instanced_materials.team_materials.get(&(b.model.mesh.id(), *team)) {
    material = mat.clone();
} else {
    if let Some(original) = materials.get(b.model.material.id()) {
        material = extended_materials.add(ExtendedMaterial {
            base: original.clone(),
            extension: TeamMaterialExtension {
                team_color: color,
            },
        });
    } else {
        material = extended_materials.add(ExtendedMaterial {
            base: StandardMaterial{
                ..default()
            },
            extension: TeamMaterialExtension {
                team_color: color,
            },
        });
    }

    instanced_materials.team_materials.insert((b.model.mesh.id(), *team), material.clone());
}

new_unit = commands.spawn((
    MaterialMeshBundle{
        mesh: b.model.mesh.clone(),
        material: material.clone(),
        transform: Transform::from_translation(new_unit_position),
        ..default()
    },
barren gorge
#

okay, so you do set up different material extensions per team color. I assume then that the number of units spawned per team vastly outnumbers the amount of teams?

regal sierra
#
#import bevy_pbr::pbr_fragment::pbr_input_from_standard_material
#import bevy_pbr::pbr_functions::alpha_discard

#ifdef PREPASS_PIPELINE
#import bevy_pbr::prepass_io::{VertexOutput, FragmentOutput}
#import bevy_pbr::pbr_deferred_functions::deferred_output
#else
#import bevy_pbr::forward_io::{VertexOutput, FragmentOutput}
#import bevy_pbr::pbr_functions::{apply_pbr_lighting, main_pass_post_lighting_processing}
#endif

@group(2) @binding(120) var<uniform> white_replacement: vec4<f32>;

@fragment
fn fragment(
    in: VertexOutput,
    @builtin(front_facing) is_front: bool,
) -> FragmentOutput {
    var pbr = pbr_input_from_standard_material(in, is_front);

    var base_color = pbr.material.base_color;

    let is_white = 
        base_color.r > 0.95 &&
        base_color.g > 0.95 &&
        base_color.b > 0.95 &&
        base_color.a > 0.95;

    if (is_white) {
        base_color = white_replacement;
    }

    pbr.material.base_color = base_color;

    var out: FragmentOutput;
    out.color = apply_pbr_lighting(pbr);
    out.color = main_pass_post_lighting_processing(pbr, out.color);

    return out;
}
barren gorge
#

also: is this the version that works efficiently, or the version that works inefficiently?

barren gorge
#

okay, thats what i would expect

#

now what about the version that works inefficiently?

regal sierra
# barren gorge now what about the version that works inefficiently?

the same but instead of spawning this

MaterialMeshBundle{
      mesh: b.model.mesh.clone(),
      material: material.clone(),
      transform: Transform::from_translation(new_unit_position),
      ..default()
  },

there will be spawning of scene from resource

and also I do not know how to control the material used in the scene in this case.

barren gorge
regal sierra
barren gorge
#

Also, to be clear: There is one example of an animation implementation using vertex shader: bevys built-in PBR material. The skinning/morphing is done on the GPU, CPU-side its basically just computing the joint matrix/morph weights

regal sierra
#

when I spawned the scenes, I took the bundle with the unit from the producer entity component

pub struct UnitProductionBuildingComponent {
    pub available_to_build: HashMap<String, (UnitBundles, ProductionData)>,
    pub build_order: Vec<(i32, UnitBundles, ProductionData, CompanyTypes, (i32, i32, i32, i32, i32, i32, i32), String)>,
    pub spawn_point: Vec3,
    pub elapsed_time: u128,
}

the entity had this bundle from this resource when it was created

#[derive(Resource)]
pub struct ProducableUnits {
    pub barrack_producables: HashMap<String, (UnitBundles, ProductionData)>,
    pub factory_producables: HashMap<String, (UnitBundles, ProductionData)>,
}

and the bundle was added to this resource in the startup system. This bundle stores the scene uploaded during the startup

barren gorge
barren gorge
#

you just have a Handle<Scene> you insert repeatedly, yes?

regal sierra
#

bundle is always something like this

#[derive(Clone)]
pub struct AssaultBundle {
    pub scene: Scene,
    pub lod: PbrBundle,
    pub unit_component: UnitComponent,
    pub combat_component: CombatComponent,
    pub supplies_consumer: SuppliesConsumerComponent,
    pub controller: KinematicCharacterController,
}
#

and spawning is something like this

match &infantry_producer.1.build_order[0].1 {
    UnitBundles::Shock(b) => {
        unit_type = b.combat_component.unit_type.clone();

        new_unit = commands.spawn((
            SceneBundle{
                scene: b.scene.clone(),
                transform: Transform::from_translation(new_unit_position),
                ..default()
            },
            b.unit_component.clone(),
            CombatComponent {
                team: *team,
                current_health: b.combat_component.current_health,
                max_health: b.combat_component.max_health,
                unit_type: b.combat_component.unit_type.clone(),
                attack_type: b.combat_component.attack_type.clone(),
                attack_animation_type: b.combat_component.attack_animation_type.clone(),
                attack_frequency: b.combat_component.attack_frequency,
                attack_elapsed_time: b.combat_component.attack_elapsed_time,
                detection_range: b.combat_component.detection_range,
                attack_range: b.combat_component.attack_range,
                enemies: b.combat_component.enemies.clone(),
                is_static: b.combat_component.is_static,
                unit_data: (
                    tile,
                    (
                        infantry_producer.1.build_order[0].3,
                        infantry_producer.1.build_order[0].4,
                        infantry_producer.1.build_order[0].5.clone(),
                    ),
                ),
            },
            b.supplies_consumer.clone(),
            b.controller.clone(),
            SelectableUnit,
            LOD{
                detailed: (b.model.mesh.clone(), material.clone()),
                simplified: b.lod.clone(),
            },
        )).id();
    },
}
barren gorge
#

To be sure: could you write a systems that logs out the number of Image, StandardMaterial, and Mesh assets every once in a while?

#

you should be able to see that with SceneBundle the at least one of those numbers grows a lot, while with the other way with team materials it doesn't

regal sierra
barren gorge
#

if you repeatedly clone and spawn SceneBundle, do the asset counts go up? if they do, the issue is relatively clear

barren gorge
#

Note that i suspect your "how do i update the material after spawn" woes are likely nicely solved by updating to newer bevy by using component hooks to react to the insert events for StandardMaterial (filtering on something like CombatComponent so you dont overwrite the materials of other entities)

regal sierra
barren gorge
regal sierra
#

i'll check for scene now

#

so i need to clone a scene multiple times?

barren gorge
barren gorge
#

for this to actually work and show anything make sure your scene actually contains like, a mesh with a material. ideally the same thing you use in your game

regal sierra
regal sierra
# barren gorge hmmm, strange

these scenes didn't really cause any problems, I had problems when I tried to use skeletal animation. The fact is that this assumes that each unit will be a bundle of many entities, and this caused lags.

I guess I can use shape keys instead of skeleton and it will work better but I will have to solve the problem of replacing the material of these entities

barren gorge
#

besides the different spanwing, what are the differences between the efficient and the inefficient versions?

regal sierra
barren gorge
#

so

#

the efficient version just... didn't animate at all?

#

am i understanding that correctly?

regal sierra
barren gorge
regal sierra
#

in one case it's just mesh and material and in other case it's the same with ready to animate armature

barren gorge
regal sierra
#

no

barren gorge
#

okay.

#

this whole time i thought both versions had animations...

#

but the one spawned with scene was slower

#

Okay, next question: Please explain exactly how you determined the skeletal animation version is too slow for you.

Please tell me:

  • did you compile with --release when testing the performance?
  • did you have any opt-level config in your Cargo.toml at the time?
  • approx what number of entities with skeletal animation were being spawned?
  • what did you use to check the performance?
regal sierra
barren gorge
#

What i would recommend:

  • update to bevy 0.17 (there were a bunch of optimisations since then)
  • check performance with animations again
  • if its still bad, read profiling.md from the bevy repo and set up Tracy for profiling, then send the saved trace here