#get world data from SubApp into main app world?

51 messages · Page 1 of 1 (latest)

high ember
#

Sub app has Extract as a system param for fetching info from the main world through the sub app, but I'm not sure how to get info from a SubApp back into the main world through a system in the main app. E.G:

I want to reflect PipelineCache's Vec<RenderPipelineDescriptor> from the RenderApp's world, into my main app's world. But, I'm not sure how to do it.

This is what I've tried.


#[derive(Resource, Default)]
pub struct RenderPipelineDescriptors(pub Vec<RenderPipelineDescriptor>);

/// copy's render world render pipelines into mainworld pipeline holder for inspection.
pub fn update_reflected_pipelines(
    render_world: &mut World,
    mut main_world: World,
) {
    let Some(pipeline_cache) = render_world.get_resource::<PipelineCache>() else {return;};

    let descriptors = RenderPipelineDescriptors(
        pipeline_cache
        .pipelines()
        .filter_map(|pipeline| match &pipeline.descriptor {
            PipelineDescriptor::RenderPipelineDescriptor(non_compute_pipeline) => 
                Some(
                    *(*non_compute_pipeline).clone()
                ),
            PipelineDescriptor::ComputePipelineDescriptor(_) => None,
        })
        .collect::<Vec<_>>()
    );

    main_world.insert_resource(descriptors);
}

... in main app
    let render_app = app.get_sub_app(RenderApp).unwrap();
    let mut app_world = app.world;
    app
    .add_systems(Update, update_reflected_pipelines(&render_app.world, app.world))

however, I get the error:

the trait bound `(): IntoSystem<(), (), _>` is not satisfied
the following other types implement trait `IntoSystemConfigs<Marker>`:

Ideas?

high ember
#

aparently you can use channels to send info? How would I do that with sub apps?

rose garnet
#

Create a channel. Put the sender in a resource and extract it to the subapp, and the receiver in a resource that doesn't get extracted

#

The sender can send messages on the channel in a system on the sub apps schedule

#

The receiver can check for those messages in a system running on the main world

digital shore
high ember
# rose garnet The sender can send messages on the channel in a system on the sub apps schedule

Reciever<T> does not implement Sync

#[derive(Resource, Default)]
pub struct RenderPipelineReceiver(pub Receiver<Vec<RenderPipelineDescriptor>>);
`std::sync::mpsc::Receiver<std::vec::Vec<bevy::render::render_resource::RenderPipelineDescriptor>>` cannot be shared between threads safely
within `RenderPipelineReceiver`, the trait `Sync` is not implemented for `std::sync::mpsc::Receiver<std::vec::Vec<bevy::render::render_resource::RenderPipelineDescriptor>>`, which is required by `RenderPipelineReceiver: Sync`
see issue #48214

Im able to turn Sender<T> into a resource though. How should I pickup the channel messages?

rose garnet
#

That's wild

#

std channels have traditionally been kinda useless for anything nice, so usually you want crossbeam, flume, or async_channel instead

high ember
rose garnet
#

I would personally just use a crossbeam channel and you're fine

#

Otherwise you can make it a NonSend resource

high ember
rose garnet
#

And crossbeam's channels are much nicer

high ember
high ember
# rose garnet Because at the top ```rs use crossbeam_channel::{Receiver, Sender};```

now the problem I have is: how do i spawn a thread to not block the render world?

this

    thread::spawn(
        move || render_pipelines_sender.send(descriptors)
    );

results in:

`render_pipelines_sender` escapes the function body hererustc
ecs_triangle.rs(30, 5): `render_pipelines_sender` is a reference that is only valid in the function body
ecs_triangle.rs(30, 5): has type `bevy::prelude::Res<'1, RenderPipelineSender>`
borrowed data escapes outside of function
argument requires that `'1` must outlive `'static`rustc
ecs_triangle.rs(30, 5): `render_pipelines_sender` is a reference that is only valid in the function body
ecs_triangle.rs(30, 5): has type `bevy::prelude::Res<'1, RenderPipelineSender>`
rose garnet
high ember
rose garnet
#

But why?

#

Spawning a thread is probably not the right idea

high ember
rose garnet
#

What are you trying to accomplish that you believe spawning a thread will do?

high ember
rose garnet
#

No

#

Rule of thumb: Don't touch threads unless you have a good reason to

high ember
# high ember im assuming putting it in a new thread will send the data soemwhere that `Reciev...

full code:

