#Next Generation Scenes

1 messages · Page 3 of 1

upper drum
#

Hi people. I had a look at the two alternative PRs for upgrading Sprite to required components. As a user, I have a preference that #15562 is better than the blessed #15489.

My reasoning is that:

  • the upgrade with the former is going to be much smoother. I know there's no promise of stability but reducing churn when upgrading could be a factor to consider in this case.
  • I don't feel too strongly about splitting Sprite and SpriteProperties in that PR, however I would very much prefer to not couple Sprite and TextureAtlas.
  • the blessed PR sort-of reverses # 5103, which back in its time was considered, discussed and merged (moving TextureAtlas to a separate component).
  • if there's a good motivation to couple Sprite and TextureAtlas, that change could come later, possibly after 0.15. First upgrade Sprite to required components with the least amount of churn as possible, then couple Sprite and TextureAtlas, with a separate discussion why that is necessary.

I did not follow the initial decision for the blessed solution though, I might miss some context.

magic lava
#

I can get behind keeping TextureAtlas separate

round ridge
#

Personally, I think that the current TextureAtlas setup is confusing for a few reasons, and I actually like the fact that it's become a field because it helps explain a couple of the "forms" that Sprite can take.

In the long run, I'd very much prefer to see a refactor that introduces a Sprite asset (ala Unity's Sprite) which combines the Image and the 'mesh' involved in the sprite:

  • IMO passing the atlas image around with the "mesh" is confusing
  • TextureAtlas is a misnomer (it's actually a reference to a single texture atlas sprite)
  • We don't currently support alpha-packed sprites, because TextureAtlasLayout only uses URect
#

Another thing that comes to mind, is that with required components, the ergonomics are probably going to be better with a single driver component (it'll be easier to discover, and we can guarantee a sensible initial entity layout). So if you did want multiple "shapes" of sprite, it might make sense to separate out the low level rendering primitive, and multiple drivers, one for each layout (i.e. ImageSprite, TextureAtlasSprite or something). (It would also then mean that the low-level component wouldn't need to require Transform which would make it more appropriate to use as a building block.)

magic lava
#

I was thinking we could move the texture atlas abstraction all the way to Image so sprites and other API can just have a Handle<Image> and users don't have to think about whether that image is part of an atlas or not.
In the mean time though I don't feel like shaking things up too much, not without a clear plan (and we do not have a clear plan :v)

round ridge
#

Additionally, you can make any whole texture a Sprite in the import settings. (And all Sprites still have an associated Texture2D if you need to refer to the underlying texture.)

upper drum
# round ridge Personally, I think that the current `TextureAtlas` setup is confusing for a few...

I agree that the current API is not ideal and there’s more to be done there, also considering the iteraction between Sprite and MeshMaterial2d.

I’m coming from the angle of reducing friction as much as possible for 0.15 and later change the Sprite / TextureAltlas API as needed, whenever that lands.

Right now there’s already lots of churn due to required components (which is totally justified as it adds a lot of value) but the change to couple Sprite and TextureAtlas itself adds no immediate benefit, it’s just an extra (relatively significant) obstacle needed for the upgrade and currently not really well justified, in my opinion (from what i could see myself, i might have missed it).

thick slate
#

Yeah, my feeling here is that a genuinely good 2D API is another rework away, so we should try and minimize breaking changes for now

round ridge
#

People are already going to have to adjust anywhere they're adding Sprite components - I don't consider moving TextureAtlas to be as significant as an indpendent breaking change. Also, it's the same way a number of things are going to change when removing Component from Handle<T>.

#

If TextureAtlas stops being a component, it'll at least be impossible to miss by mistake. 😅

#

(The mitigation being made by keeping it as a component is going to primarily be people querying for TextureAtlas and not Sprite, I guess.)

thick slate
#

This will break UI texture atlases too though, and require a bunch of boilerplate to abstract over them

#

Which is the opposite of the direction I'd like

round ridge
#

Ah good point.

#

It is going to mean that you either need to split Sprite, or you're going to have a weird inconsistency where the atlas image is specified in Sprite but the atlas sprite is a separate component (which seems confusing to me).

fallow cloak
#

I have no strong opinions one way or the other, I’m just surprised the sprite migration ended up being the most controversial 😅

magic belfry
#

FYI @split harness I implemented your proposal for reflection probes even though it's not checked off as blessed in the HackMD yet. Happy to throw this out if there's a better approach you or someone else comes up with
https://github.com/bevyengine/bevy/pull/15737

GitHub

Objective
Getting closer to the end! Another part of the required components migration: reflection probes.
Solution
As per the proposal added by Cart (Proposal 2), make LightProbe require Transform...

fallow cloak
split harness
#

(please discuss in that thread)

split harness
#

(across reflection probes and cameras)

#

But that can happen later

thick slate
#

bevy_sprite migration is merged 🙂

fallow cloak
#

Following the ui migration do we also plan to remove the component impl for TextureAtlas?

magic lava
thick slate
#

It'll make the migration a lot easier

fallow cloak
#

Yep

#

Would that mean moving textureAtlas into whatever the relevant ui component is? (Idk ui at all)

thick slate
#

Text rework is merged 😄 The UI required components work should now be unblocked

magic lava
#

Thanks for editing the migration guide @thick slate :)

magic lava
#

It would have taken me 5 times as long to write something half as good, so I'm genuinely grateful haha

fallow cloak
#

Should we use MeshletMesh3d for consistency with Mesh3d?

magic belfry
#

I would probably say yes, but no strong opinion and personally don't care that much. Of course we don't have a 2D version of meshlets (but... could we technically? idk lol)

#

for those ultra high detail hyper-realistic 2D scenes with millions of vertices, which are of course very common

cobalt stone
#

Also need to remove the bundle and swap to required components to mimic the new mesh api

magic belfry
#

*deprecate the bundle

magic lava
#

Alright, done :)

magic belfry
#

(I have a math exam on Friday :P)

thick slate
#

I'm running out of stuff to do that isn't "review and fix rendering PRs"

unique fulcrum
#

so 0.15 rc soon? 🙂

native mesa
#

Hopefully

#

Once main is less obviously broken

crisp garden
#

With the state main is in it's kind of scary to think that there could've been a world where bevy didn't have rcs yet ferris_spooky

golden notch
#

retained render world is broken rn (review my pr :))

#

am a bit nervous about this all being smushed up against the rc candidate but i guess that's also the point of rc

crisp garden
#

Definitely would've been better if this was all ready earlier, but for many of these things delaying it would've just gotten us the same situation in the next cycle anyway 🙃

golden notch
#

definitely, it's the point of being pre-1.0, our users might not feel the same way though 😅

crisp garden
#

I wonder if someone will drop a "Remove bundles" PR the second 0.15 releases and we no longer need them on main 😂

magic belfry
#

as far as the required components migrations go, UI seems to be the last one standing

#

which Alice (or me tomorrow, but not today) will probably handle

#

also someone please make a PR to yeet SpatialBundle, afaik that still hasn't been done

#

*deprecate

#

it was left for later since other bundles still used it, but now they've been migrated so it's not an issue

crisp garden
#

I can look at SpatialBundle after I finish this review

#

Took me a while to realize what SpatialBundle is ... But did other bundles actually use it? 🤔

magic belfry
#

oh lol just ReflectionProbeBundle

#

for some reason

native mesa
crisp garden
#

If it's a sketchy massive change now, it wouldn't have gotten merged at the start of a next cycle either, and at that point finding all the bugs would've been slow and brushed up to the rc again 🙃

crisp garden
#

Did we do any changes to hierarchies by any chance? There's a lot of entities that just have SpatialBundle::default() + children ... It would be really nice if we could just yeet that bundle and have it behave the same instead of making it Transform::default(), Visibility::default() 🤔

thick slate
crisp garden
#

Does unwrap_or_default introduce branching?

thick slate
#

(and we shouldn't introduce that change at this point in the cycle)

crisp garden
#

Also we already throw warnings if they are missing no?

native mesa
#

for the time-being we need to either have have unbroken chain of Transform and Visibility from the root on down, or marker components for in-tree transform/visibility roots.

#

and also yeah, i wouldn't support any changes to propagation at this point in the cycle regardless.

crisp garden
#

Yea it's not really blocking, the migration is trivial, but if we have no plans to change this, this would be a significant downgrade in ergonomics of these cases, spawning just SpatialBundle each time is harder to forget than spawning Transform + Visibility

#

And these entities have nothing to require them either

thick slate
crisp garden
#

Grouping entities under a parent usually, sometimes they do have and use transform but not the visibility

native mesa
#

We could re-introduce SpatialBundle.

#

or you could write your own.

#

seeing as bundles still exist.

cobalt stone
#

#[require(Transform, Visibility)] struct Spatial; ?

native mesa
#

you could even make that custom component more semantic and call it EntityGroup

#

since that's what they actually are

crisp garden
#

None of those really feel like the solution that matches required components ... The components aren't required on the entity by anything else, and it feels weird that every entity in the hierarchy needs transform and visibility for the children to show up, especially when we spam your terminal with messages about it missing when it could just assume default values instead 🤔

#

Other engines don't usually have this issue because every node has transform/visibility/process mode/whatever else is propagated

native mesa
#

it's an important part of how we parallelize propagation. We have to start at tree roots for transform and visibility, and it's hard to fine those roots when they don't align with the hierarchy root.

#

if we started at all roots and did unwrap_or_default we'd end up adding transform and visibility to trees that didn't need it at all.

#

which could be quite expensive, considering that lots of things are now entities, and you might have a few hundred observers or whatever floating around.

crisp garden
#

Ah yea, I see what you mean ... Depending on how the filters work it might even pick up every non-tree entity

native mesa
#

exactly

#

I do want to fix our needing to start at roots (we actually don't have this requirement at Foresight now) but it's not something we can deal with now.

crisp garden
#

Well, like I said, as long as we have plans to fix it I don't think this is a big issue for now

#

Transform::default(), Visibility::default() looks a bit ugly, but if it's just for one or two more releases it's kind of whatever

native mesa
#

yeah i imagine we'll work something better out

#

quite soon

crisp garden
#

If this got into a 1.0 release it would be one of the sillier things in the engine 😂

native mesa
#

agreed

crisp garden
#

At least these cases are easy, just yeet

Mesh3d(meshes.add(Cuboid::new(0.5, 0.5, 0.5))),
SpatialBundle::INHERITED_IDENTITY,
cobalt stone
#

I will say, as much as I dislike required components, the ergonomics are much nicer than bundles

crisp garden
#

Well yea that's the whole goal of required components, to improve the ergonomics and be less awful than bundles

magic belfry
#

I like to imagine the horror scenario where we discover that required components were a mistake, and revert back to using bundles everywhere

#

imagine the churn lmao

cobalt stone
#

I think we probably need a bypass at the very least

#

insert_without_required()

crisp garden
#

@magic belfry Is the extremely odd use of Mesh3d + SpatialBundle an artifact of your refactor or was it always confusing like this? thonk

magic belfry
#

Afaik one idea was to have a ReflectionProbe component or similar just for reflection probes, and then have environment map lights separately for cameras

#

i.e. not reuse the same component for both, which is confusing to begin with imo

white lichen
#

i agree with reusing the component being confusing

#

just cus you can doesnt mean you should

#

would you ever want to write a system thats generic over both cases?

crisp garden
magic belfry
cobalt stone
#

This is exactly why I don't love required components. It discourages the flexibility that ECS usually encourages.

crisp garden
cobalt stone
magic belfry
#

At least with the current name it's really confusing to me

cobalt stone
#

It was named that way for parity with existing light sources (I wrote the feature)

crisp garden
#

The double use came with reflection probes right?

magic belfry
#

yeah

crisp garden
#

Deprecate SpatialBundle
Remove it from examples
Try to run examples
Get entirely unrelated errors

thread 'Compute Task Pool (12)' panicked at /home/nisevoid/gamedev/bevy/crates/bevy_text/src/text_access.rs:278:43:
called `Option::unwrap()` on a `None` value
```You'd almost think main is broken ![thonk](https://cdn.discordapp.com/emojis/1153851465765490742.webp?size=128 "thonk")
#

I like how there was this one use of SpatialBundle where just Transform would've been enough, and it was an example I wrote bavy

tired topaz
tired topaz
#

I wish the engine would provide some way to create and showcase its different archetypes again

tired topaz
#

I mean, the required component change just kinda went a bit overboard.

Removing obvious and never modified values from the bundles would have been enough. Like computed visibility, globaltransform etc

#

Or just nest the bundles better and when inserting bundles, provide methods like insert_and_ignore and insert_and_overwrite` to handle already existing components on entities

magic belfry
#

can't have duplicates in the same insert though ofc

#

and as for nested bundles, they're pretty horrible for ergonomics

tired topaz
#

Yeah maybe, but manual bundles feel way worse than before to me ☹️

round ridge
# tired topaz I mean, the required component change just kinda went a bit overboard. Removin...

Particularly, I don't think components should be requiring unrequired components (i.e. Sprite requiring Transform, as none of the engine systems need Transform, only GlobalTransform (I haven't checked this example, but I think it gets the point across)). Though I also think that it might be better query-based rather than an attribute, because in the past in other engines I've commonly written "if an entity matches my 'input requirements', add my state components".

