#bevy_rand + bevy_turborand

154 messages · Page 1 of 1 (latest)

edgy copper
random glen
#

Hello! I'm trying to work with bevy_rand in an async compute task - I fork the rng and then move it into the async task. The task owns the forked Rng, and passes out a &mut Rng to its helper functions.

I want to make these helper functions into one shot systems (for testing purposes - in game they will only be called within the async compute task). Since these are methods on something that holds a &mut Rng (or even if the object didn't hold the &mut Rng - we would pass it in as a param) - I want to implement SystemParam on &'static mut Rng/EntropyComponent.

I don't have a lot of experience implementing SystemParam so not sure if I'm doing something super cursed or if implementing SystemParam on references is okay? Then again I'm not sure if the lifetimes line up

#

Would appreciate your thoughts on the SystemParam part @edgy copper 🙏

#

One way I guess this might be done is by passing the Component' s component id (assuming it's unique per entity)🤔

little cloak
#

@random glen Just as a thought here but, do these helper methods need to be specific about what type of Rng source they take and it being a reference? Perhaps the source/Rng can be defined with a generic or an impl so like:

fn test_rng(mut source: impl Rng) {
    println!("{}", source.gen::<u64>())
}

and then using it becomes just:

test_rng(rng.fork_rng());
#

Keep in mind that RngCore already implements an auto trait implementation for references of an RngCore, which Rng is then implemented automatically to anything that implements RngCore. So in effect, this also works with the above idea:

let mut r = rng.fork_rng();

test_rng(&mut r);
#

This then at least makes your helper methods less coupled to bevy internals, lessening the need to complicate how you test them.

random glen
# little cloak <@213037346575089667> Just as a thought here but, do these helper methods need ...

Just thinking out loud here

I'm procedurally generating the world in my game (long-running task that isn't required for the next frame - so it must be Async Compute).

Each task is composed of several "recipes" applied to a graph, each of which I want to be able to run and test dynamically in my test ui tool.

In the test app I can create an entity, and run one shot systems on it (press a button and run a recipe on it). The graph can be a component on the entity.

For the game however - I can't bring anything in from the world into the Async Compute task. (So the graph is created and recipes are run on it inside the task - no recipes or anything).

Maybe the solution is in running the games procedural generation using one shot systems? I just don't want the systems to be blocking the frame rate in case I want to do the generation in the background🤔

After writing this all out my issue is maybe only tangentially related to bevy_rand - maybe I should post in the ECS channel🤔

little cloak
#

Yeah, sounds like it. The bevy_rand part seems easy to work around here, but the rest of it seems more involved

edgy copper
thick mirage
#

Hello, I would like to randomly choose an item in a vector with a bias or weight with a function like rand's choose_weighted(). But I also would like to use Bevy's bevy_rand crate's facilities for using PRNGs with the ECS system, and it does not seem to include a choose_weighted()-like function. Am I mistaken? Is there a recommended Bevy way to do that?

little cloak
#

@thick mirage You just pull in SliceRandom trait from rand crate and get in there with .choose_weighted(). bevy_rand is just meant to make it easier to have PRNGs integrated into the ECS via Resource/Components, and then you can just use all the goodies from the rand ecosystem without all that needing to be implemented/exported.

thick mirage
#

Thank you... I am new to Rust and Bevy. With Bevy I can easily get a ResMut<GlobalEntropy<WyRand>> ressource, that forks into a RngCore which is the type of rand_core's generators. Given that, how do I "pull the SliceRandom trait" from rand to be able to use choose_weighted()?

little cloak
#

You'll need to add the rand crate, and then use rand::prelude::SliceRandom. bevy_rand depends on rand_core only, not rand in order to minimise the dep size

#

Then you can use the resource directly as that implements RngCore and can be provided to the choose_weighted method without needing to fork

thick mirage
#

Great! Thank you very much. I wish you a nice day Sachymetsu.

random glen
#

Some small questions @little cloak (here or on the github issue is fine)

little cloak
little cloak
#

For any folks paying attention, I've got a bevy_rand PR to basically rework my global resources to being just... entities and Single queries https://github.com/Bluefinger/bevy_rand/pull/35

The rationale is to basically unify the behaviour/APIs around seeding/reseeding to make use of component hooks for RngSeed/Entropy sources. This means it is easier to support and reason about the way global and entity local sources interact. Plus, with some type aliases as helpers, accessing GlobalEntropy sources is now less verbose, without needing to use ResMut.

For those that wish to help test out these potential changes and help polish the code/docs, feel free to look at the PR and pull the changes (if you are living dangerously on the bevy main branch).

untold solar
#

@little cloak I really like the Global marker decision, were you biased in any way towards it or is it something you came up with naturally?

