mirror of
https://github.com/gre/gl-react.git
synced 2026-01-25 16:43:36 +00:00
we need that because lerna don't always allow us to dedup (because symlinks). it's ok for the cookbooks/tests but it will be up to user to dedup anyway. + fix gl-react for latest yarn/npm. comment symlinks undo script + cookbook-rn & cookbook-expo will probably need to be fixed because don't support symlinks... however, the old way to undo this don't work.
2755 lines
65 KiB
JavaScript
Executable File
2755 lines
65 KiB
JavaScript
Executable File
//@flow
|
|
declare var jest;
|
|
declare var test;
|
|
declare var expect;
|
|
|
|
// TODO bus example: a bus that is not used, but use another bus, still should render, and we can capture it.
|
|
// TODO texture array is to be tested
|
|
// TODO review all our web examples and see if we missed to test something
|
|
|
|
// Using a single file because it runs sequencially
|
|
// and there is no weird issues with headless-gl that way
|
|
import {
|
|
Shaders,
|
|
Node,
|
|
LinearCopy,
|
|
NearestCopy,
|
|
GLSL,
|
|
Visitor,
|
|
Visitors,
|
|
Uniform,
|
|
Bus,
|
|
VisitorLogger,
|
|
connectSize
|
|
} from "gl-react";
|
|
import { globalRegistry } from "webgltexture-loader";
|
|
import { Surface } from "gl-react-headless";
|
|
import loseGL from "gl-react-headless/lib/loseGL";
|
|
import React from "react";
|
|
import renderer from "react-test-renderer";
|
|
import ndarray from "ndarray";
|
|
import invariant from "invariant";
|
|
|
|
import {
|
|
delay,
|
|
create,
|
|
CountersVisitor,
|
|
createNDArrayTexture,
|
|
createOneTextureLoader,
|
|
red2x2,
|
|
white3x3,
|
|
yellow3x3,
|
|
yellow3x2,
|
|
expectToBeCloseToColorArray
|
|
} from "./utils";
|
|
|
|
test("renders a red shader", () => {
|
|
const shaders = Shaders.create({
|
|
red: {
|
|
frag: GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
void main() {
|
|
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
|
|
}`
|
|
}
|
|
});
|
|
const inst = create(
|
|
<Surface
|
|
width={1}
|
|
height={1}
|
|
webglContextAttributes={{ preserveDrawingBuffer: true }}
|
|
>
|
|
<Node shader={shaders.red} />
|
|
</Surface>
|
|
);
|
|
const surface = inst.getInstance();
|
|
expectToBeCloseToColorArray(
|
|
surface.capture().data,
|
|
new Uint8Array([255, 0, 0, 255])
|
|
);
|
|
inst.unmount();
|
|
});
|
|
|
|
test("it does not crash when editing back and forth a shader", () => {
|
|
const fa = GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
void main() {
|
|
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
|
|
}`;
|
|
const fb = GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
void main() { gl_FragColor=vec4(1.0, 0.0, 0.0, 1.0); }`;
|
|
const inst = create(
|
|
<Surface width={1} height={1}>
|
|
<Node shader={{ frag: fa }} />
|
|
</Surface>
|
|
);
|
|
const surface = inst.getInstance();
|
|
surface.flush();
|
|
inst.update(
|
|
<Surface width={1} height={1}>
|
|
<Node shader={{ frag: fb }} />
|
|
</Surface>
|
|
);
|
|
surface.flush();
|
|
inst.update(
|
|
<Surface width={1} height={1}>
|
|
<Node shader={{ frag: fa }} />
|
|
</Surface>
|
|
);
|
|
surface.flush();
|
|
inst.unmount();
|
|
});
|
|
|
|
test("it does not crash when editing back and forth from a shader that crashed (resilient)", () => {
|
|
const fa = GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
void main() {
|
|
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
|
|
}`;
|
|
const fb = GLSL`THIS IS NOT A SHADER`;
|
|
|
|
const inst = create(
|
|
<Surface width={1} height={1}>
|
|
<Node shader={{ frag: fa }} />
|
|
</Surface>
|
|
);
|
|
const surface = inst.getInstance();
|
|
surface.flush();
|
|
|
|
class IgnoreErrorVisitor extends Visitor {
|
|
onSurfaceDrawError() {
|
|
return true;
|
|
}
|
|
}
|
|
inst.update(
|
|
<Surface visitor={new IgnoreErrorVisitor()} width={1} height={1}>
|
|
<Node shader={{ frag: fb }} />
|
|
</Surface>
|
|
);
|
|
surface.flush();
|
|
|
|
inst.update(
|
|
<Surface width={1} height={1}>
|
|
<Node shader={{ frag: fa }} />
|
|
</Surface>
|
|
);
|
|
surface.flush();
|
|
inst.unmount();
|
|
});
|
|
|
|
test("renders HelloGL", () => {
|
|
const shaders = Shaders.create({
|
|
helloGL: {
|
|
frag: GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
void main() {
|
|
gl_FragColor = vec4(uv.x, uv.y, 0.5, 1.0);
|
|
}`
|
|
}
|
|
});
|
|
const inst = create(
|
|
<Surface
|
|
width={4}
|
|
height={4}
|
|
webglContextAttributes={{ preserveDrawingBuffer: true }}
|
|
>
|
|
<Node shader={shaders.helloGL} />
|
|
</Surface>
|
|
);
|
|
const surface = inst.getInstance();
|
|
const snap = surface.capture();
|
|
expect(snap.shape).toEqual([4, 4, 4]);
|
|
expectToBeCloseToColorArray(
|
|
snap.data,
|
|
new Uint8Array([
|
|
32,
|
|
32,
|
|
128,
|
|
255,
|
|
96,
|
|
32,
|
|
128,
|
|
255,
|
|
159,
|
|
32,
|
|
128,
|
|
255,
|
|
223,
|
|
32,
|
|
128,
|
|
255,
|
|
32,
|
|
96,
|
|
128,
|
|
255,
|
|
96,
|
|
96,
|
|
128,
|
|
255,
|
|
159,
|
|
96,
|
|
128,
|
|
255,
|
|
223,
|
|
96,
|
|
128,
|
|
255,
|
|
32,
|
|
159,
|
|
128,
|
|
255,
|
|
96,
|
|
159,
|
|
128,
|
|
255,
|
|
159,
|
|
159,
|
|
128,
|
|
255,
|
|
223,
|
|
159,
|
|
128,
|
|
255,
|
|
32,
|
|
223,
|
|
128,
|
|
255,
|
|
96,
|
|
223,
|
|
128,
|
|
255,
|
|
159,
|
|
223,
|
|
128,
|
|
255,
|
|
223,
|
|
223,
|
|
128,
|
|
255
|
|
])
|
|
);
|
|
inst.unmount();
|
|
});
|
|
|
|
test("ndarray texture", () => {
|
|
const shaders = Shaders.create({
|
|
helloTexture: {
|
|
frag: GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
uniform sampler2D t;
|
|
void main() {
|
|
gl_FragColor = texture2D(t, uv);
|
|
}`
|
|
}
|
|
});
|
|
class HelloTextureStateful extends React.Component {
|
|
state = { t: red2x2 };
|
|
flush() {
|
|
this.refs.node.flush();
|
|
}
|
|
render() {
|
|
return (
|
|
<Node ref="node" shader={shaders.helloTexture} uniforms={this.state} />
|
|
);
|
|
}
|
|
}
|
|
let helloTexture: HelloTextureStateful;
|
|
const inst = create(
|
|
<Surface
|
|
width={64}
|
|
height={64}
|
|
webglContextAttributes={{ preserveDrawingBuffer: true }}
|
|
>
|
|
<HelloTextureStateful ref={ref => (helloTexture = ref)} />
|
|
</Surface>
|
|
);
|
|
invariant(helloTexture, "helloTexture is defined");
|
|
const surface = inst.getInstance();
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(0, 0, 1, 1).data,
|
|
new Uint8Array([255, 0, 0, 255])
|
|
);
|
|
helloTexture.setState({ t: yellow3x3 });
|
|
helloTexture.flush();
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(0, 0, 1, 1).data,
|
|
new Uint8Array([255, 255, 0, 255])
|
|
);
|
|
helloTexture.setState({ t: null });
|
|
helloTexture.flush();
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(0, 0, 1, 1).data,
|
|
new Uint8Array([0, 0, 0, 0])
|
|
);
|
|
inst.unmount();
|
|
});
|
|
|
|
test("renders a color uniform", () => {
|
|
const shaders = Shaders.create({
|
|
clr: {
|
|
frag: GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
uniform vec4 color;
|
|
void main() {
|
|
gl_FragColor = color;
|
|
}`
|
|
}
|
|
});
|
|
|
|
class ColorSurface extends React.Component {
|
|
render() {
|
|
const { color } = this.props;
|
|
return (
|
|
<Surface
|
|
ref="surface"
|
|
width={1}
|
|
height={1}
|
|
webglContextAttributes={{ preserveDrawingBuffer: true }}
|
|
>
|
|
<Node shader={shaders.clr} uniforms={{ color }} />
|
|
</Surface>
|
|
);
|
|
}
|
|
}
|
|
|
|
const inst = create(<ColorSurface color={[1, 0, 0, 1]} />);
|
|
const surface = inst.getInstance().refs.surface;
|
|
expectToBeCloseToColorArray(
|
|
surface.capture().data,
|
|
new Uint8Array([255, 0, 0, 255])
|
|
);
|
|
inst.update(<ColorSurface color={[0, 1, 0, 1]} />);
|
|
surface.flush();
|
|
expectToBeCloseToColorArray(
|
|
surface.capture().data,
|
|
new Uint8Array([0, 255, 0, 255])
|
|
);
|
|
inst.update(<ColorSurface color={[0.5, 0, 1, 1]} />);
|
|
surface.flush();
|
|
expectToBeCloseToColorArray(
|
|
surface.capture().data,
|
|
new Uint8Array([128, 0, 255, 255])
|
|
);
|
|
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(
|
|
<Surface
|
|
width={1}
|
|
height={1}
|
|
webglContextAttributes={{ preserveDrawingBuffer: true }}
|
|
>
|
|
<Node
|
|
ref={ref => {
|
|
node = ref;
|
|
}}
|
|
shader={shaders.clr}
|
|
uniforms={{ color: [1, 0, 0, 1] }}
|
|
/>
|
|
</Surface>
|
|
);
|
|
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: {
|
|
frag: GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
uniform vec4 color;
|
|
void main() {
|
|
gl_FragColor = color;
|
|
}`
|
|
}
|
|
});
|
|
|
|
class ColorSurface extends React.Component {
|
|
render() {
|
|
const { color } = this.props;
|
|
return (
|
|
<Surface
|
|
ref="surface"
|
|
width={1}
|
|
height={1}
|
|
webglContextAttributes={{ preserveDrawingBuffer: true }}
|
|
>
|
|
<LinearCopy>
|
|
<Node shader={shaders.clr} uniforms={{ color }} />
|
|
</LinearCopy>
|
|
</Surface>
|
|
);
|
|
}
|
|
}
|
|
const inst = create(<ColorSurface color={[0, 0, 1, 1]} />);
|
|
const surface = inst.getInstance().refs.surface;
|
|
expectToBeCloseToColorArray(
|
|
surface.capture().data,
|
|
new Uint8Array([0, 0, 255, 255])
|
|
);
|
|
inst.unmount();
|
|
});
|
|
|
|
test("no needs to flush if use of sync", () => {
|
|
const shaders = Shaders.create({
|
|
clr: {
|
|
frag: GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
uniform vec4 color;
|
|
void main() {
|
|
gl_FragColor = color;
|
|
}`
|
|
}
|
|
});
|
|
|
|
class ColorSurface extends React.Component {
|
|
render() {
|
|
const { color } = this.props;
|
|
return (
|
|
<Surface
|
|
ref="surface"
|
|
width={1}
|
|
height={1}
|
|
webglContextAttributes={{ preserveDrawingBuffer: true }}
|
|
>
|
|
<LinearCopy>
|
|
<Node sync shader={shaders.clr} uniforms={{ color }} />
|
|
</LinearCopy>
|
|
</Surface>
|
|
);
|
|
}
|
|
}
|
|
|
|
const inst = create(<ColorSurface color={[1, 0, 0, 1]} />);
|
|
const surface = inst.getInstance().refs.surface;
|
|
expectToBeCloseToColorArray(
|
|
surface.capture().data,
|
|
new Uint8Array([255, 0, 0, 255])
|
|
);
|
|
inst.update(<ColorSurface color={[0, 1, 0, 1]} />);
|
|
expectToBeCloseToColorArray(
|
|
surface.capture().data,
|
|
new Uint8Array([0, 255, 0, 255])
|
|
);
|
|
inst.update(<ColorSurface color={[0, 0, 1, 1]} />);
|
|
expectToBeCloseToColorArray(
|
|
surface.capture().data,
|
|
new Uint8Array([0, 0, 255, 255])
|
|
);
|
|
inst.unmount();
|
|
});
|
|
|
|
test("Node can have a different size and be scaled up", () => {
|
|
const shaders = Shaders.create({
|
|
red: {
|
|
frag: GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
void main() {
|
|
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
|
|
}`
|
|
}
|
|
});
|
|
const inst = create(
|
|
<Surface
|
|
width={200}
|
|
height={200}
|
|
webglContextAttributes={{ preserveDrawingBuffer: true }}
|
|
>
|
|
<LinearCopy>
|
|
<Node width={1} height={1} shader={shaders.red} />
|
|
</LinearCopy>
|
|
</Surface>
|
|
);
|
|
const surface = inst.getInstance();
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(100, 100, 1, 1).data,
|
|
new Uint8Array([255, 0, 0, 255])
|
|
);
|
|
inst.unmount();
|
|
});
|
|
|
|
test("Surface can be resized", () => {
|
|
const shaders = Shaders.create({
|
|
red: {
|
|
frag: GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
void main() {
|
|
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
|
|
}`
|
|
}
|
|
});
|
|
const renderForSize = (width, height) => (
|
|
<Surface
|
|
width={width}
|
|
height={height}
|
|
webglContextAttributes={{ preserveDrawingBuffer: true }}
|
|
>
|
|
<LinearCopy>
|
|
<Node shader={shaders.red} backbuffering />
|
|
</LinearCopy>
|
|
</Surface>
|
|
);
|
|
const inst = create(renderForSize(1, 1));
|
|
const surface = inst.getInstance();
|
|
inst.update(renderForSize(20, 20));
|
|
surface.flush();
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(10, 10, 1, 1).data,
|
|
new Uint8Array([255, 0, 0, 255])
|
|
);
|
|
inst.update(renderForSize(100, 100));
|
|
surface.flush();
|
|
inst.update(renderForSize(120, 100));
|
|
surface.flush();
|
|
inst.update(renderForSize(100, 500));
|
|
surface.flush();
|
|
inst.update(renderForSize(500, 100));
|
|
surface.flush();
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(400, 50, 1, 1).data,
|
|
new Uint8Array([255, 0, 0, 255])
|
|
);
|
|
inst.unmount();
|
|
});
|
|
|
|
test("bus uniform code style", () => {
|
|
const shaders = Shaders.create({
|
|
red: {
|
|
frag: GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
void main() {
|
|
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
|
|
}`
|
|
},
|
|
helloTexture: {
|
|
frag: GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
uniform sampler2D t;
|
|
void main() {
|
|
gl_FragColor = texture2D(t, uv);
|
|
}`
|
|
}
|
|
});
|
|
const inst = create(
|
|
<Surface
|
|
width={20}
|
|
height={20}
|
|
webglContextAttributes={{ preserveDrawingBuffer: true }}
|
|
>
|
|
<Node shader={shaders.helloTexture}>
|
|
<Bus uniform="t">
|
|
<Node shader={shaders.red} />
|
|
</Bus>
|
|
</Node>
|
|
</Surface>
|
|
);
|
|
expectToBeCloseToColorArray(
|
|
inst.getInstance().capture(10, 10, 1, 1).data,
|
|
new Uint8Array([255, 0, 0, 255])
|
|
);
|
|
inst.unmount();
|
|
});
|
|
|
|
test("bus example 1", () => {
|
|
const shaders = Shaders.create({
|
|
red: {
|
|
frag: GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
void main() {
|
|
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
|
|
}`
|
|
}
|
|
});
|
|
class Example extends React.Component {
|
|
render() {
|
|
return (
|
|
<Surface
|
|
width={20}
|
|
height={20}
|
|
webglContextAttributes={{ preserveDrawingBuffer: true }}
|
|
>
|
|
<Bus ref="bus">
|
|
<Node shader={shaders.red} />
|
|
</Bus>
|
|
<LinearCopy>{() => this.refs.bus}</LinearCopy>
|
|
</Surface>
|
|
);
|
|
}
|
|
}
|
|
const inst = create(<Example />);
|
|
expectToBeCloseToColorArray(
|
|
inst.getInstance().refs.bus.capture(10, 10, 1, 1).data,
|
|
new Uint8Array([255, 0, 0, 255])
|
|
);
|
|
inst.unmount();
|
|
});
|
|
|
|
test("bus example 2", () => {
|
|
const shaders = Shaders.create({
|
|
red: {
|
|
frag: GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
void main() {
|
|
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
|
|
}`
|
|
}
|
|
});
|
|
class Example extends React.Component {
|
|
render() {
|
|
return (
|
|
<Surface
|
|
ref="surface"
|
|
width={20}
|
|
height={20}
|
|
webglContextAttributes={{ preserveDrawingBuffer: true }}
|
|
>
|
|
<Bus ref="bus1">
|
|
<Node width={1} height={2} shader={shaders.red} />
|
|
</Bus>
|
|
<Bus ref="bus2">
|
|
<LinearCopy>{() => this.refs.bus1}</LinearCopy>
|
|
</Bus>
|
|
<LinearCopy>{() => this.refs.bus2}</LinearCopy>
|
|
</Surface>
|
|
);
|
|
}
|
|
}
|
|
const inst = create(<Example />);
|
|
const surface = inst.getInstance().refs.surface;
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(10, 10, 1, 1).data,
|
|
new Uint8Array([255, 0, 0, 255])
|
|
);
|
|
inst.unmount();
|
|
});
|
|
|
|
test("bus example 3", () => {
|
|
const shaders = Shaders.create({
|
|
red: {
|
|
frag: GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
void main() {
|
|
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
|
|
}`
|
|
}
|
|
});
|
|
class Red extends React.Component {
|
|
render() {
|
|
return <Node shader={shaders.red} />;
|
|
}
|
|
}
|
|
class Example extends React.Component {
|
|
render() {
|
|
return (
|
|
<Surface
|
|
ref="surface"
|
|
visitor={new Visitor()}
|
|
width={20}
|
|
height={20}
|
|
webglContextAttributes={{ preserveDrawingBuffer: true }}
|
|
>
|
|
<Bus ref="bus">
|
|
<Red />
|
|
</Bus>
|
|
<LinearCopy>{() => this.refs.bus}</LinearCopy>
|
|
</Surface>
|
|
);
|
|
}
|
|
}
|
|
const inst = create(<Example />);
|
|
const surface = inst.getInstance().refs.surface;
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(10, 10, 1, 1).data,
|
|
new Uint8Array([255, 0, 0, 255])
|
|
);
|
|
inst.unmount();
|
|
});
|
|
|
|
test("bus example 4", () => {
|
|
const shaders = Shaders.create({
|
|
red: {
|
|
frag: GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
void main() {
|
|
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
|
|
}`
|
|
},
|
|
helloTexture: {
|
|
frag: GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
uniform sampler2D t;
|
|
void main() {
|
|
gl_FragColor = texture2D(t, uv);
|
|
}`
|
|
}
|
|
});
|
|
class Example extends React.Component {
|
|
render() {
|
|
return (
|
|
<Surface
|
|
ref="surface"
|
|
width={20}
|
|
height={20}
|
|
webglContextAttributes={{ preserveDrawingBuffer: true }}
|
|
>
|
|
<Node
|
|
shader={shaders.helloTexture}
|
|
uniforms={{
|
|
t: () => this.refs.red
|
|
}}
|
|
>
|
|
<Node ref="red" width={1} height={1} shader={shaders.red} />
|
|
</Node>
|
|
</Surface>
|
|
);
|
|
}
|
|
}
|
|
const inst = create(<Example />);
|
|
const surface = inst.getInstance().refs.surface;
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(10, 10, 1, 1).data,
|
|
new Uint8Array([255, 0, 0, 255])
|
|
);
|
|
inst.unmount();
|
|
});
|
|
|
|
test("bus example 5", () => {
|
|
const shaders = Shaders.create({
|
|
red: {
|
|
frag: GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
void main() {
|
|
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
|
|
}`
|
|
},
|
|
helloTexture: {
|
|
frag: GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
uniform sampler2D t;
|
|
void main() {
|
|
gl_FragColor = texture2D(t, uv);
|
|
}`
|
|
}
|
|
});
|
|
class Red extends React.Component {
|
|
render() {
|
|
return <Node shader={shaders.red} />;
|
|
}
|
|
}
|
|
class Root extends React.Component {
|
|
render() {
|
|
return (
|
|
<Node
|
|
shader={shaders.helloTexture}
|
|
uniforms={{
|
|
t: () => this.refs.red
|
|
}}
|
|
>
|
|
<Bus ref="red">
|
|
<Red />
|
|
</Bus>
|
|
</Node>
|
|
);
|
|
}
|
|
}
|
|
const inst = create(
|
|
<Surface
|
|
width={20}
|
|
height={20}
|
|
visitor={new Visitor()}
|
|
webglContextAttributes={{ preserveDrawingBuffer: true }}
|
|
>
|
|
<Root />
|
|
</Surface>
|
|
);
|
|
const surface = inst.getInstance();
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(10, 10, 1, 1).data,
|
|
new Uint8Array([255, 0, 0, 255])
|
|
);
|
|
inst.unmount();
|
|
});
|
|
|
|
test("bus example 6", () => {
|
|
const shaders = Shaders.create({
|
|
red: {
|
|
frag: GLSL`
|
|
precision highp float;
|
|
void main() {
|
|
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
|
|
}
|
|
`
|
|
},
|
|
pink: {
|
|
frag: GLSL`
|
|
precision highp float;
|
|
void main() {
|
|
gl_FragColor = vec4(1.0, 0.0, 1.0, 1.0);
|
|
}
|
|
`
|
|
},
|
|
helloTexture: {
|
|
frag: GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
uniform sampler2D t;
|
|
void main() {
|
|
gl_FragColor = texture2D(t, uv);
|
|
}
|
|
`
|
|
}
|
|
});
|
|
class Red extends React.Component {
|
|
render() {
|
|
return <Node shader={shaders.red} />;
|
|
}
|
|
}
|
|
class Pink extends React.Component {
|
|
render() {
|
|
return <Node shader={shaders.pink} />;
|
|
}
|
|
}
|
|
class Root extends React.Component {
|
|
render() {
|
|
const { pink } = this.props;
|
|
return (
|
|
<Node
|
|
shader={shaders.helloTexture}
|
|
uniforms={{
|
|
t: () => (pink ? this.refs.pink : this.refs.red)
|
|
}}
|
|
>
|
|
<Bus ref="red">
|
|
<Red />
|
|
</Bus>
|
|
<Bus ref="pink">
|
|
<Pink />
|
|
</Bus>
|
|
</Node>
|
|
);
|
|
}
|
|
}
|
|
const inst = create(
|
|
<Surface
|
|
width={20}
|
|
height={20}
|
|
visitor={new Visitor()}
|
|
webglContextAttributes={{ preserveDrawingBuffer: true }}
|
|
>
|
|
<Root pink={false} />
|
|
</Surface>
|
|
);
|
|
const surface = inst.getInstance();
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(10, 10, 1, 1).data,
|
|
new Uint8Array([255, 0, 0, 255])
|
|
);
|
|
inst.update(
|
|
<Surface
|
|
width={20}
|
|
height={20}
|
|
webglContextAttributes={{ preserveDrawingBuffer: true }}
|
|
>
|
|
<Root pink={true} />
|
|
</Surface>
|
|
);
|
|
surface.flush();
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(10, 10, 1, 1).data,
|
|
new Uint8Array([255, 0, 255, 255])
|
|
);
|
|
inst.unmount();
|
|
});
|
|
|
|
test("bus: same texture used in multiple sampler2D is fine", () => {
|
|
const shaders = Shaders.create({
|
|
orangy: {
|
|
frag: GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
void main() {
|
|
gl_FragColor = vec4(0.5, 0.2, 0.1, 0.4);
|
|
}`
|
|
},
|
|
add: {
|
|
frag: GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
uniform sampler2D a, b;
|
|
void main() {
|
|
gl_FragColor = texture2D(a, uv) + texture2D(b, uv);
|
|
}
|
|
`
|
|
}
|
|
});
|
|
class Example extends React.Component {
|
|
render() {
|
|
return (
|
|
<Surface
|
|
ref="surface"
|
|
width={20}
|
|
height={20}
|
|
webglContextAttributes={{ preserveDrawingBuffer: true }}
|
|
>
|
|
<Bus ref="bus">
|
|
<Node shader={shaders.orangy} />
|
|
</Bus>
|
|
<Node
|
|
shader={shaders.add}
|
|
uniforms={{
|
|
a: () => this.refs.bus,
|
|
b: () => this.refs.bus
|
|
}}
|
|
/>
|
|
</Surface>
|
|
);
|
|
}
|
|
}
|
|
const inst = create(<Example />);
|
|
expectToBeCloseToColorArray(
|
|
inst.getInstance().refs.surface.capture(10, 10, 1, 1).data,
|
|
new Uint8Array([255, 102, 50, 204])
|
|
);
|
|
inst.unmount();
|
|
});
|
|
|
|
test("a surface can be captured and resized", () => {
|
|
const shaders = Shaders.create({
|
|
helloGL: {
|
|
frag: GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
void main() {
|
|
gl_FragColor = vec4(uv.x, uv.y, 0.0, 1.0);
|
|
}`
|
|
}
|
|
});
|
|
const render = (w, h) => (
|
|
<Surface width={w} height={h}>
|
|
<LinearCopy>
|
|
<Node shader={shaders.helloGL} width={2} height={2} />
|
|
</LinearCopy>
|
|
</Surface>
|
|
);
|
|
const inst = create(render(2, 2));
|
|
const surface = inst.getInstance();
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(0, 1, 1, 1).data,
|
|
new Uint8Array([64, 191, 0, 255])
|
|
);
|
|
inst.update(render(20, 20));
|
|
surface.flush();
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(12, 1, 2, 1).data,
|
|
new Uint8Array([159, 64, 0, 255, 172, 64, 0, 255])
|
|
);
|
|
inst.unmount();
|
|
});
|
|
|
|
test("a node can be captured and resized", () => {
|
|
const shaders = Shaders.create({
|
|
helloGL: {
|
|
frag: GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
void main() {
|
|
gl_FragColor = vec4(uv.x, uv.y, 0.0, 1.0);
|
|
}`
|
|
}
|
|
});
|
|
let node;
|
|
const render = (w, h) => (
|
|
<Surface width={20} height={20}>
|
|
<LinearCopy>
|
|
<Node
|
|
shader={shaders.helloGL}
|
|
ref={ref => (node = ref)}
|
|
width={w}
|
|
height={h}
|
|
/>
|
|
</LinearCopy>
|
|
</Surface>
|
|
);
|
|
const inst = create(render(2, 2));
|
|
const surface = inst.getInstance();
|
|
invariant(node, "node is defined");
|
|
expectToBeCloseToColorArray(
|
|
node.capture(0, 1, 1, 1).data,
|
|
new Uint8Array([64, 191, 0, 255])
|
|
);
|
|
inst.update(render(20, 20));
|
|
surface.flush();
|
|
expectToBeCloseToColorArray(
|
|
node.capture(12, 1, 1, 2).data,
|
|
new Uint8Array([159, 19, 0, 255, 159, 32, 0, 255])
|
|
);
|
|
inst.unmount();
|
|
});
|
|
|
|
test("Uniform children redraw=>el function", () => {
|
|
let surface, updatingTexture;
|
|
class UpdatingTexture extends React.Component {
|
|
props: {
|
|
redraw: Function
|
|
};
|
|
pixels = null;
|
|
setPixels = (pixels, w, h) => {
|
|
this.pixels = pixels;
|
|
this.refs.faketexture.width = w;
|
|
this.refs.faketexture.height = h;
|
|
this.props.redraw();
|
|
};
|
|
getPixels = () => this.pixels;
|
|
getRootRef = () => this.refs.faketexture;
|
|
render() {
|
|
return (
|
|
<faketexture
|
|
ref="faketexture"
|
|
width={0}
|
|
height={0}
|
|
getPixels={this.getPixels}
|
|
/>
|
|
);
|
|
}
|
|
}
|
|
class Example extends React.Component {
|
|
render() {
|
|
return (
|
|
<Surface
|
|
ref={ref => (surface = ref)}
|
|
width={4}
|
|
height={4}
|
|
webglContextAttributes={{ preserveDrawingBuffer: true }}
|
|
>
|
|
<NearestCopy>
|
|
<LinearCopy>
|
|
{redraw => (
|
|
<UpdatingTexture
|
|
ref={ref => (updatingTexture = ref)}
|
|
redraw={redraw}
|
|
/>
|
|
)}
|
|
</LinearCopy>
|
|
</NearestCopy>
|
|
</Surface>
|
|
);
|
|
}
|
|
}
|
|
const inst = create(<Example />);
|
|
invariant(surface, "surface is defined");
|
|
invariant(updatingTexture, "updatingTexture is defined");
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(1, 1, 1, 1).data,
|
|
new Uint8Array([0, 0, 0, 0])
|
|
);
|
|
inst.update(<Example />);
|
|
surface.flush();
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(1, 1, 1, 1).data,
|
|
new Uint8Array([0, 0, 0, 0])
|
|
);
|
|
updatingTexture.setPixels(red2x2, 2, 2);
|
|
surface.flush();
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(1, 1, 1, 1).data,
|
|
new Uint8Array([255, 0, 0, 255])
|
|
);
|
|
updatingTexture.setPixels(white3x3, 3, 3);
|
|
surface.flush();
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(1, 1, 1, 1).data,
|
|
new Uint8Array([255, 255, 255, 255])
|
|
);
|
|
updatingTexture.setPixels(yellow3x3, 3, 3);
|
|
surface.flush();
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(2, 2, 1, 1).data,
|
|
new Uint8Array([255, 255, 0, 255])
|
|
);
|
|
inst.unmount();
|
|
});
|
|
|
|
test("Bus redraw=>el function", () => {
|
|
let surface, updatingTexture;
|
|
class UpdatingTexture extends React.Component {
|
|
props: {
|
|
redraw: Function,
|
|
initialPixels: any,
|
|
initialWidth: number,
|
|
initialHeight: number
|
|
};
|
|
static defaultProps = {
|
|
initialWidth: 2,
|
|
initialHeight: 2
|
|
};
|
|
pixels = this.props.initialPixels;
|
|
setPixels = (pixels, w, h) => {
|
|
this.pixels = pixels;
|
|
this.refs.faketexture.width = w;
|
|
this.refs.faketexture.height = h;
|
|
this.props.redraw();
|
|
};
|
|
getPixels = () => this.pixels;
|
|
getRootRef = () => this.refs.faketexture;
|
|
render() {
|
|
return (
|
|
<faketexture
|
|
ref="faketexture"
|
|
width={this.props.initialWidth}
|
|
height={this.props.initialHeight}
|
|
getPixels={this.getPixels}
|
|
/>
|
|
);
|
|
}
|
|
}
|
|
class Example extends React.Component {
|
|
render() {
|
|
return (
|
|
<Surface
|
|
ref={ref => (surface = ref)}
|
|
width={4}
|
|
height={4}
|
|
visitor={new Visitor()}
|
|
webglContextAttributes={{ preserveDrawingBuffer: true }}
|
|
>
|
|
<Bus ref="bus">
|
|
{redraw => (
|
|
<UpdatingTexture
|
|
initialPixels={yellow3x3}
|
|
initialWidth={3}
|
|
initialHeight={3}
|
|
ref={ref => (updatingTexture = ref)}
|
|
redraw={redraw}
|
|
/>
|
|
)}
|
|
</Bus>
|
|
<NearestCopy>
|
|
<LinearCopy>{() => this.refs.bus}</LinearCopy>
|
|
</NearestCopy>
|
|
</Surface>
|
|
);
|
|
}
|
|
}
|
|
const inst = create(<Example />);
|
|
invariant(surface, "surface is defined");
|
|
invariant(updatingTexture, "updatingTexture is defined");
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(2, 2, 1, 1).data,
|
|
new Uint8Array([255, 255, 0, 255])
|
|
);
|
|
updatingTexture.setPixels(red2x2, 2, 2);
|
|
surface.flush();
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(2, 2, 1, 1).data,
|
|
new Uint8Array([255, 0, 0, 255])
|
|
);
|
|
inst.unmount();
|
|
});
|
|
|
|
test("many Surface updates don't result of many redraws", () => {
|
|
const shaders = Shaders.create({
|
|
justBlue: {
|
|
frag: GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
uniform float blue;
|
|
void main() {
|
|
gl_FragColor = vec4(0.0, 0.0, blue, 1.0);
|
|
}`
|
|
}
|
|
});
|
|
|
|
const visitor = new CountersVisitor();
|
|
const wrap = children => (
|
|
<Surface
|
|
visitor={visitor}
|
|
width={2}
|
|
height={2}
|
|
webglContextAttributes={{ preserveDrawingBuffer: true }}
|
|
>
|
|
{children}
|
|
</Surface>
|
|
);
|
|
const JustBlue = ({ blue }) => (
|
|
<Node shader={shaders.justBlue} uniforms={{ blue }} />
|
|
);
|
|
|
|
const inst = create(wrap(<JustBlue blue={0} />));
|
|
const surface = inst.getInstance();
|
|
const globalCounters = visitor.getCounters();
|
|
expect(globalCounters.onSurfaceDrawStart).toEqual(1);
|
|
inst.update(wrap(<JustBlue blue={0} />));
|
|
inst.update(wrap(<JustBlue blue={0} />));
|
|
inst.update(wrap(<JustBlue blue={1} />));
|
|
inst.update(wrap(<JustBlue blue={0.5} />));
|
|
inst.update(wrap(<JustBlue blue={0.5} />));
|
|
inst.update(wrap(<JustBlue blue={0} />));
|
|
inst.update(wrap(<JustBlue blue={0.8} />));
|
|
surface.flush();
|
|
expect(globalCounters.onSurfaceDrawStart).toEqual(2);
|
|
inst.update(wrap(<JustBlue blue={0.2} />));
|
|
inst.update(wrap(<JustBlue blue={1} />));
|
|
surface.flush();
|
|
expect(globalCounters.onSurfaceDrawStart).toEqual(3);
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(0, 0, 1, 1).data,
|
|
new Uint8Array([0, 0, 255, 255])
|
|
);
|
|
inst.unmount();
|
|
});
|
|
|
|
test("many Surface flush() don't result of extra redraws", () => {
|
|
const shaders = Shaders.create({
|
|
justBlue: {
|
|
frag: GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
uniform float blue;
|
|
void main() {
|
|
gl_FragColor = vec4(0.0, 0.0, blue, 1.0);
|
|
}`
|
|
}
|
|
});
|
|
const visitor = new CountersVisitor();
|
|
|
|
const wrap = children => (
|
|
<Surface
|
|
visitor={visitor}
|
|
width={2}
|
|
height={2}
|
|
webglContextAttributes={{ preserveDrawingBuffer: true }}
|
|
>
|
|
{children}
|
|
</Surface>
|
|
);
|
|
const JustBlue = ({ blue }) => (
|
|
<Node shader={shaders.justBlue} uniforms={{ blue }} />
|
|
);
|
|
|
|
const inst = create(wrap(<JustBlue blue={0} />));
|
|
const surface = inst.getInstance();
|
|
const surfaceCounters = visitor.getSurfaceCounters(surface);
|
|
expect(surfaceCounters.onSurfaceDrawStart).toEqual(1);
|
|
surface.flush();
|
|
surface.flush();
|
|
surface.flush();
|
|
expect(surfaceCounters.onSurfaceDrawStart).toEqual(1);
|
|
inst.update(wrap(<JustBlue blue={0} />));
|
|
surface.flush();
|
|
surface.flush();
|
|
surface.flush();
|
|
surface.flush();
|
|
surface.flush();
|
|
expect(surfaceCounters.onSurfaceDrawStart).toEqual(2);
|
|
inst.unmount();
|
|
});
|
|
|
|
test("GL Components that implement shouldComponentUpdate shortcut Surface redraws", () => {
|
|
const shaders = Shaders.create({
|
|
justBlue: {
|
|
frag: GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
uniform float blue;
|
|
void main() {
|
|
gl_FragColor = vec4(0.0, 0.0, blue, 1.0);
|
|
}`
|
|
}
|
|
});
|
|
|
|
let justBlueNode;
|
|
const visitor = new CountersVisitor();
|
|
Visitors.add(visitor);
|
|
|
|
const wrap = children => (
|
|
<Surface
|
|
width={2}
|
|
height={2}
|
|
webglContextAttributes={{ preserveDrawingBuffer: true }}
|
|
>
|
|
<LinearCopy>{children}</LinearCopy>
|
|
</Surface>
|
|
);
|
|
class JustBlue extends React.PureComponent {
|
|
render() {
|
|
const { blue } = this.props;
|
|
return (
|
|
<Node
|
|
ref={ref => {
|
|
justBlueNode = ref;
|
|
}}
|
|
shader={shaders.justBlue}
|
|
uniforms={{ blue }}
|
|
/>
|
|
);
|
|
}
|
|
}
|
|
const inst = create(wrap(<JustBlue blue={0} />));
|
|
const surface = inst.getInstance();
|
|
invariant(justBlueNode, "justBlueNode is defined");
|
|
const justBlueNodeCounters = visitor.getNodeCounters(justBlueNode);
|
|
expect(justBlueNodeCounters.onNodeDraw).toEqual(1);
|
|
inst.update(wrap(<JustBlue blue={0} />));
|
|
surface.flush();
|
|
inst.update(wrap(<JustBlue blue={0} />));
|
|
surface.flush();
|
|
inst.update(wrap(<JustBlue blue={0} />));
|
|
surface.flush();
|
|
expect(justBlueNodeCounters.onNodeDraw).toEqual(1);
|
|
inst.update(wrap(<JustBlue blue={1} />));
|
|
surface.flush();
|
|
expect(justBlueNodeCounters.onNodeDraw).toEqual(2);
|
|
inst.update(wrap(<JustBlue blue={1} />));
|
|
surface.flush();
|
|
expect(justBlueNodeCounters.onNodeDraw).toEqual(2);
|
|
inst.update(wrap(<JustBlue blue={0.4} />));
|
|
inst.update(wrap(<JustBlue blue={0.5} />));
|
|
surface.flush();
|
|
expect(justBlueNodeCounters.onNodeDraw).toEqual(3);
|
|
invariant(justBlueNode, "justBlueNode is defined");
|
|
justBlueNode.flush();
|
|
justBlueNode.flush();
|
|
expect(justBlueNodeCounters.onNodeDraw).toEqual(3);
|
|
Visitors.remove(visitor);
|
|
inst.unmount();
|
|
Visitors.remove(visitor);
|
|
});
|
|
|
|
test("nested GL Component update will re-draw the Surface", () => {
|
|
const shaders = Shaders.create({
|
|
justBlue: {
|
|
frag: GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
uniform float blue;
|
|
void main() {
|
|
gl_FragColor = vec4(0.0, 0.0, blue, 1.0);
|
|
}`
|
|
}
|
|
});
|
|
|
|
let justBlue, justBlueNode;
|
|
const visitor = new CountersVisitor();
|
|
|
|
class StatefulJustBlue extends React.Component {
|
|
state = { blue: 0 };
|
|
render() {
|
|
const { blue } = this.state;
|
|
return (
|
|
<Node
|
|
ref={ref => (justBlueNode = ref)}
|
|
shader={shaders.justBlue}
|
|
uniforms={{ blue }}
|
|
/>
|
|
);
|
|
}
|
|
}
|
|
const inst = create(
|
|
<Surface
|
|
visitor={visitor}
|
|
width={2}
|
|
height={2}
|
|
webglContextAttributes={{ preserveDrawingBuffer: true }}
|
|
>
|
|
<LinearCopy>
|
|
<LinearCopy>
|
|
<StatefulJustBlue ref={ref => (justBlue = ref)} />
|
|
</LinearCopy>
|
|
</LinearCopy>
|
|
</Surface>
|
|
);
|
|
|
|
const surface = inst.getInstance();
|
|
|
|
invariant(justBlue, "justBlue is defined");
|
|
invariant(justBlueNode, "justBlueNode is defined");
|
|
const surfaceCounters = visitor.getSurfaceCounters(surface);
|
|
const justBlueNodeCounters = visitor.getNodeCounters(justBlueNode);
|
|
expect(surfaceCounters.onSurfaceDrawStart).toEqual(1);
|
|
expect(justBlueNodeCounters.onNodeDraw).toEqual(1);
|
|
justBlue.setState({ blue: 0.1 });
|
|
justBlueNode.flush();
|
|
expect(surfaceCounters.onSurfaceDrawStart).toEqual(2);
|
|
expect(justBlueNodeCounters.onNodeDraw).toEqual(2);
|
|
justBlue.setState({ blue: 0.0 });
|
|
justBlue.setState({ blue: 0.1 });
|
|
justBlue.setState({ blue: 0.2 });
|
|
justBlueNode.flush();
|
|
justBlueNode.flush();
|
|
expect(surfaceCounters.onSurfaceDrawStart).toEqual(3);
|
|
expect(justBlueNodeCounters.onNodeDraw).toEqual(3);
|
|
surface.flush();
|
|
expect(surfaceCounters.onSurfaceDrawStart).toEqual(3);
|
|
expect(justBlueNodeCounters.onNodeDraw).toEqual(3);
|
|
justBlue.setState({ blue: 0.3 });
|
|
surface.flush();
|
|
expect(surfaceCounters.onSurfaceDrawStart).toEqual(4);
|
|
expect(justBlueNodeCounters.onNodeDraw).toEqual(4);
|
|
justBlue.setState({ blue: 1 });
|
|
justBlueNode.flush();
|
|
expect(surfaceCounters.onSurfaceDrawStart).toEqual(5);
|
|
expect(justBlueNodeCounters.onNodeDraw).toEqual(5);
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(0, 0, 1, 1).data,
|
|
new Uint8Array([0, 0, 255, 255])
|
|
);
|
|
inst.unmount();
|
|
});
|
|
|
|
test("Node `clear` and discard;", () => {
|
|
const shaders = Shaders.create({
|
|
paint: {
|
|
frag: GLSL`
|
|
precision highp float;
|
|
|
|
varying vec2 uv;
|
|
uniform bool drawing;
|
|
uniform vec4 color;
|
|
uniform vec2 center;
|
|
uniform float brushRadius;
|
|
|
|
void main() {
|
|
if (drawing) {
|
|
vec2 d = uv - center;
|
|
if (length(d) < brushRadius) {
|
|
gl_FragColor = color;
|
|
}
|
|
else {
|
|
discard;
|
|
}
|
|
}
|
|
else {
|
|
discard;
|
|
}
|
|
}`
|
|
}
|
|
});
|
|
class Paint extends React.Component {
|
|
state = {
|
|
drawing: false,
|
|
color: [0, 0, 0, 0],
|
|
center: [0, 0],
|
|
brushRadius: 0.2
|
|
};
|
|
render() {
|
|
return <Node shader={shaders.paint} clear={null} uniforms={this.state} />;
|
|
}
|
|
}
|
|
let paint: Paint;
|
|
const inst = create(
|
|
<Surface
|
|
width={10}
|
|
height={10}
|
|
webglContextAttributes={{ preserveDrawingBuffer: true }}
|
|
>
|
|
<NearestCopy>
|
|
<Paint width={100} height={100} ref={ref => (paint = ref)} />
|
|
</NearestCopy>
|
|
</Surface>
|
|
);
|
|
invariant(paint, "paint is defined");
|
|
const surface = inst.getInstance();
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(0, 0, 1, 1).data,
|
|
new Uint8Array([0, 0, 0, 0])
|
|
);
|
|
paint.setState({
|
|
drawing: true,
|
|
color: [1, 0, 0, 1],
|
|
center: [0, 0],
|
|
brushRadius: 0.6
|
|
});
|
|
surface.flush();
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(0, 0, 1, 1).data,
|
|
new Uint8Array([255, 0, 0, 255])
|
|
);
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(7, 7, 1, 1).data,
|
|
new Uint8Array([0, 0, 0, 0])
|
|
);
|
|
paint.setState({
|
|
drawing: true,
|
|
color: [0, 1, 0, 1],
|
|
center: [0.1, 0.1],
|
|
brushRadius: 0.2
|
|
});
|
|
surface.flush();
|
|
paint.setState({
|
|
drawing: false, // actually not drawing here ;)
|
|
color: [1, 0, 0, 1],
|
|
center: [0, 0],
|
|
brushRadius: 0.6
|
|
});
|
|
surface.flush();
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(0, 0, 1, 1).data,
|
|
new Uint8Array([0, 255, 0, 255])
|
|
);
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(3, 3, 1, 1).data,
|
|
new Uint8Array([255, 0, 0, 255])
|
|
);
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(7, 7, 1, 1).data,
|
|
new Uint8Array([0, 0, 0, 0])
|
|
);
|
|
inst.unmount();
|
|
});
|
|
|
|
test("Node `backbuffering`", () => {
|
|
const shaders = Shaders.create({
|
|
colorShift: {
|
|
frag: GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
uniform sampler2D t;
|
|
void main() {
|
|
vec4 c = texture2D(t, uv);
|
|
gl_FragColor = vec4(c.b, c.r, c.g, c.a); // shifting the rgb components
|
|
}`
|
|
}
|
|
});
|
|
const render = t => (
|
|
<Surface
|
|
width={10}
|
|
height={10}
|
|
webglContextAttributes={{ preserveDrawingBuffer: true }}
|
|
>
|
|
<LinearCopy>
|
|
<Node shader={shaders.colorShift} uniforms={{ t }} backbuffering />
|
|
</LinearCopy>
|
|
</Surface>
|
|
);
|
|
const inst = create(render(red2x2)); // init with red
|
|
const surface = inst.getInstance();
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(0, 0, 1, 1).data,
|
|
new Uint8Array([0, 255, 0, 255])
|
|
);
|
|
inst.update(render(red2x2));
|
|
surface.flush();
|
|
|
|
// since node was drawn once, there were a first shift.
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(0, 0, 1, 1).data,
|
|
new Uint8Array([0, 255, 0, 255])
|
|
);
|
|
inst.update(render(Uniform.Backbuffer));
|
|
surface.flush();
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(0, 0, 1, 1).data,
|
|
new Uint8Array([0, 0, 255, 255])
|
|
);
|
|
inst.update(render(Uniform.Backbuffer));
|
|
surface.flush();
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(0, 0, 1, 1).data,
|
|
new Uint8Array([255, 0, 0, 255])
|
|
);
|
|
inst.update(render(Uniform.Backbuffer));
|
|
inst.update(render(Uniform.Backbuffer));
|
|
surface.flush();
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(0, 0, 1, 1).data,
|
|
new Uint8Array([0, 255, 0, 255])
|
|
);
|
|
surface.glView.simulateContextLost();
|
|
inst.unmount();
|
|
});
|
|
|
|
test("Node `backbuffering` in `sync`", () => {
|
|
const shaders = Shaders.create({
|
|
colorShift: {
|
|
frag: GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
uniform sampler2D t;
|
|
void main() {
|
|
vec4 c = texture2D(t, uv);
|
|
gl_FragColor = vec4(c.b, c.r, c.g, c.a); // shifting the rgb components
|
|
}`
|
|
}
|
|
});
|
|
const render = t => (
|
|
<Surface
|
|
width={10}
|
|
height={10}
|
|
webglContextAttributes={{ preserveDrawingBuffer: true }}
|
|
>
|
|
<LinearCopy>
|
|
<LinearCopy>
|
|
<Node
|
|
shader={shaders.colorShift}
|
|
uniforms={{ t }}
|
|
backbuffering
|
|
sync
|
|
/>
|
|
</LinearCopy>
|
|
</LinearCopy>
|
|
</Surface>
|
|
);
|
|
const inst = create(render(red2x2)); // init with red
|
|
const surface = inst.getInstance();
|
|
// since node was drawn once, there were a first shift.
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(0, 0, 1, 1).data,
|
|
new Uint8Array([0, 255, 0, 255])
|
|
);
|
|
inst.update(render(Uniform.Backbuffer));
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(0, 0, 1, 1).data,
|
|
new Uint8Array([0, 0, 255, 255])
|
|
);
|
|
inst.update(render(Uniform.Backbuffer));
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(0, 0, 1, 1).data,
|
|
new Uint8Array([255, 0, 0, 255])
|
|
);
|
|
inst.update(render(Uniform.Backbuffer));
|
|
inst.update(render(Uniform.Backbuffer));
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(0, 0, 1, 1).data,
|
|
new Uint8Array([0, 0, 255, 255])
|
|
);
|
|
inst.unmount();
|
|
});
|
|
|
|
test("Node `backbuffering` with Uniform.backbufferFrom", () => {
|
|
const shaders = Shaders.create({
|
|
colorShift: {
|
|
frag: GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
uniform sampler2D t;
|
|
void main() {
|
|
vec4 c = texture2D(t, uv);
|
|
gl_FragColor = vec4(c.b, c.r, c.g, c.a); // shifting the rgb components
|
|
}`
|
|
},
|
|
darken: {
|
|
frag: GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
uniform sampler2D t;
|
|
uniform float m;
|
|
void main() {
|
|
gl_FragColor = m * texture2D(t, uv);
|
|
}`
|
|
}
|
|
});
|
|
class Darken extends React.Component {
|
|
render() {
|
|
const { children: t } = this.props;
|
|
return <Node shader={shaders.darken} uniforms={{ t, m: 0.8 }} />;
|
|
}
|
|
}
|
|
class Effect extends React.Component {
|
|
getMainBuffer = () => {
|
|
const { main } = this.refs;
|
|
return main ? Uniform.backbufferFrom(main.getNodeRef()) : null;
|
|
};
|
|
render() {
|
|
const { initWithImage } = this.props;
|
|
return (
|
|
<Surface
|
|
ref="surface"
|
|
width={10}
|
|
height={10}
|
|
webglContextAttributes={{ preserveDrawingBuffer: true }}
|
|
>
|
|
<NearestCopy>
|
|
<LinearCopy backbuffering ref="main">
|
|
<Darken>
|
|
<Node
|
|
shader={shaders.colorShift}
|
|
uniforms={{
|
|
t: !initWithImage ? this.getMainBuffer : initWithImage
|
|
}}
|
|
/>
|
|
</Darken>
|
|
</LinearCopy>
|
|
</NearestCopy>
|
|
</Surface>
|
|
);
|
|
}
|
|
}
|
|
|
|
const inst = create(<Effect initWithImage={red2x2} />);
|
|
const surface = inst.getInstance().refs.surface;
|
|
|
|
for (let i = 1; i <= 6; i++) {
|
|
const val = Math.round(255 * Math.pow(0.8, i)); // Darken effect will multiply the color by 0.8 each draw time
|
|
const expected = [0, 0, 0, val];
|
|
expected[i % 3] = val; // the colorShift will shift the r,g,b components with the color val
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(0, 0, 1, 1).data,
|
|
new Uint8Array(expected)
|
|
);
|
|
inst.update(<Effect />);
|
|
surface.flush();
|
|
}
|
|
|
|
surface.glView.simulateContextLost();
|
|
inst.unmount();
|
|
});
|
|
|
|
test("texture can be null", () => {
|
|
const shaders = Shaders.create({
|
|
helloTexture: {
|
|
frag: GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
uniform sampler2D t;
|
|
void main() {
|
|
gl_FragColor = texture2D(t, uv);
|
|
}`
|
|
}
|
|
});
|
|
const inst = create(
|
|
<Surface
|
|
width={64}
|
|
height={64}
|
|
webglContextAttributes={{ preserveDrawingBuffer: true }}
|
|
>
|
|
<Node shader={shaders.helloTexture} uniforms={{ t: null }} />
|
|
</Surface>
|
|
);
|
|
const surface = inst.getInstance();
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(0, 0, 1, 1).data,
|
|
new Uint8Array([0, 0, 0, 0])
|
|
);
|
|
inst.unmount();
|
|
});
|
|
|
|
test("array of textures", () => {
|
|
class MergeChannels extends React.Component {
|
|
render() {
|
|
const { red, green, blue } = this.props;
|
|
return (
|
|
<Node
|
|
shader={{
|
|
frag: GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
uniform sampler2D channels[3];
|
|
float monochrome (vec3 c) {
|
|
return 0.2125 * c.r + 0.7154 * c.g + 0.0721 * c.b;
|
|
}
|
|
void main() {
|
|
gl_FragColor = vec4(
|
|
monochrome(texture2D(channels[0], uv).rgb),
|
|
monochrome(texture2D(channels[1], uv).rgb),
|
|
monochrome(texture2D(channels[2], uv).rgb),
|
|
1.0
|
|
);
|
|
}
|
|
`
|
|
}}
|
|
uniforms={{
|
|
channels: [red, green, blue]
|
|
}}
|
|
/>
|
|
);
|
|
}
|
|
}
|
|
|
|
let bus;
|
|
const inst = create(
|
|
<Surface
|
|
width={1}
|
|
height={1}
|
|
webglContextAttributes={{ preserveDrawingBuffer: true }}
|
|
>
|
|
<Bus ref={ref => (bus = ref)}>
|
|
<faketexture width={3} height={3} getPixels={() => white3x3} />
|
|
</Bus>
|
|
<MergeChannels
|
|
red={() => bus}
|
|
green={
|
|
<LinearCopy>
|
|
<faketexture width={3} height={3} getPixels={() => white3x3} />
|
|
</LinearCopy>
|
|
}
|
|
blue={white3x3}
|
|
/>
|
|
</Surface>
|
|
);
|
|
const surface = inst.getInstance();
|
|
expectToBeCloseToColorArray(
|
|
surface.capture().data,
|
|
new Uint8Array([255, 255, 255, 255])
|
|
);
|
|
inst.unmount();
|
|
});
|
|
|
|
test("Node uniformsOptions texture interpolation", () => {
|
|
const shaders = Shaders.create({
|
|
helloTexture: {
|
|
frag: GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
uniform sampler2D t;
|
|
void main() {
|
|
gl_FragColor = texture2D(t, uv);
|
|
}`
|
|
}
|
|
});
|
|
function render(t, tOptions) {
|
|
return (
|
|
<Surface
|
|
width={500}
|
|
height={40}
|
|
webglContextAttributes={{ preserveDrawingBuffer: true }}
|
|
>
|
|
<Node
|
|
shader={shaders.helloTexture}
|
|
uniforms={{ t }}
|
|
uniformsOptions={tOptions ? { t: tOptions } : undefined}
|
|
sync
|
|
/>
|
|
</Surface>
|
|
);
|
|
}
|
|
const inst = create(render(null));
|
|
const surface = inst.getInstance();
|
|
const redToBlue = ndarray(new Uint8Array([255, 0, 0, 255, 0, 0, 255, 255]), [
|
|
2,
|
|
1,
|
|
4
|
|
]);
|
|
inst.update(render(redToBlue));
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(0, 2, 1, 1).data,
|
|
new Uint8Array([255, 0, 0, 255])
|
|
);
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(250, 7, 1, 1).data,
|
|
new Uint8Array([127, 0, 128, 255])
|
|
);
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(499, 5, 1, 1).data,
|
|
new Uint8Array([0, 0, 255, 255])
|
|
);
|
|
inst.update(render(redToBlue, { interpolation: "nearest" }));
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(200, 0, 1, 1).data,
|
|
new Uint8Array([255, 0, 0, 255])
|
|
);
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(300, 0, 1, 1).data,
|
|
new Uint8Array([0, 0, 255, 255])
|
|
);
|
|
inst.update(render(redToBlue, { interpolation: "linear" }));
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(0, 2, 1, 1).data,
|
|
new Uint8Array([255, 0, 0, 255])
|
|
);
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(250, 7, 1, 1).data,
|
|
new Uint8Array([127, 0, 128, 255])
|
|
);
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(499, 5, 1, 1).data,
|
|
new Uint8Array([0, 0, 255, 255])
|
|
);
|
|
inst.unmount();
|
|
});
|
|
|
|
test("can be extended with addTextureLoaderClass", async () => {
|
|
const shaders = Shaders.create({
|
|
helloTexture: {
|
|
frag: GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
uniform sampler2D t;
|
|
void main() {
|
|
gl_FragColor = texture2D(t, uv);
|
|
}`
|
|
}
|
|
});
|
|
|
|
const loader = createOneTextureLoader(
|
|
gl => createNDArrayTexture(gl, red2x2),
|
|
[2, 2]
|
|
);
|
|
globalRegistry.add(loader.Loader);
|
|
const inst = create(
|
|
<Surface
|
|
width={64}
|
|
height={64}
|
|
webglContextAttributes={{ preserveDrawingBuffer: true }}
|
|
>
|
|
<Node shader={shaders.helloTexture} uniforms={{ t: loader.textureId }} />
|
|
</Surface>
|
|
);
|
|
const surface = inst.getInstance();
|
|
surface.flush();
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(0, 0, 1, 1).data,
|
|
new Uint8Array([0, 0, 0, 0])
|
|
);
|
|
expect(loader.counters).toMatchSnapshot();
|
|
await loader.resolve();
|
|
surface.flush();
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(0, 0, 1, 1).data,
|
|
new Uint8Array([255, 0, 0, 255])
|
|
);
|
|
expect(loader.counters).toMatchSnapshot();
|
|
inst.unmount();
|
|
expect(loader.counters).toMatchSnapshot();
|
|
globalRegistry.remove(loader.Loader);
|
|
});
|
|
|
|
test("Surface `preload` prevent to draw anything", async () => {
|
|
const shaders = Shaders.create({
|
|
helloTexture: {
|
|
frag: GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
uniform sampler2D t;
|
|
void main() {
|
|
gl_FragColor = texture2D(t, uv);
|
|
}`
|
|
}
|
|
});
|
|
|
|
let onLoadCounter = 0;
|
|
const visitor = new CountersVisitor();
|
|
const loader = createOneTextureLoader(
|
|
gl => createNDArrayTexture(gl, red2x2),
|
|
[2, 2]
|
|
);
|
|
globalRegistry.add(loader.Loader);
|
|
const el = (
|
|
<Surface
|
|
width={64}
|
|
height={64}
|
|
visitor={visitor}
|
|
onLoad={() => {
|
|
++onLoadCounter;
|
|
}}
|
|
preload={[loader.textureId]}
|
|
webglContextAttributes={{ preserveDrawingBuffer: true }}
|
|
>
|
|
<Node shader={shaders.helloTexture} uniforms={{ t: loader.textureId }} />
|
|
</Surface>
|
|
);
|
|
const inst = create(el);
|
|
const surface = inst.getInstance();
|
|
const counters = visitor.getCounters();
|
|
surface.flush();
|
|
inst.update(el);
|
|
surface.flush();
|
|
inst.update(el);
|
|
surface.flush();
|
|
expect(counters.onSurfaceDrawEnd).toEqual(0);
|
|
expect(onLoadCounter).toEqual(0);
|
|
await loader.resolve();
|
|
surface.flush();
|
|
expect(onLoadCounter).toEqual(1);
|
|
expect(counters.onSurfaceDrawEnd).toEqual(1);
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(0, 0, 1, 1).data,
|
|
new Uint8Array([255, 0, 0, 255])
|
|
);
|
|
inst.unmount();
|
|
globalRegistry.remove(loader.Loader);
|
|
});
|
|
|
|
test("Uniform.textureSizeRatio allows to send the ratio of a texture in uniform", async () => {
|
|
const shaders = Shaders.create({
|
|
contain: {
|
|
frag: GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
uniform sampler2D t;
|
|
uniform float tRatio;
|
|
void main() {
|
|
vec2 p = uv * vec2(max(1.0, 1.0/tRatio), max(1.0, tRatio)); // "contain" the texture to preserve ratio (without center alignment)
|
|
gl_FragColor =
|
|
step(0.0, p.x) * step(p.x, 1.0) * // returns 1.0 if x is in [0,1] otherwise 0.0
|
|
step(0.0, p.y) * step(p.y, 1.0) * // returns 1.0 if y is in [0,1] otherwise 0.0
|
|
texture2D(t, p);
|
|
}`
|
|
}
|
|
});
|
|
const loader = createOneTextureLoader(
|
|
gl => createNDArrayTexture(gl, yellow3x2),
|
|
[3, 2]
|
|
);
|
|
globalRegistry.add(loader.Loader);
|
|
const el = (
|
|
<Surface
|
|
width={64}
|
|
height={64}
|
|
webglContextAttributes={{ preserveDrawingBuffer: true }}
|
|
>
|
|
<Node
|
|
shader={shaders.contain}
|
|
uniforms={{
|
|
t: loader.textureId,
|
|
tRatio: Uniform.textureSizeRatio(loader.textureId)
|
|
}}
|
|
/>
|
|
</Surface>
|
|
);
|
|
const inst = create(el);
|
|
const surface = inst.getInstance();
|
|
await loader.resolve();
|
|
surface.flush();
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(0, 0, 1, 1).data,
|
|
new Uint8Array([255, 255, 0, 255])
|
|
);
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(0, 32, 1, 1).data,
|
|
new Uint8Array([255, 255, 0, 255])
|
|
);
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(0, 63, 1, 1).data,
|
|
new Uint8Array([0, 0, 0, 0])
|
|
);
|
|
inst.unmount();
|
|
globalRegistry.remove(loader.Loader);
|
|
});
|
|
|
|
test("Surface `preload` that fails will trigger onLoadError", async () => {
|
|
const shaders = Shaders.create({
|
|
helloTexture: {
|
|
frag: GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
uniform sampler2D t;
|
|
void main() {
|
|
gl_FragColor = texture2D(t, uv);
|
|
}`
|
|
}
|
|
});
|
|
|
|
let onLoadCounter = 0;
|
|
let onLoadErrorCounter = 0;
|
|
const loader = createOneTextureLoader(
|
|
gl => createNDArrayTexture(gl, red2x2),
|
|
[2, 2]
|
|
);
|
|
globalRegistry.add(loader.Loader);
|
|
const el = (
|
|
<Surface
|
|
width={64}
|
|
height={64}
|
|
onLoad={() => {
|
|
++onLoadCounter;
|
|
}}
|
|
onLoadError={() => {
|
|
++onLoadErrorCounter;
|
|
}}
|
|
preload={[loader.textureId]}
|
|
webglContextAttributes={{ preserveDrawingBuffer: true }}
|
|
>
|
|
<Node shader={shaders.helloTexture} uniforms={{ t: loader.textureId }} />
|
|
</Surface>
|
|
);
|
|
const inst = create(el);
|
|
const surface = inst.getInstance();
|
|
await loader.reject(new Error("simulate texture fail"));
|
|
expect(onLoadCounter).toEqual(0);
|
|
expect(onLoadErrorCounter).toEqual(1);
|
|
globalRegistry.remove(loader.Loader);
|
|
inst.unmount();
|
|
globalRegistry.remove(loader.Loader);
|
|
globalRegistry.remove(loader.Loader);
|
|
});
|
|
|
|
test("renders a shader inline in the Node", () => {
|
|
class ColorSurface extends React.Component {
|
|
render() {
|
|
const { color } = this.props;
|
|
return (
|
|
<Surface
|
|
ref="surface"
|
|
width={1}
|
|
height={1}
|
|
webglContextAttributes={{ preserveDrawingBuffer: true }}
|
|
>
|
|
<Node
|
|
shader={{
|
|
frag: GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
uniform vec4 color;
|
|
void main() { gl_FragColor = color; }
|
|
`
|
|
}}
|
|
uniforms={{ color }}
|
|
/>
|
|
</Surface>
|
|
);
|
|
}
|
|
}
|
|
|
|
const inst = create(<ColorSurface color={[1, 0, 0, 1]} />);
|
|
const surface = inst.getInstance().refs.surface;
|
|
expectToBeCloseToColorArray(
|
|
surface.capture().data,
|
|
new Uint8Array([255, 0, 0, 255])
|
|
);
|
|
inst.update(<ColorSurface color={[0, 1, 0, 1]} />);
|
|
surface.flush();
|
|
expectToBeCloseToColorArray(
|
|
surface.capture().data,
|
|
new Uint8Array([0, 255, 0, 255])
|
|
);
|
|
inst.update(<ColorSurface color={[0.5, 0, 1, 1]} />);
|
|
surface.flush();
|
|
expectToBeCloseToColorArray(
|
|
surface.capture().data,
|
|
new Uint8Array([128, 0, 255, 255])
|
|
);
|
|
surface.glView.simulateContextLost();
|
|
inst.unmount();
|
|
});
|
|
|
|
test("testing connectSize() feature", () => {
|
|
const shaders = Shaders.create({
|
|
Useless: {
|
|
frag: GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
uniform vec2 size;
|
|
void main() {
|
|
gl_FragColor = vec4(size.x / 50.0, size.y / 50.0, 0.0, 1.0);
|
|
}`
|
|
}
|
|
});
|
|
|
|
class Useless extends React.Component {
|
|
render() {
|
|
const { width, height } = this.props;
|
|
return (
|
|
<Node shader={shaders.Useless} uniforms={{ size: [width, height] }} />
|
|
);
|
|
}
|
|
}
|
|
|
|
const ConnectedUseless = connectSize(Useless);
|
|
|
|
const inst = create(
|
|
<Surface
|
|
width={30}
|
|
height={20}
|
|
webglContextAttributes={{ preserveDrawingBuffer: true }}
|
|
>
|
|
<ConnectedUseless />
|
|
</Surface>
|
|
);
|
|
const surface = inst.getInstance();
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(1, 1, 1, 1).data,
|
|
new Uint8Array([153, 102, 0, 255])
|
|
);
|
|
inst.update(
|
|
<Surface
|
|
width={30}
|
|
height={20}
|
|
webglContextAttributes={{ preserveDrawingBuffer: true }}
|
|
>
|
|
<ConnectedUseless width={3} height={2} />
|
|
</Surface>
|
|
);
|
|
surface.flush();
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(1, 1, 1, 1).data,
|
|
new Uint8Array([15, 10, 0, 255])
|
|
);
|
|
inst.update(
|
|
<Surface
|
|
width={600}
|
|
height={400}
|
|
webglContextAttributes={{ preserveDrawingBuffer: true }}
|
|
>
|
|
<ConnectedUseless width={30} />
|
|
</Surface>
|
|
);
|
|
surface.flush();
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(1, 1, 1, 1).data,
|
|
new Uint8Array([153, 255, 0, 255])
|
|
);
|
|
inst.unmount();
|
|
});
|
|
|
|
test("handle context lost nicely", () => {
|
|
let surface;
|
|
let contextLost = 0,
|
|
contextRestored = 0;
|
|
class Example extends React.Component {
|
|
render() {
|
|
return (
|
|
<Surface
|
|
ref={ref => (surface = ref)}
|
|
width={20}
|
|
height={20}
|
|
onContextLost={() => {
|
|
++contextLost;
|
|
}}
|
|
onContextRestored={() => {
|
|
++contextRestored;
|
|
}}
|
|
webglContextAttributes={{ preserveDrawingBuffer: true }}
|
|
>
|
|
<NearestCopy>
|
|
<faketexture width={2} height={2} getPixels={() => red2x2} />
|
|
</NearestCopy>
|
|
</Surface>
|
|
);
|
|
}
|
|
}
|
|
const inst = create(<Example />);
|
|
invariant(surface, "surface is defined");
|
|
surface.glView.simulateContextLost();
|
|
expect(contextLost).toEqual(1);
|
|
inst.update(<Example />);
|
|
surface.glView.simulateContextRestored();
|
|
expect(contextRestored).toEqual(1);
|
|
surface.redraw();
|
|
surface.flush();
|
|
surface.glView.simulateContextLost();
|
|
inst.update(<Example />);
|
|
inst.update(<Example />);
|
|
inst.update(<Example />);
|
|
inst.update(<Example />);
|
|
inst.update(<Example />);
|
|
surface.glView.simulateContextRestored();
|
|
surface.glView.simulateContextLost();
|
|
surface.glView.simulateContextRestored();
|
|
expect(contextLost).toEqual(3);
|
|
expect(contextRestored).toEqual(3);
|
|
surface.flush();
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(1, 1, 1, 1).data,
|
|
new Uint8Array([255, 0, 0, 255])
|
|
);
|
|
inst.unmount();
|
|
});
|
|
|
|
test("Bus#uniform and Bus#index", () => {
|
|
const shaders = Shaders.create({
|
|
clr: {
|
|
frag: GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
uniform vec3 c;
|
|
void main() {
|
|
gl_FragColor = vec4(c, 1.0);
|
|
}
|
|
`
|
|
},
|
|
helloTexture3: {
|
|
frag: GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
uniform sampler2D t;
|
|
uniform sampler2D t2[2];
|
|
void main() {
|
|
gl_FragColor =
|
|
0.5 * texture2D(t, uv) +
|
|
0.3 * texture2D(t2[0], uv)+
|
|
0.2 * texture2D(t2[1], uv);
|
|
}`
|
|
}
|
|
});
|
|
|
|
class WeirdSwapping extends React.Component {
|
|
render() {
|
|
const { i } = this.props;
|
|
return (
|
|
<Node shader={shaders.helloTexture3}>
|
|
<Bus uniform={i === 2 ? "t2" : "t"} index={i === 2 ? 1 : 0}>
|
|
<Node shader={shaders.clr} uniforms={{ c: [1, 0, 0] }} />
|
|
</Bus>
|
|
<Bus uniform="t2" index={i === 1 ? 1 : 0}>
|
|
<Node shader={shaders.clr} uniforms={{ c: [0, 1, 0] }} />
|
|
</Bus>
|
|
<Bus
|
|
uniform={i === 2 ? "t" : "t2"}
|
|
index={i === 2 ? 0 : i === 1 ? 0 : 1}
|
|
>
|
|
<Node shader={shaders.clr} uniforms={{ c: [0, 0, 1] }} />
|
|
</Bus>
|
|
</Node>
|
|
);
|
|
}
|
|
}
|
|
|
|
const wrap = children => (
|
|
<Surface
|
|
width={4}
|
|
height={4}
|
|
webglContextAttributes={{ preserveDrawingBuffer: true }}
|
|
>
|
|
{children}
|
|
</Surface>
|
|
);
|
|
|
|
const inst = create(wrap(<WeirdSwapping i={0} />));
|
|
const surface = inst.getInstance();
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(2, 3, 1, 1).data,
|
|
new Uint8Array([128, 76, 51, 255])
|
|
);
|
|
inst.update(wrap(<WeirdSwapping i={1} />));
|
|
surface.flush();
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(2, 3, 1, 1).data,
|
|
new Uint8Array([128, 51, 76, 255])
|
|
);
|
|
inst.update(wrap(<WeirdSwapping i={2} />));
|
|
surface.flush();
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(2, 3, 1, 1).data,
|
|
new Uint8Array([51, 76, 128, 255])
|
|
);
|
|
inst.unmount();
|
|
});
|
|
|
|
// This test is mainly a placeholder for test coverage^^ for everything related to console.warn/error
|
|
test("VisitorLogger + bunch of funky extreme tests", () => {
|
|
const shaders = Shaders.create({
|
|
justBlue: {
|
|
frag: GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
uniform vec2 rg;
|
|
uniform float blue;
|
|
void main() {
|
|
gl_FragColor = vec4(rg.x, rg.y, blue, 1.0);
|
|
}`
|
|
},
|
|
helloTexture: {
|
|
frag: GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
uniform sampler2D t;
|
|
void main() {
|
|
gl_FragColor = texture2D(t, uv);
|
|
}`
|
|
},
|
|
helloTexture2: {
|
|
frag: GLSL`
|
|
precision highp float;
|
|
varying vec2 uv;
|
|
uniform sampler2D t2[2];
|
|
void main() {
|
|
gl_FragColor = 0.5*(texture2D(t2[0], uv)+texture2D(t2[1], uv));
|
|
}`
|
|
}
|
|
});
|
|
|
|
expect(Shaders.getName(shaders.justBlue)).toBeDefined();
|
|
expect(Shaders.getShortName(shaders.helloTexture)).toBeDefined();
|
|
expect(Shaders.get(shaders.helloTexture2)).toBeDefined();
|
|
|
|
const oldConsole = console;
|
|
let groupEnd = 0,
|
|
log = 0,
|
|
warn = 0,
|
|
error = 0,
|
|
group = 0,
|
|
groupCollapsed = 0;
|
|
|
|
console = {
|
|
...oldConsole,
|
|
log: () => log++,
|
|
warn: () => warn++,
|
|
error: e => error++,
|
|
group: () => group++,
|
|
groupCollapsed: () => group++,
|
|
groupEnd: () => groupEnd++
|
|
};
|
|
|
|
let justBlueNode;
|
|
const visitor = new VisitorLogger();
|
|
|
|
const wrap = children => (
|
|
<Surface visitor={visitor} width={2} height={2}>
|
|
<LinearCopy>{children}</LinearCopy>
|
|
</Surface>
|
|
);
|
|
|
|
class JustBlue extends React.PureComponent {
|
|
render() {
|
|
const { blue } = this.props;
|
|
return (
|
|
<Node
|
|
ref={ref => {
|
|
justBlueNode = ref;
|
|
}}
|
|
shader={shaders.justBlue}
|
|
uniforms={{ blue, rg: [0, 0] }}
|
|
/>
|
|
);
|
|
}
|
|
}
|
|
class BadNode extends React.Component {
|
|
render() {
|
|
const { blue } = this.props;
|
|
return (
|
|
<Node
|
|
ref={ref => {
|
|
if (ref) {
|
|
ref._draw = () => {
|
|
throw new Error("_draw crash simulation");
|
|
};
|
|
}
|
|
}}
|
|
shader={shaders.justBlue}
|
|
uniforms={{ blue, rg: [0, 0] }}
|
|
/>
|
|
);
|
|
}
|
|
}
|
|
const MissingOrInvalidUniforms = () => (
|
|
<Node
|
|
ref={ref => {
|
|
justBlueNode = ref;
|
|
}}
|
|
shader={shaders.justBlue}
|
|
uniforms={{ nope: [1, 2] }}
|
|
/>
|
|
);
|
|
|
|
class TreeWithZombiesDontBreak extends React.Component {
|
|
render() {
|
|
const { blue } = this.props;
|
|
return (
|
|
<LinearCopy>
|
|
<Node shader={shaders.justBlue} uniforms={{ blue, rg: [0, 0] }}>
|
|
<BadNode />
|
|
<Node
|
|
shader={shaders.justBlue}
|
|
uniforms={{ blue, rg: [0, 0] }}
|
|
ref={ref => {
|
|
if (ref) {
|
|
ref.redraw();
|
|
ref.flush();
|
|
}
|
|
}}
|
|
/>
|
|
</Node>
|
|
</LinearCopy>
|
|
);
|
|
}
|
|
}
|
|
|
|
class EmptyBusUsedByANode extends React.Component {
|
|
render() {
|
|
return (
|
|
<Node
|
|
shader={shaders.helloTexture}
|
|
uniforms={{ t: () => this.refs.bus }}
|
|
>
|
|
<Bus ref="bus" />
|
|
</Node>
|
|
);
|
|
}
|
|
}
|
|
|
|
// oh btw, rendering nothing should be ok. not huge break.
|
|
let inst = create(<Surface visitor={visitor} width={2} height={2} />);
|
|
let surface = inst.getInstance();
|
|
inst.update(
|
|
<Surface visitor={visitor} width={2} height={2}>
|
|
<div>
|
|
<span>or like this should just be ignored..</span>
|
|
</div>
|
|
</Surface>
|
|
);
|
|
surface.flush();
|
|
surface.flush();
|
|
surface.redraw();
|
|
surface.flush();
|
|
|
|
// useless Bus
|
|
inst.update(
|
|
<Surface visitor={visitor} width={2} height={2}>
|
|
<Bus />
|
|
</Surface>
|
|
);
|
|
surface.redraw();
|
|
surface.flush();
|
|
|
|
// empty Bus
|
|
inst.update(
|
|
<Surface visitor={visitor} width={2} height={2}>
|
|
<EmptyBusUsedByANode />
|
|
</Surface>
|
|
);
|
|
surface.redraw();
|
|
surface.flush();
|
|
|
|
inst.update(wrap(<JustBlue blue={0} />));
|
|
inst.update(wrap(<JustBlue blue={0} />));
|
|
surface.flush();
|
|
inst.update(wrap(<JustBlue blue={0} />));
|
|
surface.flush();
|
|
inst.update(wrap(<JustBlue blue={0} />));
|
|
surface.flush();
|
|
inst.update(wrap(<JustBlue blue={1} />));
|
|
surface.flush();
|
|
inst.update(wrap(<JustBlue blue={1} />));
|
|
surface.flush();
|
|
inst.update(wrap(<JustBlue blue={0.4} />));
|
|
inst.update(wrap(<JustBlue blue={0.5} />));
|
|
surface.flush();
|
|
invariant(justBlueNode, "justBlueNode is defined");
|
|
expect(justBlueNode.getGLName()).toBeDefined();
|
|
expect(justBlueNode.getGLShortName()).toBeDefined();
|
|
justBlueNode.flush();
|
|
justBlueNode.flush();
|
|
justBlueNode.redraw();
|
|
justBlueNode.flush();
|
|
inst.update(
|
|
wrap(
|
|
<NearestCopy>
|
|
<JustBlue blue={0} />
|
|
</NearestCopy>
|
|
)
|
|
);
|
|
surface.flush();
|
|
inst.update(wrap(<TreeWithZombiesDontBreak />));
|
|
surface.flush();
|
|
inst.update(
|
|
wrap(
|
|
<Node
|
|
shader={shaders.helloTexture2}
|
|
uniforms={{ t2: [white3x3, red2x2] }}
|
|
/>
|
|
)
|
|
);
|
|
surface.flush();
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(1, 1).data,
|
|
new Uint8Array([255, 128, 128, 255])
|
|
);
|
|
inst.update(
|
|
wrap(<Node shader={shaders.helloTexture} uniforms={{ t: red2x2 }} />)
|
|
);
|
|
surface.flush();
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(1, 1).data,
|
|
new Uint8Array([255, 0, 0, 255])
|
|
);
|
|
inst.update(wrap(<TreeWithZombiesDontBreak />));
|
|
surface.flush();
|
|
expect(error).toEqual(1);
|
|
inst.update(wrap(<BadNode />));
|
|
surface.flush();
|
|
expect(error).toEqual(2);
|
|
warn = 0;
|
|
inst.update(
|
|
wrap(<Node shader={shaders.helloTexture2} uniforms={{ t2: [white3x3] }} />)
|
|
);
|
|
surface.flush();
|
|
expect(warn).toBeGreaterThan(0);
|
|
warn = 0;
|
|
inst.update(
|
|
wrap(<Node shader={shaders.helloTexture2} uniforms={{ t2: white3x3 }} />)
|
|
);
|
|
surface.flush();
|
|
expect(warn).toBeGreaterThan(0);
|
|
surface.flush();
|
|
warn = 0;
|
|
inst.update(
|
|
wrap(<Node shader={shaders.helloTexture} uniforms={{ t: () => {} }} />)
|
|
);
|
|
surface.flush();
|
|
expect(warn).toBeGreaterThan(0);
|
|
warn = 0;
|
|
inst.update(
|
|
wrap(<Node shader={shaders.helloTexture} uniforms={{ t: { nope: 1 } }} />)
|
|
);
|
|
surface.flush();
|
|
expect(warn).toBeGreaterThan(0);
|
|
warn = 0;
|
|
inst.update(
|
|
wrap(
|
|
<Node
|
|
shader={shaders.helloTexture}
|
|
uniformsOptions={{ t: { interpolation: "nope" } }}
|
|
uniforms={{ t: <JustBlue blue={1} /> }}
|
|
/>
|
|
)
|
|
);
|
|
surface.flush();
|
|
expect(warn).toBeGreaterThan(0);
|
|
warn = 0;
|
|
inst.update(
|
|
wrap(
|
|
<Node
|
|
shader={shaders.helloTexture}
|
|
uniformsOptions={{ t: { wrap: "nope" } }}
|
|
uniforms={{ t: <JustBlue blue={1} /> }}
|
|
/>
|
|
)
|
|
);
|
|
surface.flush();
|
|
expect(warn).toBeGreaterThan(0);
|
|
warn = 0;
|
|
inst.update(
|
|
wrap(
|
|
<Node
|
|
shader={shaders.helloTexture}
|
|
uniformsOptions={{ t: { wrap: ["nope", "nope"] } }}
|
|
uniforms={{ t: <JustBlue blue={1} /> }}
|
|
/>
|
|
)
|
|
);
|
|
surface.flush();
|
|
expect(warn).toBeGreaterThan(0);
|
|
warn = 0;
|
|
inst.update(
|
|
wrap(
|
|
<Node
|
|
blendFunc="nope"
|
|
shader={shaders.helloTexture}
|
|
uniforms={{ t: <JustBlue blue={1} /> }}
|
|
/>
|
|
)
|
|
);
|
|
surface.flush();
|
|
expect(warn).toBeGreaterThan(0);
|
|
warn = 0;
|
|
inst.update(
|
|
wrap(
|
|
<Node
|
|
shader={shaders.helloTexture}
|
|
uniforms={{ t: Uniform.Backbuffer }}
|
|
/>
|
|
)
|
|
);
|
|
surface.flush();
|
|
expect(warn).toBeGreaterThan(0);
|
|
inst.update(wrap(<MissingOrInvalidUniforms />));
|
|
surface.flush();
|
|
inst.update(wrap(null));
|
|
surface.flush();
|
|
inst.update(wrap(<JustBlue blue={0.5} />));
|
|
surface.flush();
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(1, 1).data,
|
|
new Uint8Array([0, 0, 128, 255])
|
|
);
|
|
class Ex extends React.Component {
|
|
render() {
|
|
return (
|
|
<Surface ref="surface" visitor={visitor} width={2} height={2}>
|
|
<Bus ref="bus">
|
|
<JustBlue blue={0.2} />
|
|
</Bus>
|
|
<LinearCopy>{() => this.refs.bus}</LinearCopy>
|
|
</Surface>
|
|
);
|
|
}
|
|
}
|
|
inst.update(<Ex />);
|
|
expect(inst.getInstance().refs.bus.getGLName()).toBeDefined();
|
|
expect(inst.getInstance().refs.bus.getGLShortName()).toBeDefined();
|
|
inst.getInstance().refs.surface.redraw();
|
|
inst.getInstance().refs.surface.flush();
|
|
inst.unmount();
|
|
inst = create(
|
|
<Surface
|
|
visitor={visitor}
|
|
width={2}
|
|
height={2}
|
|
preload={[null, { invalid: true }]}
|
|
/>
|
|
);
|
|
surface = inst.getInstance();
|
|
expect(surface.getGLName()).toBeDefined();
|
|
expect(surface.getGLShortName()).toBeDefined();
|
|
surface.captureAsBlob(); // should not break. but do nothing (in gl-react-headless)
|
|
surface.captureAsDataURL(); // should not break. but do nothing (in gl-react-headless)
|
|
surface.rebootForDebug(); // should not break.
|
|
expect(surface.glIsAvailable()).toEqual(true);
|
|
inst.unmount();
|
|
Visitors.remove(visitor);
|
|
expect(group).toEqual(groupEnd);
|
|
console = oldConsole;
|
|
});
|