GLCanvas.m 8.75 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

#import "RCTBridge.h"
#import "RCTUtils.h"
#import "RCTConvert.h"
#import "RCTLog.h"
#import "GLCanvas.h"
#import "GLShader.h"
#import "GLShadersRegistry.h"
#import "GLTexture.h"
#import "GLImage.h"
#import "GLRenderData.h"

// For reference, see implementation of gl-shader's GLCanvas

@implementation GLCanvas
{
  RCTBridge *_bridge; // bridge is required to instanciate GLReactImage

  GLRenderData *_renderData;
  
21
  NSArray *_contentTextures;
22 23 24 25
  NSDictionary *_images; // This caches the currently used images (imageSrc -> GLReactImage)
  
  BOOL _opaque; // opaque prop (if false, the GLCanvas will become transparent)
  
26
  BOOL _deferredRendering; // This flag indicates a render has been deferred to the next frame (when using contents)
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
  
  GLint defaultFBO;
}

- (instancetype)initWithBridge:(RCTBridge *)bridge
                   withContext:(EAGLContext*)context
{
  if ((self = [super init])) {
    _bridge = bridge;
    _images = @{};
    self.context = context;
  }
  return self;
}

RCT_NOT_IMPLEMENTED(-init)


- (void)setOpaque:(BOOL)opaque
{
  _opaque = opaque;
  [self setNeedsDisplay];
}

NSString* srcResource (id res)
{
  NSString *src;
  if ([res isKindOfClass:[NSString class]]) {
    src = [RCTConvert NSString:res];
  } else {
    BOOL isStatic = [RCTConvert BOOL:res[@"isStatic"]];
    src = [RCTConvert NSString:res[@"path"]];
    if (!src || isStatic) src = [RCTConvert NSString:res[@"uri"]];
  }
  return src;
}

64 65
- (void)setRenderId:(NSNumber *)renderId
{
66
  if (_nbContentTextures > 0) {
67 68 69 70
    [self setNeedsDisplay];
  }
}

71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
- (void)setData:(GLData *)data
{
  _data = data;
  [self requestSyncData];
}

- (void)requestSyncData
{
  [self syncData];
}

- (void)syncData
{
  [EAGLContext setCurrentContext:self.context];
  @autoreleasepool {
  
    NSDictionary *prevImages = _images;
    NSMutableDictionary *images = [[NSMutableDictionary alloc] init];
    
90 91 92
    GLRenderData * (^traverseTree) (GLData *data);
    __block __weak GLRenderData * (^weak_traverseTree)(GLData *data);
    weak_traverseTree = traverseTree = ^GLRenderData *(GLData *data) {
93 94
      NSNumber *width = data.width;
      NSNumber *height = data.height;
95 96 97 98 99 100
      int fboId = [data.fboId intValue];
      
      NSMutableArray *contextChildren = [[NSMutableArray alloc] init];
      for (GLData *child in data.contextChildren) {
        [contextChildren addObject:weak_traverseTree(child)];
      }
101 102 103
      
      NSMutableArray *children = [[NSMutableArray alloc] init];
      for (GLData *child in data.children) {
104
        [children addObject:weak_traverseTree(child)];
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
      }
      
      GLShader *shader = [GLShadersRegistry getShader:data.shader];
      
      NSDictionary *uniformTypes = [shader uniformTypes];
      NSMutableDictionary *uniforms = [[NSMutableDictionary alloc] init];
      NSMutableDictionary *textures = [[NSMutableDictionary alloc] init];
      int units = 0;
      for (NSString *uniformName in data.uniforms) {
        id value = [data.uniforms objectForKey:uniformName];
        GLenum type = [uniformTypes[uniformName] intValue];
        
        if (value && (type == GL_SAMPLER_2D || type == GL_SAMPLER_CUBE)) {
          uniforms[uniformName] = [NSNumber numberWithInt:units++];
          NSString *type = [RCTConvert NSString:value[@"type"]];
120
          if ([type isEqualToString:@"content"]) {
121
            int id = [[RCTConvert NSNumber:value[@"id"]] intValue];
122 123
            if (id >= [_contentTextures count]) {
              [self resizeUniformContentTextures:id+1];
124
            }
125
            textures[uniformName] = _contentTextures[id];
126 127
          }
          else if ([type isEqualToString:@"framebuffer"]) {
128
            NSNumber *id = [RCTConvert NSNumber:value[@"id"]];
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
            GLFBO *fbo = [GLShadersRegistry getFBO:id];
            textures[uniformName] = fbo.color[0];
          }
          else if ([type isEqualToString:@"image"]) {
            NSObject *val = value[@"value"];
            NSString *src = srcResource(val);
            if (!src) {
              RCTLogError(@"invalid uniform '%@' texture value '%@'", uniformName, value);
            }
           
            GLImage *image = images[src];
            if (image == nil) {
              image = prevImages[src];
              if (image != nil)
                images[src] = image;
            }
            if (image == nil) {
              image = [[GLImage alloc] initWithBridge:_bridge withOnLoad:^{
147
                [self setNeedsDisplay];
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
              }];
              image.src = src;
              images[src] = image;
            }
            textures[uniformName] = [image getTexture];
          }
          else {
            RCTLogError(@"invalid uniform '%@' value of type '%@'", uniformName, type);
          }
        }
        else {
          uniforms[uniformName] = value;
        }
      }
      
      int maxTextureUnits;
      glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureUnits);
      if (units > maxTextureUnits) {
        RCTLogError(@"Maximum number of texture reach. got %i >= max %i", units, maxTextureUnits);
      }
      
      for (NSString *uniformName in shader.uniformTypes) {
        if (uniforms[uniformName] == nil) {
          RCTLogError(@"All defined uniforms must be provided. Missing '%@'", uniformName);
        }
      }

175 176 177 178 179 180 181 182 183
      return [[GLRenderData alloc]
              initWithShader:shader
              withUniforms:uniforms
              withTextures:textures
              withWidth:width
              withHeight:height
              withFboId:fboId
              withContextChildren:contextChildren
              withChildren:children];
184 185
    };
    
186
    _renderData = traverseTree(_data);
187 188 189 190 191 192
    _images = images;
    
    [self setNeedsDisplay];
  }
}