little cloak
#

@untold solar Honestly, after I messed with the observer feature code, and saw how nice the whole RngSeed+Entropy components combo worked, the resource version of GlobalEntropy needing to have both Seed AND the Rng instance contained within the same resource kinda irked me

#

Not having similar APIs and behaviours between the two just stuck out as a sore thumb

untold solar
#

Mhm

#

I'll need to look in depth how you expose initialization for the global resource

little cloak
#

And with Single queries being a thing, it felt less necessary to have a dedicated Resource

untold solar
#

Also, having "required resources" seems very nice

#

That said, we need to ensure there is only one Global marked entity for that to work correctly

#

And that will be ported to bevy core eventually

little cloak
#

Single helps mitigate that by warning/panicking if that criteria isn't met

untold solar
#

yeah, but no need to give users more branches to trip on

little cloak
#

If I had resources as entities, then I could move back to having Resources for global sources, because then it'll all share the same hooks/requirements/behaviours

#

It's easier to manage footguns of just one kind of API instead of two different APIs

untold solar
#

The plan is to replace Res<T> with Single<T, With<Global>> internally

#

When default query filters are added, we'll do Without<Global> as default, so people can opt-in to processing resource-entities

little cloak
#

Yes, pretty much. Also, having RngSeed be a separate component and an immutable one also makes handling the hooks/behaviour of reseeding pretty solid

#

Though, Entropy is kinda a weird component where you can't really modify its state directly, but all its methods are &mut, and it''ll be really weird needing to track change detection on it given all PRNG internal states are black boxes

#

I'd love to opt out of change detection by default for Entropy

#

though that's microoptimisation territory

#

In any case, I don't plan to merge that PR until after Xmas, maybe after the New Year, so to give anyone a chance to test out the changes and report back

#

Then I'll cut a new release early jan

untold solar
#

still require &mut to ensure singular borrows

little cloak
#

My experience with bevy_turborand with that was rather painful, it's not worth going the interior mutability route given you need &mut to ensure singular borrows

little cloak
little cloak
#

In prep for a new release, I'm going ahead and merging the anti-resource PR, since I've gotten no further feedback or issues, and it'll be going into a new major version with a migration path.

little cloak
#

bevy_rand is now no_std compatible on the main branch. Feel free to test it out if living on the bleeding edge

fallen sparrow
#

I see the crate seems focused on low level random stuff like entropy, seeding etc..

Is there existing support or planned support for things like helper functions for vectors, noise generation (perlin, simplex, etc..) ?

Would be nice to have a one stop shop for all of our random needs.

little cloak
#

@fallen sparrow For helper functions, you can just hook into the rand ecosystem, like rand_distr and so forth. Anything that builds on top of RngCore will work with bevy_rand.

Stuff like perlin/simplex noise is not a PRNG, and has different purpose and output to something like PRNG algorithms like WyRand, Xoshiro, and so forth.

#

bevy_math should have extension methods that accept RngCore trait objects/impls, so it already integrates fine there

tacit plover
#

Been hitting the getrandom RUSTFLAG thing a lot while working on no_std for bevy_asset. Is there any movement to try and "fix" this using optional dependencies for wasm_js, custom, and rdrand/rndr backends? They could literally be empty crates which just enable the required RUSTFLAGS line.

# bevy_utils Cargo.toml
[features]
getrandom-opt-in-backends = [
    "dep:bevy_getrandom_backend_web",
    "dep:bevy_getrandom_backend_asm",
]
getrandom-custom = [
    "dep:bevy_getrandom_backend_custom",
]

[target.'cfg(all(target_family = "wasm", any(target_os = "unknown", target_os = "none")))'.dependencies]
bevy_getrandom_backend_web = { version = ..., optional = true }

# ...
#

Asking here because while I think this should be fixed within Bevy the technique might make more sense to try out in bevy_rand first. Also, @little cloak is obviouslySME-Rand lol

little cloak
#

@tacit plover empty crates technique for getting round getrandom backend nonsense? Lmao, the rand folks are going to love this. Probably can give this a try with bevy_rand first for sure.

tacit plover
#

It's kinda like you've made a crate which provides a getrandom backend...it just provides one of the opt-in ones haha

little cloak
#

If this works out, I am inclined to then create an issue on getrandom to highlight this as a problem with their approach, which was to prevent backends from being activated via feature flags

#

Because if it can be done anyway, then why have this ergonomic hit in the first place?

tacit plover
#

Exactly. I'm not even sure there is a way to prevent this

little cloak
#

I think there needs to be an enforceable application only flags and library flags, but that's a rust/cargo problem, not a crate problem

