Skip to content
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

Add update drawable methods for each property #469

Merged
merged 2 commits into from
Oct 1, 2019

Conversation

mzgoddard
Copy link
Contributor

Resolves

Make VM and Render faster.

Proposed Changes

  • Add an update method for RenderWebGL and Drawable for each Drawable Property.
  • Deprecate RenderWebGL.updateDrawableProperties and Drawable.updateProperties.

Reason for Changes

  • Add an update method for RenderWebGL and Drawable for each Drawable Property.

The current updateDrawableProperties and updateProperties spend more time looking for what the changes are in the given properties than actually making changes to Drawable.

This is because most updateDrawableProperties calls from RenderedTarget change 1 or 2 properties. So of the 13 properties (6 base properties and 7 effect properties) 1 or 2 change. The remaining 12 or 11 checks for if something is in properties are false.

If we use an active api in place of the current passive api, we can inform renderer with Drawable changes much faster.

  • Deprecate RenderWebGL.updateDrawableProperties and Drawable.updateProperties.

I think it may be best to deprecate existing methods to use one common path for changing drawable values. An additional benefit of this will be decreasing our temporary objects. Each update call currently creates at least one object. We can at a minimum decrease that by one.

Test Coverage

There does not currently seem to be a clear path to test these changes. RenderWebGL does not have a unit testing environment.

I could create new integration scratch file tests. Direction on this would be greatly appreciated.

Benchmark Data

I've opted to include % of function time results from chrome inspector for 14844969 and 130041250. These motion block heavy projects provide a good way to see the relative performance inside these functions.

14844969
14844969 changeX before after
RenderedTarget.setXY 100.00% 100.00%
RenderWebGL.getFencedPositionOfDrawable 61.71% 81.41%
EventEmitter.emit 6.90% 7.41%
RenderWebGL.updateDrawableProperties 23.27% 0.00%
Drawable.updateProperties 22.73% 0.00%
RenderWebGL.updateDrawablePosition 0.00% 1.52%
Drawable.updatePosition 0.00% 1.52%

changeX changes the x position of a sprite. This version does not include the other changes that will improve getFencedPosition. Using the new position calls releases a lot of time to be instead spent in getFencedPosition.

14844969 switchCostume before after
RenderedTarget.setCostume 100.00% 100.00%
RenderWebGL.updateDrawableProperties 86.17% 0.00%
Drawable.updateProperties 26.78% 0.00%
RenderWebGL.updateDrawableSkinIdRotationCenter 0.00% 94.68%
SVGSkin.setRotationCenter 10.66% 23.62%
Drawable.set skin 38.32% 61.49%
events.emit 2.54% 2.34%

Changing skins is a unique case with the update calls. Relative to the other update calls changing the skin and rotation center do a lot more work. This will be improved separately from this change.

This table also illustrates well the time spent in updateProperties not performing any changes for skin changes. switchCostume doesn't modify effects, position, direction, scale or visibility. So that time is spent just checking if blocks and looping over the effects.

130041250
130041250 moveSteps before after
RenderedTarget.setXY 100.00% 100.00%
RenderWebGL.getFencedPositionOfDrawable 68.16% 82.28%
events.emit 3.02% 3.47%
RenderWebGL.updateDrawableProperties 17.76% 0.00%
Drawable.updateProperties 17.50% 0.00%

Like the changeX in the other project, the time is able to shift to time in getFenced. The improvement was to the point that the profiler did not sample any calls in the new function.

130041250 turnLeft before after
RenderedTarget.setDirection 100.00% 100.00%
events.emit 9.26% 50.35%
RenderWebGL.updateDrawableProperties 90.62% 0.00%
Drawable.updateProperties 89.45% 0.00%

Since turnLeft and setDirection do not need to check the fenced position, this call goes from almost all time spent in update calls to no time.

An additional thread is clear here like switchCostume, there is spent in some work emitting events from RenderedTarget, that we can improve in a later PR.

// TODO: vm's requests to drawableID that do not have a Drawable object.
if (!drawable) return;
drawable.updateEffect(effectName, value);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are 9 methods which all do basically the same thing but on a different property. It would probably be more DRY to add a getDrawable method to RenderWebGL and then have the VM call the respective update methods directly on the drawable, though that would have the downside of coupling the Drawable's API and class structure to the VM somewhat.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. That's a fair thought.

I think making such a change in this PR will be out of scope. We might be repeating ourselves some, but we are not changing the API in a large way.


that would have the downside of coupling the Drawable's API and class structure to the VM

This would be a pretty big change. RenderWebGL is essentially designed as a foreign function interface in case we need it to become one.


Hmm. I think this code is DRY. As each method calls a different property, they are not doing the same thing. There is the drawable safety check but that's unavoidable. It's the nature of checking that a variable has value before operating on it. It doesn't look dry, but I think that's just the limits of the JS syntax.

Copy link
Contributor

@cwillisf cwillisf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

*/
updateDrawableSkinId (drawableID, skinId) {
const drawable = this._allDrawables[drawableID];
// TODO: vm's requests to drawableID that do not have a Drawable object.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make this todo comment (and the associated ones below) clearer in terms of what actually needs to be done?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively, we should file an issue either here or in VM and note it in these todo comments as well as the one in the now deprecated updateDrawableProperties function.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure.

For clarity: The text of this current comment is an abbreviation of the comment in updateDrawable. https://github.com/LLK/scratch-render/pull/469/files#diff-0e66131b35a2396bf8b007193ee7bc77R1437-R1442

    if (!drawable) {
        /**
         * @todo fix whatever's wrong in the VM which causes this, then add a warning or throw here.
         * Right now this happens so much on some projects that a warning or exception here can hang the browser.
         */
         return;

This is a very old issue. And not one these new updateDrawable* methods are introducing.

Copy link
Contributor

@kchadha kchadha left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor comment about todo comments to make them a little easier to track together. Otherwise LGTM!

@kchadha kchadha assigned mzgoddard and unassigned kchadha Jun 25, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants