GLCanvas.m 11.4 KB
Newer Older
1 2 3 4

#import "RCTBridge.h"
#import "RCTUtils.h"
#import "RCTConvert.h"
5
#import "RCTEventDispatcher.h"
6 7 8 9 10 11 12
#import "RCTLog.h"
#import "GLCanvas.h"
#import "GLShader.h"
#import "GLShadersRegistry.h"
#import "GLTexture.h"
#import "GLImage.h"
#import "GLRenderData.h"
13
#import "UIView+React.h"
14 15 16 17 18 19

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

@implementation GLCanvas
{
  RCTBridge *_bridge; // bridge is required to instanciate GLReactImage
20
  
21 22
  GLRenderData *_renderData;
  
23
  NSArray *_contentTextures;
24 25 26 27
  NSDictionary *_images; // This caches the currently used images (imageSrc -> GLReactImage)
  
  BOOL _opaque; // opaque prop (if false, the GLCanvas will become transparent)
  
28
  BOOL _deferredRendering; // This flag indicates a render has been deferred to the next frame (when using contents)
29 30
  
  GLint defaultFBO;
31 32 33
  
  NSMutableArray *_preloaded;
  BOOL _preloadingDone;
34 35 36 37
  
  CADisplayLink *displayLink;
  
  NSTimer *animationTimer;
38 39 40
}

- (instancetype)initWithBridge:(RCTBridge *)bridge
41
                   withContext:(EAGLContext *)context
42 43 44 45
{
  if ((self = [super init])) {
    _bridge = bridge;
    _images = @{};
46 47
    _preloaded = [[NSMutableArray alloc] init];
    _preloadingDone = false;
48 49 50 51 52 53 54
    self.context = context;
  }
  return self;
}

RCT_NOT_IMPLEMENTED(-init)

55 56 57 58
-(void)setImagesToPreload:(NSArray *)imagesToPreload
{
  if (_preloadingDone) return;
  if ([imagesToPreload count] == 0) {
59
    [self dispatchOnLoad];
60 61
    _preloadingDone = true;
  }
62 63 64
  else {
    _preloadingDone = false;
  }
65 66
  _imagesToPreload = imagesToPreload;
}
67

68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
- (void)dispatchOnLoad
{
  if (_onLoad) {
    [_bridge.eventDispatcher sendInputEventWithName:@"load" body:@{ @"target": self.reactTag }];
  }
}

- (void)dispatchOnProgress: (double)progress withLoaded:(int)loaded withTotal:(int)total
{
  if (_onProgress) {
    NSDictionary *event =
  @{
    @"target": self.reactTag,
    @"progress": @(progress),
    @"loaded": @(loaded),
    @"total": @(total) };
    [_bridge.eventDispatcher sendInputEventWithName:@"progress" body:event];
  }
}

88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
- (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;
}

107 108
- (void)setRenderId:(NSNumber *)renderId
{
109
  if (_nbContentTextures > 0) {
110 111 112 113
    [self setNeedsDisplay];
  }
}

114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
- (void)setAutoRedraw:(BOOL)autoRedraw
{
  if (autoRedraw) {
    if (!animationTimer)
      animationTimer = // FIXME: can we do better than this?
      [NSTimer scheduledTimerWithTimeInterval:1.0/60.0
                                       target:self
                                     selector:@selector(setNeedsDisplay)
                                     userInfo:nil
                                      repeats:YES];
  }
  else {
    if (animationTimer) {
      [animationTimer invalidate];
    }
  }
}

- (void)setEventsThrough:(BOOL)eventsThrough
{
  self.userInteractionEnabled = !eventsThrough;
}

137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
- (void)setData:(GLData *)data
{
  _data = data;
  [self requestSyncData];
}

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

- (void)syncData
{
  [EAGLContext setCurrentContext:self.context];
  @autoreleasepool {
152
    
153 154 155
    NSDictionary *prevImages = _images;
    NSMutableDictionary *images = [[NSMutableDictionary alloc] init];
    
156 157 158
    GLRenderData * (^traverseTree) (GLData *data);
    __block __weak GLRenderData * (^weak_traverseTree)(GLData *data);
    weak_traverseTree = traverseTree = ^GLRenderData *(GLData *data) {
159 160
      NSNumber *width = data.width;
      NSNumber *height = data.height;
161 162 163 164 165 166
      int fboId = [data.fboId intValue];
      
      NSMutableArray *contextChildren = [[NSMutableArray alloc] init];
      for (GLData *child in data.contextChildren) {
        [contextChildren addObject:weak_traverseTree(child)];
      }
167 168 169
      
      NSMutableArray *children = [[NSMutableArray alloc] init];
      for (GLData *child in data.children) {
170
        [children addObject:weak_traverseTree(child)];
171 172 173 174 175 176 177 178 179 180 181 182
      }
      
      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];
        
183
        if (type == GL_SAMPLER_2D || type == GL_SAMPLER_CUBE) {
184
          uniforms[uniformName] = [NSNumber numberWithInt:units++];
185 186 187 188
          if (!value) {
            GLTexture *emptyTexture = [[GLTexture alloc] init];
            [emptyTexture setPixelsEmpty];
            textures[uniformName] = emptyTexture;
189
          }
190 191 192 193 194 195 196 197
          else {
            NSString *type = [RCTConvert NSString:value[@"type"]];
            if ([type isEqualToString:@"content"]) {
              int id = [[RCTConvert NSNumber:value[@"id"]] intValue];
              if (id >= [_contentTextures count]) {
                [self resizeUniformContentTextures:id+1];
              }
              textures[uniformName] = _contentTextures[id];
198
            }
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
            else if ([type isEqualToString:@"fbo"]) {
              NSNumber *id = [RCTConvert NSNumber:value[@"id"]];
              GLFBO *fbo = [GLShadersRegistry getFBO:id];
              textures[uniformName] = fbo.color[0];
            }
            else if ([type isEqualToString:@"uri"]) {
              NSString *src = srcResource(value);
              if (!src) {
                RCTLogError(@"texture uniform '%@': Invalid uri format '%@'", 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:^{
                  [self onImageLoad:src];
                }];
                image.src = src;
221
                images[src] = image;
222 223
              }
              textures[uniformName] = [image getTexture];
224
            }
225 226
            else {
              RCTLogError(@"texture uniform '%@': Unexpected type '%@'", uniformName, type);
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
            }
          }
        }
        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);
        }
      }