tacit plover
little cloak
#

That would be lovely, for sure.

#

If I understand it, the empty crate has a build.rs file to enable the flags? Or something I am not understanding here?

tacit plover
#

I believe library crates can define those, but I may be mistaken

#

In which case, a build.rs would work too

little cloak
#

I will try both, but yeah. Rn, just waking up, so brain not yet fully functioning lmao. Gotta feed the damn cats

little cloak
#

Right, I am trying this out with my bevy_rand workspace, but I don't think the empty crate approach is quite working

#

I still get compilation failures, with a build script or otherwise

#

I wonder if the build script is "scoped" to just the crate itself and not the overall build environment

#

I might be doing something wrong in any case tho

tacit plover
#

Yeah I was just testing it myself, it appears to not work as I hoped and it does scope the variables to avoid cross contamination (which I guess this is)

#

I really thought this would work, I'm surprised a crate can't define some flags that need to be used for one of its dependencies

little cloak
#

The rand folks did their homework it seems

tacit plover
#

Annoyingly so!

little cloak
#

Still, I'm inclined to raise an issue anyway given the ergonomic hit this has on bigger, more complex projects

#

Though if I do, it would be nice if I'm not the only one adding data to such an issue

tacit plover
#

Yeah it's frustrating. critical-section has this same problem (needing to link against an extern item) but doesn't have the same ergonomics pain

little cloak
#

Yeah, I think we would have to give a counter example of a crate facing the same issue but resolving it differently

#

critical-section seems like a good one to provide

tacit plover
#

I think the change getrandom should do is move all the opt-in backends into a new dependency, getrandom-experimental-backends or whatever name, and then have custom be the default backend if none is selected using a feature on getrandom itself. This would then allow external crates to provide the custom implementation

little cloak
#

Probably the issue should have a name like "improving the backend ergonomics story", something less negative to hopefully make it more amenable for discussion. I know that this was a contentious change on getrandom's part so I'm quite certain they are wary of further drama on this topic

tacit plover
little cloak
#

So yeah, any change we propose kinda has to provide that assurance

#

Or at least be a visible opt-in that can be easy to audit (so if something does break, it can be triaged quickly)

#

The backend crate idea would be visible on the cargo tree output, for example

tacit plover
#

Might even make sense to split getrandom into getrandom-core and mutually exclusive getrandom-x crates. Nth mentioned that if you declare a package.links value in Cargo.toml, Rust will refuse to compile your crate in cases where multiple dependencies have the same package.links value

#

So getrandom-core is the basic API, getrandom-x is some official backend x, and getrandom is the collection of getrandom-x crates as features

little cloak
#

I think embassy makes use of that as well, because they have feature flags that definitely are mutually exclusive for configuring their executor

#

I think, I could be wrong

tacit plover
#

There's definitely options here

little cloak
#

But yeah, it just could be better, because it's not great having to spam env flags just to get WASM test builds to run

little cloak
#

checking critical-section/embassy-executor, they just opt for checking all exclusive feature flag permutations

#

embassy-executor has a macro for this

#

Is there a crate making use of the package.links feature?

tacit plover
little cloak
tacit plover
little cloak
#

Yeah, I'd hope they'd course-correct sooner rather than later

little cloak
#

Also, if you have a branch up, I can do a test run with a bevy_rand branch to see if that works out or not

tacit plover
#

The main getrandom package isn't changed yet, I've just carved out getrandom-core and migrated the Windows 10 backend to the new structure

#

Mostly just feeling out if this is an ergonomic path forward

#

So far, it feels much nicer

#

All a custom backend has to do is implement getrandom_core::Backend, and call set_backend!(MyCustomBackend) to setup the appropriate linking

#

Zero invocations of that macro is a linking error for missing symbols (exactly like not backend selected/available in getrandom 0.3). Two or more is also a linking error for duplicate symbols, preventing the kind of supply chain attack/implicit change the maintainers are worried about.

little cloak
#

Looking at the windows backend, it looks pretty clean and straightforward

main hedge
#

Is there a way to ensure each forked entropy gets the same seed as the original?
I want to be able to feed the global one with seed and have the same seed distibuted between all entropies.
I initialize it with: app.add_plugins(EntropyPlugin::<ChaCha8Rng>::with_seed(seed));
But then when I use fork_rng() or fork_seed() it creates an entropy with a different seed (checked with get_seed())

little cloak
#

@main hedge normally you don't want seeds to all be the same across different RNGs, and forking is meant for this use case. If for whatever reason you do want seeds to be the same, just .clone(). Simples.

main hedge
#

I'm in the process of making randomly generated dungeons, so I want to be able to regenerate faulty ones, so I'm logging faulty seeds and then initializing the entropies with them

