-
-
Notifications
You must be signed in to change notification settings - Fork 3.3k
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
[p5.js 2.0] State machines and renderer refactoring #7270
base: dev-2.0
Are you sure you want to change the base?
Changes from 1 commit
09f57c9
ab22f1d
c5441dc
e93cbd5
94e8267
c80e364
f0b3766
7299220
84ecc61
282cabb
3a356d5
6ef1afc
2f7f824
9c47a4c
c39cfa6
263ae57
8427978
5c68e24
e3c3683
f1d8735
7a91e53
b07b438
55c45ed
beb432f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,19 @@ class Renderer2D extends Renderer { | |
super(elt, pInst, isMainCanvas); | ||
this.drawingContext = this.canvas.getContext('2d'); | ||
this._pInst._setProperty('drawingContext', this.drawingContext); | ||
this.elt = elt; | ||
|
||
// Extend renderer with methods of p5.Element with getters | ||
this.wrappedElt = new p5.Element(elt, pInst); | ||
for (const p of Object.getOwnPropertyNames(p5.Element.prototype)) { | ||
if (p !== 'constructor' && p[0] !== '_') { | ||
Object.defineProperty(this, p, { | ||
get() { | ||
return this.wrappedElt[p]; | ||
} | ||
}) | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For changing the hierachy of the OOP structure, This change above more or less preserve the existing behavior outwardly by aliasing Another less magical option is to make a breaking change so that the renderer's underlying There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm ok with either of those (maybe we could make a Another thought: do users expect a renderer to come out of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm...perhaps returning p5.Element for those using it could be something There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right, I think that's OK if renderers are private if renderers have a way of adding public methods to p5. So things like sphere() could exist just in the WebGL renderer, added to p5 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, that one is somewhere on my list for this PR, might go with the existing method (defining |
||
} | ||
|
||
getFilterGraphicsLayer() { | ||
|
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 should probably be using
structuredClone()
because states may have objects as properties that won't be fully preserved if copied in this way. It is currently notstructuredClone()
because somewhere in the WebGL renderer states is a reference towindow
which cannot be cloned.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 believe only some base types work in structured clone, and I'm not certain all the things we put into this state conform to that (e.g. in WebGL, currently we have a camera object.) We have a few options here, do you have any preference between:
clone()
that state properties can optionally implement so that we can have custom object types in the stateI think the former might be a smaller refactor, but that extra flexibility might also mean a higher surface area for bugs. If we go for the latter, for WebGL, I think that would mean storing camera properties rather than the camera itself, and adding a custom handler on
pop()
to reapply those properties to the active camera.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 for performance we might want the option of not deep copying everything if it isn't necessary, so I maybe slightly lean towards the former?
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.
Some of the key things not cloneable for our use will be (including nested in objects) symbols, functions, and DOM nodes.
Does this mean we will loop through each direct properties of the
state
object and if there is a methodclone()
, call it and store that?I'm slightly more in favor of this and I think it is similar to the above option (if I understand it correctly) but with the renderer itself doing the
clone()
step before setting it in the state object.For something like
p5.Camera
it was never suited for deep cloning anyway as it would be duplicating the underlying p5 instance and what not. Saving the camera properties then reapplying them to the active camera feels more semantic but I'm not sure if there will be any performance impact (through repeated instantiation of heavy object for example)?Another option is that the base
p5.Renderer.states
object is essentially a convenience in that the push and pop stack will be implemented already in the base class so the derived class don't need to implement it, but if the derived class needs more complex behavior including using non-structuredCloneable states, it can perhaps be handled independently in the derived class'spush()
pop()
implementation, if that made sense.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.
Yep!
My comment about perf was mostly about avoiding automatic deep copying of everything, so I'm good with this too if the more expensive clones are done explicitly when needed. So maybe this method would mean that by default, everything is a simple type that should be replaced instead of mutated in place so we can keep doing
Object.assign(...)
instead of astructuredClone(...)
, and then a renderer can do whatever copy it needs manually in itspush
override? And it should be just a few things needing that custom behaviour.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 we can further limit state properties to be basically primitives only (copy types only),
Object.assign()
should be enough, it is just when the state properties are objects or arrays thenstructuredClone()
would be needed. I'm fine going with the primitives only approach for an overall lighter state management by default if there's no obvious need for reference types to be used as state properties?If that sounds good, I can try to have a look at WebGL renderer states or since there are other WebGL stuff going on as well, maybe you'd have a better idea of when to review them? I'm going to move back onto the hierachy refactor and instance states next so it's not immediately blocking at this point.
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.
hmm right, I guess we have a lot of array states currently in WebGL as that's the format colors are stored in, and those are all treated as reference types right now. To avoid deep copies on all those, we'd have to wrap them in some other class to avoid the structured clone, so it feels like maybe that'd add more overhead than it's worth. Are there many 2D properties that need a clone? Would relying on object reference for everything by default be a possibility?
I tried doing some quick find-and-replacing this morning to start converting state into WebGL here: https://github.com/limzykenneth/p5.js/compare/2.0-modules...davepagurek:p5.js:2.0-webgl?expand=1 This is currently using the
.copy()
method on things if it exists, but we can change how that's done based on what we decide here, and currently this changeover has broken more tests that will need looking into. But anyway since this is just changing how some properties are accessed, I think it's ok to make these changes whenever, and it won't be too big of a merge for the other contributors.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.
For 2D the only one using reference type is
tint
using an array of colors as far as I remember, but it is actually currently on the base p5.Renderer class as WebGL can tint as well. If there is either a guarantee or just enforcement through documentation that if reference types are set as state properties, it must not be separately modified, eg. additional tints creates new array and never reuses it, it would still be fine to justObject.assign()
.You should be able to push to this PR directly if you want to do the conversion and test fixing as necessary. I'll merge from dev-2.0 regularly as I work on this too.