Skip to content

Commit 7a1d314

Browse files
committed
feat(gl-react): add Node#setDrawProps escape hatch
fixes #109
1 parent 5617f2b commit 7a1d314

File tree

3 files changed

+133
-40
lines changed

3 files changed

+133
-40
lines changed

packages/gl-react/src/Node.js

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,7 @@ const NodePropTypes = {
378378
*/
379379
export default class Node extends Component {
380380
props: Props;
381+
drawProps: Props = this.props;
381382
context: SurfaceContext;
382383
framebuffer: ?Framebuffer;
383384
backbuffer: ?Framebuffer;
@@ -444,6 +445,10 @@ export default class Node extends Component {
444445
}
445446

446447
componentWillReceiveProps(nextProps: Props, nextContext: *) {
448+
this._syncNextDrawProps(nextProps, nextContext);
449+
}
450+
451+
_syncNextDrawProps(nextProps: Props, nextContext: *) {
447452
const nextWidthHeight = nodeWidthHeight(nextProps, nextContext);
448453
if (this.framebuffer) {
449454
this.framebuffer.syncSize(...nextWidthHeight);
@@ -453,9 +458,10 @@ export default class Node extends Component {
453458
}
454459
// FIXME should we do same for backbuffer?
455460
invariant(
456-
nextProps.backbuffering === this.props.backbuffering,
461+
nextProps.backbuffering === this.drawProps.backbuffering,
457462
"Node backbuffering prop must not changed. (not yet supported)"
458463
);
464+
this.drawProps = nextProps;
459465
}
460466

461467
_resolveElement = (
@@ -509,7 +515,7 @@ export default class Node extends Component {
509515
}
510516

511517
getGLShortName(): string {
512-
const { shader } = this.props;
518+
const { shader } = this.drawProps;
513519
const shaderName = isShaderIdentifier(shader)
514520
? // $FlowFixMe FIXME
515521
Shaders.getShortName(shader)
@@ -518,7 +524,7 @@ export default class Node extends Component {
518524
}
519525
520526
getGLName(): string {
521-
const { shader } = this.props;
527+
const { shader } = this.drawProps;
522528
const shaderName = isShaderIdentifier(shader)
523529
? // $FlowFixMe FIXME
524530
Shaders.getName(shader)
@@ -527,7 +533,7 @@ export default class Node extends Component {
527533
}
528534
529535
getGLSize(): [number, number] {
530-
return nodeWidthHeight(this.props, this.context);
536+
return nodeWidthHeight(this.drawProps, this.context);
531537
}
532538
533539
getGLOutput(): WebGLTexture {
@@ -539,6 +545,23 @@ export default class Node extends Component {
539545
return framebuffer.color;
540546
}
541547
548+
/**
549+
* Imperatively set the props with a partial subset of props to apply.
550+
* 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.
551+
* 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).
552+
* @param {Props} patch a subset of props to apply on top of the previous draw props.
553+
*/
554+
setDrawProps(patch: Props) {
555+
// $FlowFixMe
556+
const nextProps: Props = {
557+
...this.drawProps,
558+
...patch,
559+
};
560+
this._syncNextDrawProps(nextProps, this.context);
561+
this.redraw();
562+
if (nextProps.sync) this.flush();
563+
}
564+
542565
/**
543566
* Capture the node pixels.
544567
* Without parameters, it will capture the full rectangle,
@@ -620,15 +643,15 @@ export default class Node extends Component {
620643
// when a FBO is not created, _draw() happens on the final Canvas (null fbo)
621644
// NB we can just do this in WillMount because this context will not change.
622645
invariant(
623-
!this.props.backbuffering,
646+
!this.drawProps.backbuffering,
624647
"`backbuffering` is currently not supported for a Root Node. " +
625648
"Try to wrap %s in a <LinearCopy> or <NearestCopy>.",
626649
this.getGLName()
627650
);
628651
} else {
629652
const fbo = createFBO(gl, width, height);
630653
this.framebuffer = fbo;
631-
if (this.props.backbuffering) {
654+
if (this.drawProps.backbuffering) {
632655
const fbo = createFBO(gl, width, height);
633656
this.backbuffer = fbo;
634657
}
@@ -760,7 +783,7 @@ export default class Node extends Component {
760783
blendFunc,
761784
clear,
762785
onDraw,
763-
} = this.props;
786+
} = this.drawProps;
764787
765788
//~ PREPARE phase
766789
@@ -821,7 +844,7 @@ export default class Node extends Component {
821844
}
822845
} else if (obj === Backbuffer) {
823846
// maybe it's backbuffer?
824-
if (!this.props.backbuffering) {
847+
if (!this.drawProps.backbuffering) {
825848
console.warn(
826849
`${nodeName}, uniform ${uniformKeyName}: you must set \`backbuffering\` on Node when using Backbuffer`
827850
);

packages/tests/all.test.js

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,76 @@ test("renders a color uniform", () => {
343343
inst.unmount();
344344
});
345345

346+
test("use the imperative setDrawProps escape hatch", () => {
347+
const shaders = Shaders.create({
348+
clr: {
349+
frag: GLSL`
350+
precision highp float;
351+
varying vec2 uv;
352+
uniform vec4 color;
353+
void main() {
354+
gl_FragColor = color;
355+
}`,
356+
},
357+
white: {
358+
frag: GLSL`
359+
precision highp float;
360+
varying vec2 uv;
361+
void main() {
362+
gl_FragColor = vec4(1.0);
363+
}`,
364+
},
365+
});
366+
367+
let node;
368+
const inst = create(
369+
<Surface
370+
width={1}
371+
height={1}
372+
webglContextAttributes={{ preserveDrawingBuffer: true }}
373+
>
374+
<Node
375+
ref={ref => {
376+
node = ref;
377+
}}
378+
shader={shaders.clr}
379+
uniforms={{ color: [1, 0, 0, 1] }}
380+
/>
381+
</Surface>
382+
);
383+
const surface = inst.getInstance();
384+
invariant(node, "nodeRef is set");
385+
expectToBeCloseToColorArray(
386+
surface.capture().data,
387+
new Uint8Array([255, 0, 0, 255])
388+
);
389+
node.setDrawProps({
390+
uniforms: {
391+
color: [1, 1, 0, 1],
392+
},
393+
});
394+
// check it's still lazy
395+
expectToBeCloseToColorArray(
396+
surface.capture().data,
397+
new Uint8Array([255, 0, 0, 255])
398+
);
399+
surface.flush();
400+
expectToBeCloseToColorArray(
401+
surface.capture().data,
402+
new Uint8Array([255, 255, 0, 255])
403+
);
404+
node.setDrawProps({
405+
shader: shaders.white,
406+
uniforms: {},
407+
});
408+
surface.flush();
409+
expectToBeCloseToColorArray(
410+
surface.capture().data,
411+
new Uint8Array([255, 255, 255, 255])
412+
);
413+
inst.unmount();
414+
});
415+
346416
test("composes color uniform with LinearCopy", () => {
347417
const shaders = Shaders.create({
348418
clr: {

packages/tests/flow/snapshot.txt

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,83 +1,83 @@
1-
../all.test.js:2416
1+
../all.test.js:2486
22
v----
3-
2416: <Node
4-
2417: shader={shaders.helloTexture}
5-
2418: uniformsOptions={{ t: { interpolation: "nope" } }}
6-
2419: uniforms={{ t: <JustBlue blue={1} /> }}
7-
2420: />
3+
2486: <Node
4+
2487: shader={shaders.helloTexture}
5+
2488: uniformsOptions={{ t: { interpolation: "nope" } }}
6+
2489: uniforms={{ t: <JustBlue blue={1} /> }}
7+
2490: />
88
-^ props of React element `Node`
9-
2418: uniformsOptions={{ t: { interpolation: "nope" } }}
9+
2488: uniformsOptions={{ t: { interpolation: "nope" } }}
1010
^^^^^^ string. This type is incompatible with
1111
65: interpolation: Interpolation,
1212
^^^^^^^^^^^^^ string enum. See: ../node_modules/gl-react/lib/Node.js.flow:65
1313

14-
../all.test.js:2428
14+
../all.test.js:2498
1515
v----
16-
2428: <Node
17-
2429: shader={shaders.helloTexture}
18-
2430: uniformsOptions={{ t: { wrap: "nope" } }}
19-
2431: uniforms={{ t: <JustBlue blue={1} /> }}
20-
2432: />
16+
2498: <Node
17+
2499: shader={shaders.helloTexture}
18+
2500: uniformsOptions={{ t: { wrap: "nope" } }}
19+
2501: uniforms={{ t: <JustBlue blue={1} /> }}
20+
2502: />
2121
-^ props of React element `Node`
22-
2430: uniformsOptions={{ t: { wrap: "nope" } }}
22+
2500: uniformsOptions={{ t: { wrap: "nope" } }}
2323
^^^^^^ string. This type is incompatible with
2424
66: wrap: [WrapMode, WrapMode] | WrapMode,
2525
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ union: tuple type | WrapMode. See: ../node_modules/gl-react/lib/Node.js.flow:66
2626
Member 1:
2727
66: wrap: [WrapMode, WrapMode] | WrapMode,
2828
^^^^^^^^^^^^^^^^^^^^ tuple type. See: ../node_modules/gl-react/lib/Node.js.flow:66
2929
Error:
30-
2430: uniformsOptions={{ t: { wrap: "nope" } }}
30+
2500: uniformsOptions={{ t: { wrap: "nope" } }}
3131
^^^^^^ string. This type is incompatible with
3232
66: wrap: [WrapMode, WrapMode] | WrapMode,
3333
^^^^^^^^^^^^^^^^^^^^ tuple type. See: ../node_modules/gl-react/lib/Node.js.flow:66
3434
Member 2:
3535
66: wrap: [WrapMode, WrapMode] | WrapMode,
3636
^^^^^^^^ WrapMode. See: ../node_modules/gl-react/lib/Node.js.flow:66
3737
Error:
38-
2430: uniformsOptions={{ t: { wrap: "nope" } }}
38+
2500: uniformsOptions={{ t: { wrap: "nope" } }}
3939
^^^^^^ string. This type is incompatible with
4040
66: wrap: [WrapMode, WrapMode] | WrapMode,
4141
^^^^^^^^ string enum. See: ../node_modules/gl-react/lib/Node.js.flow:66
4242

43-
../all.test.js:2440
43+
../all.test.js:2510
4444
v----
45-
2440: <Node
46-
2441: shader={shaders.helloTexture}
47-
2442: uniformsOptions={{ t: { wrap: ["nope", "nope"] } }}
48-
2443: uniforms={{ t: <JustBlue blue={1} /> }}
49-
2444: />
45+
2510: <Node
46+
2511: shader={shaders.helloTexture}
47+
2512: uniformsOptions={{ t: { wrap: ["nope", "nope"] } }}
48+
2513: uniforms={{ t: <JustBlue blue={1} /> }}
49+
2514: />
5050
-^ props of React element `Node`
51-
2442: uniformsOptions={{ t: { wrap: ["nope", "nope"] } }}
51+
2512: uniformsOptions={{ t: { wrap: ["nope", "nope"] } }}
5252
^^^^^^^^^^^^^^^^ array literal. This type is incompatible with
5353
66: wrap: [WrapMode, WrapMode] | WrapMode,
5454
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ union: tuple type | WrapMode. See: ../node_modules/gl-react/lib/Node.js.flow:66
5555
Member 1:
5656
66: wrap: [WrapMode, WrapMode] | WrapMode,
5757
^^^^^^^^^^^^^^^^^^^^ tuple type. See: ../node_modules/gl-react/lib/Node.js.flow:66
5858
Error:
59-
2442: uniformsOptions={{ t: { wrap: ["nope", "nope"] } }}
59+
2512: uniformsOptions={{ t: { wrap: ["nope", "nope"] } }}
6060
^^^^^^ string. This type is incompatible with
6161
66: wrap: [WrapMode, WrapMode] | WrapMode,
6262
^^^^^^^^ string enum. See: ../node_modules/gl-react/lib/Node.js.flow:66
6363
Member 2:
6464
66: wrap: [WrapMode, WrapMode] | WrapMode,
6565
^^^^^^^^ WrapMode. See: ../node_modules/gl-react/lib/Node.js.flow:66
6666
Error:
67-
2442: uniformsOptions={{ t: { wrap: ["nope", "nope"] } }}
67+
2512: uniformsOptions={{ t: { wrap: ["nope", "nope"] } }}
6868
^^^^^^^^^^^^^^^^ array literal. This type is incompatible with
6969
66: wrap: [WrapMode, WrapMode] | WrapMode,
7070
^^^^^^^^ string enum. See: ../node_modules/gl-react/lib/Node.js.flow:66
7171

72-
../all.test.js:2452
72+
../all.test.js:2522
7373
v----
74-
2452: <Node
75-
2453: blendFunc="nope"
76-
2454: shader={shaders.helloTexture}
77-
2455: uniforms={{ t: <JustBlue blue={1} /> }}
78-
2456: />
74+
2522: <Node
75+
2523: blendFunc="nope"
76+
2524: shader={shaders.helloTexture}
77+
2525: uniforms={{ t: <JustBlue blue={1} /> }}
78+
2526: />
7979
-^ props of React element `Node`
80-
2453: blendFunc="nope"
80+
2523: blendFunc="nope"
8181
^^^^^^ string. Inexact type is incompatible with exact type
8282
177: blendFunc: BlendFuncSrcDst,
8383
^^^^^^^^^^^^^^^ exact type: object type. See: ../node_modules/gl-react/lib/Node.js.flow:177

0 commit comments

Comments
 (0)