Android shader compilation failure [Android 8, Android 9]
Shamash2014 opened this issue · 7 comments
Bug reports
[Sat Sep 19 2020 08:41:43.614] ERROR [GLError: gl-shader: Error compiling shader:
Compile failed.
ERROR: 0:1: '' : Invalid directive 14
ERROR: 1 compilation errors. No code generated.
]
Also Invalid directive
changes from 10 to 14
Example code:
//@flow
import React, { PureComponent, Component } from "react";
import { Shaders, Node, GLSL, Bus } from "gl-react";
import { Surface } from "../../gl-react-implementation";
import marioPNG from "../../images/pixeleditor-mario.png";
import respondToTouchPosition from "../../HOC/respondToTouchPosition";
type Vec2 = [number, number];
const shaders = Shaders.create({
paint: {
frag: GLSL`
precision highp float;
varying vec2 uv;
uniform vec4 color;
uniform vec2 size, mouse;
uniform float brushRadius;
uniform bool drawing;
void main() {
vec2 p = floor(uv * size) / size;
if (drawing) {
vec2 d = uv - mouse;
if (length(d) < brushRadius) {
gl_FragColor = color;
}
else {
discard;
}
}
else {
discard;
}
}`
},
initTexture: {
frag: GLSL`
precision highp float;
varying vec2 uv;
uniform sampler2D t;
void main(){
gl_FragColor=texture2D(t,uv);
}`
},
pixelEditor: {
frag: GLSL`
precision highp float;
varying vec2 uv;
uniform vec4 color;
uniform vec2 size, mouse, gridBorder;
uniform float brushRadius;
uniform sampler2D t;
void main() {
vec2 p = floor(uv * size) / size;
vec2 remain = mod(uv * size, vec2(1.0));
float m =
step(remain.x, 1.0 - gridBorder.x) *
step(remain.y, 1.0 - gridBorder.y);
float inBrushSize =
step(length(p + (0.5 / size) - mouse), brushRadius);
vec4 c = mix(texture2D(t, uv), color, 0.6 * inBrushSize);
gl_FragColor = vec4(
m * c.rgb,
mix(1.0, c.a, m));
}`
}
});
class Paint extends PureComponent {
state = {
initialized: false
};
onDraw = () => {
if (!this.state.initialized) {
this.setState({ initialized: true });
}
};
render() {
const { initialTexture, onPaintNodeRef, ...rest } = this.props;
const { initialized } = this.state;
return (
<Node
ref={onPaintNodeRef}
sync={!initialized}
shader={!initialized ? shaders.initTexture : shaders.paint}
width={rest.size[0]}
height={rest.size[1]}
uniforms={!initialized ? { t: initialTexture } : rest}
clear={null}
onDraw={this.onDraw}
/>
);
}
}
class PixelEditor extends PureComponent {
render() {
const { gridBorder, ...rest } = this.props;
const { size, brushRadius, mouse, color } = rest;
return (
<Node
shader={shaders.pixelEditor}
uniformsOptions={{
t: { interpolation: "nearest" }
}}
uniforms={{
size,
gridBorder,
brushRadius,
mouse,
color
}}
>
<Bus uniform="t">
<Paint {...rest} />
</Bus>
</Node>
);
}
}
const size = [16, 16];
const gridBorder = [1 / 8, 1 / 8];
const tools = {
"brush-1": { brushRadius: 0.55 / 16 },
"brush-2": { brushRadius: 1.1 / 16 },
"brush-4": { brushRadius: 2.2 / 16 },
rubber: { brushRadius: 4 / 16, forceColor: [0, 0, 0, 0] },
"color-picker": { colorPick: true }
};
const Example = respondToTouchPosition(
class extends Component {
render() {
const { color, toolKey, touching, touchPosition, width } = this.props;
const tool = tools[toolKey];
return (
<Surface
style={{ width, height: width }}
preload={[marioPNG]}
webglContextAttributes={{ preserveDrawingBuffer: true }}
>
<PixelEditor
gridBorder={gridBorder}
initialTexture={marioPNG}
drawing={touching}
color={tool.forceColor || color}
mouse={[touchPosition.x, touchPosition.y]}
brushRadius={tool.brushRadius || 0}
size={size}
onPaintNodeRef={this.onPaintNodeRef}
/>
</Surface>
);
}
onColorChange = rgb => {
this.props.setToolState({ color: rgb.concat([1]) });
};
paintNode: Node;
onPaintNodeRef = (ref: Node) => {
this.paintNode = ref;
};
colorPick = ([x, y]: Vec2) => {
x = Math.floor(x * size[0]);
y = Math.floor(y * size[1]);
const ndarray = this.paintNode.capture(x, y, 1, 1);
this.props.setToolState({
color: Array.from(ndarray.data).map(n => n / 255)
});
};
}
);
Example.defaultProps = {
color: [1, 1, 1, 1],
toolKey: "brush-4"
};
export default Example;
export { tools };
(Example is pixel editor from cookbook)
library version
Unimodules with expo is used on top of bare React Native
├── gl-react@4.0.1
└─┬ gl-react-native@4.0.1
└── gl-react-expo@4.0.1
Expected behavior
- Shaders successfully compiled
Actual behavior
- On certain mobile phones shaders are not compiled and causing a crash
NB!: Seems like GLImage from gl-react-image
actually works correctly
Steps to reproduce the behavior
- Open the screen with Pixel Editor
In this repo watched?
Sorry for the late response. I was a bit busy this year (and not a lot of contributions on gl-react yet 🙏 )
I will do a big pass on all github issues. Is this still happening to you? I fail to see where the error is in your GLSL code.
I plan to upgrade to GLSL ES 3.0 too which maybe will help resolving this.
Hi yes it still reproducible, but I found a workaround by not using a Shaders.create
for creating a shader. With this call it may fail on android 8 or 9. but once this shader caching is removed - it actually works
interesting. so you mean by creating the shader in the <Node>
directly?
@gre Let me give you an example of the code which worked for me.
This one won`t work on certain phones.
const shaders = Shaders.create({
paint: {
frag: GLSL`
precision highp float;
varying vec2 uv;
uniform vec4 color;
uniform vec2 size, mouse;
uniform float brushRadius;
uniform bool drawing;
void main() {
vec2 p = floor(uv * size) / size;
if (drawing) {
vec2 d = uv - mouse;
if (length(d) < brushRadius) {
gl_FragColor = color;
}
else {
discard;
}
}
else {
discard;
}
}`
},
initTexture: {
frag: GLSL`
precision highp float;
varying vec2 uv;
uniform sampler2D t;
void main(){
gl_FragColor=texture2D(t,uv);
}`
},
pixelEditor: {
frag: GLSL`
precision highp float;
varying vec2 uv;
uniform vec4 color;
uniform vec2 size, mouse, gridBorder;
uniform float brushRadius;
uniform sampler2D t;
void main() {
vec2 p = floor(uv * size) / size;
vec2 remain = mod(uv * size, vec2(1.0));
float m =
step(remain.x, 1.0 - gridBorder.x) *
step(remain.y, 1.0 - gridBorder.y);
float inBrushSize =
step(length(p + (0.5 / size) - mouse), brushRadius);
vec4 c = mix(texture2D(t, uv), color, 0.6 * inBrushSize);
gl_FragColor = vec4(
m * c.rgb,
mix(1.0, c.a, m));
}`
}
});
This one is actually working fine, even on android 7 or some low tier tablets
const shaders = {
paint: {
frag: GLSL`
precision highp float;
varying vec2 uv;
uniform vec4 color;
uniform vec2 size, mouse;
uniform float brushRadius;
uniform bool drawing;
void main() {
vec2 p = floor(uv * size) / size;
if (drawing) {
vec2 d = uv - mouse;
if (length(d) < brushRadius) {
gl_FragColor = color;
}
else {
discard;
}
}
else {
discard;
}
}`
},
initTexture: {
frag: GLSL`
precision highp float;
varying vec2 uv;
uniform sampler2D t;
void main(){
gl_FragColor=texture2D(t,uv);
}`
},
pixelEditor: {
frag: GLSL`
precision highp float;
varying vec2 uv;
uniform vec4 color;
uniform vec2 size, mouse, gridBorder;
uniform float brushRadius;
uniform sampler2D t;
void main() {
vec2 p = floor(uv * size) / size;
vec2 remain = mod(uv * size, vec2(1.0));
float m =
step(remain.x, 1.0 - gridBorder.x) *
step(remain.y, 1.0 - gridBorder.y);
float inBrushSize =
step(length(p + (0.5 / size) - mouse), brushRadius);
vec4 c = mix(texture2D(t, uv), color, 0.6 * inBrushSize);
gl_FragColor = vec4(
m * c.rgb,
mix(1.0, c.a, m));
}`
}
};
And than basically shader is executed on the Node. I haven't found any significant performance problem with this approach as well :)