Raylib: [rlgl] Instanced rendering support

Created on 9 Mar 2020  路  16Comments  路  Source: raysan5/raylib

Instanced Rendering

"Instancing is a technique where we draw many objects at once with a single render call"

I learned about this technique from this guide from LearnOpenGL here. I wanted to use it with raylib but I could not find a way to do so without modifications to rlgl.

The goal is to make it easy and intuitive for a user new to raylib or instanced rendering and take their existing draw code and change it to a instanced version so they can quickly profile the performance difference and see if it makes sense for their program.

DrawModelsInstanced

I have seen other frameworks have a seperate function for drawing models with instancing. This is a valid approach however it is limited to just models. To support more than that you would need a instanced version for each function and any you add in the future which adds a lot of complexity to the api.

DrawModelsInstanced();
DrawModelsExInstanced();

DrawTexturesInstanced();
DrawTexturesExInstanced();

// And so on for all draw functions...

Proposed API

BeginModeInstanced(int instanceCount);
// Drawing here will draw instanceCount times
EndModeInstanced();

I starting with one function called rlSetInstances(int instanceCount) and got it to work and then split it into 2 functions once I looked at the Begin and End functions as that felt more consistent and intuitive. It needs a couple of variables added to RLGL.State to track if it is used and the instanceCount to draw with.

I also to modified rlglDraw and rlDrawMesh to use the instanced versions of those functions if instancing is currently being used.

glDrawArraysInstanced
glDrawElementsInstanced

It looks like they are both available in OpenGL 3.1 or higher. I have not looked fully into supporting the feature on older OpenGL versions. It makes sense to put these changes around a SUPPORT_GPU_INSTANCING define although I have not tried that yet.

Feature Screenshots

Note that these are not proper benchmarks. They are to see how many objects I can throw at the GPU using instancing while keeping a constant 60FPS. Proper benchmarks should be done across different platforms and setups.

The original bunnymark example drops FPS around 50000 bunnies. With instancing it can reach 500000+ bunnies.
textures_bunnymark_instanced1

I also ported the asteroids example from LearnOpenGL. There were some matrix math problems that took me a while to figure out but I managed to get it to work in the end.

With instanced rendering we can now set this value to 100000 that, with the rock model having 576 vertices, equals around 57 million vertices drawn each frame without any performance drops!

asteroids_instanced1

These examples are still a work in progress but they show the benefits that can be gained when you can use instancing. I also plan on making other examples using this technique to help test the different cases of this api. I will update this issue with more screenshots of my demos as I make them.

I don't think it will be ready in time for raylib 3.0. This issue is to for tracking the progress of this feature. When I feel I am ready to share my instancing demos I will link to them here.

new feature

Most helpful comment

Been a while since I opened this issue. Still more I want to do with this but I decided to share what I have so far. You can find the repo here.

Note that this is a work in progress so I will probably change things a bunch.

All 16 comments

@ChrisDill Thank you very much! An amazing feature and a great issue description! Congrats! :)

I had thought of this myself, but didn't have the time! Great idea, and looks to work well, and the proposed API looks to fit in well with the rest of raylib... do you have a fork you are working on I might have a bit of time I can contribute to it.... (@ChrisDill )

@chriscamacho I have a local project with these examples using a copy of raylib/src. I will share that in its own repo when it is ready and then I will add the changes to my fork and prepare a pull request. When it is available I will link to it here.

@ChrisDill Not sure if it is in your scope of work, but it may be beneficial to add support for OpenGL 2.0 and OpenGL ES 2.0 using https://github.com/KhronosGroup/OpenGL-Registry/blob/master/extensions/EXT/EXT_draw_instanced.txt as the source. This would also add WebGL support I think, and GLES 3.0 has these features built in. (I doubt it will work on OpenGL 1.0.... lol)

Any progress on instancing support?

@Gamerfiend That is something I will look into once I am happy with my instancing examples. I don't have experience with OpenGL ES or WebGL so it will be interesting to learn and if it is possible see what performance gains you can get.

@kororro I have made some progress but have been working on other things too. Almost ready to share the examples I have made.

@ChrisDill Do not worry about OpenGLES/WebGL, I can work on that part, I just need to query for the required extension and register if it is supported.

I further investigated it and those are my findings:

  • PLATFORM_WEB (WebGL 1.0): EXT_draw_instanced is not directly supported, but they have the custom extension ANGLE_instanced_arrays, I think I can try to map the calls to use it.

  • PLATFORM_ANDROID (OpenGL ES 2.0): It could be supported, we need to query for EXT_draw_instanced, probably new devices support it (<2 years old).

  • PLATFORM_RPI (OpenGL ES 2.0): Not supported.

Been a while since I opened this issue. Still more I want to do with this but I decided to share what I have so far. You can find the repo here.

Note that this is a work in progress so I will probably change things a bunch.

@ChrisDill Thanks for sharing! I'll take a look!

Quite keen to try this feature out. In case it's of use to anyone here is a proof of concept rlDrawMeshInstanced() function I'm using to reduce load in the meantime. It's not as nice as @ChrisDill's proposed API though :)

@seanpringle Thanks for sharing! I like the idea of the function passing in transforms as that is a common use case for models.

Currently I use opengl directly to create instance buffers. I would like rlgl to be able to create them so examples are not tied to opengl but I am unsure on the api for that.

@seanpringle @ChrisDill I also like the rlDrawMeshInstanced() function, maybe it's lower level than the proposed API but I also thinks that instancing is probably a functionality that only some advance users will require, so, it could be nice to keep it on rlgl. It also can be exposed in a higher level if required at some point.

1318 is not intended to derail this ticket. I think an eventual higher level API might be able to handle the instance buffer VBO(s) more intelligently, possibly with double buffering and better draw hints for static and dynamic instances.

@raysan5 Yeah instancing is powerful for specific use cases. I can add the rl prefix to make that clear that this is a low level rlgl feature.

I am considering replacing begin and end with setters like rlSetInstancedMode or rlSetInstancedCount. I am not sure if that would make a better api than using Begin and End though.

@ChrisDill I'm about to merge https://github.com/raysan5/raylib/pull/1318, do you find any problems with this implementation? It's lower level than your proposed API but more intended for advance users.

PR #1318 has been merged, rlDrawMeshInstanced() has been added.

As commented, this implementation has some limitations in terms of the instanced properties: only transform Matrix can be changed for every instance. Other properties like color are not considered with this implementation, a lower level API could be required for them... maybe in a future.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

RobLoach picture RobLoach  路  7Comments

Geams picture Geams  路  3Comments

raysan5 picture raysan5  路  12Comments

williamfjm picture williamfjm  路  3Comments

yashrk picture yashrk  路  5Comments