- 
          
 - 
                Notifications
    
You must be signed in to change notification settings  - Fork 4.2k
 
New Resources API #8126
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
New Resources API #8126
Conversation
| 
           Decided against adding   | 
    
| 
           Okay, I feel this is ready now. I eliminated the use of  The traits   | 
    
| 
           I think the issue people might have with this initially is that the semantics aren't completely parallel with bundles/systems. In those APIs the elements are related/coupled in some way. For resources it is less often the case (even on a plugin basis) that multiple resources are related to eachother. However there's nothing stopping people from just using multiple calls for unrelated resources. I'm in favor of this anyway. I think it's fine to not be opinionated on this matter and let it come down to a choice of style for each author.  | 
    
| 
           I'm not 100% sure what this wins us other than reducing the number of lines of code we need to write (a valid win onto itself). My primary concern here is that this increases compile time ( app.insert_resource((A::new(0), A::new(1), A::new(2)));Unlike systems where each instance isn't unique, resources are. What value does the resource   | 
    
| 
           @james7132 Yes, the main goal of this PR is usability when inserting or initializing many resources. However, with  As for order of operations, it works in the order of the tuple. So, for your example, the final value would be  app.insert_resource(A::new(0));
app.insert_resource(A::new(1));
app.insert_resource(A::new(2));If this needs to be documented, I will gladly do so. As for any increase in mental burden, as @iiYese pointed out, nothing is stopping users from writing it out like before. On compile times: I don't think there is an issue. Sure, it isn't lowering times, but it isn't calling macros on every invocation and with incremental compilation the compile time cost will really only be seen on fresh builds.  | 
    
          
 Would be nice if we could just disallow this at a type level. If not I think adding a warning for tuples that contain more than one of the same resource should be considered.  | 
    
| 
           @iiYese AFAIK there's no way to currently do that. It would require specialization or negative bounds/impls. With negative bounds, you would create and implement a trait for checking equality and then use it as a negative bound. Ex: trait EqTrait {}
