You are browsing as a guest. Sign up (or log in) to start making projects!

Kiwian

@Kiwian

Joined June 1st, 2026

  • 2Devlogs
  • 1Projects
  • 0Ships
  • 0Votes
Heallo !! I like making voxel thingies in C# and OpenGL.
check out my website !! (https://kiwiandoesthings.place)
Open comments for this post

7h 43m 55s logged

Thread safety and OpenGL

Threads are a huge part of KiwiCubed. When running an integrated server, you are already running 5 central threads at once (render/tick/network for the client, and tick/network for the server). And getting these threads to work together is rarely simple.

After I had first finished getting the server to send over player join packets, and getting player models to parse and render correctly, I figured I’d be done. However, whenever the client would try to render a new player, I’d get a weird OpenGL error saying that no vertex array object was bound.

This was weird, because I have a simple abstraction layer over OpenGL, that currently is very aggressive with making sure that no errors like this occur, forsaking a small amount of performance (because OpenGL is not what I want to spend my time debugging when implementing features). Because of this, it should have been basically impossible for the program to ever be in an invalid OpenGL state.

After adding some logs to see the exact ID of every vertex array object generated, saw that the ID for the player was always 0, and finally realized the problem.

When the client received a packet informing it of a new entity, the NetworkHandler would call a callback registered by a different part of the program. This callback would immediately spawn and setup the full entity. Crucially, for entities with EntityRenderableComponent‘s, this included generating OpenGL buffers and uploading data to them.

The thing is, LiteNetLib (the networking library I use) doesn’t run on the main thread. It uses some worker thread(s?) so it can send and receive packets asynchronously. This meant that, all entity setup code from network-received entities would be run on a background thread.

If you don’t know, OpenGL is very thread aware, and only lets you do operations from the thread where the OpenGL context is generated. You can manually set up a “shared context”, but the shared context actually doesn’t support specifically vertex array object creation, which makes it useless for my purposes anyways.

Basically, what would happen, is that the NetworkHandler would receive the entity and set it up on that thread, and then OpenGL would SILENTLY fail the several operations involved in setting up an entity.

Moral of the story: I hate threading. And also why does OpenGL not give errors for trying to use it from the wrong thread? wtf
(I used lazy initialization to fix it. Kind of a band-aid fix but it was the best I could think of at the time)

Thread safety and OpenGL

Threads are a huge part of KiwiCubed. When running an integrated server, you are already running 5 central threads at once (render/tick/network for the client, and tick/network for the server). And getting these threads to work together is rarely simple.

After I had first finished getting the server to send over player join packets, and getting player models to parse and render correctly, I figured I’d be done. However, whenever the client would try to render a new player, I’d get a weird OpenGL error saying that no vertex array object was bound.

This was weird, because I have a simple abstraction layer over OpenGL, that currently is very aggressive with making sure that no errors like this occur, forsaking a small amount of performance (because OpenGL is not what I want to spend my time debugging when implementing features). Because of this, it should have been basically impossible for the program to ever be in an invalid OpenGL state.

After adding some logs to see the exact ID of every vertex array object generated, saw that the ID for the player was always 0, and finally realized the problem.

When the client received a packet informing it of a new entity, the NetworkHandler would call a callback registered by a different part of the program. This callback would immediately spawn and setup the full entity. Crucially, for entities with EntityRenderableComponent‘s, this included generating OpenGL buffers and uploading data to them.

The thing is, LiteNetLib (the networking library I use) doesn’t run on the main thread. It uses some worker thread(s?) so it can send and receive packets asynchronously. This meant that, all entity setup code from network-received entities would be run on a background thread.

If you don’t know, OpenGL is very thread aware, and only lets you do operations from the thread where the OpenGL context is generated. You can manually set up a “shared context”, but the shared context actually doesn’t support specifically vertex array object creation, which makes it useless for my purposes anyways.

Basically, what would happen, is that the NetworkHandler would receive the entity and set it up on that thread, and then OpenGL would SILENTLY fail the several operations involved in setting up an entity.

Moral of the story: I hate threading. And also why does OpenGL not give errors for trying to use it from the wrong thread? wtf
(I used lazy initialization to fix it. Kind of a band-aid fix but it was the best I could think of at the time)

Replying to @Kiwian

0
0
Open comments for this post

3h 6m 53s logged

Finally fixed a client-side rendering issue I introduced while trying to implement multiplayer.


See, for normal entities, their positions are only updated roughly every 3 frames (1 tick w/ 20tps & 60fps). Because of this, the render thread actually keeps track of each entity’s current and last position, and then interpolates between those positions based on how far through the current tick is when rendering. This is the same method I used for determining the camera position for the player. However, there is an issue with this method.

It’s very hard to get the tick thread to run on an exact 50ms interval because of OS-level limitations. Because of this, based on how the render/tick threads are aligned, the render thread may find that the value used for seeing how far along the current tick is (usually 0.0-1.0) is actually 1.0+. When this happens, it causes visible stuttering and jittering with entity positions.
Now, this issue doesn’t really affect entities, as there is a simple solution, which is to simply keep a slightly larger buffer of entity positions. Then, instead of using the current/last tick positions to interpolate, it uses the 2/3 last positions in the buffer, allowing the tick thread some wiggle room.

Of course, this doesn’t work for the player, which needs to update visually as quickly as possible. And I finally made the connection that this method I was planning to use on other entities wouldn’t work on the player. So I’ve finally changed the engine to query player input and apply player physics every frame, separate from all other entities, and I’ve refactored the physics system to be able to handle being called at differing rates (20tps vs 60fps);

Finally fixed a client-side rendering issue I introduced while trying to implement multiplayer.


See, for normal entities, their positions are only updated roughly every 3 frames (1 tick w/ 20tps & 60fps). Because of this, the render thread actually keeps track of each entity’s current and last position, and then interpolates between those positions based on how far through the current tick is when rendering. This is the same method I used for determining the camera position for the player. However, there is an issue with this method.

It’s very hard to get the tick thread to run on an exact 50ms interval because of OS-level limitations. Because of this, based on how the render/tick threads are aligned, the render thread may find that the value used for seeing how far along the current tick is (usually 0.0-1.0) is actually 1.0+. When this happens, it causes visible stuttering and jittering with entity positions.
Now, this issue doesn’t really affect entities, as there is a simple solution, which is to simply keep a slightly larger buffer of entity positions. Then, instead of using the current/last tick positions to interpolate, it uses the 2/3 last positions in the buffer, allowing the tick thread some wiggle room.

Of course, this doesn’t work for the player, which needs to update visually as quickly as possible. And I finally made the connection that this method I was planning to use on other entities wouldn’t work on the player. So I’ve finally changed the engine to query player input and apply player physics every frame, separate from all other entities, and I’ve refactored the physics system to be able to handle being called at differing rates (20tps vs 60fps);

Replying to @Kiwian

0
2

Followers

Loading…