-
Notifications
You must be signed in to change notification settings - Fork 3.7k
fix billboard subregion not displayed #12958
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
base: main
Are you sure you want to change the base?
Conversation
|
Thank you for the pull request, @Beilinson! ✅ We can confirm we have a CLA on file for you. |
| !BoundingRectangle.equals(subRegion, item.subRegion) | ||
| ) { | ||
| billboard.setImageSubRegion(billboard.image, subRegion); | ||
| item.subRegion = BoundingRectangle.clone(subRegion, item.subRegion); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this basically doubles all BoundingRectangle objects added to the scene. I tried making this test against the internal width/height, but those are only set after the subRegion promise resolves so that wont work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I notice that a billboard's imageSubRegion is created as a constant property (based on the lack of the createPropertyCallback arg passed in to createPropertyDescriptor).
Since the property never changes, maybe the right move here is to remove this code from update entirely and find another home for it? (Like a constructor or some setter - something that's only called once, not every frame).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting, I'll try to put something together tmrw
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
createProperty creates a setter that uses the value directly if it is a Property, so users can for example do:
billboard.imageSubRegion = new CallbackProperty(...);And then createProperty will set the internal value to actually be a CallbackProperty, so I dont think what you wrote can work here, correct me if I'm wrong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ooh I see what you're saying. So the the value of the property is constant, but the reference to a property can change, essentially.
I agree, that invalidates my idea. Follow up question, though: how does this "double all BoundingRectangle objects added to the scene`? Every time you clone, GC (eventually) deletes the old one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mzschwartz5 this is ready for re-review
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I need to look at this more tomorrow, but my initial reaction is that this still seems like a bandaid. We already have state that tells us the subregion has been added already. We should find a way to use it.
Like (spitballing), maybe we can defer setting:
this._loadState = BillboardLoadState.LOADING;
until we've called some function on atlas to check if the subregion already exists. Basically splitting up TextureAtlas.prototype.addImageSubRegion into addImageSubRegion and checkSubRegionExists or something...?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that might not be a bad idea however that still wouldn't fix the problem, the checkSubRegionExists method would still return a promise to an index which could still only resolve on the next frame, and if this check is initiated every frame it would never render properly.
I've setup an alternative version of the code which now works how you suggested (split up syncronous check vs asyncronous addSubRegion), it seems to work as well, not sure how much I like this I think the previous implementation is clearer but its more or less the same thing just moving this check further up or down the code structure (actually this also changes some stuff with loadState like you suggested)
Image from newest state showing that it still works
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mzschwartz5 this is again ready for re-review (will be available tmrw for any additional convo on this). Please compare the latest commit (using your suggestions) against the older version (first 7 commits).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I personally think the older version is better - smaller footprint, slightly more understandable process, slightly better theoretical performance. The only real downside from my perspective is slightly worse theoretical memory usage, but its only reverting it to the same level that was before #12495
|
Could this fix be included in the upcoming v1.135? |
@plainheart. We can try. It's a simple PR, but I'm still not satisfied with our understanding and just asked for a little follow up on my recent findings. The release is in 2 days, so I'll keep an eye on this. |
| if (this._id !== id) { | ||
| // Another load was initiated and resolved resolved before this one. This operation is cancelled. | ||
| return; | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a race condition that is handled by loadImage but wasn't handled by addImageSubRegion before
972d4b5 to
6f1349b
Compare
|
Sandcastle with |
| for (const [index, parentIndex] of this._subRegions.entries()) { | ||
| if (imageIndex === parentIndex) { | ||
| const boundingRegion = this._rectangles[index]; | ||
| if (boundingRegion.equals(subRegion)) { | ||
| // The subregion is already being tracked |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do note that unlike my previous implementation, if there are many (thousands+) of subregions then this could get pretty slow, since this is checked by all billboards with subRegions every frame. This could be solved by creating a new BidirectionalMap core class (which would store an additional inverse Map), and using it instead of the regular map for this _subRegions map. I don't think this is needed for this bug fix, but is a possible future improvement if this implementation is chosen over my previous one.
Description
As per #12585, image subregions currently do not work since 1.127. The cause was a conditionless
addImageSubregioncall on theBillboardVisualizerevery frame prior to rendering, which resulted in a large promise queue but also made sure thatimage.statewasLOADINGfor every render.This PR adds a comparison against the previous loading subRegion, which skips adding a new subregion if not needed, letting the first call resolve properly and set the state to
LOADED.Issue number and link
#12585
Testing plan
Sandboxes from #12585
Author checklist
CONTRIBUTORS.mdCHANGES.mdwith a short summary of my change