-
Notifications
You must be signed in to change notification settings - Fork 0
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
perf(): optimize rect rendering + calcOwnMatrix #1
base: master
Are you sure you want to change the base?
Conversation
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.
class OptimizedRect extends fabric.Rect { | ||
m; | ||
calcOwnMatrix() { | ||
// return [1, 0, 0, 1, this.left, this.top]; |
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.
to test the perf of this impl of calcOwnMatrix
try to uncomment this line and compare results. Should be the same
And the most important part is that we move to the top of the list of 2D renderers! |
How did you measure the difference between save/restore and trasnfrom/setTransform? |
I suggested we can use a |
Ages ago i removed it and then i said maybe is faster without but the speed was the same, so i didn't touch it. All of those checks for performances are non breaking, from the rect optimization when rx/ry is not used to the save/restore, each of those can be done anytime if they come with a way to test the performance and they do not add 500k of code there is not much discussion to have around them, if is faster is faster. For the matrices i would start from originX/originY tho because that reduces by a bunch the cost of calculating a matrix and possibly change how we approach matrix calculation and the discussion around setCoords. The big issue is that originX/originY is the biggest breaking change so now i would like to do it but also not |
|
||
m[4] = x; | ||
m[5] = y; | ||
return (this.m = m); |
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.
is this entirely equivalent? if doing rotate/scale/skew as a 2x2 and then slapping the top/left at the end is the same as starting with the translate as we do today and multiply the ohers as a 2by3, we don't need to swap the order, we can keep the translate as the first step.
Consider also that multiplyTransformMatrixArray should be used in sensitive parts since it adds the inital iMatrix multiplication that is not useful, so you can remove it
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.
should be 100% eq since translation is not affected by the plane when using center origin.
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.
if doing rotate/scale/skew as a 2x2 and then slapping the top/left at the end is the same as starting with the translate as we do today and multiply the ohers as a 2by3, we don't need to swap the order, we can keep the translate as the first step.
True, that is why I left it commented out, because if the gain is not huge it is too messy to keep
Keep in mind that there may be issues with strokeRect and fillRect with:
Paths behave in a different way, you can draw a path and do transform operations and then fill or stroke, i don't think you can do that with stroke/fill rect as much you can't do that with the text. |
I love the input @asturur and agree. Regarding matrix/coords hashing I would like to propose my solution: |
The same hash can be used for setCoords maybe with an addition of width/height/ more props |
the issue with that is that i don't really want to move that everything needs set and we need to keep track over every change. I want to see if after some improvements matrix caching is still necessary ( today isn't because both are slow from my last check ) |
I agree with @asturur, I don't like relying on Something like this: [
this.top,
this.left,
this.scaleX,
this.scaleY,
this.skewX,
this.skewY,
this.angle,
this.originX,
this.originY,
this.width,
this.height,
this.strokeWidth,
this.flipX,
this.flipY,
] |
In an optimized fabric world I would not want to use set at all. |
setting matrix to the object does not make sense, people work with scale in mind, not with matrices.
That is why i think we are running ahead ourselves thinking the matrices can't be calculated once per object per render |
I am just saying something like
|
yeah is what i don't like |
I am not against getter/setter |
I did not suggest setting a matrix to the object, rather setting an array of the matrix inputs as cache key. For instance: calcOwnMatrix(): TMat2D {
const matrixInputs = [
this.top,
this.left,
this.scaleX,
this.scaleY,
this.skewX,
this.skewY,
this.angle
]
const cache = this.ownMatrixCache;
if (cache && ownMatrixCache.matrixInputs.every((a, i) => a === matrixInputs[i])) {
return cache.value;
}
const center = this.getRelativeCenterPoint(),
options = {
angle: this.angle,
translateX: center.x,
translateY: center.y,
scaleX: this.scaleX,
scaleY: this.scaleY,
skewX: this.skewX,
skewY: this.skewY,
flipX: this.flipX,
flipY: this.flipY,
},
value = composeMatrix(options);
this.ownMatrixCache = { matrixInputs, value };
return value;
}
Of course then if we see that calculating on-the-fly has no additional cost then we can drop it. But otherwise doing |
@jiayihu and I worked on this and decided to publish our findings.
They are all present under
OptimizedRect
classstrokeRect
/fillRect
are to be used instead ofAny usage of path2D is extremely heavy.
As @asturur suggested matrix cache key is a bad idea, turns out it is very bad. I used a different approach, deleting the matrix instead and recalculating it if undefined. This can be done in the source code in
_set
for example.calcTransformMatrix
should be changed as well to use a different approach.Using
save
,restore
is bad for performance, usingsetTransform
vs.transform
is betterIt proves that removing originX/Y is good for perf
perf.mov