Bevy for embedded platforms (retro consoles) #10680
Replies: 6 comments 23 replies
-
I'm happy to support any PRs that swap over from I think |
Beta Was this translation helpful? Give feedback.
-
Okay, so I did more digging tonight. bevy_ecs has 13 direct dependencies. It seems that tracing's dependence on Arc, which depends on AtomicPtr, is killing the idea of a no_std bevy_ecs. With that major issue plus some others I found, my motivation for this is kinda killed, but I'll post my notes for all the libraries I did figure out for those who may want to know: bevy_ptrNo modifications necessary. bevy_reflectbevy_mathNo modifications necessary. bevy_reflect_deriveNo modifications necessary. bevy_utilsbevy_ptrbevy_tasksTODO bevy_utilsI always feel uneasy when I see a "utils" thing, because that's usually just a box for random things people felt were too small to deserve their own space. I'm unsure what bevy_ecs needs this for, but it looks to be a significant hurdle. It seems bevy_ecs uses bevy_utils for tracing, SyncCell, and a few other things. ahashgetrandomNot looking good. Depends on syscalls to get random numbers from the OS. I'm not sure what bevy_ecs would need that for and am hoping we can just hide it behind a feature flag. tracingDisable default features and you'll get most of the way there. This has already been done but for some reason we explicitly enabled std. I'm not yet sure why. instantHeavy dependency on SystemTime, which has dependence on the OS. I don't think we can make this one no_std. We'll have to see if we can feature gate it. uuidTODO hashbrownTODO bevy_utils_proc_macrosTODO petgraphTODO thiserrornonmaxTODO async-channelTODO event-listenerTODO thread_localTODO fixedbitsetTODO rustc-hashTODO downcast-rsTODO serdeDisable default features, enable alloc and derive features. thiserrorReplace with "thiserror-core", a drop-in replacement fork of thiserror that uses a Rust nightly feature to support no_std. Then disable default features. |
Beta Was this translation helpful? Give feedback.
-
Just figured I'd add to this with my own efforts to get bevy running in no_std: https://github.com/seurimas/bevy Many changes in that fork may be unnecessary, particularly the ones affecting now-optional crates like bevy_reflect/bevy_tasks. I'm targeting the Playdate (https://github.com/seurimas/bevy_playdate), building off of the crank/crankstart libraries to get all that running. Getting Bevy to work in no_std was mostly an effort in removal and falling back on alloc and third-party drop-ins for Some take aways: bevy_reflect, bevy_a11y, bevy_log (easy fixes)I had to make all of these optional features in various crates. It's possible bevy_reflect could have been fitted out for no_std, but the many, many std-default crates involved made the prospect seem like more effort than it was worth. I think bevy_reflect could be more properly optional in a lot of crates in the real Bevy, even for non-no_std purposes. The changes are fairly minimal, and some people might want to avoid the overhead. Additionally, some crates already make a point of making bevy_reflect optional. bevy_tasks (easy fix?)I've not really put my application through its paces well enough to say for sure that removing this is safe. Time will tell, but it seems that tasks are mostly used by other crates that are not included. Petgraph (upstream, but possibly on their radar already)petgraph is currently not properly no_std compatible, due to dependencies having default features, mostly. As such, I had to make those changes, which could be merged back in, as they seem open to supporting no_std: https://github.com/seurimas/petgraph web_time/instant (upstream, no good solution unless we supply our own)We need Instant::now, but that's not supported outside of std. I had to implement my own: https://github.com/seurimas/instant Obviously, this isn't a broadly applicable solution. Until we get some sort of
|
Beta Was this translation helpful? Give feedback.
-
I'm super excited for this, I've just been able to fit it onto an esp32-c3 with wifi and a simple motor / depth sensor program in an std environment using |
Beta Was this translation helpful? Give feedback.
-
@seurimas was you finally able to run |
Beta Was this translation helpful? Give feedback.
-
Updates on this: getting |
Beta Was this translation helpful? Give feedback.
-
I enjoy home brewing for older consoles. I enjoy embedded in general.
An ECS for such a machine would be very interesting, as I can see a few interesting advantages to the architecture on an older console, and I don't think there has ever been an opportunity to use ECS on such a console since it's a relatively new concept.
I was hoping to talk a bit about the idea of running the core of Bevy on embedded systems, including retro consoles from around the mid90s to 2010 range. I don't think running on such devices should be a high priority goal for Bevy at the moment because many of the features needed are not yet stable Rust. I also think the goal should not apply to all crates in the project, as most would be irrelevant or impractical in such an environment. I think just getting the ECS alone to work in such an environment would be revolutionary for retro homebrew.
The console I am most familiar with is the Game Boy Advanced. I'm going to use that console as my primary example, but the constraints of the hardware I'm going to mention are pretty universal to all embedded platforms.
Memory
Organizing the usage of memory maps
Many systems of this type have different kinds of memory. In the GBA you have three types of memory:
&static _
so I don't think Bevy really needs to worry about this. Data that needs frequent/fast access (including executable code) is often copied into one of the working memory areas. I don't think any of these things should be managed by Bevy.This is a very common kind of setup for embedded systems. Some will include sections of memory with verification mechanisms, for safety critical data. This RAM is typically useful to game projects and will likely just get wrangled in with the rest of the system memory.
Typically a GBA project will have two allocators, one for the IWM, and another for the EWM. Enabling developers to pick which allocator to use would be a powerful boon, and likely the biggest advantage to using an ECS on a console like this. I think the ability to select storage with
#[component(storage = "SparseSet")]
could revolutionize the usage of these two allocators, making it easy to store frequently accessed components in the "InternalTable" and infrequently accessed components in the "ExternalTable". All these tables could just be the standard one but backed by different allocators.Avoiding Out of Memory conditions
With memory spaces being 32kb or 256kb in size, it's easy to run yourself out of memory if you get careless. The fact that you can have "partially loaded" entities by simply removing components is a huge advantage.
Portability
You can run any game on a PC with the appropriate emulator but it would be nice to have native ports. With an ECS being so modular, it would be easy to swap out your GBA graphics and sound systems with PC graphics and sound systems. You would need to swap out your "InternalTable" and "ExternalTable" allocators to both just use the default allocator. For a game designed to run on 288kb of RAM and maybe a 32Mb ROM cartridge, it should be easy for such a game to run on even old PCs.
What it would take to make this possible
A quick browse through the source code, a major constraint that I can see at the moment is that I can't add custom storage without modifying this struct and the way it is referenced.
Rather than speculate on what other issues there could be, I decided to just try building bevy_ecs for the Game Boy Advance.
I cloned the agb template and added
bevy_ecs = { version = "0.12.0", default-features = false }
to the list of dependencies.I encountered the following problems:
alloc::sync
andalloc::task
are unavailable. That's a bit of an odd one since as far as I understand a single core CPU should only need synchronization to protect data structs from interrupts (this is typically implemented by disabling interrupts whenever a mutex is locked). All pointer operations can be considered atomic since an interrupt can't interrupt mid-instruction execution. They'll either wait until the instruction completes or cancel the instruction and jump back to it later, depending on the platform and its configuration.std
directly, rather than going throughcore
. This is by far the most common error I got.These two issues were the bulk of it, but there was over 5000 errors and I was not willing to read each one individually.
I think the idea of bevy_ecs in an embedded environment is an interesting idea, possible, and benificial, but I don't think it should be a high priority for the team, if a priority at all. I started this discussion so I could assess why someone would want this functionality and what would be needed to make it a reality.
Related discussions
It seems that there was a push to make Bevy no_std friendly in the past but was cancelled: #705
I don't think we should make all of Bevy no_std so I agree with the discussion, but just the ECS being no_std would be powerful for the reasons explained above.
If the Bevy team disagrees then that's fine. This can be archived and used as documentation of why it's difficult to make the project no_std.
Beta Was this translation helpful? Give feedback.
All reactions