little cloak
#

I see I see. Let me know how that goes and if you encounter any problems

main hedge
#

and with_seed()

#

I'm trying to figure out a way to provide the entropy with that exact external seed

#

Well
It seems that's not up to you

little cloak
main hedge
#

Like, it seems that the structs the crate wraps already generate something different than the provided seed

main hedge
little cloak
#

Depending on the PRNG, the internal state of the PRNG will not equal exactly to the given seed. The seed simply provides entropy/randomness for the internal state to be configured to

#

So, as long as you give it the same seed, it'll result in the same internal state

main hedge
#

Oh right
I could just not use ChaCha8Rng

#

I'll look for one that takes the exact seed and provided and also exposes one
Like
It's not your wrappers' fault

#

Thanks for making the crate btw
I'll be back with more information in the future, which will hopefully be something like "yay, solved"

little cloak
#

More complex PRNGs like ChaCha have more complex internal states. I don't think you should worry/care about the internal state of a PRNG, as long as you know its initial state (the seed), as all supported algos for bevy_rand are deterministic

#

That's why I split the Seed/Entropy Components, makes it easier to track that initial state

main hedge
#

I just realized that the entropy references from which I extract the seed have to be mutable so I wonder if they, too, make a new seed when I'm trying to fetch it

#
pub fn get_seed(&mut self) -> [u8; 32] {
        self.0.fork_seed().clone_seed()
    }```

Does this code make new seeds?
little cloak
main hedge
#

(alright, checking)

little cloak
#

RngSeed is immutable, because it represents the initial seed for an Entropy component, so it will only allow & and not &mut

#

For the global entropy, there's the system param: GlobalRngEntity<Rng>

main hedge
#

That's exactly what I need (I think)

#

Thanks

little cloak
#

Inserting RngSeed on an entity will automatically initialise Entropy component on that same entity

main hedge
little cloak
main hedge
#

Is there anything relationship-related with the entropies?
When despawning my entropies I get:
```2025-06-27T20:08:53.952981Z WARN bevy_ecs::error::handler: Encountered an error in command <bevy_ecs::system::commands::entity_command::remove<bevy_rand::component::E ntropy<bevy_prng::chacha::ChaCha8Rng>>::{{closure}} as bevy_ecs::error::command_handling::CommandWithEntity<core::result::Result<(), bevy_ecs::world::error::EntityMutableFetchError>>>::with_entity::{{closure}}: The entity with ID 8v1 does not exist (enable track_location feature for more details)

Looks like it's trying to despawn the entropy after it's already been despawned or something?
little cloak
main hedge
little cloak
little cloak
#

I've published a patch release, v0.11.1 for bevy_rand which should silence that warning when despawning entities with RngSeed on them

main hedge
main hedge
harsh mulch
#

Is there any reason (it seems that) I’m forced to rely on a component hook to have my source’s Entropy component added? from_seed is not public so I can’t seem to construct my own Entropy component from my seed.

Use case is that I have an rng seed u32 which I have saved in my save game. I want to add a source to my
Army entity but also have immediate access to the Entropy variable so I can fork it for each Regiment in the Army. The army and regiment creation happen in the same function so I’m wanting to add all these immediately rather than have it deferred.

Maybe I’m missing something. I’m still on bevy_rand 0.9 but I noticed the code here seems to be the same on the latest version too.

https://github.com/Bluefinger/bevy_rand/blob/a694689aa17687453d857dde2523e4f1929c1c73/src/seed.rs#L86

Edit: Looks like this works.

let seed: u64 = 234; // TODO: Get from save game.
let army_rng_seed = RngSeed::<WyRand>::from_seed(seed.to_ne_bytes());
let mut army_rng = Entropy::new(WyRand::from_seed(army_rng_seed.clone_seed()));

commands.spawn((
    Name::new("Army"),
    army_rng_seed,
    army_rng.clone(),
));

for r in regiments.iter() {
    commands.spawn((
        Name::new("Regiment"),
        army_rng.fork_rng(),
    ));
}
little cloak
#

In any case, newer versions of bevy_rand solve the manual linking of child Rng sources by using relations, so you can just "link" an entity to get a forked seed/rng from a parent source

#

for v0.9, there is an experimental feature to enable this, but the API for it is a bit verbose/not great.

#

The benefit of the relations approach is that you just update the parent entity with a new seed component, and everything downstream gets updated automatically

harsh mulch
harsh mulch
unique pewter
#

Looks like removing the "-dev" from everything and bevy_rand passes all tests for 0.18

little cloak
#

Until rand updates, I think bevy_rand as is can be considered stable-ish. I don't think I'll be changing the APIs much