floooh / sokol

minimal cross-platform standalone C headers
https://floooh.github.io/sokol-html5
zlib License
6.53k stars 467 forks source link

[Question] Can sokol reuse webgl ids from glGen* #1042

Closed caiiiycuk closed 1 month ago

caiiiycuk commented 1 month ago

We recently ported the Perimeter game and it is not optimal, it generates a bunch of new buffers every frame and releases them at the end of the frame. The emscripten webgl layer have internal arrays of ids and they grow very fast because of this the game goes OOM after ~1 hour of playing.

We want to solve this by implementing buffer caching. We will cache sokol buffers using their length and type, this will help to avoid recreation of these buffers. However, sokol already have some pool mechanism, we set pool size on initialization. I wonder if sokol can internally reuse opengl ids to avoid such type of problem?

floooh commented 1 month ago

No, creating and destroying a sokol-gfx buffer will also create and destroy the underlying GL buffer object, which means it will "eat through" GL buffer ids.

For a buffer cache, it's probably best to create buffers in a number of size buckets with SG_USAGE_DYNAMIC. When you need a buffer, first check if a free buffer is in the matching size bucket, if not create a new one. Copy the data with sg_update_buffer() and start using the buffer (you can copy less data into the buffer than its size).

When no longer needed (e.g. instead of sg_destroy_buffer) put the buffer back into its bucket.

Since dynamic buffer objects are internally double-buffered, you should be able to immediately reclaim the buffer in the same frame it has been "released" and copy new data without overwriting any "in flight" data.

Only thing you need to be aware of is that you can only fill a buffer with data once per frame.

If there's a max number of buffers of predictable sizes that are created and destroyed each frame, an alternative model would be to create a fixed number of buffers upfront with SG_USAGE_STREAM, and just update them each frame with new data via sg_update_buffer() calls.

Basically SG_USAGE_DYNAMIC expects that buffer data is updated infrequently, while SG_USAGE_STREAM expects that buffers are updated each frame.

In any case, don't use the sg_append_buffer(), this has known performance issues on all GL backends.

caiiiycuk commented 1 month ago

Thank you for detailed explanation!