crisp garden
rough cave
#

I think Ricky would argue it's not feature parity, because required components remove flexibility from the engine.

crisp garden
#

Feature partify between the bundle that was deprecated and the way you would normally insert the component

rough cave
#

But you used to be able to opt out of bundles, but you can't opt out of required components 😦

crisp garden
#

Once changes to the engine are made so that people won't assume that sprites should always have Transform (for example actually supporting custom transform types, or having 2d vs 3d transforms) it would start to make a lot of sense for these requires to change

round ridge
#

I don't particularly want to re-tread this ground too much, but to be straight to the point: there may well be many people that didn't assume that Sprites had Transform and depended on it not being the case. We don't really know.

#

To compare Unity's DOTS, they deliberately don't add LocalTransform to static entities which aren't expected to be dynamically moved, simply to save time and memory. 🤷‍♀️

tired topaz
#

I mean a bundle could also have contained options, a lot of helper methods or different kind of constructors.

And could also have made the "don't touch" members private.

crisp garden
#

Well you can already never assume Sprite always have Transform (because you can remove it, by accident or intentionally), and the require between them definitely isn't great, but the only one that really works with people's current mental model of how bevy works

magic belfry
#

for that particular thing I do think it'd make sense to make sprites and meshes only require GlobalTransform if no rendering systems need Transform

thick slate
#

I'm interested in that change as part of the transform rework

crisp garden
#

Yea the only reason why I'm on board with having Transform be required on things like Sprite is because until the transform rework it seems like a pointless UX downgrade

magic lava
rough cave
#

IMO the real solution in the future is a more powerful version of the Construct trait that has been proposed for BSN, and that Required Components aren't needed, because the ergonomics of authoring components is provided by Construct types. This would keep both the flexibility and ergonomics.

#

Effectively, I was fairly happy with parts of the "baking" workflow provided by Unity DOTS.

crisp garden
magic belfry
#

like if you added a Sprite as a child without Transform, it wouldn't follow the parent correctly

crisp garden
rough cave
#

@crisp garden when I say a powerful version, I mean: Construct is currently proposed to be "Component X can be constructed from type Y", I propose that it should instead be "Type Y can be used to construct component X (and maybe more)".

round ridge
crisp garden
crisp garden
magic belfry
rough cave
#

@magic belfry the type signature is the wrong way around though in terms of who is implementing the trait

round ridge
rough cave
#

Unity DOTS has a concept of "Authoring Components", which are blobs of data that are exposed in the editor for you to attach to entities when designing them. Then there's a "baking" process that iterates all authoring components, and asks each one to "bake": it has access to what is effectively an EntityCommandBuffer, an dit adds the desired changes to it. The resulting command buffer effectively describes the resulting entity. This can all be calculated statically at asset processing time, rather than runtime.

#

@magic belfry the Construct trait is implemented on the target type, saying "I can be made from X". This means that a type can only have one construct type.

magic belfry
#

Is it? 🤔 that seems wrong if it is

rough cave
#

Iirc, might be wrong.

magic belfry
#

I think you're right

rough cave
#

Yeah, you do impl Construct for MyComponent { type Props = MySourceDataType; ... }

round ridge
#

I vaguely remember having a long conversation about this before and it turning out there's another Into involved somewhere, but I can't remember exactly. 😅

rough cave
#

Yeah, I think that the Into is on the Props type.

#

So you can do: InputType -> Into<TargetType::Props> -> TargetType::Props -> TargetType

magic belfry
rough cave
#

But again, that's a 1-1 relationship. Except for the into. But that limits flexibility to whoever imagined the Props type. If the Props type is too restrictive, you won't be able to implement From for TargetType::Props for your CustomPropsType.

#

On a 2 year complex Unity DOTS FPS project, we really enjoyed working with the ability to write one Authoring Component that described <Logical Block of Functionality Exposed in Editor> that would bake into <Several Related Components>.

crisp garden
#

I think ideally once the transform rework is done we would be able to do things like this:

- "Scene root"
  - "Player A", Player, Transform3d
    - Mesh3d, Transform3d
    - PlayerLabel, Transform3d
      - Image, UiTransform
      - Text, UiTransform
