-
-
Notifications
You must be signed in to change notification settings - Fork 4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Vectors on schema with explicit properties are used by reference not by value. #5181
Comments
That's intentional to avoid unnecessary allocations that can trigger garbage collection pauses. We should find a way to trigger the changes without cloning. We actually have an exception for position, scale, rotation where update is always triggered: https://github.com/aframevr/aframe/blob/master/src/core/component.js#L414 We can perhaps extend the exception to all |
As I see it there are 2 different issues here: Forcing update to trigger would help with 2, but not with 1. While the glitch examples constructed focussed on issue 2, issue 1 is also problematic (e.g. has caused problems when using multiple cursors as they scribble over each others' raycaster vectors). I wonder if we can get away with copying rather than cloning? Outline proposal:
This does result in additional vec3s being created & destroyed, but only at the rate of component/entity creation/destruction - which should be far slower than the rate at which component attributes can change (e.g. raycaster direction changes ~every frame). I presume that creating/destroying entities & components already involves object allocations, hence it would be considered acceptable to create/destroy an additional vec3 or two as part of that processing? Does that sound reasonable? |
Allocating vec3s is what we want to avoid. Increases drastically memory footprint and risk of GC pauses. Performance takes precedence. Err on best perf side by default. Issue 1 is a matter of documenting. If you want to retain state for vecX schemas the component should handle that internally. Maybe a flag could be offered for the vec4 schemas that copies the data automatically. Default should be no copy. What I don't like it's inconsistency. Schemas with vecX will be updated on every setAttribute call. Shemas without won't. That can be confusing. Another alternative is a special flag for setAttribute that forces |
Yes, I understand we want to minimize allocation of vec3s, but there's a huge difference between allocating one per component instantiation, and allocating one per-update (which could be per-frame). If we look at core A-Frame components that have this issue, we just have Raycasters you'll typically have a handful at most per-scene. A one-time allocation of a handful of vec3s at the point the scene is created can't create a significant perf issue. With the Can you be more specific with the scenarios you are concerned about where allocating an additional vec3 for instance of a component with a vec3 on the schema is actually going to have a non-negligible impact on performance? My personal experience is that the current behaviour manifests in really weird ways that take a huge amount of time to get to the bottom of. If I could save that time on my projects, and put it into more time performance tuning of my applications, I expect I'd easily make back the performance cost of those extra vec3s many times over. |
A further thought - we have full control (via the component lifecycle) of when these vec3s are created & destroyed. Could we mitigate the GC impact by having a pool of these vectors, allocating from that, and freeing back to it when done. Seems all the code to handle this logic already exists and is in use for a range of object types...? |
Description:
Also: https://glitch.com/edit/#!/vec3-line-update-issue?path=index.html%3A25%3A53
Slightly complex to explain. Let's start with this code.
Where does the box end up? At position 1, 0, 4 - as expected
But what about if we define a component like this:
and use it in the exact same way...
Now, the box ends up at position -1, 0, 4.... !
This happens for reasons described in this comment, which I'll explain again here..
The root cause is this code
this function
isObjectOrArray
is used to determine whether to copy or deep clone an object. It does not classify Vector3s as objects (because value.constructor is set toVector3
, notObject
orArray
)This means that when A-Frame copies data to
oldData
, it just assigns theVector3
objects by reference, rather than doing a deep copy.In the case of the
position
attribute, which uses an unnamed single property in its schema, this doesn't happen. When we come to that code, the top-levelvec3
object passed intosetAttribute()
has already been decomposed into x, y & z components.But the problem does occur whenever a vec3 is used as a named property in a schema.
Examples of this causing problems with real components include:
start
&end
properties)origin
&direction
properties)The effects manifest in a couple of ways in these different components.
The
raycaster
code usesdata.origin
anddata.direction
directly in code. As a result, if you write code like this:then subsequent updates to vec3 result in immediate changes to the raycaster origin, and adjustments to the raycaster origin via
setAttribute
have no effect - the raycasterupdate()
method doesn't even get called because A-Frame can't see any difference between the old data & the new data - they both refer to the same vec3.One particular situation where this causes problems is trying to use multiple simultaneous cursors, as described here:
#5075 (comment)
For the
line
component it's a bit different. This doesn't directly usedata.start
anddata.end
, but copies the x, y & z data from these on in theupdate()
method.This means that with a line initialized like this...
then subequent updates to vec3 don't have any unexpected immediate effects on the line.
However, subsequent code like this will not update the line, as A-Frame won't even invoke the update() method for the same reasons as with raycaster
See this additional glitch to illustrate the weirdness seen with updates to line start.
https://glitch.com/edit/#!/vec3-line-update-issue?path=index.html%3A25%3A53
I believe all this weirdness could be fixed by extending the checks here to consider Vector2, Vector3 & Vector4 as equivalent to arrays/objects and therefore needing a deep copy.
(I believe these are the only property types supported in the current schema API that would have this problem)
I have wondered whether there are significant (possibly intentional) performance benefits in re-using Vector3s, rather than cloning them, but I'm unconvinced that any derived performance benefits would be significant enough to justify all the counter-intuitive weirdness that arises by not cloning vectirs when they are passed on a component schema.
I'd be happy to take this further into a fix with some UTs (based on the glitches linked above) - but first would appreciate a review of the analysis above, and some agreement that vectors pass into components should be cloned (i.e. passed by value, not be reference).
The text was updated successfully, but these errors were encountered: