I've been building up to it, but it's time... My voxel engine shall be oxidized! My Blog: blog.transrigh... Mastodon: social.transri... Discord: / discord
I should note that release mode can have a truly massive impact on the speed of code. I was working on some rust code involving fairly simple image generation that took a second or two in debug, and around 80ms in release mode. Debug mode takes away a lot of the compiler's ability to optimize away abstractions. I haven't seen any other situations with such massive performance differences, but it definitely taught me to never think about performance of code running in debug mode.
It honestly is amazing how much of a performance difference there is between debug and release. While I consider it best practice for myself to make sure programs still perform well in debug mode (since I use it), it makes me feel a lot more confident that come deployment time, it'll run butter smooth.
Debug mode don't take away the compiler ability to optimize away things, it is what is expected form a debug build, because well, it's for debugging. If you use an actual debugger you want to see all of what the code is doing, and compiler optimizations are nowaday absolute bonkers and result in hard to follow dataflow, you want to see if it actually calls the function you want, so you put a breakpoint, but in release maybe that whole branch is optimized away! So yeah it's not that it can't optimize, it is that it does'nt want to! Debug builds are made to be as close as what is expected to happen. When writing simple functions and looking at the outputed assembly, it sometimes calls some noop functions because it has to introduce that call to make the code follow the intended path. What you can do tho is to set the build cfg on a dependency basis, you can set your deps to be build with release mode even when building the project in debug, this is what bevy recommend for example.
Rust's "zero-cost abstractions" are only really zero-cost when you enable optimizations. Everyone uses iterators and closures and whatnot that make the code much easier to write and read, but without optimizations enabled this stuff doesn't get optimized away, and it's much slower than the regular control flow you'd write in a language like C. That's why there tends to be a 10-20x speed difference between debug and release in Rust. When working on resource-intensive code like games, a lot of people put this in their manifest: [profile.dev.package."*"] opt-level = 2 This optimizes all your dependencies while keeping your own code unoptimized, so you can still debug it easily.
Debug builds also contain all symbol information required for debugging As well as any debug information for any more modules/crates/libraries that you use For instance, if there are debug print commands or other debug-specific control flows
if you have data in a String or a Vec that you aren't going to resize, then it's better to use Box or Box (or any other pointer type e.g. Rc, depending on your need). it reduces the size from 24 bytes (ptr + len + capacity) to 16 (ptr + len), and in the case of Rc you also benefit from cheap cloning.
so... much... dynamic... dispatch Maybe a function pointer or a static dynamic function reference instead of a reference counted dynamic function value would be faster. At the end of the day, I don't really know your codebase, so do what you like to do.
Btw for C#, what version of C# did you use? It has nullables so that T is guaranteed to exist and only "T?" can be null. I've found that this feature alone makes it miles more enjoyable to work with.
Real. C# seems to be implementing more and more modern features. With the version of C# I recently updated to (C# 12) it introduced compact array construction, meaning instead of `new int[] {1, 2, 3, 4}`, you can just do `[1, 2, 3, 4]`. You can even construct lists with this, instead of `new List()`, it's literally just `[]`.
@Speykious I'm not sure of the exact version, I've used many over the years. I know C# does have nullables, but I found working with them to be annoying at best. It's still null anyways, just wrapped in a prettier package.
If you don't use bevy, how exactly do you render to the screen? I would love to try something similar but I alway lose motivation after trying to open window for 5 hours without any success.
yoo chat. this is basically my dream game! I like modded minecraft with tech mods n stuff but its too limited in my opinion! And its annoying having 10 different types of copper ore lmao!
I’ve implemented a voxel engine using C++ and OpenGL, and I’m wondering if it’s better to use a single shader for all elements. I’m unsure of the "best" approach, would it be more efficient to switch between different shaders (e.g., a "GUI" shader and a "world" shader) despite the overhead, or would it be preferable to use one shader for everything?
There is probably a document out there that kind of ranks the performance cost of OpenGL operations, but I would say don't worry about it until it becomes a problem. Premature optimization leads to more headaches than its worth. Switching shaders does have a performance cost, but grouping your render calls by shader makes it almost pointless to worry about.
Shouldn't be a problem as long as you're meshing your voxels and not drawing them one by one. In (old) minecraft the solid geometry and the water are drawn as two separate meshes and then all the mobs, entities, ui elements, etc are just separate draw calls with no optimization done whatsoever You can always do a stress test to get a feel for your headroom, like drawing N random entities and ramping up N until performance dips below your target framerate
we all fall into some stereotypes, who cares, there's no shame in being a stereotype if it''s something that makes you happy. so i might point out that the only thing *you* are helping by making that comment is the process by which people internalize shame about who they are. living your life for what others think of you is a miserable way to be, and I, for one, am happy for Destiny that she seems to be doing what makes her happy and sharing it with others. it's tough to put yourself and your work out there to be judged by the world and I just wish everyone could try to encourage this in others instead of tearing them down. :c
Why do you have name and description be strings on the voxel? Wouldn't it be better to have a basic enum that has all the variants of the voxel, and then have a method that gives you name and description of that enum variant? This would give you lower memory footprint and better clone performance. If you don't need owned strings you can even return static string slices from the enum method so you save yourself having to create the string every time you call the method
Since all voxels are always wrapped in reference counters, the string never gets copied. So the memory footprint isn't affected by how much data is stored on a given voxel, just how many variants of voxels exist. Performance is also great since RCs are really cheap to clone.
@@DestinyHailstorm I don't know that seems a bit weird still, I'd put this info in the Voxelidentifier and make methods to get the static information. This way you get type safety over the identifier and the static data, and you have more flexibility when creating voxels and less code duplication. Not sure if there is another reason why it's this way Even the VoxelIdentifier, it uses strings to identify it, this too would be a nice enum. And you still have the flexibility of being able to create "custom runtime" voxels by using a string identifier in a "Other(String)" variant. This would give you more safety over matching voxels in your code, also faster because you will be comparing numbers rather than strings
@@specy_ You've misunderstood the use of the code. While in a pure Rust code base an enum would be reasonable to accomplish all of this, I want to allow users to mod the game with Lua, so these values need to be able to be created at runtime, not compile time. While currently identifiers are made using strings, later I'll add an internal cached hash code or something to speed up the comparisons. I can convert the fields into method calls, but they are static, so I don't need them to be. Them being methods would mean it is no longer static.
@@DestinyHailstorm ye I thought of that, that's why I had suggested the Other(String) variant. You still have all the flexibility but with added type safety, but I understand that's more annoying to work with, especially for the rust to lua interoperability. You would still have a pool of voxels, by going enum you'd just move the static data from the pool to the code itself
While I get your idea in principle, it doesn't make any sense to do still. Having the behavior be different for internal vs external isn't something I want to do, and I'd rather everything be the same and slightly inefficient over trying to squeeze every drop of performance out of my engine. If it becomes an issue in the future, there are many ways I can easily address those problems. But I've yet to run into those issues.
Behold, someone who cares more about the programming and problem solving, than they do about making a game itself, granted, if you can pull it off its pretty neat, but...lets be real here, does the player really care, if you're not asking your self "what does the player think" at any point in your project, you're just tryna flex your programming might
Maintainability and DEX directly translates to longevity and reliability. It sounds like you’re the one who cares a little bit too much about specific programming when you’re apparently speaking for ‘players who don’t care’. I’m pretty sure most people would prefer the developer be happy working on their game
I should note that release mode can have a truly massive impact on the speed of code. I was working on some rust code involving fairly simple image generation that took a second or two in debug, and around 80ms in release mode.
Debug mode takes away a lot of the compiler's ability to optimize away abstractions.
I haven't seen any other situations with such massive performance differences, but it definitely taught me to never think about performance of code running in debug mode.
It honestly is amazing how much of a performance difference there is between debug and release.
While I consider it best practice for myself to make sure programs still perform well in debug mode (since I use it), it makes me feel a lot more confident that come deployment time, it'll run butter smooth.
Debug mode don't take away the compiler ability to optimize away things, it is what is expected form a debug build, because well, it's for debugging. If you use an actual debugger you want to see all of what the code is doing, and compiler optimizations are nowaday absolute bonkers and result in hard to follow dataflow, you want to see if it actually calls the function you want, so you put a breakpoint, but in release maybe that whole branch is optimized away! So yeah it's not that it can't optimize, it is that it does'nt want to! Debug builds are made to be as close as what is expected to happen. When writing simple functions and looking at the outputed assembly, it sometimes calls some noop functions because it has to introduce that call to make the code follow the intended path. What you can do tho is to set the build cfg on a dependency basis, you can set your deps to be build with release mode even when building the project in debug, this is what bevy recommend for example.
Rust's "zero-cost abstractions" are only really zero-cost when you enable optimizations. Everyone uses iterators and closures and whatnot that make the code much easier to write and read, but without optimizations enabled this stuff doesn't get optimized away, and it's much slower than the regular control flow you'd write in a language like C. That's why there tends to be a 10-20x speed difference between debug and release in Rust.
When working on resource-intensive code like games, a lot of people put this in their manifest:
[profile.dev.package."*"]
opt-level = 2
This optimizes all your dependencies while keeping your own code unoptimized, so you can still debug it easily.
Debug builds also contain all symbol information required for debugging
As well as any debug information for any more modules/crates/libraries that you use
For instance, if there are debug print commands or other debug-specific control flows
Honestly. I was making a clickbot that generated audio, and what took 30s on debug took at most 1s on release.
I should learn rust.
i am currently making a game in godot, but after that i might learn rust for some other stuff
bevy is rising, maybe looking into it a bit will help?
I wish i could use rust with godot, gdscript has caused me so many headaches
Really nice video… and your model is super cute… I’d like to give headpats if that’s okay
Sure :)
@@DestinyHailstorm patpat :3
@@centdemeern1 💀
i love this channel :3
Oh, I thought "rewrite in rust" was just a meme?
The world will be oxidized.
#[derive(Debug)] smh lol
Great video, fellow rustacean! I'm looking forward to more!
if you have data in a String or a Vec that you aren't going to resize, then it's better to use Box or Box (or any other pointer type e.g. Rc, depending on your need). it reduces the size from 24 bytes (ptr + len + capacity) to 16 (ptr + len), and in the case of Rc you also benefit from cheap cloning.
so... much... dynamic... dispatch
Maybe a function pointer or a static dynamic function reference instead of a reference counted dynamic function value would be faster. At the end of the day, I don't really know your codebase, so do what you like to do.
i really want to get into 3d graphics development as a c++/(rust in the future?) dev, where do you think i should start,?
Btw for C#, what version of C# did you use? It has nullables so that T is guaranteed to exist and only "T?" can be null. I've found that this feature alone makes it miles more enjoyable to work with.
Real. C# seems to be implementing more and more modern features. With the version of C# I recently updated to (C# 12) it introduced compact array construction, meaning instead of `new int[] {1, 2, 3, 4}`, you can just do `[1, 2, 3, 4]`. You can even construct lists with this, instead of `new List()`, it's literally just `[]`.
@Speykious I'm not sure of the exact version, I've used many over the years. I know C# does have nullables, but I found working with them to be annoying at best. It's still null anyways, just wrapped in a prettier package.
cool
Remember: factorio is also based on modded mc. So it is doubly modded inspired
If you don't use bevy, how exactly do you render to the screen? I would love to try something similar but I alway lose motivation after trying to open window for 5 hours without any success.
I ended up using glium and winit for this. Out of all the options I ran into, it was the easiest to use IMO.
yoo chat. this is basically my dream game! I like modded minecraft with tech mods n stuff but its too limited in my opinion! And its annoying having 10 different types of copper ore lmao!
Any half decent modpack will not have that issue... theres even mods to fix it for you
I’ve implemented a voxel engine using C++ and OpenGL, and I’m wondering if it’s better to use a single shader for all elements. I’m unsure of the "best" approach, would it be more efficient to switch between different shaders (e.g., a "GUI" shader and a "world" shader) despite the overhead, or would it be preferable to use one shader for everything?
There is probably a document out there that kind of ranks the performance cost of OpenGL operations, but I would say don't worry about it until it becomes a problem.
Premature optimization leads to more headaches than its worth. Switching shaders does have a performance cost, but grouping your render calls by shader makes it almost pointless to worry about.
Shouldn't be a problem as long as you're meshing your voxels and not drawing them one by one. In (old) minecraft the solid geometry and the water are drawn as two separate meshes and then all the mobs, entities, ui elements, etc are just separate draw calls with no optimization done whatsoever
You can always do a stress test to get a feel for your headroom, like drawing N random entities and ramping up N until performance dips below your target framerate
You're not helping the sterotype
Does she need to?
we all fall into some stereotypes, who cares, there's no shame in being a stereotype if it''s something that makes you happy.
so i might point out that the only thing *you* are helping by making that comment is the process by which people internalize shame about who they are.
living your life for what others think of you is a miserable way to be, and I, for one, am happy for Destiny that she seems to be doing what makes her happy and sharing it with others. it's tough to put yourself and your work out there to be judged by the world and I just wish everyone could try to encourage this in others instead of tearing them down. :c
@@juwulezYoure thinking too deep about it, it’s just a stupid joke
You’re not helping the stereotype either
@@anonymousalexander6005 It's a joke
Lua is great but ECS (ie Bevy) feels more like the HTMX of game dev
is htmx good or bad?
but my brain likes it :3
@@CGMossa the million dollar question
Why do you have name and description be strings on the voxel? Wouldn't it be better to have a basic enum that has all the variants of the voxel, and then have a method that gives you name and description of that enum variant? This would give you lower memory footprint and better clone performance. If you don't need owned strings you can even return static string slices from the enum method so you save yourself having to create the string every time you call the method
Since all voxels are always wrapped in reference counters, the string never gets copied. So the memory footprint isn't affected by how much data is stored on a given voxel, just how many variants of voxels exist. Performance is also great since RCs are really cheap to clone.
@@DestinyHailstorm I don't know that seems a bit weird still, I'd put this info in the Voxelidentifier and make methods to get the static information. This way you get type safety over the identifier and the static data, and you have more flexibility when creating voxels and less code duplication. Not sure if there is another reason why it's this way
Even the VoxelIdentifier, it uses strings to identify it, this too would be a nice enum. And you still have the flexibility of being able to create "custom runtime" voxels by using a string identifier in a "Other(String)" variant.
This would give you more safety over matching voxels in your code, also faster because you will be comparing numbers rather than strings
@@specy_ You've misunderstood the use of the code. While in a pure Rust code base an enum would be reasonable to accomplish all of this, I want to allow users to mod the game with Lua, so these values need to be able to be created at runtime, not compile time.
While currently identifiers are made using strings, later I'll add an internal cached hash code or something to speed up the comparisons.
I can convert the fields into method calls, but they are static, so I don't need them to be. Them being methods would mean it is no longer static.
@@DestinyHailstorm ye I thought of that, that's why I had suggested the Other(String) variant. You still have all the flexibility but with added type safety, but I understand that's more annoying to work with, especially for the rust to lua interoperability. You would still have a pool of voxels, by going enum you'd just move the static data from the pool to the code itself
While I get your idea in principle, it doesn't make any sense to do still. Having the behavior be different for internal vs external isn't something I want to do, and I'd rather everything be the same and slightly inefficient over trying to squeeze every drop of performance out of my engine.
If it becomes an issue in the future, there are many ways I can easily address those problems. But I've yet to run into those issues.
Behold, someone who cares more about the programming and problem solving, than they do about making a game itself, granted, if you can pull it off its pretty neat, but...lets be real here, does the player really care, if you're not asking your self "what does the player think" at any point in your project, you're just tryna flex your programming might
Maintainability and DEX directly translates to longevity and reliability. It sounds like you’re the one who cares a little bit too much about specific programming when you’re apparently speaking for ‘players who don’t care’.
I’m pretty sure most people would prefer the developer be happy working on their game
ack
ack