246
      
247 248 249 250 251 252 253 254 255
      return [[GLRenderData alloc]
              initWithShader:shader
              withUniforms:uniforms
              withTextures:textures
              withWidth:width
              withHeight:height
              withFboId:fboId
              withContextChildren:contextChildren
              withChildren:children];
256 257
    };
    
258
    _renderData = traverseTree(_data);
259 260 261 262 263 264
    _images = images;
    
    [self setNeedsDisplay];
  }
}

265
- (int)countPreloaded
266
{
267
  int nb = 0;
268
  for (id toload in _imagesToPreload) {
269 270
    if ([_preloaded containsObject:srcResource(toload)])
      nb++;
271
  }
272
  return nb;
273 274 275 276 277 278
}

- (void)onImageLoad:(NSString *)loaded
{
  if (!_preloadingDone) {
    [_preloaded addObject:loaded];
279 280 281 282 283 284
    int count = [self countPreloaded];
    int total = (int) [_imagesToPreload count];
    double progress = ((double) count) / ((double) total);
    [self dispatchOnProgress:progress withLoaded:count withTotal:total];
    if (count == total) {
      [self dispatchOnLoad];
285 286 287 288 289 290 291 292 293 294
      _preloadingDone = true;
      [self requestSyncData];
    }
  }
  else {
    // Any texture image load will trigger a future re-sync of data (if no preloaded)
    [self requestSyncData];
  }
}

295
- (void)setNbContentTextures:(NSNumber *)nbContentTextures
296
{
297 298
  [self resizeUniformContentTextures:[nbContentTextures intValue]];
  _nbContentTextures = nbContentTextures;
299 300
}

301
- (void)resizeUniformContentTextures:(int)n
302 303
{
  [EAGLContext setCurrentContext:self.context];
304
  int length = (int) [_contentTextures count];
305 306
  if (length == n) return;
  if (n < length) {
307
    _contentTextures = [_contentTextures subarrayWithRange:NSMakeRange(0, n)];
308 309
  }
  else {
310 311 312
    NSMutableArray *contentTextures = [[NSMutableArray alloc] initWithArray:_contentTextures];
    for (int i = (int) [_contentTextures count]; i < n; i++) {
      [contentTextures addObject:[[GLTexture alloc] init]];
313
    }
314
    _contentTextures = contentTextures;
315 316 317 318
  }
}


319
- (void)syncContentTextures
320 321
{
  int i = 0;
322
  for (GLTexture *texture in _contentTextures) {
323 324
    UIView* view = self.superview.subviews[i]; // We take siblings by index (closely related to the JS code)
    if (view) {
325 326 327 328
      if ([view.subviews count] == 1)
        [texture setPixelsWithView:view.subviews[0]];
      else
        [texture setPixelsWithView:view];
329 330 331 332 333 334 335 336 337
    } else {
      [texture setPixelsEmpty];
    }
    i ++;
  }
}

- (void)drawRect:(CGRect)rect
{
338
  
339 340 341 342 343
  if (!_preloadingDone) {
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glClear(GL_COLOR_BUFFER_BIT);
    return;
  }
344
  BOOL needsDeferredRendering = _nbContentTextures > 0 && !_autoRedraw;
345 346 347 348 349 350 351
  if (needsDeferredRendering && !_deferredRendering) {
    dispatch_async(dispatch_get_main_queue(), ^{
      _deferredRendering = true;
      [self setNeedsDisplay];
    });
  }
  else {
352
    [self render];
353 354 355 356
    _deferredRendering = false;
  }
}

357
- (void)render
358 359
{
  if (!_renderData) return;
360

361 362 363 364 365 366 367 368 369 370 371
  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;
      
372 373 374
      for (GLRenderData *child in renderData.contextChildren)
        weak_recDraw(child);
      
375
      for (GLRenderData *child in renderData.children)
376
        weak_recDraw(child);
377
      
378
      if (renderData.fboId == -1) {
379 380 381 382
        glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO);
        glViewport(0, 0, w, h);
      }
      else {
383
        GLFBO *fbo = [GLShadersRegistry getFBO:[NSNumber numberWithInt:renderData.fboId]];
384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407
        [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);
    
408
    [self syncContentTextures];
409 410 411 412 413 414 415 416 417 418
    
    recDraw(_renderData);
    
    glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO);
  }
}



@end