teh_cmc
Rerun was originally designed for few (i.e. dozens up to hundreds) massive entities (e.g. it's common for a single entity to have a few million 3D points and color values attached to it).
While we're slowly working towards improving the many-entities use-case, the correct thing to do in this case would probably be for Revy to identify that all these entities are really just different instances of the same batch (either automatically, or by exposing a marker component or something).
So, say, you'd set a marker component on all your tiles, Revy would then snapshot them as a single batch of 144^2 instances, and then in Rerun you'd see a single entity `/tiles` which would be a batch of 144^2 instances (each with their own set of components, that's fine!). From Rerun's point-of-view, this would be similar to a point cloud, and at 21k instances you'd be easily running at your monitor refresh rate with a lot of margin.
But by any means, try it! Not the web version though, you're definitely going to need multithreading :D
Nice project btw; I'll keep an eye on it and probably use it as a benchmark for the many-entities use-case!
If you squint enough, most ECS out there are pretty much very specialized relational databases that trade off flexibility in favor of performance for common gamedev use cases (very wide joins, very deep hierarchies (e.g. transform trees), full-table filters, etc).
Rerun's ECS goes one step further and makes time a first-class citizen, allowing for efficient joins across different components across different timestamps.
This is what makes it possible to only log diffs in Revy (we only snapshot the components that were modified during the last frame), rather than having to full snapshots every frame, which would be prohibitively expensive (both time and space). Rerun then stitches back everything together during visualization, in real-time!
Check out e.g. the live demo of the breakout example for example [1]: if you click on the pallet and then go to its parent node, you'll see that we just store that node's final transform (i.e. post-physics) every frame.
Happy gamedev!
[1] https://app.rerun.io/version/0.14.1/index.html?url=https://s...
So non-deterministic scheduling is just not an issue by default.
You could of course access the Revy logger from any system (it's just a `Resource` after all) and log arbitrary data to Rerun from there (the resource is basically a handle to the Rerun SDK). This still wouldn't be a problem. The data would once again be logged to the 3 same timelines (wall-clock, frame number and sim_time) and you would be able to visualize in which order the different systems doing the logging were scheduled during each frame.
That being said, I do intend to publish updates when new versions of either Rerun or Bevy land; if only to experiment with new APIs as they come online.
Now, to answer your question, I've been using Bevy since the 0.1 release and, in my experience, keeping up with the changes upstream has always been pretty painless. Their organization nand release process is top-notch, with some of the most high quality changelogs and migration guides I've ever seen in any project, and releases are rare enough (~about once a quarter) to just not be an issue.
The community maintains compatibility matrices such as this one [2], and things generally just work :tm:.
[1] https://www.reddit.com/r/rust/comments/1b6bqv1/revy_proofofc...
[2] https://github.com/rerun-io/revy?tab=readme-ov-file#compatib...
Feel free to ask me anything!
Demystifying Asynchronous Rust
Go-internals: Chapter 2, “Interfaces” released
“Go Internals” first chapter, a “A Primer on Go Assembly”, released
A quick look at some of Go's subtleties regarding interfaces
Show HN: Seq – a gentle overview of distributed sequential IDs, gRPC and Contexts
Of course, I don't have such calls on production systems; and while concurrent collections greatly reduce the time spent in STW phases, latency spikes are still a very real issue, as I explained here [1]. The monitoring on my production systems leaves absolutly no doubt that those latency spikes are due to garbage collections: once the GC was out of the picture, every thing was flat and fast again.
I know many people won't agree with that, and there are definitely good reasons not to; and still, as far as I'm concerned, minimizing the complexity of my software stack means there's one less thing that I'll have to worry about, and at the end of the day, that is really quite the upside.
Regarding overall performances, I'm not really convinced that the reflect calls affect them that much (there's 2 reflect calls per Read() or Write() call), but I honestly just don't know, I'd have to bench to be sure. Although, if it turns out to actually be an issue, you can still use Pointer() to keep references to your unmanaged heap and you'll be able to work with your data without GC nor reflection overhead.
In this configuration, each incoming request means allocations inside Go's RPC package [1], which in turn means that a GC pass will be triggered if GC_PERCENT [2] has been reached, which in turn means that the GC will have to scan all of those long lived pointers (Go's GC is not generational), which in turn means a huge peak in response time.
This basically leaves me with three possible solutions:
- hack into Go's RPC package to minimize allocations, which is a huge price to pay just to delay the inevitable
- build my caches in a language that offers manual memory management, then query those via RPC from my Go services; but I don't want to add a new language into the mix
- provide a generic solution for manual memory management in Go, which is where we are now