mirror of
https://github.com/gre/gl-react.git
synced 2026-01-25 16:43:36 +00:00
2757 lines
65 KiB
JavaScript
Executable File
2757 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,
|
|
TextureLoader,
|
|
TextureLoaders,
|
|
Uniform,
|
|
Bus,
|
|
VisitorLogger,
|
|
connectSize
|
|
} from "gl-react";
|
|
import { Surface } from "gl-react-headless";
|
|
import loseGL from "gl-react-headless/lib/loseGL";
|
|
import type { DisposablePromise } from "gl-react/lib/helpers/disposable";
|
|
import React from "react";
|
|
import renderer from "react-test-renderer";
|
|
import baboon from "baboon-image";
|
|
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: baboon });
|
|
helloTexture.flush();
|
|
expectToBeCloseToColorArray(
|
|
surface.capture(0, 0, 1, 1).data,
|
|
new Uint8Array([126, 153, 153, 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>
|
|
);
|
|
expect(
|
|
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 />);
|
|
expect(
|
|
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 />);
|
|
expect(
|
|
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");
|
|
expect(node.capture(0, 1, 1, 1).data, new Uint8Array([64, 191, 0, 255]));
|
|
inst.update(render(20, 20));
|
|
surface.flush();
|
|
expect(
|
|
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]
|
|
);
|
|
TextureLoaders.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();
|
|
TextureLoaders.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]
|
|
);
|
|
TextureLoaders.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();
|
|
TextureLoaders.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]
|
|
);
|
|
TextureLoaders.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();
|
|
TextureLoaders.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]
|
|
);
|
|
TextureLoaders.add(loader.Loader);
|
|
const el = (
|
|
<Surface
|
|
width={64}
|
|
height={64}
|
|
preload={[loader.textureId, Symbol("wrong_preload"), null]}
|
|
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);
|
|
TextureLoaders.remove(loader.Loader);
|
|
inst.unmount();
|
|
TextureLoaders.remove(loader.Loader);
|
|
TextureLoaders.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;
|
|
});
|