193
- (void)setNbContentTextures:(NSNumber *)nbContentTextures
194
{
195 196
  [self resizeUniformContentTextures:[nbContentTextures intValue]];
  _nbContentTextures = nbContentTextures;
197 198
}

199
- (void)resizeUniformContentTextures:(int)n
200 201
{
  [EAGLContext setCurrentContext:self.context];
202
  int length = (int) [_contentTextures count];
203 204
  if (length == n) return;
  if (n < length) {
205
    _contentTextures = [_contentTextures subarrayWithRange:NSMakeRange(0, n)];
206 207
  }
  else {
208 209 210
    NSMutableArray *contentTextures = [[NSMutableArray alloc] initWithArray:_contentTextures];
    for (int i = (int) [_contentTextures count]; i < n; i++) {
      [contentTextures addObject:[[GLTexture alloc] init]];
211
    }
212
    _contentTextures = contentTextures;
213 214 215 216
  }
}


217
- (void)syncContentTextures
218 219
{
  int i = 0;
220
  for (GLTexture *texture in _contentTextures) {
221 222
    UIView* view = self.superview.subviews[i]; // We take siblings by index (closely related to the JS code)
    if (view) {
223 224 225 226
      if ([view.subviews count] == 1)
        [texture setPixelsWithView:view.subviews[0]];
      else
        [texture setPixelsWithView:view];
227 228 229 230 231 232 233 234 235
    } else {
      [texture setPixelsEmpty];
    }
    i ++;
  }
}

- (void)drawRect:(CGRect)rect
{
236
  BOOL needsDeferredRendering = _nbContentTextures > 0;
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
  if (needsDeferredRendering && !_deferredRendering) {
    dispatch_async(dispatch_get_main_queue(), ^{
      _deferredRendering = true;
      [self setNeedsDisplay];
    });
  }
  else {
    [self render:rect];
    _deferredRendering = false;
  }
}

- (void)render:(CGRect)rect
{
  if (!_renderData) return;
  
  self.layer.opaque = _opaque;
  
  CGFloat scale = RCTScreenScale();
  
  @autoreleasepool {
    void (^recDraw) (GLRenderData *renderData);
    __block __weak void (^weak_recDraw) (GLRenderData *renderData);
    weak_recDraw = recDraw = ^void(GLRenderData *renderData) {
      float w = [renderData.width floatValue] * scale;
      float h = [renderData.height floatValue] * scale;
      
264 265 266
      for (GLRenderData *child in renderData.contextChildren)
        weak_recDraw(child);
      
267
      for (GLRenderData *child in renderData.children)
268
        weak_recDraw(child);
269
      
270
      if (renderData.fboId == -1) {
271 272 273 274
        glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO);
        glViewport(0, 0, w, h);
      }
      else {
275
        GLFBO *fbo = [GLShadersRegistry getFBO:[NSNumber numberWithInt:renderData.fboId]];
276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
        [fbo setShapeWithWidth:w withHeight:h];
        [fbo bind];
      }
      
      glClear(GL_COLOR_BUFFER_BIT);
      glClearColor(0.0, 0.0,  0.0,  0.0);
      
      [renderData.shader bind];
      
      for (NSString *uniformName in renderData.textures) {
        GLTexture *texture = renderData.textures[uniformName];
        int unit = [((NSNumber *)renderData.uniforms[uniformName]) intValue];
        [texture bind:unit];
      }
      
      for (NSString *uniformName in renderData.uniforms) {
        [renderData.shader setUniform:uniformName withValue:renderData.uniforms[uniformName]];
      }
      
      glDrawArrays(GL_TRIANGLES, 0, 6);
    };
    
    glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFBO);
    
300
    [self syncContentTextures];
301 302 303 304 305 306 307 308 309 310
    
    recDraw(_renderData);
    
    glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO);
  }
}



@end