Commit 8deddda8 authored by Gaëtan Renaudeau's avatar Gaëtan Renaudeau

Forward gl compile status to gl-react for inline shader feature

parent 5dc2413e
......@@ -113,7 +113,7 @@ public class GLCanvas extends GLSurfaceView
if (!shaders.containsKey(id)) {
GLShaderData shaderData = rnglContext.getShader(id);
if (shaderData == null) return null;
shaders.put(id, new GLShader(shaderData));
shaders.put(id, new GLShader(shaderData, id, rnglContext));
}
return shaders.get(id);
}
......@@ -315,9 +315,14 @@ public class GLCanvas extends GLSurfaceView
execute(new Runnable() {
public void run() {
// FIXME: maybe should set a flag so we don't do it twice??
try {
if (!syncData())
requestSyncData();
}
catch (GLShaderCompilationFailed e) {
// This is ignored. It will be handled by RNGLContext.shaderFailedToCompile
}
}
});
}
......
......@@ -21,29 +21,30 @@ public class GLShader {
private int pointerLoc; // The "pointer" attribute is used to iterate over vertex
private Map<String, Integer> uniformLocations; // The uniform locations cache
public GLShader(String name, String vert, String frag) {
this.name = name;
this.vert = vert;
this.frag = frag;
}
private Integer id;
private RNGLContext rnglContext;
private GLShaderCompilationFailed compilationFailed;
public GLShader(GLShaderData data) {
public GLShader(GLShaderData data, Integer id, RNGLContext rnglContext) {
this.name = data.name;
this.vert = data.vert;
this.frag = data.frag;
this.id = id;
this.rnglContext = rnglContext;
}
@Override
protected void finalize() throws Throwable {
super.finalize();
if (buffer != null) {
// TODO: need to check if this works properly
glDeleteProgram(program);
glDeleteBuffers(1, buffer, 0);
}
}
public void runtimeException (String msg) {
throw new RuntimeException("Shader '"+name+"': "+msg);
throw new GLShaderCompilationFailed(name, msg);
}
public void bind () {
......@@ -64,7 +65,7 @@ public class GLShader {
glGetProgramiv(program, GL_VALIDATE_STATUS, validSuccess, 0);
if (validSuccess[0] == GL_FALSE) {
glGetProgramInfoLog(program);
runtimeException("Validation failed " + glGetProgramInfoLog(program));
runtimeException(glGetProgramInfoLog(program));
}
}
......@@ -133,7 +134,7 @@ public class GLShader {
int compileSuccess[] = new int[1];
glGetShaderiv(shaderHandle, GL_COMPILE_STATUS, compileSuccess, 0);
if (compileSuccess[0] == GL_FALSE) {
runtimeException("failed to compile: " + glGetShaderInfoLog(shaderHandle));
runtimeException(glGetShaderInfoLog(shaderHandle));
return -1;
}
return shaderHandle;
......@@ -157,7 +158,7 @@ public class GLShader {
this.uniformLocations = locations;
}
private void makeProgram () {
private void makeProgram () throws GLShaderCompilationFailed {
int vertex = compileShader(vert, GL_VERTEX_SHADER);
if (vertex == -1) return;
......@@ -172,7 +173,7 @@ public class GLShader {
int[] linkSuccess = new int[1];
glGetProgramiv(program, GL_LINK_STATUS, linkSuccess, 0);
if (linkSuccess[0] == GL_FALSE) {
runtimeException("Linking failed "+glGetProgramInfoLog(program));
runtimeException(glGetProgramInfoLog(program));
}
glUseProgram(program);
......@@ -208,7 +209,18 @@ public class GLShader {
}
public boolean ensureCompile() {
if (!isReady()) makeProgram();
if (!isReady()) {
if (compilationFailed != null) throw compilationFailed;
try {
makeProgram();
rnglContext.shaderSucceedToCompile(id, uniformTypes);
}
catch (GLShaderCompilationFailed e) {
compilationFailed = e;
rnglContext.shaderFailedToCompile(id, e);
throw e;
}
}
return isReady();
}
}
package com.projectseptember.RNGL;
public class GLShaderCompilationFailed extends RuntimeException {
public final String shaderName;
public final String compileError;
public GLShaderCompilationFailed(String shaderName, String compileError) {
super("Shader '"+shaderName+"': "+compileError);
this.compileError = compileError;
this.shaderName = shaderName;
}
}
package com.projectseptember.RNGL;
import android.util.Log;
import static android.opengl.GLES20.*;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import java.util.HashMap;
import java.util.Map;
......@@ -20,6 +26,7 @@ public class RNGLContext extends ReactContextBaseJavaModule {
private Map<Integer, GLShaderData> shaders = new HashMap<>();
private Map<Integer, GLFBO> fbos = new HashMap<>();
private Map<Integer, Callback> onCompileCallbacks = new HashMap<>();
public RNGLContext (ReactApplicationContext reactContext) {
super(reactContext);
......@@ -35,9 +42,67 @@ public class RNGLContext extends ReactContextBaseJavaModule {
}
@ReactMethod
public void addShader (final Integer id, final ReadableMap config) {
public void addShader (final Integer id, final ReadableMap config, final Callback onCompile) {
final String frag = config.getString("frag");
final String name = config.getString("name");
shaders.put(id, new GLShaderData(name, STATIC_VERT, frag));
if (onCompile != null) {
onCompileCallbacks.put(id, onCompile);
}
}
@ReactMethod
public void removeShader (final Integer id) {
GLShaderData shader = shaders.remove(id);
if (shader == null) {
throw new Error("removeShader("+id+"): shader does not exist");
}
}
public void shaderFailedToCompile(Integer id, GLShaderCompilationFailed e) {
Callback onCompile = onCompileCallbacks.get(id);
if (onCompile == null) {
Log.e("RNGLContext", e.getMessage());
}
else {
onCompile.invoke(e.compileError);
}
}
public void shaderSucceedToCompile(Integer id, Map<String, Integer> uniformTypes) {
Callback onCompile = onCompileCallbacks.get(id);
onCompileCallbacks.remove(id);
if (onCompile != null) {
WritableMap res = Arguments.createMap();
WritableMap uniforms = Arguments.createMap();
for (String key : uniformTypes.keySet()) {
uniforms.putString(key, glTypeString(uniformTypes.get(key)));
}
res.putMap("uniforms", uniforms);
onCompile.invoke(null, res);
}
}
static String glTypeString (int type) {
switch (type) {
case GL_FLOAT: return "float";
case GL_FLOAT_VEC2: return "vec2";
case GL_FLOAT_VEC3: return "vec3";
case GL_FLOAT_VEC4: return "vec4";
case GL_INT: return "int";
case GL_INT_VEC2: return "ivec2";
case GL_INT_VEC3: return "ivec3";
case GL_INT_VEC4: return "ivec4";
case GL_BOOL: return "bool";
case GL_BOOL_VEC2: return "bvec2";
case GL_BOOL_VEC3: return "bvec3";
case GL_BOOL_VEC4: return "bvec4";
case GL_FLOAT_MAT2: return "mat2";
case GL_FLOAT_MAT3: return "mat3";
case GL_FLOAT_MAT4: return "mat4";
case GL_SAMPLER_2D: return "sampler2D";
case GL_SAMPLER_CUBE: return "samplerCube";
}
return "";
}
}
......@@ -173,7 +173,7 @@ RCT_NOT_IMPLEMENTED(-init)
[self setNeedsDisplay];
}
- (void)syncData
- (bool)syncData:(NSError **)error
{
@autoreleasepool {
......@@ -204,6 +204,7 @@ RCT_NOT_IMPLEMENTED(-init)
GLShader *shader = [_bridge.rnglContext getShader:data.shader];
if (shader == nil) return nil;
if (![shader ensureCompiles:error]) return nil;
NSDictionary *uniformTypes = [shader uniformTypes];
NSMutableDictionary *uniforms = [[NSMutableDictionary alloc] init];
......@@ -292,16 +293,15 @@ RCT_NOT_IMPLEMENTED(-init)
GLRenderData *res = traverseTree(_data);
if (res != nil) {
_needSync = false;
_renderData = traverseTree(_data);
_images = images;
for (NSString *src in diff([prevImages allKeys], [images allKeys])) {
[_preloaded removeObject:src];
}
return true;
}
else {
// the data is not ready, retry in one tick
[self setNeedsDisplay];
return false;
}
}
}
......@@ -374,7 +374,14 @@ RCT_NOT_IMPLEMENTED(-init)
}
if (_needSync) {
[self syncData];
NSError *error;
if(![self syncData:&error] && error==nil) {
// the data is not ready, retry in one tick
[self setNeedsDisplay];
}
else {
_needSync = false;
}
}
if ([self haveRemainingToPreload]) {
......
#import <GLKit/GLKit.h>
#import "RCTBridgeModule.h"
NS_ENUM(NSInteger) {
GLContextFailure = 87001,
GLLinkingFailure = 87002,
GLCompileFailure = 87003
};
@interface GLShader: NSObject
@property EAGLContext *context;
......@@ -18,10 +24,7 @@
*/
- (void) bind;
/**
* Check the shader validity
*/
- (void) validate;
- (bool) ensureCompiles: (NSError**)error;
/**
* Set the value of an uniform
......
......@@ -5,7 +5,7 @@
#import "RCTConvert.h"
#import "GLShader.h"
GLuint compileShader (NSString* shaderName, NSString* shaderString, GLenum shaderType) {
GLuint compileShader (NSString* shaderName, NSString* shaderString, GLenum shaderType, NSError **error) {
GLuint shaderHandle = glCreateShader(shaderType);
......@@ -20,8 +20,10 @@ GLuint compileShader (NSString* shaderName, NSString* shaderString, GLenum shade
if (compileSuccess == GL_FALSE) {
GLchar messages[256];
glGetShaderInfoLog(shaderHandle, sizeof(messages), 0, &messages[0]);
NSString *messageString = [NSString stringWithUTF8String:messages];
RCTLogError(@"Shader '%@' failed to compile: %@", shaderName, messageString);
*error = [[NSError alloc]
initWithDomain:[NSString stringWithUTF8String:messages]
code:GLCompileFailure
userInfo:nil];
return -1;
}
......@@ -41,6 +43,7 @@ GLuint compileShader (NSString* shaderName, NSString* shaderString, GLenum shade
GLint pointerLoc; // The "pointer" attribute is used to iterate over vertex
NSDictionary *_uniformTypes; // The types of the GLSL uniforms (N.B: array are not supported)
NSDictionary *_uniformLocations; // The uniform locations cache
NSError *_error;
}
- (instancetype)initWithContext: (EAGLContext*)context withName:(NSString *)name withVert:(NSString *)vert withFrag:(NSString *)frag
......@@ -51,7 +54,10 @@ GLuint compileShader (NSString* shaderName, NSString* shaderString, GLenum shade
_context = context;
_vert = vert;
_frag = frag;
[self makeProgram];
NSError *error;
if (![self makeProgram:&error]) {
_error = error;
}
}
return self;
}
......@@ -62,10 +68,10 @@ GLuint compileShader (NSString* shaderName, NSString* shaderString, GLenum shade
glDeleteBuffers(1, &buffer);
}
- (bool) ensureContext
- (bool) ensureContext: (NSError **)error
{
if (![EAGLContext setCurrentContext:_context]) {
RCTLogError(@"Shader '%@': Failed to set current OpenGL context", _name);
*error = [[NSError alloc] initWithDomain:@"Failed to set current OpenGL context" code:GLContextFailure userInfo:nil];
return false;
}
return true;
......@@ -73,7 +79,11 @@ GLuint compileShader (NSString* shaderName, NSString* shaderString, GLenum shade
- (void) bind
{
if (![self ensureContext]) return;
NSError *error;
if (![self ensureContext:&error]) {
RCTLogError(@"%@", error.domain);
return;
}
if ( glIsProgram(program) != GL_TRUE ){
RCTLogError(@"Shader '%@': not a program!", _name);
return;
......@@ -307,19 +317,6 @@ GLuint compileShader (NSString* shaderName, NSString* shaderString, GLenum shade
}
}
- (void) validate
{
glValidateProgram(program);
GLint validSuccess;
glGetProgramiv(program, GL_VALIDATE_STATUS, &validSuccess);
if (validSuccess == GL_FALSE) {
GLchar messages[256];
glGetProgramInfoLog(program, sizeof(messages), 0, &messages[0]);
NSString *messageString = [NSString stringWithUTF8String:messages];
RCTLogError(@"Shader '%@': Validation failed %@", _name, messageString);
}
}
- (void) computeMeta
{
NSMutableDictionary *uniforms = @{}.mutableCopy;
......@@ -342,15 +339,22 @@ GLuint compileShader (NSString* shaderName, NSString* shaderString, GLenum shade
_uniformLocations = locations;
}
- (void) makeProgram
- (bool) ensureCompiles: (NSError **)error
{
if (![self ensureContext]) return;
if (_error == nil) return true;
*error = _error;
return false;
}
- (bool) makeProgram: (NSError **)error
{
if (![self ensureContext:error]) return false;
GLuint vertex = compileShader(_name, _vert, GL_VERTEX_SHADER);
if (vertex == -1) return;
GLuint vertex = compileShader(_name, _vert, GL_VERTEX_SHADER, error);
if (vertex == -1) return false;
GLuint fragment = compileShader(_name, _frag, GL_FRAGMENT_SHADER);
if (fragment == -1) return;
GLuint fragment = compileShader(_name, _frag, GL_FRAGMENT_SHADER, error);
if (fragment == -1) return false;
program = glCreateProgram();
glAttachShader(program, vertex);
......@@ -362,9 +366,11 @@ GLuint compileShader (NSString* shaderName, NSString* shaderString, GLenum shade
if (linkSuccess == GL_FALSE) {
GLchar messages[256];
glGetProgramInfoLog(program, sizeof(messages), 0, &messages[0]);
NSString *messageString = [NSString stringWithUTF8String:messages];
RCTLogError(@"Shader '%@': Linking failed %@", _name, messageString);
return;
*error = [[NSError alloc]
initWithDomain:[NSString stringWithUTF8String:messages]
code:GLLinkingFailure
userInfo:nil];
return false;
}
glUseProgram(program);
......@@ -384,6 +390,8 @@ GLuint compileShader (NSString* shaderName, NSString* shaderString, GLenum shade
1.0, 1.0
};
glBufferData(GL_ARRAY_BUFFER, sizeof(buf), buf, GL_STATIC_DRAW);
return true;
}
......
......@@ -50,7 +50,7 @@
346089C61BEFD0A500C90DB5 /* GLRenderData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GLRenderData.h; sourceTree = "<group>"; };
346089C71BEFD0A500C90DB5 /* GLRenderData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GLRenderData.m; sourceTree = "<group>"; };
346089C81BEFD0A500C90DB5 /* GLShader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GLShader.h; sourceTree = "<group>"; };
346089C91BEFD0A500C90DB5 /* GLShader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GLShader.m; sourceTree = "<group>"; };
346089C91BEFD0A500C90DB5 /* GLShader.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = GLShader.m; sourceTree = "<group>"; tabWidth = 2; };
346089CA1BEFD0A500C90DB5 /* GLTexture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GLTexture.h; sourceTree = "<group>"; };
346089CB1BEFD0A500C90DB5 /* GLTexture.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = GLTexture.m; sourceTree = "<group>"; tabWidth = 2; };
346089CC1BEFD0A500C90DB5 /* RCTConvert+GLData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RCTConvert+GLData.h"; sourceTree = "<group>"; };
......
......@@ -45,9 +45,73 @@ RCT_EXPORT_MODULE()
return _context;
}
- (void)_addShader:(nonnull NSNumber *)id
withConfig:(NSDictionary *)config
withOnCompile:(RCTResponseSenderBlock)onCompile
{
NSString *frag = [RCTConvert NSString:config[@"frag"]];
NSString *name = [RCTConvert NSString:config[@"name"]];
if (!frag) {
RCTLogError(@"Shader '%@': missing frag field", name);
return;
}
GLShader *shader = [[GLShader alloc] initWithContext:_context withName:name withVert:fullViewportVert withFrag:frag];
NSError *error;
bool success = [shader ensureCompiles:&error];
if (onCompile) {
if (!success) {
onCompile(@[error.domain]);
}
else {
onCompile(@[[NSNull null],
@{
@"uniforms": shader.uniformTypes
}]);
}
}
else {
if (!success) {
RCTLogError(@"Shader '%@': %@", name, error.domain);
}
}
_shaders[id] = shader;
}
static NSString* fullViewportVert = @"attribute vec2 position;varying vec2 uv;void main() {gl_Position = vec4(position,0.0,1.0);uv = vec2(0.5, 0.5) * (position+vec2(1.0, 1.0));}";
RCT_EXPORT_METHOD(addShader:(nonnull NSNumber *)id withConfig:(NSDictionary *)config) {
NSString* glTypeString (int type) {
switch (type) {
case GL_FLOAT: return @"float";
case GL_FLOAT_VEC2: return @"vec2";
case GL_FLOAT_VEC3: return @"vec3";
case GL_FLOAT_VEC4: return @"vec4";
case GL_INT: return @"int";
case GL_INT_VEC2: return @"ivec2";
case GL_INT_VEC3: return @"ivec3";
case GL_INT_VEC4: return @"ivec4";
case GL_BOOL: return @"bool";
case GL_BOOL_VEC2: return @"bvec2";
case GL_BOOL_VEC3: return @"bvec3";
case GL_BOOL_VEC4: return @"bvec4";
case GL_FLOAT_MAT2: return @"mat2";
case GL_FLOAT_MAT3: return @"mat3";
case GL_FLOAT_MAT4: return @"mat4";
case GL_SAMPLER_2D: return @"sampler2D";
case GL_SAMPLER_CUBE: return @"samplerCube";
}
return @"";
}
NSDictionary* glTypesString (NSDictionary *types) {
NSMutableDictionary *dict = types.mutableCopy;
for (NSString *key in [dict allKeys]) {
dict[key] = glTypeString([dict[key] intValue]);
}
return dict;
}
RCT_EXPORT_METHOD(addShader:(nonnull NSNumber *)id
withConfig:(NSDictionary *)config
withOnCompile:(RCTResponseSenderBlock)onCompile) {
NSString *frag = [RCTConvert NSString:config[@"frag"]];
NSString *name = [RCTConvert NSString:config[@"name"]];
if (!frag) {
......@@ -55,9 +119,36 @@ RCT_EXPORT_METHOD(addShader:(nonnull NSNumber *)id withConfig:(NSDictionary *)co
return;
}
GLShader *shader = [[GLShader alloc] initWithContext:_context withName:name withVert:fullViewportVert withFrag:frag];
NSError *error;
bool success = [shader ensureCompiles:&error];
if (onCompile) {
if (!success) {
onCompile(@[error.domain]);
}
else {
onCompile(@[[NSNull null],
@{
@"uniforms": glTypesString(shader.uniformTypes)
}]);
}
}
else {
if (!success) {
RCTLogError(@"Shader '%@': %@", name, error.domain);
}
}
_shaders[id] = shader;
}
RCT_EXPORT_METHOD(removeShader:(nonnull NSNumber *)id) {
GLShader *shader = [_shaders objectForKey:id];
if (!shader) {
RCTLogError(@"removeShader(%@): shader does not exist", id);
return;
}
[_shaders removeObjectForKey:id];
}
@end
@implementation RCTBridge (RNGLContext)
......
......@@ -10,8 +10,8 @@ See README install instructions.
React.NativeModules.RNGLContext is %s`, RNGLContext);
// Hook Shaders to RNGLContext
Shaders.list().map(id => RNGLContext.addShader(id, Shaders.get(id)));
Shaders.on("add", (id, shader) => RNGLContext.addShader(id, shader));
Shaders.on("add", (id, shader, onCompile) => RNGLContext.addShader(id, shader, onCompile));
Shaders.on("remove", id => RNGLContext.removeShader(id));
module.exports = {
Surface
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment