Replies: 11 comments 3 replies
-
Agreed to pretty much everything! The whole dynamic vs proxy vs concrete/canonical thing is really frustrating and confusing (especially for newcomers to Bevy's reflection). I have some comments/questions/thoughts. I'll spread them out over multiple replies to make it a bit easier, though! |
Beta Was this translation helpful? Give feedback.
-
Could you elaborate as to why? I'm still of the mindset that |
Beta Was this translation helpful? Give feedback.
-
This means that |
Beta Was this translation helpful? Give feedback.
-
Yeah I think this will be really helpful for compatibility. See #5772 |
Beta Was this translation helpful? Give feedback.
-
I imagine this is doable, but we lose out on things that require |
Beta Was this translation helpful? Give feedback.
-
Only for |
Beta Was this translation helpful? Give feedback.
-
I agree that (3) isn't ideal, but I have a feeling But I do agree that wherever it makes sense to use |
Beta Was this translation helpful? Give feedback.
-
It is pretty annoying to deal with but I don't think we want to prevent either case. I imagine concrete-inside-dynamic has a chance to provide better performance in places where it does happen. And dynamic-inside-concrete is potentially too useful. Especially in game dev where there's definitely a lot more use cases for reflection, I don't think it'd be great to limit reflection as a tool. By "coloring" reflection types, users wouldn't be able to store dynamic data inside a reflectable component. |
Beta Was this translation helpful? Give feedback.
-
Another piece to this puzzle might also be improving This might also help with some of the issues in (3) since part of the problem is also in wanting to take a reference but needing an owned value. |
Beta Was this translation helpful? Give feedback.
-
Regarding the So I think Unique Reflect still seems like the more "complete" solution. |
Beta Was this translation helpful? Give feedback.
-
Totally unrelated but the link to the rfc in the note under Unique Reflect title has the tld misspelled as .cm instead of .com. |
Beta Was this translation helpful? Give feedback.
-
bevy_reflect
is a great tool for the gamedev usecase, but there are still a number of pain points in its design which lead both to bugs and to inefficiencies and to me, the most painful area is dealing with dynamic types (or proxy types). here is an attempt to compile exactly what is wrong with them, and how we might fix them:pain points
1. proxies are a poor substitute for concrete values.
Name
component may not be working correctly #12001 (fix is reflect: treat proxy types correctly when serializing #12024 which requires just forgetting about proxy types and converting usingFromReflect
)reflect_hash
andreflect_partial_eq
inconsistent on Dynamic types #6601DynamicTypePath
impl for dynamic types returns the non-proxied type path (e.g."bevy_reflect::DynamicStruct"
), rather than the type path of the proxied type.TypePath
implementation but it definitely can cause issues.Any::type_id
(which is already otherwise confusing sinceBox<dyn Reflect>: Any
but at least that case requiresAny
to be in scope).2.
Reflect
doesn't always accurately model dynamic types and sometimes it has to stretch to support them.fn(&mut dyn Reflect)
, we can't assume the structure is the same.TypeInfo
from&dyn Reflect
, we could have aCow<'static, TypeInfo>
but this would be extremely slow for dynamic types, having to regenerate them whenever their structure changes.reflect_hash
andreflect_partial_eq
inconsistent on Dynamic types #6601 is also very relevant here.reflect_hash
is completely useless outside ofDynamicMap
and i would love to remove it completely.3. overreliance on proxies in the wider engine leads to both poor performance and unfriendly design.
ReflectComponent
(and similar APIs), even despite the fact they are nearly always called with the correct concrete type (in trait object form), still must useReflect::apply
which is slow as described below.ReflectSerialize
implementation, we'll convert back to the concrete type, wasting a whole bunch more work.DynamicScene
by reference, which would remove both conversions.Reflect::apply
-based workflows but it might be worth defaulting to eagerFromReflect::from_reflect
conversions.potential solutions
note these are not necessarily mutually exclusive.
Unique Reflect
Note
see the RFC and #7207 for details.
in the words of the RFC's author, @nicopap:
so we would:
PartialReflect
(although i am starting to prefer the nameIntrospect
), encapsulating the behavior of reading and writing to values (e.g.Struct::field
would returnOption<&dyn PartialReflect>
).Reflect
a subtrait ofPartialReflect
.PartialReflect
toReflect
and infallible conversions in the other direction.PartialReflect
but notReflect
, since they don't support any non-introspective operations.T: Reflect
it must be the type you want it, i.e. we won't bother helping you out viaFromReflect
, but aT: PartialReflect
is just "some data" 🤷.and then future aspects include:
Reflectable
supertrait ofReflect
which mediates things likeFromReflect
,Typed
,TypePath
(maybeGetTypeRegistration
) to simplify bounds, improve future compatibility somewhat and widely improve consistency/predictability between all the reflection traits.PartialReflect: ?Any
to allow it to be put on more types.get_represented_type_info
and require instead if a typed trait object is needed,dyn Reflect
(notdyn PartialReflect
) must be used.together, this handily solves (2) and does some for (1), but nothing for (3).
investigate how to better structure downstream APIs.
building on unique reflect, we would reconsider APIs which consume
Reflect
andPartialReflect
(likeReflectComponent
andbevy_scene
).i have thoughts about how to put this together but i'm not confident enough about them to recommend anything although i am completely sure this is something we need to change as soon as possible.
this would definitely alleviate (3) somewhat and might block solutions to (2).
the
DynamicValue
propositionWarning
this is an idea i had at the weekend so its not yet fully formed.
inspired in part by
mirror_mirror::Value
, we could introduce one dynamic type to rule them all and reconfigureDynamicStruct
and friends to containDynamicValue
s rather thanBox<dyn Reflect>
s. to make this worthwhile we could add aFromReflect::from_value
method which would let us specialize (and optimize) the common case where things are deeply dynamically-reflected.Other(Box<dyn Reflect>)
variant which lets us store non-dynamic data inDynamicValue
if we really want to.this would (i assume) improve (3) measurably as well and does a lot to reduce the impact of (1).
strictly separate dynamically-reflected values from concrete ones.
Warning
more weekend ideas: read at your own risk.
its possible we might want to disallow altogether dynamic values inside concrete ones and concrete (non-leaf) types in dynamic ones. this is largely because it has caused just so many issues in the past.
in this way, we would disallow all of the following:
its easy to see how
DynamicValue
makes the top section easy and natural to enforce, and the bottom is easy to enforce with unique reflect but whether this is the direction we want to go in at all is harder to answer.this mostly solves (1) and (2) combined with other suggestions.
comments
please leave below your thoughts on the solutions i proposed, and suggest more inconvenient aspects of dynamic types/parallel issues which you think might need a cohesive solution.
Beta Was this translation helpful? Give feedback.
All reactions