impl<T> EqTrait for (T, T) {}
impl<A, B> MyTrait for (A, B) where (A, B): !EqTrait {}This technique could also be used with specialization, but would only result in a panic, not a compile error. However, one problem is expanding this to larger tuples, as checks between each type would grow exponentially (Ex:  One solution was to move most of the work to compile time so that the runtime cost is constant, but that can only be done on nightly: #![feature(const_trait_impl)]
#![feature(const_type_id)]
use std::any::TypeId;
trait MyTrait {
    const EQ: bool;
    fn foo(self);
}
impl<A: 'static, B: 'static> MyTrait for (A, B) {
    const EQ: bool = TypeId::of::<A>() == TypeId::of::<B>();
    fn foo(self) {
        if Self::EQ {
            println!("Error");
        } else {
            println!("Works");
        }
    }
}
fn main() {
    (2, 3).foo();
}Though, I'm not sure how I feel about runtime warnings. If the call is from a system, it would be repeated ad nauseam.  | 
    
| 
           Yeah I'm torn on this one. Pros: 
 Cons: 
 Can someone take a few samples of clean compiles with / without these changes (make sure you use the same base commit)  | 
    
| 
           @cart I went ahead and did some benchmarks with this PR branch and a branch based off the base commit (2010164). I ran  Device specs: rustc 1.70.0-nightly (900c35403 2023-03-08), Windows 10, Ryzen 5 2600 CPU, 16GB DDR4 RAM, 1TB HDD The first 5 trials were with other programs running (Firefox, Spotify, etc.). For the last 5, I closed those programs (keeping some up for noting the times). I switched branches between each test. Afterwards, I calculated the confidence interval using a t-score. 
 From all this, we can say that in the worst reality, the difference between the true means (with 95% confidence) would be 1.44sec (In the best reality, it would be -0.72sec). But based on the sample mean, we should see a 0.36sec difference. That's a 1.21% increase from before. Frankly, that's pretty significant for a relatively small change, even if it's not very noticeable. Mind you, these are on clean builds, so most of the time, users won't deal with these costs. Edit: I should also note that this is only for bevy_ecs, the effect on total build time would only be a fraction of a percent. Despite these costs, I still think it's worth it. We already have similar interfaces, so this one should be no big deal. I think consistency and usability are more important than 0.36sec on clean builds. Edit 2: I thought I added this, but I guess not: The t-test was inconclusive that there is a difference between these samples. The p-value was something like 0.15, which means that at <85% confidence we can say there is a difference, but it isn't strong enough for 90-95%.  | 
    
| 
           I think the cost there is pretty reasonable / stomachable (if it even exists at all). Thanks for measuring! I think this should come down to "is this the API we want". I'm still largely on the fence, with a slight bias toward merging in the interest of API consistency (pros and cons feel reasonably matched to me). I'd like to hear final thoughts from a few more ECS people before making a call.  | 
    
| 
           @bevyengine/ecs-team  | 
    
| 
           Nice writeup on the benchmarks, that does alleviate my concerns about codegen. My only objection at this point is that this will add slightly more friction when switching between  Overall I'd say that I'm not really opposed to this PR, but I don't think it wins us too much either.  | 
    
| 
           On the plus side things get a little more consistent with the add_systems api. On the minus side, internal code is a little harder to understand as things are put behind a macro. Overall I'm pretty neutral on the change. Taking a bit from my jam game         // before
        app.insert_resource(RapierConfiguration {
            timestep_mode: TimestepMode::Fixed {
                dt: 1. / 60.,
                substeps: 1,
            },
            ..Default::default()
        })
        .add_plugin(RapierPhysicsPlugin::<NoUserData>::pixels_per_meter(100.))
        .insert_resource(FixedTime::new_from_secs(1.0 / 60.0))
        .insert_resource(PhysicsSettings {
            initial_jump_speed: 400.0,
            gravity_pressed: 40.0,
            gravity_unpressed: 200.0,
            horizontal_speed: 200.0,
            max_speed: 700.0,
        });
        // after
        app.insert_resources(RapierConfiguration {
            timestep_mode: TimestepMode::Fixed {
                dt: 1. / 60.,
                substeps: 1,
            },
            ..Default::default()
        })
        .add_plugin(RapierPhysicsPlugin::<NoUserData>::pixels_per_meter(100.))
        .insert_resources((
            FixedTime::new_from_secs(1.0 / 60.0),
            PhysicsSettings {
                initial_jump_speed: 400.0,
                gravity_pressed: 40.0,
                gravity_unpressed: 200.0,
                horizontal_speed: 200.0,
                max_speed: 700.0,
            }
        ));Looks slightly cleaner. Note: There is also #8097 which does something similar for   | 
    
| 
           Alrighty I think we should move forward with this. Marginal legibility/ergo increase in some cases and consistency with the direction other apis are going in. This would be a hard thing to justify in a "stable bevy" world, so its now or never.  | 
    
| 
           @JonahPlusPlus can you please rebase or otherwise fix the merge conflicts then?  | 
    
| 
           @alice-i-cecile Sure, I'll get on it.  | 
    
| 
           That should do it, but let me know if there are more merge conflicts. By the time I finished fixing these merge conflicts, there was already another one.  | 
    
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm neutral on the syntax change but this appears correct now.
| 
           @cart can you do a quick skim and merge this? I'd like to avoid leaving this longer because of the merge conflicts it generates.  | 
    
| 
           Not a fan of grouping resources together... in my opinion it's not the same as systems where you often want to be able to schedule several systems at the same time, resources each have their own meaning  | 
    
| 
           Sorry for the back and forth @JonahPlusPlus, but thats now two maintainers (@mockersf and @james7132 ... who is also notably an ECS SME) expressing concerns / no votes. Given the overall "neutral sentiment" and my only "slightly in favor" vote, that is enough for me to think we should hold off. Closing for now, but feel free to keep the conversation going. If sentiment changes to something closer to "generally in favor" we can revisit.  | 
    
| 
           I'm not a maintainer, I'm not sure how much my opinion matters, but I'm in favor. I feel like API consistency is good to strive for. Bevy has been steadily moving in the direction of "add multiple things with a single call, with tuple syntax" for a while now, and I love it personally. First, we had the bundles/components unification, which let us combine many bundles and components into a single  Then, we moved to  Sounds like we are also going to have  We should have it for resources too. If we don't, it's going to feel like an outlier, given that all the rest of bevy follows this pattern. I like this pattern/syntax and I think we should be consistent everywhere, even if the pros/cons for resources are less clear than with systems or bundles/components. @james7132 As for what happens if the user specifies the same resource type multiple times, we should look at how we handle bundles/components, where the same issue occurs. What happens when you put the same component/bundle type multiple times in a tuple in a single   | 
    
| 
           Looking at the compile time increases, the messiness of init_resources and the added complexity in our code, I've shifted from "neutral" to opposed.  | 
    
          
 This would indeed address the ambiguity issues with duplicated resources being inserted. For bundle insertion/removal, deduplication currently has the global allocator and panics included (increasing codegen even more), but is only done at bundle registration time. We could duplicate this infrastructure and track every possible   | 
    
| 
           We don't currently add any behavior to, or implement any traits on, tuples-of-resources. For that reason, I don't think this syntax is appropriate for resources (or plugins for that matter). I'm in favor of leaving this API as it is. 
 This syntax enables us to share some properties across the members of the given tuple, and to reason about the members within as a single thing. For systems, this enables useful patterns around scheduling and run conditions. Resources (and plugins) don't have these added 'decorators' and don't benefit from coupling the way that systems do. Even after the schedule-first API, users won't configure the entire app with a single call. Systems will still be added with multiple calls on the app builder. The 'multiple things' in a single call are grouped together for specific, observable reasons. 
 The existing API is consistent: add behavior to the app by invoking methods of the app builder. These methods have different signatures because they each add different constructs. In the case of plugins, grouping might even mislead users into believing that the member plugins are ordered or coupled somehow. The API would be less consistent because the plugin tuple wouldn't add or specify any behavior the way that system tuples do. In the case of resources, a new user might wonder what the tuple grouping does. The only reason I would be in favor: this change could leave us the space to implement traits on resource tuples in the future. I don't know what the purpose of that would be. Even after a post-Bevy-1.0 world, though, it wouldn't be the end of the world to extend the API with pluralized methods.  | 
    
| 
           How about I create a prototype crate that adds this functionality for now? That way users can opt into this right now and see whether or not it should be an official part of Bevy.  | 
    
| 
           Here, published a crate. There are some limitations (can't implement the trait for the base case due to orphan rules), but it otherwise works. Give it a try and see how it works.  | 
    
Objective
With 0.10, I really enjoyed merging
add_systemcalls intoadd_systems. So I wanted to see if that can be done for calls likeinsert_resourceandinit_resource.Solution
This PR deprecates
<App/Commands/World>::insert_resourceand<App/Commands/World>::init_resourcein favor of<App/Commands/World>::insert_resourcesand<App/Commands/World>::init_resources.It introduces new traits:
InitResourcesandInsertResources(separate because of different constraints).InitResourceshas an associated type for whetherComponentIdor[ComponentId; _]should be returned fromWorld::init_resources.Examples
Changelog
Added
<App/Commands/World>::init_resourcesInitResourcesInitResourceCommand<App/Commands/World>::insert_resourcesInsertResourcesInsertResourcesCommandChanged
Commandimplementers to have-Commandsuffix<App/Commands/World>::init_resourceto<App/Commands/World>::init_resources<App/Commands/World>::insert_resourceto<App/Commands/World>::insert_resourcesRemoved
<App/Commands/World>::init_resource<App/Commands/World>::insert_resourcesMigration Guide
<App/Commands/World>::init_resourceshould be changed to<App/Commands/World>::init_resources.<App/Commands/World>::insert_resourceshould be changed to<App/Commands/World>::insert_resource