From 7a1d31401d820a7b4581023c11ceb3a56b6ede1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Renaudeau?= Date: Sat, 20 May 2017 15:03:47 +0200 Subject: [PATCH] feat(gl-react): add Node#setDrawProps escape hatch fixes https://github.com/gre/gl-react/issues/109 --- packages/gl-react/src/Node.js | 39 ++++++++++++++---- packages/tests/all.test.js | 70 ++++++++++++++++++++++++++++++++ packages/tests/flow/snapshot.txt | 64 ++++++++++++++--------------- 3 files changed, 133 insertions(+), 40 deletions(-) diff --git a/packages/gl-react/src/Node.js b/packages/gl-react/src/Node.js index c058206e..4c4da8de 100755 --- a/packages/gl-react/src/Node.js +++ b/packages/gl-react/src/Node.js @@ -378,6 +378,7 @@ const NodePropTypes = { */ export default class Node extends Component { props: Props; + drawProps: Props = this.props; context: SurfaceContext; framebuffer: ?Framebuffer; backbuffer: ?Framebuffer; @@ -444,6 +445,10 @@ export default class Node extends Component { } componentWillReceiveProps(nextProps: Props, nextContext: *) { + this._syncNextDrawProps(nextProps, nextContext); + } + + _syncNextDrawProps(nextProps: Props, nextContext: *) { const nextWidthHeight = nodeWidthHeight(nextProps, nextContext); if (this.framebuffer) { this.framebuffer.syncSize(...nextWidthHeight); @@ -453,9 +458,10 @@ export default class Node extends Component { } // FIXME should we do same for backbuffer? invariant( - nextProps.backbuffering === this.props.backbuffering, + nextProps.backbuffering === this.drawProps.backbuffering, "Node backbuffering prop must not changed. (not yet supported)" ); + this.drawProps = nextProps; } _resolveElement = ( @@ -509,7 +515,7 @@ export default class Node extends Component { } getGLShortName(): string { - const { shader } = this.props; + const { shader } = this.drawProps; const shaderName = isShaderIdentifier(shader) ? // $FlowFixMe FIXME Shaders.getShortName(shader) @@ -518,7 +524,7 @@ export default class Node extends Component { } getGLName(): string { - const { shader } = this.props; + const { shader } = this.drawProps; const shaderName = isShaderIdentifier(shader) ? // $FlowFixMe FIXME Shaders.getName(shader) @@ -527,7 +533,7 @@ export default class Node extends Component { } getGLSize(): [number, number] { - return nodeWidthHeight(this.props, this.context); + return nodeWidthHeight(this.drawProps, this.context); } getGLOutput(): WebGLTexture { @@ -539,6 +545,23 @@ export default class Node extends Component { return framebuffer.color; } + /** + * Imperatively set the props with a partial subset of props to apply. + * This is an escape hatch to perform a redraw with different props without having to trigger a new React draw. Only use it when reaching a performance bottleneck. + * NB: at any time, render() needs to consistently render the same props you set in setDrawProps to avoid any potential blink (if a React draw would occur). + * @param {Props} patch a subset of props to apply on top of the previous draw props. + */ + setDrawProps(patch: Props) { + // $FlowFixMe + const nextProps: Props = { + ...this.drawProps, + ...patch, + }; + this._syncNextDrawProps(nextProps, this.context); + this.redraw(); + if (nextProps.sync) this.flush(); + } + /** * Capture the node pixels. * Without parameters, it will capture the full rectangle, @@ -620,7 +643,7 @@ export default class Node extends Component { // when a FBO is not created, _draw() happens on the final Canvas (null fbo) // NB we can just do this in WillMount because this context will not change. invariant( - !this.props.backbuffering, + !this.drawProps.backbuffering, "`backbuffering` is currently not supported for a Root Node. " + "Try to wrap %s in a or .", this.getGLName() @@ -628,7 +651,7 @@ export default class Node extends Component { } else { const fbo = createFBO(gl, width, height); this.framebuffer = fbo; - if (this.props.backbuffering) { + if (this.drawProps.backbuffering) { const fbo = createFBO(gl, width, height); this.backbuffer = fbo; } @@ -760,7 +783,7 @@ export default class Node extends Component { blendFunc, clear, onDraw, - } = this.props; + } = this.drawProps; //~ PREPARE phase @@ -821,7 +844,7 @@ export default class Node extends Component { } } else if (obj === Backbuffer) { // maybe it's backbuffer? - if (!this.props.backbuffering) { + if (!this.drawProps.backbuffering) { console.warn( `${nodeName}, uniform ${uniformKeyName}: you must set \`backbuffering\` on Node when using Backbuffer` ); diff --git a/packages/tests/all.test.js b/packages/tests/all.test.js index 21117e27..6ae2a793 100755 --- a/packages/tests/all.test.js +++ b/packages/tests/all.test.js @@ -343,6 +343,76 @@ test("renders a color uniform", () => { inst.unmount(); }); +test("use the imperative setDrawProps escape hatch", () => { + const shaders = Shaders.create({ + clr: { + frag: GLSL` + precision highp float; + varying vec2 uv; + uniform vec4 color; + void main() { + gl_FragColor = color; + }`, + }, + white: { + frag: GLSL` + precision highp float; + varying vec2 uv; + void main() { + gl_FragColor = vec4(1.0); + }`, + }, + }); + + let node; + const inst = create( + + { + node = ref; + }} + shader={shaders.clr} + uniforms={{ color: [1, 0, 0, 1] }} + /> + + ); + const surface = inst.getInstance(); + invariant(node, "nodeRef is set"); + expectToBeCloseToColorArray( + surface.capture().data, + new Uint8Array([255, 0, 0, 255]) + ); + node.setDrawProps({ + uniforms: { + color: [1, 1, 0, 1], + }, + }); + // check it's still lazy + expectToBeCloseToColorArray( + surface.capture().data, + new Uint8Array([255, 0, 0, 255]) + ); + surface.flush(); + expectToBeCloseToColorArray( + surface.capture().data, + new Uint8Array([255, 255, 0, 255]) + ); + node.setDrawProps({ + shader: shaders.white, + uniforms: {}, + }); + surface.flush(); + expectToBeCloseToColorArray( + surface.capture().data, + new Uint8Array([255, 255, 255, 255]) + ); + inst.unmount(); +}); + test("composes color uniform with LinearCopy", () => { const shaders = Shaders.create({ clr: { diff --git a/packages/tests/flow/snapshot.txt b/packages/tests/flow/snapshot.txt index 9e30ef09..2867c131 100644 --- a/packages/tests/flow/snapshot.txt +++ b/packages/tests/flow/snapshot.txt @@ -1,25 +1,25 @@ -../all.test.js:2416 +../all.test.js:2486 v---- -2416: }} -2420: /> +2486: }} +2490: /> -^ props of React element `Node` -2418: uniformsOptions={{ t: { interpolation: "nope" } }} +2488: uniformsOptions={{ t: { interpolation: "nope" } }} ^^^^^^ string. This type is incompatible with 65: interpolation: Interpolation, ^^^^^^^^^^^^^ string enum. See: ../node_modules/gl-react/lib/Node.js.flow:65 -../all.test.js:2428 +../all.test.js:2498 v---- -2428: }} -2432: /> +2498: }} +2502: /> -^ props of React element `Node` -2430: uniformsOptions={{ t: { wrap: "nope" } }} +2500: uniformsOptions={{ t: { wrap: "nope" } }} ^^^^^^ string. This type is incompatible with 66: wrap: [WrapMode, WrapMode] | WrapMode, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ union: tuple type | WrapMode. See: ../node_modules/gl-react/lib/Node.js.flow:66 @@ -27,7 +27,7 @@ 66: wrap: [WrapMode, WrapMode] | WrapMode, ^^^^^^^^^^^^^^^^^^^^ tuple type. See: ../node_modules/gl-react/lib/Node.js.flow:66 Error: - 2430: uniformsOptions={{ t: { wrap: "nope" } }} + 2500: uniformsOptions={{ t: { wrap: "nope" } }} ^^^^^^ string. This type is incompatible with 66: wrap: [WrapMode, WrapMode] | WrapMode, ^^^^^^^^^^^^^^^^^^^^ tuple type. See: ../node_modules/gl-react/lib/Node.js.flow:66 @@ -35,20 +35,20 @@ 66: wrap: [WrapMode, WrapMode] | WrapMode, ^^^^^^^^ WrapMode. See: ../node_modules/gl-react/lib/Node.js.flow:66 Error: - 2430: uniformsOptions={{ t: { wrap: "nope" } }} + 2500: uniformsOptions={{ t: { wrap: "nope" } }} ^^^^^^ string. This type is incompatible with 66: wrap: [WrapMode, WrapMode] | WrapMode, ^^^^^^^^ string enum. See: ../node_modules/gl-react/lib/Node.js.flow:66 -../all.test.js:2440 +../all.test.js:2510 v---- -2440: }} -2444: /> +2510: }} +2514: /> -^ props of React element `Node` -2442: uniformsOptions={{ t: { wrap: ["nope", "nope"] } }} +2512: uniformsOptions={{ t: { wrap: ["nope", "nope"] } }} ^^^^^^^^^^^^^^^^ array literal. This type is incompatible with 66: wrap: [WrapMode, WrapMode] | WrapMode, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ union: tuple type | WrapMode. See: ../node_modules/gl-react/lib/Node.js.flow:66 @@ -56,7 +56,7 @@ 66: wrap: [WrapMode, WrapMode] | WrapMode, ^^^^^^^^^^^^^^^^^^^^ tuple type. See: ../node_modules/gl-react/lib/Node.js.flow:66 Error: - 2442: uniformsOptions={{ t: { wrap: ["nope", "nope"] } }} + 2512: uniformsOptions={{ t: { wrap: ["nope", "nope"] } }} ^^^^^^ string. This type is incompatible with 66: wrap: [WrapMode, WrapMode] | WrapMode, ^^^^^^^^ string enum. See: ../node_modules/gl-react/lib/Node.js.flow:66 @@ -64,20 +64,20 @@ 66: wrap: [WrapMode, WrapMode] | WrapMode, ^^^^^^^^ WrapMode. See: ../node_modules/gl-react/lib/Node.js.flow:66 Error: - 2442: uniformsOptions={{ t: { wrap: ["nope", "nope"] } }} + 2512: uniformsOptions={{ t: { wrap: ["nope", "nope"] } }} ^^^^^^^^^^^^^^^^ array literal. This type is incompatible with 66: wrap: [WrapMode, WrapMode] | WrapMode, ^^^^^^^^ string enum. See: ../node_modules/gl-react/lib/Node.js.flow:66 -../all.test.js:2452 +../all.test.js:2522 v---- -2452: }} -2456: /> +2522: }} +2526: /> -^ props of React element `Node` -2453: blendFunc="nope" +2523: blendFunc="nope" ^^^^^^ string. Inexact type is incompatible with exact type 177: blendFunc: BlendFuncSrcDst, ^^^^^^^^^^^^^^^ exact type: object type. See: ../node_modules/gl-react/lib/Node.js.flow:177