Skip to content

2.0 perf updates #7543

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

Merged
merged 13 commits into from
Feb 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion preview/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,27 @@
import p5 from '../src/app.js';

const sketch = function (p) {
let fbo;
p.setup = async function () {
p.createCanvas(400, 400);
p.createCanvas(400, 400, p.WEBGL);
fbo = p.createFramebuffer()
};

p.draw = function () {
p.push();
p.background(200);
fbo.begin()
p.background('blue')
p.strokeWeight(10);
p.push()
p.stroke('red')
p.line(-100, -100, 100, 100);
p.pop()
p.translate(200, 200)
p.line(-100, -100, 100, 100);
fbo.end()
p.image(fbo, 0, 0)
p.pop();
};
};

Expand Down
12 changes: 5 additions & 7 deletions src/color/creating_reading.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,13 @@ function creatingReading(p5, fn){
[OKLAB]: [100, [-125, 125], [-125, 125], 1],
[OKLCH]: [100, 150, 360, 1],
clone: function(){
let ret = {};
for(const key in this){
if(typeof this[key] !== 'function'){
ret[key] = structuredClone(this[key]);
}else{
ret[key] = this[key];
const cloned = { ...this };
for (const key in cloned) {
if (cloned[key] instanceof Array) {
cloned[key] = [...cloned[key]];
}
}
return ret;
return cloned;
}
};

Expand Down
16 changes: 12 additions & 4 deletions src/color/p5.Color.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ import HSBSpace from './color_spaces/hsb.js';
const map = (n, start1, stop1, start2, stop2) =>
((n - start1) / (stop1 - start1) * (stop2 - start2) + start2);

const serializationMap = {};

class Color {
// Reference to underlying color object depending on implementation
// Not meant to be used publicly unless the implementation is known for sure
Expand Down Expand Up @@ -242,10 +244,16 @@ class Color {
* </div>
*/
toString(format) {
// NOTE: memoize
return serialize(this._color, {
format
});
const key = `${this._color.space.id}-${this._color.coords.join(",")}-${this._color.alpha}-${format}`;
let colorString = serializationMap[key];

if(!colorString){
colorString = serialize(this._color, {
format
});
serializationMap[key] = colorString;
}
return colorString;
}

/**
Expand Down
5 changes: 3 additions & 2 deletions src/color/setting.js
Original file line number Diff line number Diff line change
Expand Up @@ -964,9 +964,10 @@ function setting(p5, fn){
].includes(mode)
) {
// Set color mode.
this._renderer.states.colorMode = mode;
this._renderer.states.setValue('colorMode', mode);

// Set color maxes.
this._renderer.states.setValue('colorMaxes', this._renderer.states.colorMaxes.clone());
const maxes = this._renderer.states.colorMaxes[mode];
if (arguments.length === 2) {
maxes[0] = max1; // Red
Expand Down Expand Up @@ -1335,7 +1336,7 @@ function setting(p5, fn){
* </div>
*/
fn.noStroke = function() {
this._renderer.states.strokeColor = null;
this._renderer.states.setValue('strokeColor', null);
return this;
};

Expand Down
33 changes: 33 additions & 0 deletions src/core/States.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
export class States {
#modified = {};

constructor(initialState) {
for (const key in initialState) {
this[key] = initialState[key];
}
}

setValue(key, value) {
if (!(key in this.#modified)) {
this.#modified[key] = this[key];
}
this[key] = value;
}

getDiff() {
const diff = this.#modified;
this.#modified = {};
return diff;
}

getModified() {
return this.#modified;
}

applyDiff(prevModified) {
for (const key in this.#modified) {
this[key] = this.#modified[key];
}
this.#modified = prevModified;
}
}
94 changes: 43 additions & 51 deletions src/core/p5.Renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import * as constants from '../core/constants';
import { Image } from '../image/p5.Image';
import { Vector } from '../math/p5.Vector';
import { Shape } from '../shape/custom_shapes';
import { States } from './States';

class ClonableObject {
constructor(obj = {}) {
Expand Down Expand Up @@ -70,15 +71,7 @@ class Renderer {
}

// Renderer state machine
this.states = Object.assign({}, Renderer.states);
// Clone properties that support it
for (const key in this.states) {
if (this.states[key] instanceof Array) {
this.states[key] = this.states[key].slice();
} else if (this.states[key] && this.states[key].clone instanceof Function) {
this.states[key] = this.states[key].clone();
}
}
this.states = new States(Renderer.states);

this.states.strokeColor = new Color([0, 0, 0]);
this.states.fillColor = new Color([255, 255, 255]);
Expand Down Expand Up @@ -122,33 +115,25 @@ class Renderer {
// and push it into the push pop stack
push() {
this._pushPopDepth++;
const currentStates = Object.assign({}, this.states);
// Clone properties that support it
for (const key in currentStates) {
if (currentStates[key] instanceof Array) {
currentStates[key] = currentStates[key].slice();
} else if (currentStates[key] && currentStates[key].clone instanceof Function) {
currentStates[key] = currentStates[key].clone();
}
}
this._pushPopStack.push(currentStates);
return currentStates;
this._pushPopStack.push(this.states.getDiff());
}

// Pop the previous states out of the push pop stack and
// assign it back to the current state
pop() {
this._pushPopDepth--;
Object.assign(this.states, this._pushPopStack.pop());
this.updateShapeVertexProperties();
this.updateShapeProperties();
const diff = this._pushPopStack.pop() || {};
const modified = this.states.getModified();
this.states.applyDiff(diff);
this.updateShapeVertexProperties(modified);
this.updateShapeProperties(modified);
}

bezierOrder(order) {
if (order === undefined) {
return this.states.bezierOrder;
} else {
this.states.bezierOrder = order;
this.states.setValue('bezierOrder', order);
this.updateShapeProperties();
}
}
Expand All @@ -165,6 +150,7 @@ class Renderer {
if (value === undefined) {
return this.states.splineProperties[key];
} else {
this.states.setValue('splineProperties', this.states.splineProperties.clone());
this.states.splineProperties[key] = value;
}
this.updateShapeProperties();
Expand All @@ -182,7 +168,7 @@ class Renderer {
if (d === undefined) {
return this.states.curveDetail;
} else {
this.states.curveDetail = d;
this.states.setValue('curveDetail', d);
}
}

Expand Down Expand Up @@ -277,31 +263,31 @@ class Renderer {
}

fill(...args) {
this.states.fillSet = true;
this.states.fillColor = this._pInst.color(...args);
this.states.setValue('fillSet', true);
this.states.setValue('fillColor', this._pInst.color(...args));
this.updateShapeVertexProperties();
}

noFill() {
this.states.fillColor = null;
this.states.setValue('fillColor', null);
}

strokeWeight(w) {
if (w === undefined) {
return this.states.strokeWeight;
} else {
this.states.strokeWeight = w;
this.states.setValue('strokeWeight', w);
}
}

stroke(...args) {
this.states.strokeSet = true;
this.states.strokeColor = this._pInst.color(...args);
this.states.setValue('strokeSet', true);
this.states.setValue('strokeColor', this._pInst.color(...args));
this.updateShapeVertexProperties();
}

noStroke() {
this.states.strokeColor = null;
this.states.setValue('strokeColor', null);
}

getCommonVertexProperties() {
Expand All @@ -314,25 +300,31 @@ class Renderer {
}
}

updateShapeProperties() {
this.currentShape.bezierOrder(this.states.bezierOrder);
this.currentShape.splineProperty('ends', this.states.splineProperties.ends);
this.currentShape.splineProperty('tightness', this.states.splineProperties.tightness);
updateShapeProperties(modified) {
if (!modified || modified.bezierOrder || modified.splineProperties) {
const shape = this.currentShape;
shape.bezierOrder(this.states.bezierOrder);
shape.splineProperty('ends', this.states.splineProperties.ends);
shape.splineProperty('tightness', this.states.splineProperties.tightness);
}
}

updateShapeVertexProperties() {
updateShapeVertexProperties(modified) {
const props = this.getCommonVertexProperties();
for (const key in props) {
this.currentShape[key](props[key]);
if (!modified || Object.keys(modified).some((k) => k in props)) {
const shape = this.currentShape;
for (const key in props) {
shape[key](props[key]);
}
}
}

textSize(s) {
if (typeof s === 'number') {
this.states.textSize = s;
this.states.setValue('textSize', s);
if (!this.states.leadingSet) {
// only use a default value if not previously set (#5181)
this.states.textLeading = s * constants._DEFAULT_LEADMULT;
this.states.setValue('textLeading', s * constants._DEFAULT_LEADMULT);
}
return this._applyTextProperties();
}
Expand All @@ -342,8 +334,8 @@ class Renderer {

textLeading (l) {
if (typeof l === 'number') {
this.states.leadingSet = true;
this.states.textLeading = l;
this.states.setValue('leadingSet', true);
this.states.setValue('textLeading', l);
return this._pInst;
}

Expand All @@ -358,7 +350,7 @@ class Renderer {
s === constants.BOLD ||
s === constants.BOLDITALIC
) {
this.states.fontStyle = s;
this.states.setValue('fontStyle', s);
}

return this._applyTextProperties();
Expand All @@ -383,10 +375,10 @@ class Renderer {

textAlign (h, v) {
if (typeof h !== 'undefined') {
this.states.textAlign = h;
this.states.setValue('textAlign', h);

if (typeof v !== 'undefined') {
this.states.textBaseline = v;
this.states.setValue('textBaseline', v);
}

return this._applyTextProperties();
Expand All @@ -399,7 +391,7 @@ class Renderer {
}

textWrap (wrapStyle) {
this.states.textWrap = wrapStyle;
this.states.setValue('textWrap', wrapStyle);
return this.states.textWrap;
}

Expand Down Expand Up @@ -647,8 +639,8 @@ class Renderer {

_updateTextMetrics() {
if (this._isOpenType()) {
this.states.textAscent = this.states.textFont._textAscent();
this.states.textDescent = this.states.textFont._textDescent();
this.states.setValue('textAscent', this.states.textFont._textAscent());
this.states.setValue('textDescent', this.states.textFont._textDescent());
return this;
}

Expand Down Expand Up @@ -684,8 +676,8 @@ class Renderer {

document.body.removeChild(container);

this.states.textAscent = ascent;
this.states.textDescent = descent;
this.states.setValue('textAscent', ascent);
this.states.setValue('textDescent', descent);

return this;
}
Expand Down
Loading