It's interesting how this pattern comes up in so many areas of programming. Nice look at Odin too. Minor criicism: I think your mic is much closer to your keyboard than your face. I'm a clicky keyboard user too, so I get it, but it's a bit of a strain to hear you over it.
@DylanFalconer last time I wrote a command buffer was in C on an 8-bit embedded microcontroller, kind of just discovered it due to necessity, but definitely a nicer implementation here with Odin, the union array is pretty slick.
With my games I have a global array `end_of_frame_tasks` which just collects callbacks and calls them at the end of the frame. The callbacks can perform arbitrary logic and their number usually doesn't exceed 100 tasks per frame, so it's not a perf issue. To do that in Odin, I guess I'd have to replicate a closure with a function pointer and a buffer with saved context for the function?
You could expand this to be multi-stage and batch the cleanup tasks by the earliest point at which you could execute them. For example, if you're drawing two passes and don't need a resource from the first pass for the second, you could clean that up well before the frame ends.
Subscribed man, great simple explanation of command buffers, the grocery shopping metaphor was perfect. Maybe an expanded video on queuing commands and batching by priority for a more advanced expansion to this would be awesome. For the really advanced, multi-threaded update/render loops with asynchronous command buffers.
Thanks for the video! I _just_ wrote a naive debug draw buffer with raylib in C and was storing separate arrays for each type (cube, sphere, grid etc) - using unions will help! Though in C will need to track type...
tagged unions let you figure out the type, then you just cast it. the advantage of a separate array for each type is that they're going to be more tightly packed, which will mean more items will fit in the CPU RAM cache at once, and you can handle just that type when looping over the items, which will speed up branch prediction. the advantage of a single array of a union type is that you just have the one array, which might be more convenient for your use case (for example if you're copying the data to some other layer, like the GPU, or have to serialize it to disk). but the cost is that your loops will have to dispatch to different bits of handler code for each item in the array, harming branch prediction, and there will be wasted space because each member of a union has the size of the largest union member, and has to add padding to compensate.
If you are storing pointers, they could be pointing to anywhere - may or may not be an issue depending on fragmentation and array size. Probably not an issue for debug stuff. Not sure what you mean by storing the type of each void* as C doesn't have run time type information
I'm going to have to disagree with you there. Buffered commands make sense in some situations and not others. In games that are heavily dependent on server response/lag then buffered commands can desync what you are seeing and what your character is doing making the game almost unplayable. For games like tournament fighting games where moves need to be executed in a precise order in long chains i.e. Tekken, then they make sense.
I guess (without looking right now) that is probably just a queue c: Command queueing is kinda of different since you're reifying that struct so you can use it whenever. There's actually a lot of use cases and is super useful.
Someone else mentioned that, too. I didn't think of it, though I'd guess that most games with input buffering use a command buffer or something very similar for it
It's interesting how this pattern comes up in so many areas of programming. Nice look at Odin too.
Minor criicism: I think your mic is much closer to your keyboard than your face. I'm a clicky keyboard user too, so I get it, but it's a bit of a strain to hear you over it.
Loved to see Odin in this example, great little language.
Yeah, it's great for these kinds of things!
@DylanFalconer last time I wrote a command buffer was in C on an 8-bit embedded microcontroller, kind of just discovered it due to necessity, but definitely a nicer implementation here with Odin, the union array is pretty slick.
With my games I have a global array `end_of_frame_tasks` which just collects callbacks and calls them at the end of the frame.
The callbacks can perform arbitrary logic and their number usually doesn't exceed 100 tasks per frame, so it's not a perf issue.
To do that in Odin, I guess I'd have to replicate a closure with a function pointer and a buffer with saved context for the function?
You could expand this to be multi-stage and batch the cleanup tasks by the earliest point at which you could execute them. For example, if you're drawing two passes and don't need a resource from the first pass for the second, you could clean that up well before the frame ends.
Respect for using vim!
Subscribed man, great simple explanation of command buffers, the grocery shopping metaphor was perfect. Maybe an expanded video on queuing commands and batching by priority for a more advanced expansion to this would be awesome. For the really advanced, multi-threaded update/render loops with asynchronous command buffers.
Those are great suggestions, thanks! I'll touch on more advanced use-cases in the future for sure
Thanks for the video!
I _just_ wrote a naive debug draw buffer with raylib in C and was storing separate arrays for each type (cube, sphere, grid etc) - using unions will help! Though in C will need to track type...
I have a hunch that you could use an array of void* pointers and an array that stores the type of each void*
Not sure if that's stupid or not lol
tagged unions let you figure out the type, then you just cast it.
the advantage of a separate array for each type is that they're going to be more tightly packed, which will mean more items will fit in the CPU RAM cache at once, and you can handle just that type when looping over the items, which will speed up branch prediction.
the advantage of a single array of a union type is that you just have the one array, which might be more convenient for your use case (for example if you're copying the data to some other layer, like the GPU, or have to serialize it to disk). but the cost is that your loops will have to dispatch to different bits of handler code for each item in the array, harming branch prediction, and there will be wasted space because each member of a union has the size of the largest union member, and has to add padding to compensate.
If you are storing pointers, they could be pointing to anywhere - may or may not be an issue depending on fragmentation and array size. Probably not an issue for debug stuff. Not sure what you mean by storing the type of each void* as C doesn't have run time type information
I'm going to have to disagree with you there. Buffered commands make sense in some situations and not others. In games that are heavily dependent on server response/lag then buffered commands can desync what you are seeing and what your character is doing making the game almost unplayable. For games like tournament fighting games where moves need to be executed in a precise order in long chains i.e. Tekken, then they make sense.
That's a different kind of buffered command, but may be implemented using what I show here
great concise video. thanks, subscribed.
Awesome, thank you!
I haven't looked but isn't this what raylib does internally too?
What exactly?
Raylib doesn't provide game loop, only reading inputs, play sounds you make it play and draw things you make it to draw.
@NeZversSounds the batching that happens when you are calling draw functions with the same draw state, is kind of a command buffer of shapes
I guess (without looking right now) that is probably just a queue c:
Command queueing is kinda of different since you're reifying that struct so you can use it whenever.
There's actually a lot of use cases and is super useful.
What about doing all of the update and drawing between BeginDrawing and EndDrawing?
You can do that for simple games/apps. It falls apart when you want to create more complex game loops that have different update timings
Nice to the point video.
Great that it uses Odin and Raylib, make things clear (for me at least).
Thanks!
Glad you liked it!
Great video, but I really thought the video was going to go into input buffering, a technique many fighting games employ.
Someone else mentioned that, too. I didn't think of it, though I'd guess that most games with input buffering use a command buffer or something very similar for it
this is why we don't code like this any more
We stopped coding altogether
@@DylanFalconer you might have, but natural born coders won't
Everyone and should are red flags to me.
Also would have been nice to know when and when not to use this.
You can read between the lines on that.
I discussed when to use them at 00:06
Edit out the keyboard thanks
Thanks, I'll see what I can do in the future