pub fn expose_render_pipelines(
    pipeline_cache: Res<PipelineCache>,
    render_pipelines_sender: Res<RenderPipelineSender>
) {
    //let Some(pipeline_cache) = (**render_world).get_resource::<PipelineCache>() else {return;};

    let descriptors = //        RenderPipelineDescriptors(
        pipeline_cache
        .pipelines()
        .filter_map(|pipeline| match &pipeline.descriptor {
            PipelineDescriptor::RenderPipelineDescriptor(non_compute_pipeline) => 
                Some(
                    *(*non_compute_pipeline).clone()
                ),
            PipelineDescriptor::ComputePipelineDescriptor(_) => None,
        })
        .collect::<Vec<_>>()
        ;

    thread::spawn(
        move || render_pipelines_sender.send(descriptors)
    );

}
rose garnet
#

Also, that's one big assumption :p

#

Channels work in a way where two threads can use them to communicate, but they do not rely on having two threads to communicate, except in the special-cased 0-sized channel

#

Basically, there's nothing extra you need to do in order to ensure these channels work

#
pub fn expose_render_pipelines(
    pipeline_cache: Res<PipelineCache>,
    render_pipelines_sender: Res<RenderPipelineSender>
) {
    //let Some(pipeline_cache) = (**render_world).get_resource::<PipelineCache>() else {return;};

    let descriptors = //        RenderPipelineDescriptors(
        pipeline_cache
        .pipelines()
        .filter_map(|pipeline| match &pipeline.descriptor {
            PipelineDescriptor::RenderPipelineDescriptor(non_compute_pipeline) => 
                Some(
                    *(*non_compute_pipeline).clone()
                ),
            PipelineDescriptor::ComputePipelineDescriptor(_) => None,
        })
        .collect::<Vec<_>>()
        ;

    render_pipelines_sender.send(descriptors); // this should just work
}```
high ember
# rose garnet ```rs pub fn expose_render_pipelines( pipeline_cache: Res<PipelineCache>, ...

thats what i was running and getting a black screen with. This is my main app for initializing everything

fn main() {
    let (
        tx,
        rx
    ): (Sender<Vec<RenderPipelineDescriptor>>, Receiver<Vec<RenderPipelineDescriptor>>)= crossbeam_channel::bounded(1);

    let mut app = App::new();
    app
    .add_plugins(DefaultPlugins)
    .init_resource::<RenderPipelineDescriptors>()
    .insert_resource(RenderPipelineReceiver(rx))
    .add_systems(Update, recieve_render_pipelines)
    ;

    let render_app = app.get_sub_app_mut(RenderApp).unwrap();

    render_app
    .insert_resource(RenderPipelineSender(tx))
    .add_systems(PreUpdate, expose_render_pipelines);


     app
    .add_systems(Startup, setup)
    .add_systems(Update, display_render_pipline_info)
    .run();
}
rose garnet
#

Try increasing the size of the channel

#

And ensure there's a main-world system actually receiving from the channel

#

Otherwise what will happen is:

#
  • render world sends event
  • render world tries to send another event, hitting the buffer limit, and blocks until the buffer is freed
high ember
high ember
# rose garnet And ensure there's a main-world system actually receiving from the channel

here is the system for that

#[derive(Resource, Default, Deref, DerefMut)]
pub struct RenderPipelineDescriptors(pub Vec<RenderPipelineDescriptor>);

/// recieves exposed render pipelines
pub fn recieve_render_pipelines(
    render_pipelines_receiver: Res<RenderPipelineReceiver>,
    mut render_pipelines: ResMut<RenderPipelineDescriptors>,
) {
    let Ok(pipelines) = render_pipelines_receiver.0.recv() else {return;};

    println!("recieved render pipelines, updating RenderPipeline descriptors");

    **render_pipelines = pipelines;
}
rose garnet
#

And commenting out just the send line stops the black screen?

high ember
high ember
#

I'm not sure whats blocking the thread then.

rose garnet
#

Does it still go black if you remove the system?

#

Ah

#

I think recieve_render_pipelines is what's blocking your thread

#

render_pipelines_receiver.0.recv() will block until a message is received, but one isn't by the time this system first runs so it blocks indefinitely

#

It would be available on the next frame I bet

#

Change that to .try_recv()

#

Also, probably need to change ```rs
render_app
.insert_resource(RenderPipelineSender(tx))
.add_systems(Render, expose_render_pipelines); // RenderApp only uses the Render schedule