```Mix transforms, introduce your own transforms, and skip past entities without transform/visibility/whatever as if they had a default value
#

Currently all required components does is just highlight how flawed bevy's transfrom and visibility propagation are, as well as maybe how weird some of our rendering stuff is (sprite being a purely 2D thing for example seems unnecessary)

hybrid bloom
fallow cloak
hybrid bloom
#

at any rate, Props having to anticipate every reasonable way of constructing a component is kind of a problem imo

crisp garden
#

If the target for Construct can be a Bundle and not a Component, then in theory it could be a non-1-to-1 tho 🤔

crisp garden
magic belfry
#

Just so I'm understanding correctly; the current proposal is to have this:

// The constructor for Handle<T> uses AssetPath
impl<T: Asset> Construct for Handle<T> {
    type Props = AssetPath<'static>;
    fn construct(
        context: &mut ConstructContext,
        path: Self::Props,
    ) -> Result<Self, ConstructError> {
        todo!()
    }
}

but we'd ideally want this, right?

// AssetPath is a constructor for Handle<T> (there could be others too)
// (naming might be confusing when the relationship is this way)
impl<T: Asset> Construct for AssetPath<'static> {
    // This could be a bundle
    type Output = Handle<T>;
    fn construct(
        &self,
        context: &mut ConstructContext,
    ) -> Result<Self::Output, ConstructError> {
        todo!()
    }
}

and presumably this would just cause some serialization issues for BSN?

crisp garden
#

I think there's a better way to handle it than that tbh, this impl wouldn't work in a lot of cases

magic belfry
#

I feel like I still don't fundamentally understand how exactly constructs work despite reading the proposals many times lol

#

ah yea my impl wouldn't work for a lot of things

crisp garden
#

From what I understand it works roughly like this:
BSN sees a name of a component, say Friction.
It searches for the functions associated with Friction
If it finds them it uses them to deserialize the value into the Props type of Friction's Construct impl
It then calls the function in Construct to turn the Props into a Friction

#

Yea pretty sure it works roughly like that, since it seems like EntityPatch works on the props, not the component itself

#

One thing I'm not entirely sure about is if the name actually needs to match a Component or if it can be anything implementing Bundle and if we can register things under fake names. If those two are possible we could have 1 thing in BSN map to multiple components. Still not entirely sure what the value of that would be other than maybe hiding some optimizations from bsn

neon matrix
#

Hey all 👋 I know 0.15 is probably gonna land a bunch of changes with required components, but I was wondering what's the current state otherwise & what's next! I'd love to start getting involved with this one, but it definitely seems pretty hefty

thick slate
#

Beyond that, I think that the next step is going to be in the Construct trait and the BSN tooling itself, but I'm not sure what the best way to help with that is 🙂

covert sundial
#

is there already a branch with something related to bsn, or will it follow after the whole required component thing is ready ? 🙂

thick slate
#

Cart's been busy with the release and looking into strategies for reactivity

covert sundial
#

okay, will use i-cant-believe-its-not-bsn then until its ready:P

neon matrix
frosty shell
#

the trait still exists

#

but derived Bundle structs won't be in the engine anymore (deprecated, and then removed)

#

you will still be able to write your own bundle structs

frosty shell
native mesa
#

We are removing existing all bundles from our api, but we are not removing the bundle machinery itself.

rare whale
#

A few years ago a buddy of mine got a gig at a startup named "Bunnndle". Because, of course, all the good names were taken.

crisp surge
#

I'm working on migrating the UI bundles (the last builtin bundles: NodeBundle, ButtonBundle, and ImageBundle). Currently, there's a design decision to consider: should we merge Style and Node, or should they remain separate? https://github.com/bevyengine/bevy/issues/15889

GitHub

Almost all of our bundle types have been migrated to required components after #14791. However, UI bundles have not been done yet. #15550 will conflict with that, but since that PR needs more time ...

copper dragon
#

This would be good opportunity tbh

timid leaf
crisp garden
copper dragon
#

I'm def in favor of blowing up style

split harness
#

Yes we should blow up Style, but not for 0.15

#

And I do want Node to be the "public interface" for most style properties (again not for 0.15), implying that the computed properties should be moved elsewhere

rare whale
#

My proposal for the obscure WG name for this is "Dolce Stil Novo": https://en.wikipedia.org/wiki/Dolce_Stil_Novo

Dolce Stil Novo (Italian pronunciation: [ˈdoltʃe ˌstil ˈnɔːvo]), Italian for "sweet new style", is the name given to a literary movement in 13th and 14th century Italy. Influenced by the Sicilian School and Tuscan poetry, its main theme is Divine Love. The name Dolce Stil Novo was used for the first time by Dante Alighieri in Purgatorio, the sec...

#

There's been a discussion of moving the flex and grid params to their own components, with different components for parents and children.

crisp surge
#

There are many Node: : default() following with a Style in the examples and making Node contain the style properties will reduce many noise boilerplates like these.

thick slate
#

Yeah, I'm looking forward to the follow-up design 🙂

split harness
#

#ui-dev message

thick slate
#

I like net negative PRs

golden notch
#

one minor ergonomic improvement i am LOVING is that, where previously whenever you wanted to add a non-bundle component to a spawn you usually had to insert the bundle into a new tuple which requires many keystrokes, now almost everything is already in a tuple, so you can just boop newline add component done, its soooo nice

crisp garden
#

But I guess if that turns out to be an issue we could probably just extend the all_tuples for Bundle to 32 or something? 🤔

#

I mean, realistically @magic belfry did already hit the tuple limit with avian, but that was because of "required components at home" so I assume that won't be a problem next release 😂

golden notch
#

i still think its a huge win for things like cameras where you often want to add/remove things like camera settings

quartz sleet
crisp garden
#

Yea, buit that's exactly the worse ergonomics we want to avoid ferris_sob

quartz sleet
#

iirc increasing the all_tuples count would be quite mean to compile times

#

But yeah

#

Really we just need better lang support

#

🥺

keen patio
#

Maybe BSN can un-flatten long tuples to support any lengths

crisp garden
#

BSN should generally not be affected by that limitation afaik

thick slate
#

Yeah, with macros we should be fine

devout ravine
#

Is there a milestone for the progress of the MVP for new scenes?

#

nm, I scrolled up )

plush crystal
#

porting my question from #ecs over to here: I feel like required components is currently quite limiting due to lack of "construct" functionality being available from 0.15.0. However, after looking at the proposal for "construct", I still don't think it allows the "bevy_asset_loader" workflow to integrate. If I have something like [require(Sprite)] on my component, and I want the default for this to contain an image handle that has been loaded into an AssetCollection resource prior to entity spawn, there doesn't seem to be a clear way to do this, even with "construct".

#

feels like its missing a require[Sprite(get_from_world)] where get_from_world is a function that I have defined which allows me to pull the handle from the resource and return the component. Currently, all I can do is provide a function with no inputs, so all I can use is constants.

copper dragon
#

Would it be dumb to make a bsn copy of the editor prototype ui code based off what we know of bsn?
I'm curious to see how it actually feels to use bsn and how much it'd actually save for the editor.

devout ravine
#

@plush crystal You would probably need to wrap the handle in some kind of newtype but it would work then, as you can access AssetCollection from context.world

plush crystal
devout ravine
#

oh I see yeah

#

yeah, only one construct implementation is a problem

devout ravine
#

maybe Construct should be type paramed by Props and Output, not just Output

#

not sure how to "autoselect" correct one in that case tho

quartz sleet
#

That's true. Why don't custom constructors get world access when they run? Seems like an oversight. Could maybe PR?

#

Unless I'm missing something (quite possible)

plush crystal
#

if there's already a PR for component constructors, I can add this query to that

split harness
# quartz sleet That's true. Why don't custom constructors get world access when they run? Seems...

Using Construct style World-mutating constructors in required components would be unsound given how spawning works currently, as it could change the location of a spawned / inserted-into entity mid-insert. We'd need to rework the spawning flow to make this work correctly (and we'd likely need to write the constructed components to scratch space prior to writing them to the real storage for Reasons)

I do think a read-only world constructor is sound though, which would enable the "read from resource scenario"

#

also @plush crystal

quartz sleet
#

That ought to be enough right? That means you can access asset server in custom constructors

#

Or a resource you inserted in your plugin

plush crystal
split harness
#

Ex: one general case this wouldn't allow is generating a new custom id for an entity's component based on some ResMut<MyIdAllocator> resource

#

MyIdAllocator would need to use interior mutability

#

In general something wanting to read world during construction will be doing some form of cross entity "coordination" , and coordination pretty much implies mutation

cobalt stone
#

Not saying there aren't, but for specifically for ID allocators I wouldve probably made it multi thread compatible regardless to prevent serial scheduling.

frosty shell
#

World mutation can always be done in hooks and/or observers

split harness
# frosty shell World mutation can always be done in hooks and/or observers

If this was stated as an argument that such operations could be deferred when needed, I agree (although it dictates a more complicated two-step design with more overhead).

If it was stated as an argument that mutation is possible in the context of inserts, "entity shape changes" are still deferred to the next flush / don't actually happen during the insert (for similar reasons to why &mut World can't be soundly passed to required component constructors in the current impl)

frosty shell
#

The former

quartz sleet
split harness
#

The mutation is slightly more relevant in this context when the Resource isn't doing the asset server load, but instead the component constructor is

#

Obviously in the case of AssetServer, the internalized mutation isn't irrelevant to the &mut vs & conversation

#

Just pointing out the general patterns at play here

plush crystal
#

Not sure if I fully understand what's being discussed here, but to go back to my true scenario: I was creating a 2D mesh handle and a color material in-code, the asset server had nothing to do with it. Also, to expand on this, I like using the bevy_asset_loader workflow, where the interface with asset dependencies is ultimately just a plain set of Handle<T> fields in a Resource.

#

and the ultimate solution I was after was one where I could create a component that requires other components that have these handles in them by default, hence the need for world access

split harness
#

I understand your scenario, agree it is covered by &World access, and I'm pretty sure it is sound to pass that to a require constructor

#

Largely just trying to discuss the other scenarios / contrast with Construct.

#

Also note that require(construct(MyComponent)) is implemented (but was removed for the Required Components PR because Construct hasn't landed yet)

#

However, this impl does not insert directly (it does the construction in a deferred manner, similar to deferred operations in hooks and observers)

#

This was done for the soundness reasons mentioned above

#

That impl would solve your problem as well, but in a slightly less efficient way compared to passing &World in to the constructor

plush crystal
#

thanks for the detailed response!

split harness
#

Another thing to be aware of: At the time of component initialization, World has already been broken up into its smaller pieces (Storages, Components, etc). Providing a direct &World reference would also require non trivial restructuring of entity init code

thick slate
#

@spring basin, parent-children relations are currently special cased, via indented nesting

#

While I like that syntax a lot, I'd like to be sure we can express other forms of relations

#

And maybe even alternate hierarchical relations using a variation on the nesting syntax

spring basin
#

Yup, this is a bit un-fleshed out at the moment, but definitely one thing to talk about is how Entity appears inside bsn! syntax and other parts. Is struct Targeting(Entity) something we want to nicely support? Or should there be a separate syntax for relations that is the one that is recommended?

#

Being able to model things like Inventory/ContainedIn with a syntax that is as convenient as [ ] is for Parent/Children is definitely something to consider.

#

Off the top of my head, I could even propose that Children [ Name("foo"), Name("bar") ] would be the primary thing – and that would work with struct Children(Vec<Entity>) and any other similarily defined component. Then we wouldn't have [ ] as a special syntax at the top level (necessarily), but we'd end up with ugly double bracing on every nested object ( Name("foo"), Children [ Name("bar) ])

thick slate
spring basin
#

For all of this, I would expect that the possible component at the other end of the relation would automatically be filled in (by component hooks) when one end is specified, so the syntax wouldn't need to specify both Parent and Children on every entity.

native mesa
#

as i mentioned in the other thread, i think this is mostly a question of how we should uniquely identify/reference nodes within a bsn document. If that's possible, then relations of any sort should be pretty easy to encode.

#

there is also, like, relations within a scene and relations to external entities. I do hope we can get internal relations to be part of the bsn structure, but external relations seem like maybe not that useful, and you'd need to set up dynamically in rust.

spring basin
#

I think the question is – do we encode them as components? And if we encode them as components, are those going to be shown in the data model exactly as components, so entity.get::<Targeting>() returns a component which will Deref to Entity? Will mutating that component be supported?

native mesa
#

that's less of a strict bsn question, i was thinking more about specifying it on the syntax level. but also important to think about.

fallow cloak
#

maybe some label in the actual bsn that gets erased at runtime?

spring basin
#

I didn't actually mean "Name" as the pointer above, I used it as a placeholder component for the entity as I needed something 🙂

fallow cloak
#

oh yeah for sure (I actually didn't even read that far up) 😅

#

just something that came to mind

native mesa
#

my hopes and dreams in terms of the bsn scene language itself would be something akin to internal links in a html or markdown document. You write anchors @whatever which are unique to that scene, and then you can use them to reference specific entities.

fallow cloak
#

yep :)

native mesa
#

the anchors don't have to exist at runtime, just be used to hydrate the relation when the scene is loaded.

spring basin
#

If it's just hydration, then the next question is how relations will look in save/load? I think bsn as a file format was meant for that use case as well, but it's obvious there needs to be something happening when saving so that GlobalTransform and similar components wont get saved.

fallow cloak
#

some syntax ideas:

MyEntity(Comp1, Comp2) [] // might be ambiguous with single-component entities
my_entity: (Comp1, Comp2) [] // might get confused with component fields
@my_entity (Comp1, Comp2) [] // might get confused with `Construct`
native mesa
#

however the hierarchy works today in the current scene system will probably be the approach for that

fallow cloak
#

could just save the entity name as the label, with a number tacked on if there's duplicates

spring basin
#

Depending on how relations are written, struct RubberBand { first: Entity, second: Entity } might be supported or might not be. So I think it's also a question that are entities (as references) supported everywhere like inside struct fields (and lists of entities for Vec<Entity>) or are they only supported for components of form struct Foo(Entity) (and same for vec) or are they something distinct entirely.

spring basin
fallow cloak
#

for the label stuff mentioned it would make sense to let labels work as substitutes for Entity anywhere, yeah

spring basin
rare whale
#

Here's a concrete use case: in bevy_reactor, various entities are "owned" by the parent but are not part of the visible hierarchy, so they don't use the Parent/Child relation. For example, signals, effects, mutables and callbacks are all "owned" - like entity-scoped observers, they are despawned when their owner is, but they don't take up slots in flexboxes and grids. In the future, this will be modeled by an Owner/Owned relation similar to Parent/Child.

One important aspect of this is that these relations are implicit: when you create an effect or signal, you don't explictly set its owner or insert an Owner component, this happens automatically as a side-effect when you call the creation API. Users of the templating system don't ever need to think about ownership at this level.

So I'd imagine that certain kinds of relations, like Parent/Child and Owner/Owned, would either be completely automatic, or would have special syntactic sugar in the templating framework. Other kinds of relations which are created explicitly can have an explicit syntax.

tired topaz
#

Okay, I think this might be the correct place.

Disclosure: I am quite skeptical towards required components. And I am currently migrating crates i maintain.

Just thought I would share some things I noticed during this work.

  • The Required components really makes the code feel more ECSy, which is actually really nice
  • The only way I was able to migrate my existing crates to 0.15 was through the deprecation messages on the bundles I was using
  • I work in a team where not everyone has the same level of bevy/rust interest as i do. As well as a team that has to deal with multiple different technologies and stacks and languages.

Okay, with that intro out of the way, the required components change has made the code look much much nicer, and more clearly communicates the ECS right out of the start. Hopefully this will help developers that are used to OOP, which bundles kind of represent understand the differences between OOP and the ECS way of thinking more clearly.

But while doing migrations, I generally noticed that I the deprecation messages was really helpful, and while migrating, I kept the bundle in code, close by, so that I could in my editor reach the bundle, see which components I needed, which components I had explicitly set and which was set to default values, but I needed them anyway. Then I removed the bundle and got rid of the deprecation message, and stuff worked again mostly.

This is where my remaining skepticism towards this "required component" comes in. Previously, when I intended to show an object somewhere in the world, I would spawn a PbrBundle, then modify the needed things. Likewise for a camera, I could spawn in the Camera3dBundle see what options was available for me, modify what was needed, and then put the rest to default values.

tired topaz
#

Now being quite well-versed in bevy, and following the discord and github and @thick slate merge trains etc. I had not much trouble migrating, I have also used Rust quite exensively, and the difference between writing Rust code, and Bevy code is quite large. The bundles really helped learn what kind of combinations was needed to achieve my intent.

For new users of bevy, that came to bevy because they liked Rust, the type safety and the implicit promise of "the compiler will save you from so many issues that you previously only noticed in runtime" . The discoverability and explicitness of the Bundles was very good for newbies to Bevy, and this is where I feel Bevy might lose interest, this "Required Component" change is generally a good one I now believe. But the handholding provided by Bundles, as an intro towards bevy. Is surely going to be missed by newcomers.

And currently I don't have any good idea on how to give the same kind of "compiler tells me when I mess up" feature as was previously provided by the bundles, and is one of the primary reasons why Rust is so loved by developers. I am not writing this to complain about the "Required Components" change, I am writing this out of concern that we might take this issue a bit to lightly. I read in another channel/thread that someone jokingly said something along the lines of "Bevy: fighting Rust, with Rust".

So my final question, is this issue percieved by anyone else, and if so, is there anything we can do to help newcomers?

dapper sky
#

Learning how to use bevy_ui has for me been helped a lot by how Node is an extremely large component (having eaten Style), I will say that.

#

there's been some chatter about how this can potentially be addressed with adding doc comments via the proc-macros that are already in use.

#

(this was chatter a while ago, iirc)

#

A concern I have is that, because the available knowledge to a proc macro is pretty isolated, there will not be a single glossary of every required component that a single component invokes on the doc page for that component. That's probably fine, but does mean if someone isn't used to or struggles to deal with link-diving they might get frustrated.

tired topaz
#

Yeah, I have the same concern. I have met too many developers whose sole way of learning is to use the suggestions from the LSP

dapper sky
#

The other one is "well, this knowledge will get exposed by the bevy editor", as the bevy editor may end up with a wider pool of available knowledge through whatever tooling gets developed to enable that. But the bevy-as-a-programming-framework lot may find that frustrating on their own.

dapper sky
tired topaz
#

I am glad I am not the only one feeling this. An idea I had for this, is basically some kind of "intent grouping modules" . With that I mean just ordering components in modules, in such a way that I could for example write:

completely random example:
archetype::ui::< here the suggestions would show me all the components I could combine that would make sense>

tired topaz
dapper sky
#

that the rust LSP can bring up doc files is a boon 😅

tired topaz
#

but I have also followed the discussions enough to learn I can just use my code-jump on the require macro that is part of components 😛 which I dont believe a newcomer will pick up naturally

tired topaz
dapper sky
#

But yeah, to counterpoint it may be that bevy is complex enough that docs and field auto-completes may never be enough to get a newcomer to get what they need to do within a short timespan. It might be that this is true for all game engines and frameworks, given the sheer density of intersecting practices.

tired topaz
#

If anyone has played Magicka, thats how I feel with the ECS.

Like oooh I can combine these components to create this outcome! cool!

But for someone that wants to achieve the Outcome, the feeling of randomly combining elements and getting strange Outcomes is not a positive feeling 😂

#

If I somehow could be guided to the possible outcomes better, it would feel very nice

dapper sky
tired topaz
#

True

#

Soon we might have to maintain the "BevyLSP" 😄

native mesa
tired topaz
#

Sure, its just that many developers have started learning on the LSP. Me being one of them. Having the correct part of the docs, and correct suggestions for the specific context I am in is almost like a superpower.

dapper sky
#

sick :)

tired topaz
#

Yeah the docs where actually kind of amazing, when the Intent was already communicated through the bundles

#

As in the migration was quite smooth 🙂

dapper sky
#

I think one silly thing I mentioned was having a required components associated type, that nestedly groups the required components mentioned by a requires(...) invocation in an associated type of the components trait. But that would get messy fast and would come up against rust type size limits probably? I am wandering quite far out of anywhere I'm explored when I say that.

#

but again that doesn't really present knowledge in a meaningful way

#

at least, it doesn't if the trees get deep

tired topaz
#

And by doing the migration, I learned that I need to add both Mesh3d and MeshMaterial3d to get something visible, and if I want to change the place they spawn, I have to manually add Transform even thought its part of the required components.

But this was because I was migrating and could look throught the Bundles members, and had knowledge before. A new person might be very much confused about how all these things relate

dapper sky
native mesa
#

We can also use debug tooling to log when ECS structure is malformed

tired topaz
#

hmm, this could be amazing. I mean there is always some combinations that will generate some behaviour. Seeing a thing in 3D space, is a combination of a few components. We could after the fact identify what combinations are done and what standard behaviours we are expected to be seeing.

But bundles was nice since it shifted that to compile time

#

and bundles was messy because it was not very composable...

#

Can we get the best of them both somehow, the context, the discoverabilty and explicitness that Bundles provided, with the compsablity, and boilerplate-reduction that required components provides?

native mesa
#

I think it will probably just spur people to read the docs more. Bundles did allow people to rely on autocomplete, and kind of taught people to work that way.

tired topaz
#

Yeah, that is true. But its not only bevy that does this. basically every language/library today does this. Because it allows you to stay in the context of what you were doing, reducing context switching

#

and its very much generally liked, and relied upon. RTFM is basically always a last resort, and Rust as a whole has generally reduced that need quite a lot

wind dock
#

Wasn’t there a discussion around functions that turn an “impl Bundle”? Like a component constructor that can return other components?

magic belfry
#

That's largely considered an antipattern and not something we should encourage, at least if you mean having the constructor on a component

#

quoting Cart on a similar idea in the text rework

The suggested "constructor that returns two components" pattern feels really nasty to me, especially in the context of the scene system. People don't expect constructors to behave that way, and I'd prefer it if we didn't embrace that pattern in Bevy, especially for something as fundamental as text. The init dataflow should be as simple as spawning the components you want explicitly.

wind dock
#

Isn’t that basically what required components are? It’s a type of constructor that you define via rust attributes instead of a function.

native mesa
wind dock
#

Not sure if there's any value in that comparison. Just thought it was interesting

magic belfry
wind dock
#

That’s good to know. It is an interesting framing to think about required components as a limited and implicit form of BSN

#

Thinking about a Player component for example that requires a bunch of other components. It’s reasonable that you’d also create a “player.bsn” scene so you can edit it in the editor. Then you’d need to decide which components are required and which go in the scene.

#

So there is a bit of a tension there.

crisp garden
# tired topaz Now being quite well-versed in bevy, and following the discord and github and <@...

I think for the most part any time the bundle tells you "what combination was needed to achieve your intent", that hints at the ergonomics of those components being rather confusing. The thing with Material and MeshMaterial for example is rather confusing and unnecessary, but bundles have been conviniently hiding flaws in first party component designs. They are more obvious now, the problem is that fixing all of them in the same release would've been insanely breaking and not really necessary (forgetting MeshMaterial is about as big of a footgun as forgetting to use MaterialMeshBundle)

spring basin
#

FWIW, I never used bundles for anything in Bevy. When I constructed things, I'd just write all the components as a tuple anyway, or earlier on added them one by one even. If I needed things that needed to appear together to make sense, it usually also meant that they had some relation to each other, meaning I'd have a constructor function that returns that type of entity, and then I'd customize that with calls on top. Bundles, with all the nested defaulting etc. never felt right for the job, and I didn't like the fact that even though I inserted a struct, then that struct vanished in a puff of logic and I'd be left with just components anyway, so I'd rather just insert the components themselves. For me, this all seems brilliant – but it will only be truly useful for me if the Construct semantics fit my needs exactly, so I can use less constructing functions for entities.

tired topaz
# crisp garden I think for the most part any time the bundle tells you "what combination was ne...

The key-word is "Forgetting", because forgetting implies knowledge. With structures or constructors, the knowledge is not needed, its embedded within the code.
As @spring basin put it, he added them one-by-one, I am assuming then, that the knowledge of what to add, was imbued within the bundles.

The feature of the bundles, was that when you wanted the "basic" functionality, there was a pre-provided way of creating this, that did not wait until runtime to show if it was correct or not, it was collected at compile-time.

Constructor functions could provide the same functionality, as could a smaller subset of bundles. I am just a big fan of expliciveness, before there was a "Construct structure", that vanished in a puff of logic. Now its a implicit trail of dependencies that appears in a puff of logic. With the added consequence that it relies on the developers knowledge to larger degree than before, and the way to gain that knowledge is not as straightforward as it was before.

There was also more freedom before, since the Bundles was optional, and one could just add the components one by one if they did not fit your need. For example, I dont know why a Mesh3d requires a Transform, but now I cant even opt out of it by not using the "Constructor Structs".

Could there be a way to grab a few components, (Transform, Mesh3D).hydrate_with_required() that would return a tuple with all required components.

This would solve freedom part. By providing a way to get the required components, and also opt out of it. Still wouldnt really help with the discoverability part though

crisp garden
#

I said "forgetting" but new users don't usually understand bundles at all. Even when they know they need to use it to get things to work, they'll think it's something beyond a list of components, and be confused by the "structure that vanishes in a puff of logic" thing

tired topaz
#

Yeah, and that is part of my point, they don't have to understand the bundles, they are almost impossible to fail to use. They get what they wanted to do to work, and when they learn that bundles was just a list of components, they are moving on to more advanced things

crisp garden
tired topaz
#

Nah, new users take the first example, which uses a bundle, and then goes from there.

They try querying the bundle, and it throws a compile error at them. Compile errors are one of the core pillars of Rust development, its what gives trust, that things fail, before it has a chance to run

#

instead of after

crisp garden
#

That might be so for the first example, but the moment a user departs from examples they'll forget about bundles. Even after using bevy for 2 years I still frequently make mistakes like just adding Transform without GlobalTransform or SpatialBundle

tired topaz
#

That has not been my experience at all. And while I also makes mistakes where I only add a single component and forget the rest. There is nowhere in bevy, where I can read the docs, or get the LSP to give me any clue about what the things I need / can edit are. They are magically added at runtime.

For example, reading the docs at Mesh3D nowhere does it mention Transform, or Visibility.

#

and even worse, There is going to be very hard for me to figure out that I can use InheritedVisibility and ViewVisibility That in turn are required for Visibility. But if I actually found that, manually adding InheritedVisibility would probably just make it even worse, since from there, nothing points me to Visibility.

Before, this was quite neatly collected under bundles. I understand that the Required Component is the way forward, but I am still looking for a solution to the lack of discoverability.

Going from "This struct goes up in a poof of logic, where all the members became Components" is in my opinion an easier thing to realise than "I added this one component, and now I have 5 components, but nothing is still visible"

crisp garden
#

I've never found bundles listing thingss to be particularly helpful tho. You might see components, but unless you know about them and understand what they do you'd have to go trough everything one by one when half of the bundle is just noise like GlobalTransform or ComputedVisibility that you'd never interact with

tired topaz
#

I have found them immensly helpful, and when trying to understand what makes something, become something. Its a bit annoying that they arent really explaining what things I should modify, and what things are modified for me. Bundles definelty wasnt perfect, but they definetly helped learn bevy

#

Using components now, I actually feel like we are using builder patterns, but without any aid from the LSP or the Rust language

wind dock
#

I haven’t used required components yet but I’ve also learned a lot by inspecting bundles

spring basin
#

What I mean by "one-by-one" is that I used to do world.spawn().insert(Foo).insert(Bar).insert(Baz).id() before world.spawn((Foo, Bar, Baz)).id() came along. So I never used bundles. I used example code as reference.

crisp garden
#

The bundles in this context is more referring to things like SpatialBundle or PbrBundle. world.spawn((A, B, C)) will probably be more common than ever now with required components, since you might do world.spawn((Mesh3d(mesh_handle), MeshMaterial3d(material_handle), Transform::from_xyz(-1.0, 20., 3.0)) instead of world.spawn(MaterialMeshBundle{ ... })

#

I hate that Mesh3d and MeshMaterial3d are two separate components 😂

magic belfry
#

With type-erased materials we could maybe combine them, though rendering people really seemed to want Mesh2d/Mesh3d without materials to be a thing

crisp garden
crisp garden
magic belfry
#

I think it was mainly to enable some niche custom rendering scenarios or smth

#

at least based on what I remember from that long default mesh material convo starting here #rendering-dev message

crisp garden
golden notch
#

it’s important to be able to render a mesh without a material

magic belfry
#

A bundle would still kinda work to group the mesh and material together but... it was ergonomically much worse than keeping them separate imo

golden notch
#

i’d strongly push back against any component that unifies them, they’re not a single concept

fallow cloak
#

Not all materials use meshes either

round ridge
golden notch
wind dock
#

Crazy idea but what if instead of required components, components could have a default bsn scene. Bsn could have a custom LSP so that it’s easy to use in editors.

round ridge
#

I think I'm on team type-erased-material-in-mesh3d. It lines up pretty well with how other engines think about things, and anything you'd do with multiple materials you can do with multiple entities. 🤔

golden notch
round ridge
spring basin
crisp garden
golden notch
golden notch
fallow cloak
golden notch
#

an easy example would be where you might want to build the mesh in the vertex shsader instead (for a simple quad, etc)

fallow cloak
crisp garden
#

Only the picking example really makes any sense wrt problems from combining mesh and material. MeshMaterial3d is just a material for a mesh, other things need their own material, and there should be no confusing which pipeline picks up a material 🤔

golden notch
fallow cloak
#

meshes need to be queried separate from the material component

#

for preparation

crisp garden
golden notch
native mesa
fallow cloak
#

I'm assuming it's anything that impls bevy_pbr::Material

golden notch
native mesa
#

cool

crisp garden
native mesa
#

shaders do not a material imply

golden notch
#

yes, but it could jsut be as simple as hard coding a fragment shader in a pipeline descriptor

crisp garden
#

Having Mesh3d standalone has essentially the same issue as Handle<Mesh>, there's this mesh that has no context and is up for grabs by any and all systems, which causes major problems if any of them don't require other things to trigger behavior

golden notch
#

or you could be implementing your own material-like trait, which hopefully @fallow cloak s work will make less necessary if it's general enough

fallow cloak
golden notch
fallow cloak
golden notch
#

generic systems and resources also fracture storage, which may or may not matter, but totally could

fallow cloak
#

or maybe it's fine, idk

golden notch
golden notch
#

like i mentioned above, you also get to benefit from more fine grained change detection with generics, so it can be a win too

fallow cloak
#

true

crisp garden
crisp garden
golden notch
# crisp garden We used to have `Mesh2dHandle` vs `Handle<Mesh>` for this distinction. But reali...

i think there's just a tension here in that, ideally, there would not even be a 2d/3d distinction in the renderer but it's obviously super important in terms of application logic, and so in the latter case, sometimes you really do need to query for the equivalent of Handle<Mesh> which propogating newtype wrappers makes more complicated or requiring more generic systems. i think the newtype wrappers have been an obvious win btw, but i think this is basically the tension

crisp garden
#

There should be a distinction because you're trying to render a 3D mesh to a 3D camera, not to your other 2D camera. It's already annoying enough to have issues like gizmos writing to your 3D gizmos to 2D cameras 🙃

#

Doing a query for Handle<Mesh> sounds like you're just going to create all sorts of problems when multiple of these overlap, just because entity A needs to have X behavior with a mesh, does not mean entity B should also have that just because I want to render a mesh

golden notch
golden notch
crisp garden
#

All picking (and other plugins that might want to function on meshes generically) would need is a way to register what components are valid mesh sources (which only the end user can define probably)

golden notch
# crisp garden At that point you could've just had `RenderMesh{ mesh: Handle<Mesh>, material: H...

here's another concrete example like picking that might help illustrate the problem, check_visibility queries for a Mesh3d, so if you added a MyMesh3d to render with your own pipeline, you'd have to add that system again. this isn't a big deal for a single system, but it means we'd also have to, e.g., make the picking plugin generic and you'd have to add PickingPlugin<MyMesh3d> and anything else

#

like yes, sure, we could have a MeshLikeThingPlugin<T> that you have to add and adds all those things for you

#

but, like, why? like what's the big deal about entity.insert(Mesh3d(handle), MeshMaterial3d(handle)

#

maybe i just don't undestand that lol

spring basin
# wind dock Huh?

Nevermind, I was just being playful... was just referring to the fact that LSP autocomplete isn't what a lot of people use anymore when GenAI tools like Github Copilot have taken over.

crisp garden
golden notch
crisp garden
round ridge
crisp garden
golden notch
crisp garden
#

Mesh2d and Mesh3d do exist for them to be picked up by the right pipeline tho, there is no difference in the handles otherwise

golden notch
#

here's how a typical queue system works:

  1. is the entity visible in my camera
  2. look up the mesh
  3. specialize the pipeline and queue it

"the pipeline" here doesn't have to the material pipeline

crisp garden
#

Afaik bevy_sprite only picks up Mesh2d and bevy_pbr only picks up Mesh3d

#

And that's how it should work because functionally the meshes are identical otherwise

round ridge
golden notch
crisp garden
#

The material pipeline is essentially irrelevant here, because meshes, materials, pipelines etc all have absolutely no interest in whatever you spawned on meshes, they all function trough assets and plugins

#

It's the extraction and queueing systems that pick what to render, and if they start indiscriminately rendering whatever combination of components they think fits you'll end up with some weird ball of z-fighting in no time 😂

rough cave
#

Based on how it has been explained to me: Mesh2d and Mesh3d specifically exist as public APIs of Bevy's 2D and 3D pipelines respectively, and exist solely to say "I would like to render something, in this pipeline, using a mesh".

crisp garden
#

They're coupled to rendering so tightly in fact that they are in bevy_render and not bevy_mesh

rough cave
#

Yeah, because they're not representing the concept of a mesh, they're representing the public API of a render pipeline - and just happen to be called mesh (which seems confusing to me).

#

Mesh represents the concept of a mesh, Mesh2d and Mesh3d are just arbitrary names for invoking a specific rendering technique in a specific pipeline that happens to use a mesh

crisp garden
round ridge
golden notch
round ridge
rough cave
#

If you add a custom pipeline you'll need a custom marker component to invoke it, so you'll need to add a new component

crisp garden
golden notch
rough cave
#

If your custom pipeline uses normal meshes, you will get all the benefit of using the existing Mesh code, but you wouldn't be able to utilize Mesh2d and Mesh3d, because they're the components for invoking the render pipelines that you are replacing

golden notch
crisp garden
#

Also depending on how custom your renderer is, then you'll definitely need a component for a Handle<Mesh> + whatever other abstractions you associate with them, because you might not even want to depend on bevy_render

rough cave
#

Ah, so what you're actually discussing is how you change the existing pipelines to accept different meshes?

#

(Hypothetically)

golden notch
#

you might still want to render that mesh in 3d transparent phase to get blending, for example

rough cave
#

Isn't the answer that with the Bevy builtin pipelines, they have a public API surface area, and if you want to do something that the API doesn't expose, you just can't do it?

golden notch
#

custom_shader_instancing is a good example of this rn btw

golden notch
crisp garden
# golden notch to add back all the things that you'd expect to work with meshes like visibility...

All of these things could be made to work with custom meshes. Extraction is trivial when you already need to extract your material or marker component which HAS to exist in this hypothetical world because no one should make a plugin that arbitrarily affects every mesh in an app. Visibility shouldn't need mesh, just Aabb. There are other benefits to picking being able to act on specific components that give a handle rather than always only the standard bevy_render ones (beyond just the fact that the bevy_render dep is stupid)

round ridge
# golden notch `custom_shader_instancing` is a good example of this rn btw

I'm really mixed about that particular example, because it strikes me as relying on behaviour which isn't really expected to be standalone. Like, if you swapped Mesh3d for Mesh2d in that example, I think it'd still work? It made more sense prior to 0.15, because it was just Handle<Mesh>, so it didn't rely on pipeline-specific code.

golden notch
round ridge
#

(Or rather, it did, it just didn't look like it).

crisp garden
round ridge
round ridge
golden notch
#

(modulo some more small changes)

crisp garden
#

Mesh3d is really just another Handle<Mesh> masquerading as a bevy-pbr specific thing

round ridge
crisp garden
#

The instancing thing probably should have something clearly explaing, because hypothetically if I create MeshInstances with data for the number of instances, a Mesh and a Material, they would not compose the way the names suggest

golden notch
golden notch
round ridge
rough cave
#

@golden notch is your point just "pipelines shouldn't all have their own concept of a mesh"? because I think everyone is in agreement on that?

crisp garden
cobalt stone
#

What is the debate here?

rough cave
#

@cobalt stone that's what i'm trying to work out 😛

cobalt stone
#

Just joining this now

golden notch
crisp garden
golden notch
#

instead of having them be separate coomponents

cobalt stone
#

Please don't. Material is useful even if you aren't using meshes. MeshletMeshes, and soon decals need them.

crisp garden
#

And the 2D variants, and a version for Meshlet + Material

crisp garden
round ridge
crisp garden
#

Like Decal(Handle<Material>)

rough cave
#

So confused about the API design in Bevy, is there supposed to be one root component for each API feature (justifying required components), or is it supposed to be super modular collection of lego bricks that all work nicely if you happen to place them together in just the right ways?

cobalt stone
rough cave
#

People want to combine Mesh + Material, make it easy to create a single concept of "I want to render X in the 3D pipeline", other people are saying "don't combine things, I use the individual lego parts to assemble interesting custom machinery".

round ridge
crisp garden
cobalt stone
crisp garden
#

(Not saying that should be blocking for decals tho, it's already a mess today so it needs to be fixed separately anyway)

cobalt stone
#

There is a lot of mess in the renderer, this is far from the only issue 😛

#

We've been discussing it recently

rough cave
#

Very confused by components are data + behaviours, surely the one of the key aspects an ECS is the separation of data and behaviour, versus the OOP model of co-locating data and behaviour. Isn't the whole point that you should be able to swap out systems with custom ones that operate on the same types, because they are separate?

round ridge
#

Decals seem more forgivable because I'd expect it to be the same interface, definitely misread decals though. 😅

crisp garden
#

Ignoring the name and some edgecases, this would easialy be the preferable API, since it actually spawns one thing, and not two things that are nothing but add up to something

Mesh3d<M: Material> {
  mesh: Handle<Mesh>,
  material: Handle<M>,
}```
cobalt stone
golden notch
#

i.e. it's "just" data

crisp garden
#

For decals Decal<M: Material>(Handle<M>), for meshlets the above but swap all uses of Mesh for Meshlet

cobalt stone
rough cave
#

This means Bevy is going away from lego bricks that happen to combine into interesting machines, and towards the idea that there's a bunch of specific features in Bevy that you happen to invoke through the specific calling convention of adding components to entities.

round ridge
# cobalt stone MeshletMesh is the same interface, you just don't get to control the vertex shad...

I suppose that's a good way of putting it, but it does mean that for example if you write a material for meshlets you are exposed to the vertex shader, it's just not going to work (as it's in the material trait). And vice-versa, you could have a material that works for meshlets, but doesn't work for other things because the vertex shader is broken. I don't think this is too bad, but it is a foot-gun I guess!

rough cave
#

Mesh3d and Mesh2d are the calling convention for the PBR and 2D pipelines respectively, whether we think that's a good idea or not.

tired topaz
golden notch
round ridge
rough cave
#

@golden notch you don't have a choice with required components, though, they actively close off the lego brick analogy by preventing you from using your own components - if you touch a root component for an API in Bevy then you suddenly have the engine automatically add a bunch of required components and systems you don't want to use start acting on your entities.

crisp garden
cobalt stone
#

The existing low level material stuff will still exist for technical engineers, but won't be the preferred method for artists

golden notch
crisp garden
golden notch
#

although that sounds a little oo now that i'm saying it :p

rough cave
#

@golden notch right, and the thing is, again: Mesh2d and Mesh3d are the root components for 2D and PBR pipelines in Bevy.

round ridge
cobalt stone
round ridge
golden notch
crisp garden
rough cave
#

So whilst we all agree that the actual abstract concept of a mesh should be the Mesh type in bevy_mesh, the way you get them to be rendered is by invoking them is by calling specific rendering methods in specific rendering pipelines, and those methods are the Mesh2d and Mesh3d components, afaik

cobalt stone
rough cave
#

(or other supported API calls, like the Meshlet work)

fallow cloak
#

Mesh3d has its own associated systems that prepare meshes for the gpu, but that doesn't drive rendering

crisp garden
golden notch
rough cave
#

It's weird to me because for rendering a 2D entity I think of nouns like Sprite, so that would be the driver, and Mesh and Material would be internal concepts to make it work. Similarly in 3D I would expect there to be a series of nouns (Model, Billboard, ...) that are drivers for a Mesh and Material (or whatever).

crisp garden
#

Which would mean there is no root node, and all of these are just Handle<Asset>s that are extremely easy to abuse

fallow cloak
golden notch
#

yeah also to be clear, one thing we are all in agreement of is that the Material trait is kind of a mess lol

cobalt stone
#

Like I said, the entirety of bevy_render, bevy_core_pipeline, bevy_pbr, etc is a mess 😛

fallow cloak
#

core_pipeline specifically is on my hit list lol

rough cave
#

There seem to be people asking to have stuff merged for user facing simplicity, and people asking for separation to facilitate technical flexibility. I thought the whole point of required components was that you got the simple merged component at the front, and it would automatically include a bunch of more detailed components that drove very specific technical functionality, allowing for technical flexibility.

fallow cloak
#

nobody knows what it's for 🙃

cobalt stone
#

It's both a lot slower than it would be if you wrote the code from scratch, and a lot less flexible/easy to use than we originally imagined

crisp garden
golden notch
fallow cloak
#

?

fallow cloak
#

yeah the material component stays separate from the data source

#

you need that to do preparation

cobalt stone
# fallow cloak nobody knows what it's for 🙃

It was intended that you could swap bevy_pbr out with a new bevy_render/wgpu-based renderer using the same set of render nodes and core interfaces. There was even a third party renderer (not made by any of us) that proved it was possible. But it was also never really useful in practice.

rough cave
#

@golden notch right, but regardless of whether you agree with it or not, if you don't like the merged components, the principle is that you should be abel to "just use the underlying components" and bypass the driver components you don't agree with.

crisp garden
#

As long as we have a MeshMaterial3d component, and Decal, Mesh, and Meshlet all source their material from it, these components mean nothing by theirselves and can be combined in unexpected ways while also having 0 discoverability (questions like "How do I set the material for a Decal?")

golden notch
rough cave
#

@golden notch I dislike required compnents so I may have that effect on people 🤔

#

but to be clear, what i'm saying isn't an argument against required components

cobalt stone
rough cave
#

My point is that, if required compnents are largely used on "Driver components", then there are no issues

fallow cloak
crisp garden
thick slate
#

I think that one of the core lessons of the state of our current rendering is that "just expose the internals" isn't a good solution to increasing user flexibility

#

You need to actually document, and understand user needs, and expose reasonable ways to change the behavior in useful ways

rough cave
#

The API could have a "CombinedMeshAndMaterialForPBRPipeline", which had required components for mesh, material, transform, visibility, etc. Then people who don't like required components, or want more technical flexibility, would just add the constituent components: visibility, mesh, material, transform, etc.

golden notch
cobalt stone
crisp garden
thick slate
#

I think that required components is a perfectly fine tool for doing that, but in the first iteration it's going to be less flexible than it should be, since we don't understand those specific user needs well enough

wind dock
#

I think perhaps “required” is too strong of a constraint. That’s why i was thinking about bsn, since you could view required components as a default bsn scene attached to the component.

A potential alternative is Bevy could provide a standard library of bsn scenes, including defaults that can easily be bypassed. This would also allow for multiple options if that makes sense for the given component

thick slate
#

So far I have a good understanding of the flexibility we need for Transform and friends, and I think there's a good solution there. But for more sophisticated rendering steps, I don't yet!

crisp garden
fallow cloak
# crisp garden Oh you want to make them 3 separate components? But at that point, combining the...

the reasoning is like this (in my impl):

  • the Material components are actually aliases of a single material component parametrized by the material pipeline, which allows MaterialPlugin to automatically extract and prepare different material instances:
    struct MaterialComponent<T: Material<P>, P: MaterialPipeline>(Handle<T>);
  • the actual data source for each material pipeline is ad-hoc and not handled explicitly by the material plugin. So, it has to be its own component with its own prepare systems, etc. We also don't want to have a separate preparation system for every generic version of mesh prep
golden notch
thick slate
round ridge
golden notch
cobalt stone
# crisp garden Yea this is sort of how I'd imagine it would go. I can combine my raymarcher and...

Actually for deferred you can, that's basically the idea of deferred. You can write material data (albedo, roughness, normal, etc) to the deferred gbuffer texture and it'll have PBR lighting (or your own deferred lighting pass, you can also customize that) applied without you needing to do anything else special. So you can totally have a raymacher write stuff to the gbuffer and get lighting.

thick slate
#

I just want more tangible use cases to be able to design those escape hatches 😅

crisp garden
round ridge
crisp garden
thick slate
golden notch
#

i know it's not quite the same, but every dependency injection system i've ever used has a way force overrides, i imagine we'll end up in the same place but having a good design that pushes users in the right direction rather than encouraging them to mess around and break things in even worse ways make sense

round ridge
crisp garden
round ridge
#

You just can't change the default at runtime.

wind dock
thick slate
thick slate
#

Which does give you a slow and clunky escape hatch even in 0.15

#

You can just write a hook to remove the unwanted component 😂

crisp garden
crisp garden
round ridge
crisp garden
round ridge
crisp garden
#

Hooks can't mutate the world so the order is irrelevant

wind dock
round ridge
#

Oh wait no, because no structural changes.

thick slate
crisp garden
#

Why add unnecessary requires to your own components? 😂

#

I guess maybe if you inherit them?

thick slate
hybrid bloom
#

in principle, we could also allow component requirement relationships to be deleted at runtime, right? the same way we allow them to be created at runtime through App?

golden notch
crisp garden
#

Yea, that Transform there should probably just be removed in the long term anyway. It just makes no sense to do now because building not-Transform is super clunky and not a properly supported usecase. There also probably needs to be some logic to avoid silly things like Transform3d + Transform2d + UiTransform on one entity, unless we throw those into separate GlobalTransforms of course

crisp garden
thick slate
crisp garden
#

Also needs some better dynamic queries, but we're close to that 🤔

crisp garden
#

Yes please 🥺

crisp garden
#

Archetype invariants would at the very least make the separate Mesh3d + MeshMaterial3d thing more tolerable, because you can't spawn 3 materials + 3 sources and have 9 things rendered 😂

#

At least 1 source + 3 materials can be useful, I actually do that in my game. I have CellShadedMaterial + OutlineMaterial + sometimes TargetMaterial

round ridge
crisp garden
hybrid bloom
#

relying on component requirements at the level of unsafety is fundamentally unsound.

crisp garden
#

With the relevant warnings on the method

rough cave
#

IIRC cart has claimed the job of writing the required components section of the 0.15 release notes, I'm really curious to see how he explains them to the broader audience, because whether this causes everyone to go crazy adding requires "just to suggest convenient defaults" instead of specifying "critical invariants" will hinge on the explanation.

round ridge
crisp garden
crisp garden
rough cave
#

Required Components were initially introduced as "do you want BSN? then we should get rid of bundles, and required components are better than bundles" - which I never really understood in the first place, but now they've also become a poor mans implementation of archetype invariants

crisp garden
#

Iirc Jondolf was considering StaticBody, DynamicBody, etc so you don't spawn everything any type of rigid body might ever need

#

Which seems like how you would want to use required components, define a clear root and only require that which is really necessary

#

Optionally adding stuff trough plugins is also huge here, like adding Sleeping and SleepTimer from a SleepPlugin rather than always adding them even if they are completely unused

rough cave
#

I am not opposed to required components used carefully in that context: only for high level public API types, and bypassable for power users.

round ridge
rough cave
#

My concern is that it is a very powerful button which every single person can press at a moments notice on any type, and it is also potentially being marketed as "a handy way to initialise common defaults", with no way to bypass it.

crisp garden
rough cave
#

But that means we've created a pervasive general problem, and then special-cased one solution to one instance of the problem

crisp garden
#

I don't think we actually have many issues besides Transform tho. In many cases there are systems that would've otherwise added the component anyway

round ridge
rough cave
#

@crisp garden yes, because the feature hasn't been released yet

crisp garden
#

Like Mesh3d might as well require Aabb if it's going to add it anyway

crisp garden
round ridge
#

(This is what DOTS does fwiw with LocalToWorld.)

crisp garden
#

Of course this also required getting rid of components that are abused to lose their context, which Mesh3d is a good example of. If it's not used for rendering, an entity with Mesh3d doesn't necessarily need a GlobalTransform

thick slate
round ridge
thick slate
#

Right

rough cave
#

@round ridge 's point is that you don't need a custom transform system, you just need to stop everyone requiring Transform, and write your own system that populates GlobalTransform

thick slate
#

That's a good example of a case where we're too inflexible that I'd like to come back to in earnest after we land the Static work TBH

crisp garden
#

Like writing MyTransform with progatation is an absolute nightmare, I doubt many people would want that. But just setting some global positions is easy

rough cave
#

I'm concerned that a bunch of energy is going into required components, to facilitate ergonomics (which bundles previously did), to facilitate archetype invariants (which they don't actually enforce), and to mitigate spurious archetype moves. When I work in Unity DOTS, none of these concerns seem to occur or be important. Some of the issues I also think will be mitigated by future tooling evolution as well, similar to Unity DOTS.

crisp garden
#

The biggest issue IMO with bundles have always been the ergonomics. Spawning two bundles almost always breaks, you can easily forget them, and it makes the spawning flow extremely hard to read

rough cave
#

In Unity they have a concept of "authoring components", which are like edit-time components that generate a command buffer. The final "baked" entity is the result of applying the command buffer from all authoring components. This gives you a nice place to ergonomically introduce "driver components" that explode into many components, without impacting runtime complexity. It means you fully know, offline, what the archetype is for an entity, so won't involve archetype moves when spawning at runtime.

crisp garden
#

Like I don't care about all the ..default() that's being spawned, I want to see the relevant data I'm inserting, not zero values

rough cave
#

In terms of "invariants", generally queries are written such that they either iterate something, or they don't. Or they ask for something and get Option or Result back. I'm not really sure I've ever encountered a situation where I'd want to say "I wish I could prevent people from using the ECS so that I could use unwrap() more often, or assume this iterator will return at least X values".

thick slate
round ridge
#

IME it's actually quite rare to spawn two functional bundles onto the same entity.

crisp garden
#

Or worse: There is a bundle, but not the right one bavy

tired topaz
crisp garden
round ridge
tired topaz
#

and by discoverable, I am hoping that the Rust languages type system should provide the guardrails, and the LSP do the discoverying, and when all fails, the documentation come to the rescue

rough cave
#

@round ridge I mean in our current project that you and I are working on at work, our agents probably already have like 20-30 components on, and we're only 2 weeks in X)

#

(not all at the same time, mind you, lots of marker components added and removed over time to signify different things)

crisp garden
# round ridge Yeah... what on Earth are you doing. 😂

Looking at my code from before I switched it over to observers despite the spawn perf drop ... Something like this:

commands.spawn((
    Replicated,
    PlayerBundle::new(PlayerId::from(*client_id), event.name.clone()),
    InputQueue::<movement::MoveInput>::default(),
    InputQueue::<logic::actions::ActionInput>::default(),
    TimingData::default(),
    LifeBundle::new(PLAYER_MAX_HEALTH),
    MovementBundle::player().with_position(random_pos),
    ActionBundle::default(),
    CombatBundle::new(Team::T1 | Team::T2 | Team::T3),
    ManaBundle::default(),
    logic::status::ActiveStatuses::default(),
    skills::SkillLayout::default(),
    TurnQueue::default(),
))
round ridge
#

That's not as bad as I was expecting.

crisp garden
#

I would've made bigger bundles but it's not like I can just say "This bundle is this other bundle + more" in Rust, so it would get super clunky or duplicate tons of code

crisp garden
round ridge
#

I guess I should refine my statement to something like it's rare to want to put two engine "driver" bundles onto the same entity, at least for me. 😂

rough cave
#

I guess the issue is that: whether your "driver components" are Bundles, Required Components, Unity DOTS Authoring Components, or whatever - you are likely operating under the constraint that no two driver components should really be asserting overlapping components onto the final entity. I know that required components does support that, but I'm not sure that it is necessarily a selling point.

crisp garden
rough cave
#

But doesn't overlapping bundles suggest that you're trying to express two contradictory driver components that pull the same underlying levers 🤔

round ridge
#

And they're not driven by the driver.

rough cave
#

How do they collide? Because other bundles include those bundles?

round ridge
#

No, they include the components from those bundles and you can't write an insert with duplicate components (because which value should live, and which should die).

crisp garden
#

Transform especially is in a lot of bundles, so most sets of two bundles would conflict on Transform

rough cave
#

Yeah, it seems to me that Transform shouldn't be in peoples bundles, because their bundles should own a logical function area, and transform is outside of that function area

crisp garden
#

They can work on one entity, and that's very common in other engines, but in bevy those bundles would overlap (or you would be able to forget that position/visibility resulting in people asking "Why does it not work?" often)

rough cave
#

Yes but the components in other engines that say "I'm a particle effect" or "I'm a visible mesh" don't add transforms. The transform is a separate authoring concept.

#

Bundles have issues because they try to encompass "here's a grab-bag of all the tat you need to have a functioning X"

#

and then they overlap

crisp garden
round ridge
rough cave
#

@round ridge it requires it, but it doesn't author it.

round ridge
#

Same as Graphic requires RectTransform.

rough cave
#

Indeed, Graphic requires Rect Transform but doesn't author it.

rough cave
#

Bundles author entire collections of components

rough cave
#

And each would be separately authored?

#

Some engines just choose to bake transforms into their concept of an entity, I would still argue that their various components (particle systems and meshes etc) don't actually author transforms, they just expect them to be there.

crisp garden
native mesa
crisp garden
#

The usecase for baking global transform with static markers I think

rough cave
#

@crisp garden I don't really understand what point you're trying to make by pointing at an engine that argues for composition but uses inheritance to author each node of the composition tree.

crisp garden
native mesa
#

Especially embedded into scenes as part of the authoring process.

crisp garden
#

Bundles are a really weird abstraction to define default values in that regard. Required components is a lot closer to what you'd expect in other engines. Maybe with the addition that the editor should easily let you fill existing required components (would also fix that discoverability issue)

round ridge
#

It's obviously Unity-specific, but being able to remove dynamic transforms (and thus reduce updates due to transform propagation) from a game I was working on in DOTS did give us a fairly significant performance win too. 😅

crisp garden
#

Oh yea, skipping transform propagation even if it's only on-spawn could easily be a big win

native mesa
#

But it will help to reduce memory footprint

round ridge
crisp garden
rough cave
# crisp garden Godot bypasses the issue bundles have by not allowing overlaps. Every node alway...

My conjecture is that combining authoring concerns with construction concerns is an issue, and I think bundles have usability issues because they combine authoring concerns (common defaults, many associated components needed to get sensible default behaviours, discoverability), with runtime construction concerns (minimising archetype moves, telling the engine what to literally do). Required components also pollute these two concerns. Unity authoring compnents separate these concerns, authoring components focus on discoverability, ease of use, common defaults, having many components be generated from one authoring component - all are completely disconnected from the ECS' underlying ability to construct entities and add/remove components (buffered or otherwise).

native mesa
#

We can do some very clever stuff with change detection when we get relations. Sketched out an idea in the relations group for using ancestor-sets that might let us juryrig granular transform prop for 0.16 and then just fold into relations when it ships, we'll see where it goes.

crisp garden
rough cave
#

But again, that's not the point. My point is that I think that any two bundles on an entity, or any two driver components in required components on the same entity, shouldn't overlap their required components.

#

If you have a driver component/bundle that is "LameJump", and another which is "AwesomeJump", and both assert the "CanJump" component, I'd argue that those two bundles should never be on the same entity - because they both claim ownership of the "jump" domain, and don't make sense to overlap.

round ridge
#

(This reminds me of write groups from DOTS.)

rough cave
#

Similarly, I think that "LampJump" and "AwesomeJump" shouldn't require transform, just because the jump logic uses transforms.

#

That is the responsibility of a Transform driver compnent/bundle.

#

Looking at an entity, at the top level, should be a set of high-level behaviours that are all orthogonal. If those high level behaviours overlap, then something has gone awry.

native mesa
crisp garden
rough cave
rough cave
native mesa
#

i must be misunderstanding your position

rough cave
#

We somehow ended up discussing the idea that adding many bundles to one entity was surprising to some people, and completely normal to others. From there we ended up discussing the idea that the only issues with multiple bundles is when they collide. I'm now in the process of explaining why I think the collisions are a sign of the wrong abstractions being used.

crisp garden
#

The only thing I could see as authoring is the specific case where multiple requires specify a non-default function. In which case, yes that should probably not be possible unless it's one of those "overwrite the constructor of a dependency" case

#

I think technically we have an order to it still? Iirc the first component's tree ends up winning?

rough cave
#

@crisp garden what do you mean by "the only thing I could see as authoring"

crisp garden
#

You differentiate "authoring" and "depending". Depending on a component clearly means it needs to be present, so if "authoring" includes "spawning the default value" every dependency is also authoring which wouldn't work

rough cave
#

@crisp garden no, authoring does not include spawning.

#

To be clear, when I say authoring, I'm talking about the act of a human describing an entity and its components that they would like to eventually spawn. This has no relationship to executing code at all. It might be done offline, before the game is built, in an editor.

#

Dependencies, in the context of required components, are a runtime code concern - determining what components get automatically added to an entity on spawn.

#

Bundles, to me, pollute those two areas. They are designed to make authoring an entity easy, and tell the computer what literal steps to perform to instantiate it.

#

A lot of Bevy, in fact, pollutes authoring and code - because it considers itself "code-first".

crisp garden
#

Yea, bundles are a weird abstraction in that case. ..default() doesn't mean "I don't care", it means "The default value is fine"

native mesa
rough cave
#

I'm concerned that many decisions in required components, BSN, etc. are all based around the code-first principles of Bevy, that I suspect will actually fade away when it has an editor and any significant non-coder adoption.

#

I personally think that required components just make it harder to implement runtime things, because somebody wants to make it easier to author things - which are two completely separate areas.

crisp garden
#

Required components actually support this idea of authoring vs runtime behavior doesn't it? Any values you never authored become some default defined by code, the default can change and you'll still get just what you described

rough cave
#

@crisp garden no, required components exist to make authoring easier, but have runtime impact

round ridge
native mesa
rough cave
#

@native mesa depending on how they are messaged when 0.15 is released, they could be interpreted as anything from "please rarely use this to indicate things you REALLY NEED", all the way through to "please freely use this to indicate probable defaults in your public API whenever you want".

round ridge
#

I guess "custom transforms" (whilst another plan is seemingly being devised) are something complicated by requirements.

native mesa
#

ah, I see.

rough cave
#

I have deep deep concern that people will interpret it as the latter, and the Bevy ecosystem will grind to a halt for people that want to use dependencies in any non-trivial way, because the second they stray off the beaten path they will try to customize some behaviour and they will be prevented by the required components system.

crisp garden
rough cave
#

If everyone perfectly follows (what I think is) Cart's vision: you only really use required components on top-level "Driver" components, which simply orchestrate lower-level components, and you can always "bypass" the driver component and use the lower-level ones yourself - everything is fine forever.

round ridge
rough cave
#

If people don't follow that vision, and start making it so that all their public components use required components, then it becomes impossible to use their API in ways not explicitly premeditated by the author.

crisp garden
round ridge
native mesa
# rough cave <@372278098416238602> depending on how they are messaged when 0.15 is released, ...

Ok, yeah I somewhat agree with you. Let me reframe it a bit: I am concerned that we lack an Entry-style api, where you must either insert or a component or modify an existing one, but inserting a default and then modifying it is quite hard because we need to do the archetype move before the system runs.

Bsn does encourage people to "indicate the defaults in your public api" in so far as you are encouraged to wite systems that assume the components are already present, even if they are indeed optional.

crisp garden
native mesa
#

I think this is an issue with another area of the engine, that just happens to be manifesting with bsn.

rough cave
#

@native mesa my point is that if you separate authoring from execution concerns, then you can avoid all the crap about worrying about archetype moves

native mesa
#

mmm, that line of reasoning may be above my ecs-paygrade so to speak

#

this is a bit high-level for me

rough cave
#

No, I just made it sound fancy by accident I guess, it's very simple:

crisp garden
rough cave
#

For example, if you author your scene using BSN, then an asset processor should import the BSN, resolve all of the referenced components and their requirements, and bake it out into a static serialized optimized format ready for instantiation.

#

Authoring (describing the shape of an entity, e.g. with BSN), and execution (efficiently reserving memory and setting the values in the bytes), are two separate concerns

native mesa
#

ok, i see.

rough cave
#

Bundles and required components try to address both, authoring (making it easy to create an entity with a defined shape, with all sensible defaults), and execution (avoiding archetype moves, yadd yadda).

crisp garden
rough cave
#

@crisp garden I don't understand your point

native mesa
#

you could "bake" at app startup, you'd have to for embedded bsn fragments.

rough cave
#

@crisp garden what about the baking means that you have to care about what the value becomes at spawn time?

round ridge
rough cave
#

@native mesa you can bake whenever, I guess, but for a released game you'd bake statically at build time and strip the code for baking from the released game.

round ridge
#

That way, you don't need to worry about requirements during insertion at all, and still get the same behaviour of "if I add a mesh, I also get transform".

native mesa
rough cave
#

I also don't fully understand why command buffers don't batch up archetype moves to completely mitigate the issue anyway, but that's a whole other conversation.

crisp garden
native mesa
#

we could add batch commands, but turning individual commands into batching is harder?

rough cave
#

Idk, unity DOTS does it

native mesa
#

they have a different architecture, but if they can do it then (imo) it's kind of a bad look if we can't I guess? so I'll give you that.

round ridge
rough cave
#

@crisp garden the engine would do that, not you, so from your point of view it'd be exactly the same

round ridge
#

I have written an ECS command batcher before, mind.

crisp garden
native mesa
rough cave
#

@crisp garden I don't think anything I said was inaccurate was it?

#

@crisp garden but yes, flushing command buffers between systems would prevent it from batching commands

#

I don't recall saying otherwise

crisp garden
#

You said it "completely mitigates the problem", when it doesn't make the tiniest dent

rough cave
#

@crisp garden if you spawn multiple bundles on the same entity in the same command buffer in the same system, which was what we were talking about, then it very much would solve the problem

#

you just introduced the concept of trying to solve the problem across multiple systems

#

please don't call me a liar because I didn't consider a problem you made up after my sentence was written

native mesa
#

there's some miscommunication going on here, I think you are both making fine points and are well informed about the problem domain.

#

but i fee like i should try to de-escalate before we loose track of the salient points.

rough cave
#

yes, apologies @crisp garden, I got somewhat tilted by you dismissing my point as not accurate in the slightest, you didn't call me a liar, I just got annoyed.

crisp garden
rough cave
#

@crisp garden but required components wont prevent required components at home, and required components at home is a perfectly valid and normal part of ECS operation, or at the very least, is fine and reasonable in end-user code.

crisp garden
#

I doubt many people will still write required components at home when it's extremely tedious and bug prone

round ridge
#

And that same argument is true for having an authoring process.

crisp garden
#

Bevy does not need to aim for a rigid structure that disallows library authors from writing annoying APIs

native mesa
#

i'm going to check out for the night, but thanks for explaining this to me. i do think it would be good to have a bit more of a discussion about this authoring/instantiation split, and to see if cart has a reason he avoided a bsn baking step.

rough cave
#

@native mesa the baking is less of the point, that's an optimization, and one that doesn't need to be in the initial version, the issue is the way we focus on code-first designs because it happens to be authored in code right now.

#

But also goodnight 🙂

native mesa
#

it's a useful illustration of your point, which was a little abstract for me to grasp before

rough cave
#

Effectively it's like watching programmers worrying about how to make authoring RGB values easier in code by writing hexadecimal, when really the answer is to just finish the sprite editor UI program.

crisp garden
native mesa
rough cave
#

@crisp garden i'm not even sure we're talking about the same thing

native mesa
#

:p

crisp garden
rough cave
#

I believe nth was just joking, NiseVoid

crisp garden
#

Or even if I control what is spawned, I don't control how it changes, like when my network library adds a component that also requires a marker (which I obviously won't network because that's a waste of bandwidth)

crisp garden
#

(not that I actually understand reflect but okay)

crisp garden
rough cave
#

Sort of, yes. I think that Bundles and Required Components solve several problems, and some of those problems are runtime execution concerns (minimizing archetype moves, enforcing invariants), and some of those problems are developer-experience concerns (discoverability, ergonomics). I think that some of these concerns can be mitigated at authoring time, rather than at execution time. We currently solve them at execution time, instead, because we prioritise making the code nice and ergonomic to write - which prevents global reasoning about the program as a whole and optimizations you might be able to do because of that.

crisp garden
#

At the very least I think the discoverability thing is true. We should not concern ourselfs with the LSPability of required components vs bundles when a few release from now 95% of entities will either have a very clear vision of what will be there or trough scenes that are hopefully created trough an editor

#

I don't think reasoning and optimization are a big issue with required components. The optimizations could still be done, but for the most part the cost of required components should be low enough that you'd struggle to measure it. As for reasoning, I think it actually helps over a world where spawning/modifying at runtime is problematic because you're not going trough the authoring process, which happens fairly frequently, and it easily beats bundles that throw stuff in your face you don't care about, which is probably the most important thing right now since nothing is final and migrating to something that's better in every possible way is always an option

#

Of course the optimization still assumes we fix the stupid transform thing and some other related problems, which I'm not worried about considering how much demand there is to fix those things

rough cave
#

@crisp garden the only optimization I am really referring to is archetype moves, and I only bring that up because people keep mentioning it as something important to them, and allegedly required components will reduce archetype moves. So whilst I don't think we need to worry that much about archetype moves, my point is just: if you're gonna worry about it, then I wouldn't stress about using required components to solve it, because I think there are other ways of solving it.

#

I didn't phrase any of my points as "reasoning", so I'm not sure which of my points you're responding to when you talk about reasoning.

crisp garden
#

I was referring to "prevents global reasoning"

rough cave
#

Ah I see, well that's just a subset of the optimization point, so as I say - not worried about that - that's just part of my point that we prioritise ergonomic code (because currently we only have code as our authoring tool), rather than more traditional pipelines game engines use, and as such we miss out on project-wide optimizations that would make worrying about archetype moves less relevant.

#

Also, just for anyone coming to this conversation late, I'm not really sure what we're actually discussing - I feel like we got here because I made some comments about why I think overlapping bundles is a design issue, and how I think that the reason our bundles tend to overlap is by conflating authoring & runtime-execution concerns, and now I feel like I'm accidentally having a conversation that makes it sounds like I'm trying to fight against required components. Whilst I don't like required components, that's not my goal at all.

crisp garden
#

I think the big thing here is that for bevy an authoring solution can't be a full solution, because:

  1. Bevy is modular, skipping bsn and the editor are a valid usecase
  2. We've been moving to better supporting dynamic usecases (be it reflect, scripting, or just some type erased magic)
  3. We generally try to keep concepts low, which makes it preferable to have a single solution that works regardless of if you're writing scenes, authoring in code, or doing things fully programatically trough some systems or the above dynamic usecases
rough cave
#

I don't think anything I've said contradicts those concerns.

#

Also authoring includes writing code, and so somebody "just writing code" is engaging in authoring, and we can still separate "authoring an entity" from "telling bevy to instantiate one".

#

You could, for example, imagine a world where entity spawning is done using "prepared command buffers", a phrase I just made up as a play on the term "prepared statements" in databases.

round ridge
#

(this ended up as a bit of a dupe)

I don't really think that any of those refute the idea of an authoring process.

An editor editing different types compared to the actual components seems beneficial. Skipping BSN is neither here nor there - it might mean you're opting in to more complexity, but that's true requirements or not.

I don't think it impacts dynamic-ness, other than potentially making it easier to implement in the engine since some of the runtime concerns need not be.

And there's nothing saying that you can't use the same authoring mechanisms directly at runtime (this was useful in early DOTS versions as a comparison).

rough cave
#

You could have an arbitrary beautiful builder pattern API/macro/whatever for authoring entity shapes/values in an ergonomic way, ask it to build you an optimized command buffer "fragment", and you cache it over the programs lifetime, and pass it to a command buffer to execute it later.

#

Still maintains a separation of authoring and execution, without having an editor or whatever.

#

I'm not suggesting we literally do that, I'm just using it as an example.

thick slate
round ridge
#

(Another interesting aside is that DOTS solves some of the archetype moves by having specifically placed command buffer systems that multiple systems write buffers into. Since their command buffers can be batched, it can reduce all of the initialisation steps into at worst 1 more move.)

thick slate
#

FYI, I don't think that the archetype moves / command batching thing is that hard. It just needs two weeks of skilled ECS dev attention

rough cave
#

@thick slate sounds to me like you're offering :V

#

<insert the usual joke of "the person who gave the lowest estimate in the planning meeting gets assigned the task">

round ridge
#

I think the messy part of it is figuring out what to do about batch breaking when you hit arbitrary external commands. 😅

thick slate
#

Ahaha, the performance of bevy_ecs is very very low on the list of things that Bevy needs to solve

crisp garden
round ridge
rough cave
#

Not a suggested solution, but an example of how it is feasible.

crisp garden
crisp garden
round ridge
rough cave
#

Thanks for the fun conversation @crisp garden but I'm afraid my mortal body requires that I must sleep now, goodnight everyone ^_^

thick slate
#

All batching conversation to #ecs-dev please!

round ridge
#

Your networking requirements seem pretty unusual. DOTS' NetCode for example always spawns prefab instances for entities and the format of the packets is determined by the archetype of the prefab, which isn't communicated between peers.

crisp garden
round ridge
crisp garden
#

That does the same thing iirc 👀

round ridge
#

It's got all the Vec<Box<dyn PartialReflect>> you can ever eat.

crisp garden
# round ridge Your networking requirements seem pretty unusual. DOTS' NetCode for example alwa...

As for this approach, it is partially what I do, I send the client "This entity is made from scene [some number]" (the number is a hash of the scene name), but the entity also changes over time and the archetypes on both sides are never identical, so for most things I can only network individual components or groups of components, making sure to exclude anything that's not necessary, but importantly any dependencies still need to be spawned

round ridge
# crisp garden As for this approach, it is partially what I do, I send the client "This entity ...

Yeah, variations in archetype after spawn are fine in DOTS - the point was that it was the archetype of the prefab, basically only components that are in the prefab can contribute to updates (ish).

If you are in that broad case, then it doesn't really matter if you have required components, you could use some form of authoring process to produce the Scene that you instance, and both sides will have whatever is required as determined by the authoring setup.

crisp garden
#

That sounds pretty clunky, I don't personally do many archetype moves after spawning an entity, but I've seen plenty of people network components that are frequently added/removed. But even in this mostly ideal case that apparantly mostly aligns with that approach in terms of spawning, there as still plenty of places where I add things later that don't quite need all the extra baggage of an authoring process, but also need that marker that was added to also add some storage components

#

Like at best if you still want to keep reasonable ergonomics (which is something bevy tends to take fairly seriously) you could avoid some requires by having a pattern for an authoring process

round ridge
crisp garden
#

Having an authoring process does not seem to me like it makes required components or bundles unnecessary, and there is of course no reason an authoring process can't rely on required components (+ maybe an extra layer to help with suggesting those non-required but useful defaults components)

crisp garden
#

Maybe unsurprisingly the only solution to people writing "required components at home" is required components, just like how only archetype invariants would stop people from writing archetype invariants at home 🙃

#

Of course using those features more loosly than our current specific implementation. Required components don't necessarily have to be a guarantee that you can't remove for example

round ridge
#

"required components at home" will still continue to exist unless required components gain the ability to be able to add components based on matching a query IMO.

round ridge
round ridge
crisp garden
#

The important disctinction in my mind is that it only does so when using the authoring process, and that someone somewhere would still be writing the requires anyway (or you have to specify them yourself, and you'll make mistakes like when you have neither of them)

round ridge
crisp garden
#

It would behave like a cooler versions of bundles

#

Because of that leaning on the authoring process would share some of the problems, you can forget it or you can create weird things like trying to insert two scenes with overlapping values (but at least that isn't guaranteed to happen like with bundles and their inability to extend them in any sane way)

round ridge
crisp garden
#

Afaik BSN scenes have the root being an entity, so if both add a Transform whichever you insert later wins

#

Not an issue if scenes extend eachother of course, but naively using two scenes provided by libraries with all their default requires could give you some overlap

round ridge
#

I think scenes produce distinct entities by default.

crisp garden
#

There was this example for bsn:

world
    .entity_mut(e1)
    .construct_patch(bsn! {
        (Node, Style)
    });
```This mutates `e1`, and you could chain two of those
#

There's also multiple inheritance, and both could also overlap (of course there can be clear rules for what to do when that happens)

round ridge
#

I think would expect the patches to take into account the current state. 🤔

crisp garden
#

They do take the current state into account yes, but that doesn't stop overlap from happening when things are defined

#

Like if one scene says A(1) and the other says A(2) what does A become (it better not be 3)

round ridge
#

If you're making a patch, then you've explicitly asked for the newer version, no?

crisp garden
#

If people use scenes as their way to define requires and the constructors for those requires it would be very possible that they specify version

round ridge
#

I am not sure what you mean.

crisp garden
#

So if player has Transform{translation {y: 5}} and joystick_controlled has Transform{translation {y: 3}} it would predictably pick 5, then 3, then 10. Which is fine, but could lead to subtle bugs if you use a scene with things you didn't expect (like default values of required components). The one that's less obvious and predictable is what happens to entities that have Transform already

#

(those subtle bugs would specifically happen if you didn't pay much attention to the order because you didn't expect the conflicts)

round ridge
#

But to clarify, this problem is orthogonal to whether or not required components are used?

crisp garden
#

Yes and no. These issues can already happen, but you probably wouldn't need to use library-provided bsn (or custom construct types that create similar behavior) for anything other than entire hierarchies, so it's unlikely you'll run into unexpected conflicts

round ridge
#

Yeah, I think we're broadly on the same page. I personally wouldn't go out of my way to make component insertions/removals at runtime have the same level of "making things functional" that required components are currently being used for, but I don't have any problem with required components generally. I think that some authoring flow would negate 99% of the first-time user issues that required & bundles currently remedy, and I don't really miss them in DOTS, for example. However, I too must now retreat to bed. 😅

crisp garden
#

I think bevy's internal bundles are a decent spot in terms of "make things functional" (they don't enable unnecessary extra behavior for example), but I do think things like Transform, Visibility (not the computed one), etc could be moved to some convinience plugin or recommendations in the editor, rather than encoding things that aren't required as required

#

I mean honestly unless I specify them when spawning or add a component that does require them I probably don't need them ... And maybe if we had better tools for running propagation on demand a lot of things could use GlobalTransform instead too 🤔

crisp garden
split harness
rare whale
#

@split harness Thorium + Construct - wanted to follow up yesterday's comment with a few thoughts. As mentioned, I've tried to separate "what components do" from "how components are constructed". However, currently the "how" is highly constrained due to the fact that virtually all of thorium's core features rely on registration of one-shot systems. This is relatively easy to do if you have an EntityCommands, but hard to do if you are just inserting components. This is why currently only the builder pattern is provided.

Needless to say, having Construct available would solve this problem (I think). Construct provides a World, which is actually more than I need - everything I do, I can do with just EntityCommands.

That being said, there's one potential advantage of having a World which I can't get with EntityCommands and that is "priming the pump" - running the one-shot system immediately the first time to initialize the data structure synchronously instead of waiting for the next sync point.

So the TL;DR here is that I'm ready to experiment with Construct as soon as it becomes available.

split harness
#

(sans an experimental addition I want to make that would allow enumerating asset dependencies from Construct)

visual jay
#

I'm in a similar place. I used your construct document to play with the future now here. It's been really useful. I think my only deviation of note is that I used type Props: Clone rather than type Props: Default + Clone. I'd be happy to try to use the real thing.

rare whale
#

I have some problems with Construct, but the issue is long and complicated and I figure I will wait until you are freed up before opening the floodgates.

ebon bolt
#

Has there been talks of how to handle situations where you may want to pass a value to a BSN which would be used somewhere deeper in the hierarchy? Sort of how frontend frameworks have "props".

A button would be a typical use-case: In bevy, a Button has a child entity with a Text component. If I wish to utilize BSN to create a "blueprint" of my Button, and re-use it with different a different text per button, wouldn't it be difficult?

If this has already been discussed, don't hesitate to let me know, and I'll just look through the chat history. 🙂
Edit: I see it's been mentioned in the Github proposal, but under "Missing Features/Next Steps", so I'm unsure if there were any more developments in this area.

crisp garden
#

I've actually asked about features along these lines a few times, but I don't remember seeing a clear answer. I think it's possible for the macro variant to put some variables into the bsn, but I'm not sure about doing this in the context of bsn assets, and in the context of a button or other UI elements you might also want to pass in entities rather than just some text so it's easier for the caller to control what is on the button (like say an icon and text)

ebon bolt
#

Maybe that could be solved by supporting some sort of "slot" solution, where a bsn could mark where one or more child entities will be inserted in its hierarchy. The children added to the "imported" bsn would be automatically placed at the slots position.

#

As I'm very unsure of the state of the bsn syntax at the moment, I'll demonstrate it with some psuedo syntax.

import "./button.bsn" as Button

use Button (
    Transform::from_xyz(0.0, 0.0, 0.0) 
) [
    entity (
        Icon::Checkmark
    ),
    entity (
        Text::new("Click me!")
    )
]
#

Something along those lines.

dapper sky
#

having used 0.15 since the RC process started I am now yearning for Construct and the most basic editor possible :')

quartz sleet
#

Yes Construct!!

crisp garden
#

Are there any thoughts on tracking where in a scene an entity came from (if it came from one at all). Currently looking in to some approaches to network scenes between a client and server (both sides spawn the scene, and the server only sends data for components can change afterwards), and I'm not sure if something like this would also be necessary for something like reactivity

cobalt stone
#

I don't see how it would be useful for logic, but like might be a neat debugging tool?

native mesa
#

for reloading scenes (which we will need to do with the editor) we will likely need to track which scene an entity came from, and likely also embed stable IDs in the scene data so that selection can be preserved across a reload.

#

could also be useful for load/unload map logic i guess.

#

since scenes can contain other scenes, would we would need to store the path down the scene import tree?

cobalt stone
#

I don't quite understand

native mesa
#

I'm not sure that would work for nested scenes.

#

but idk, maybe

#

that seems like an implementation detail, it's still tracking.

split harness
#

(but i do not currently do that)

crisp garden
# native mesa I'm not sure that would work for nested scenes.

Not just nested scenes. Once you spawn an entity it might start living a live of its own, getting new components and spawning child entities and/or siblings. When you then hotreload you likely wouldn't want to go despawning everything blindly. Networking basically has the same problem: We can spawn a scene on both sides, but having the server's updates then apply to the client's scene rather than creating new entities is the tricky part if there is nothing to identify that it came from somewhere in a scene

native mesa
#

I’ve said this before elsewhere but perhaps not here: my ideal reactivity primitive would involve updating (or re-generating) a bsn doc and then being able to update live instances of that scene to match, possibly exploiting the diff between the old and new bsn ast.

#

That definitely seems like it would require source tracking.

#

This sort of incrementalized bsn to ECS update would be applicable to everything from the ui to the editor.

#

It does seem like if you can make a bsn diff, you should be able to turn that into a series of ECS commands, and then you’d just need to know where to start applying those commands.

timid leaf
native mesa
#

awesome!

rare whale
#

I think that trying to "patch" a scene in a hot-reloading scenario is an intractable problem.

#

In React and Solid, when you modify a source file, it replaces (not patches) all of the components defined in that source file, as well as any of their descendants. Ancestors are not replaced.

#

There are three kinds of state that we care about in a hot reloading scenario:

#
  • Global state - things like redux stores. These are preserved, the new component simply reloads state from the global store.
  • Component state - when the new component loads, it initializes its state as if it were a new component.
  • Browser/DOM state - things like cursor position and focus - these aren't preserved
#

So for example if you are working on a login page, and you edit the component for the page header (assuming that the header component is in a separate source file from the rest of the page), then it will replace the DOM for the header component, but the rest of the page will not change.

timid leaf
#

And designing it from the ground up with that goal in mind.

rare whale
#

The problem is that when you edit a source file, there's no limit on what changes you can make - the new component may have a completely different "shape". React's VDOM diffing algorithm only diffs between state changes - that is, it's the same code but with different inputs - it doesn't attempt to diff between the output of two different functions

timid leaf
#

You detect items that changed, and you detect order changes. See here

#

Similar can be done for node additions, removals, and rearrangements. See here

rare whale
#

The question I would have is: if the diffing algorithm is so good that the resulting DOM is identical to what you would get if you simply destroyed and created it anew, then what's the benefit of doing this? How is that better than destroying and re-creating it?

timid leaf
rare whale
#

What happens if the type of the state changes - like adding a new field, or renaming a property?

timid leaf
rare whale
#

In any case, my point is that React doesn't do this (at least the hot-loading solutions I have used), people who use React don't expect component state to be preserved, and this seems to be good enough. Generally, "important" state lives outside of the React components, the state within the components themselves is usually transient. People don't mind if saving a file in the editor causes them to lose their cursor position.

timid leaf
#

It's fine that React does things that way. But React and Bevy are very different beasts. I seriously doubt the ergonomics and bevy-native-feel of non-destructive hot reloading can be beat.

crisp garden
#

I think in general basing decisions on what works for react is very limiting. Kayak_ui had this design philosophy and the UX of kayak_ui suffered greatly from it, and at least that still occupied the same UI niche, rather than a more general scene system where the value of non-destructive hot reloading rises drastically

rare whale
#

Ultimately what matters is speed: if I am testing my game, and I make a change, how quickly can I get back to the same state that I was testing? If the answer is "immediately", then it doesn't really matter how it is achieved.

crisp garden
#

With destructive hot reloading you're almost certain the answer won't be "immediately" unless you design every part of your app to take the destructive behavior into account

timid leaf
#

An example of non-destructive hot reloading in a 2d scene: #showcase message. How would you recreate this seamless behavior without non-destructive hot reloading? The demo code is just this

rare whale
#

In other words, is the shape of the state defined external to the hot-reloadable component, or defined within it?

timid leaf
rare whale
#

How do you decide when a restart vs a hot-reload is necessary? I don't mean the user, I mean how does the framework know.

timid leaf
rare whale
rare whale
# timid leaf I say restart but I mean 'revert changed loadables and reapply all the node's lo...

Let me back up a minute. My comment about hot reloading being intractable is based on the "data migration problem" - the fact that in computer science there's no general solution to migrating data from one format to another, only special case solutions. Diffing a DOM is one special case solution, but it doesn't address the issue of local state, which can be in an arbitrary format. If scenes aren't allowed to define new types for their local state, then I suppose that would be one solution, but this makes scenes less modular.

timid leaf
rare whale
#

Here's an example. In my window-splitter bar, I have a struct called DragState:

#[derive(Clone, PartialEq, Default, Copy)]
struct DragState {
    dragging: bool,
    offset: f32,
}

The splitter widget creates a Mutable<DragState> upon initialization. This state is local to the splitter, it's private and only used by the widget. In fact, I can even define struct DragState within the template function, since nothing outside references it. This is not too different from many React components which define data structures for their local state.

Now, let's say I later want to add a new property to DragState. Or worse, let's say I change the f32 to an i32. This data is not diffable, so a restart of the splitter component is required. But I've only touched one source file, the splitter template.

#

Now, I don't expect that a BSN asset would necessarily allow defining of new structs; but it will likely need to be able to define variables using types that already exist, and editing the template might change the type of a variable. Which invokes the data migration problem.

timid leaf
#

For cobweb ui, there is a clean separation between template and types+logic. Types+logic all live in rust, so there is no concern about the tractability of hot-reloading them. Any change means a recompile.

#

I think any attempt at some amorphous merging of template with types+logic outside rust will have never-ending ergonomic, conceptual, and implementation troubles. Unless it's a principled ground-up scripting language (which is what React is, I take it).

keen patio
#

I was too eager to play around with bsn! so I (naively) implemented (with some help from @visual jay's Construct impl 🙏 ) part of Cart's proposal. Got most of the non .bsn-asset stuff working, including the macro and reflection based inheritance. The plan is to use this to experiment with declarative scene patterns (aka reactiveness).

Looking very promising! I am liking the way it feels to express scenes as BSN. However I'm not completely sold on inheritance yet.

keen patio
# cobalt stone Why isin't it `:rounded()`?

Actually, in the proposal inheritance does not take any arguments at all (maybe because it would get complicated with asset-bsn). That's just something I added because it seemed useful. The () is optional for scene-functions without arguments.

keen patio
#

Spread operator for children experiment:

cobalt stone
#

Do you need {} around each Text, because it's a new entity?

keen patio
#

Not quite. {} tells the parser to treat it as an expression, rather than the default of parsing structs to create partial patches.
Sadly, function calls are ambigous with tuple struct initializers (thank you rust) , so we need a way to set them apart

#

Each top level item within [] (the children of the patch) is a new entity. To add more than one component to a child you'd wrap them in a tuple ().
I think there was some discussion whether () should be required for every entity, even if it's just a single component. pros cons, dunno 🤷‍♂️

timid leaf
#

Are bsn macros going to look fundamentally different from .bsn or is there some intention to replicate the rust as close as possible?

keen patio
#

I just realized Text is just a tuple with a public String. So actually the expression stuff wouldn't be needed in this particular case.
This looks a lot cleaner:

keen patio
ebon bolt
#

I feel like the .bsn version should support importing other .bsn files as well, as an alternative to calling functions returning bsn! in the code version.

native mesa
#

i believe that is planned, yes

keen patio
#

I believe that’s the plan, should be able to do something like : @”other_scene.bsn”

ebon bolt
#

Would it be a good idea to move these imports to the top of the file? In case we need to reuse them?