Summary
I want to implement a non-breaking method for instanced draw calls without the size limit of uniform arrays. For this, I'd like to use instanced arrays as vertex attributes.
The proposed API would look as follows:
let shader = Shader::from_vertex_file(ctx, "path/to/vertex_file")?;
shader.set_instanced_array(ctx, "a_offsets", offsets.as_slice()); // Offsets could be a Vec of any OpenGL attribute type
graphics::set_shader(ctx, &shader);
...
mesh.draw_instanced(ctx, 10_000, Vec2::new(16.0, 16.0));
It is very similar to the current uniform approach. Therefore offsets in this concrete example requires at least 10000 entries.
I am opening an issue for this, as I'd like to inform you about my intentions beforehand and because you may have some feedback or concerns regarding the API design or even overall idea.
Sidenote: As I am very new to OpenGL and barely having much freetime due to school, side job and my diploma thesis, this contribution may well take some time till it is complete.
Motivation/Examples
I'd need this change for a game prototype I am writing. I need to draw the same object 100s of 1000s of times. This leaves quite a performance impact. Instancing would be the ideal solution, but tetra currently only supports instancing using uniform arrays, which have quite a significant size limitation, only allowing some 1000s instances. Using instanced arrays, this limit can practically be (almost) completely removed, allowing even more performance gain due to even less draw calls.
One concrete use case would be very heavy particle effects, where half a million equal particles need to be drawn at the same time. Or for really suffisticated cellular automata simulations, where cellular states are better not to be stored as color in a texture but a more complex object.
Alternatives Considered
Two alternative API designs I've considered are the following:
let shader = Shader::from_vertex_file(ctx, "path/to/vertex_file")?;
graphics::set_shader(ctx, &shader);
...
let mut offset_attribute = Vec::new();
for i in 0..10_000 {
offset_attribute.push(some_offset); // Push offsets to attribute
}
let mut attributes = Vec::new();
attributes.push((3, offset_attribut)); // Push attribute location and per instance values
graphics::set_instances(ctx, 10_000, attributes); // All draw calls inside will be instanced 10_000 times using the given attributes
mesh.draw(ctx, Vec2::new(16.0, 16.0)); // Will be drawn 10000 times
graphics::reset_instances(ctx); // All subsequent draw calls will be handled normally
But I think this is too long and stateful.
let shader = Shader::from_vertex_file(ctx, "path/to/vertex_file")?;
graphics::set_shader(ctx, &shader);
...
let mut offset_attribute = Vec::new();
for i in 0..10_000 {
offset_attribute.push(some_offset); // Push offsets to attribute
}
let mut attributes = Vec::new();
attributes.push((3, offset_attribut)); // Push attribute location and per instance values
mesh.draw_instanced_array(ctx, 10_000, Vec2::new(16.0, 16.0), attributes); // Draws the mesh 10000 times using the given attributes
This is concise and effective, but in my humble opinion the originally proposed design better integrates into the current "workflow" for instancing.
Type: Feature Request