#sprites #bevy-plugin #lots #draw #render #call #instanced

bevy_sprite_instancing

A Bevy plugin to render lots of instanced sprites

2 releases

0.1.1 May 25, 2023
0.1.0 May 25, 2023

#1046 in Game dev

MIT/Apache

30KB
594 lines

bevy_sprite_instancing

crates.io License

A plugin for Bevy to render lots of instanced sprites in a single draw call.

How does the plugin work?

Instead of going through all similar entity sprites one-by-one, the plugin provides SpriteInstancingGroups which can contain Entity ID's of these sprites. The plugin then collect all these sprites' transforms and submits them to the GPU as one large buffer, which is then used to draw all of them at once.

Animated example

Features

  • Animated tiles
  • Nice FPS when drawing lots of sprites (up to 1 million)

How do I use this?

For a quick example, see this example.

Here's a simple example of how to draw lots of sprites:

pub const ENTITY_COUNT: usize = 100000;

fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
	// Create a camera
	commands.spawn(Camera2dBundle::default());

	// Load a spritesheet
	let spritesheet = InstancedSpritesheet {
		image: asset_server.load("textures/my_spritesheet.png"),
		// Size of this spritesheet in tiles
		width_tiles: 32,
		height_tiles: 32,
	};

	// Create an instancing group for the sprites
	let mut instancing_group = SpriteInstancingGroup {
		entities: HashSet::new(),
	};
	let group_id = commands.spawn_empty().id();

	// Spawn the sprites
	for _ in 0..ENTITY_COUNT {
		let position = Vec3::new(..., 0.0); // some random position
		let scale = Vec3::new(..., 1.0); // some random size
		let transform = ...; // create a transform from these
		let sprite = InstancedSprite {
			group_id,
			texture_index: 0
		};

		let entity = commands.spawn((transform, sprite)).id();
		instancing_group.entities.insert(entity);
	}

	// Attach the instancing group and its spritesheet
	commands.entity(group_id).insert((instancing_group, spritesheet));
}

fn main() {
	App::new()
		.add_plugins(DefaultPlugins)
		.add_plugin(InstancedSpriteRenderPlugin)
		.add_startup_system(setup)
		.run()
}

What I didn't yet implement

There're some high-level features I haven't yet implemented:

  • View region culling: no need to submit transforms of sprites which aren't present on screen
  • GPU animations: instead of submitting a texture_index for each sprite, maybe it would be a nice idea to submit anim_start_index and anim_len through a separate instancing buffer

... and some low-level ones:

  • Ability to mark a SpriteInstancingGroup as static (i.e. entities do not move) to avoid re-submitting its instancing data each frame

Dependencies

~36–76MB
~1M SLoC