mirror of
https://github.com/gre/gl-react.git
synced 2026-01-18 16:16:59 +00:00
228 lines
7.8 KiB
Objective-C
Executable File
228 lines
7.8 KiB
Objective-C
Executable File
#import "EXGLView.h"
|
|
|
|
#include <OpenGLES/ES2/gl.h>
|
|
#include <OpenGLES/ES2/glext.h>
|
|
|
|
#import "RCTBridge+Private.h" // FIXME this is probably unrecommended and unstable. basically we need bridge.dispatchBlock to be public
|
|
#import <React/RCTBridge+JavaScriptCore.h>
|
|
|
|
#import <React/RCTUtils.h>
|
|
|
|
#import "EXUnversioned.h"
|
|
#import "EXGL.h"
|
|
|
|
|
|
@interface EXGLView ()
|
|
|
|
@property (nonatomic, weak) EXGLViewManager *viewManager;
|
|
|
|
@property (nonatomic, strong) EAGLContext *eaglCtx;
|
|
@property (nonatomic, assign) GLuint viewFramebuffer;
|
|
@property (nonatomic, assign) GLuint viewColorbuffer;
|
|
@property (nonatomic, assign) GLuint viewDepthStencilbuffer;
|
|
@property (nonatomic, assign) GLuint msaaFramebuffer;
|
|
@property (nonatomic, assign) GLuint msaaRenderbuffer;
|
|
|
|
@property (nonatomic, strong) CADisplayLink *displayLink;
|
|
|
|
@property (nonatomic, assign) EX_UNVERSIONED(EXGLContextId) exglCtxId;
|
|
|
|
@property (nonatomic, assign) NSNumber *msaaSamples;
|
|
|
|
@end
|
|
|
|
@implementation EXGLView
|
|
|
|
RCT_NOT_IMPLEMENTED(- (instancetype)init);
|
|
|
|
// Specify that we want this UIView to be backed by a CAEAGLLayer
|
|
+ (Class)layerClass {
|
|
return [CAEAGLLayer class];
|
|
}
|
|
|
|
- (instancetype)initWithManager:(EXGLViewManager *)viewManager
|
|
{
|
|
if ((self = [super init])) {
|
|
_viewManager = viewManager;
|
|
self.contentScaleFactor = RCTScreenScale();
|
|
|
|
// Initialize properties of our backing CAEAGLLayer
|
|
CAEAGLLayer *eaglLayer = (CAEAGLLayer *) self.layer;
|
|
eaglLayer.opaque = YES;
|
|
eaglLayer.drawableProperties = @{
|
|
kEAGLDrawablePropertyRetainedBacking: @(YES),
|
|
kEAGLDrawablePropertyColorFormat: kEAGLColorFormatRGBA8,
|
|
};
|
|
|
|
// Initialize GL context, view buffers will be created on layout event
|
|
_eaglCtx = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
|
|
_msaaFramebuffer = _msaaRenderbuffer = _viewFramebuffer = _viewColorbuffer = _viewDepthStencilbuffer = 0;
|
|
|
|
// Set up a draw loop
|
|
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(draw)];
|
|
_displayLink.preferredFramesPerSecond = 60;
|
|
[_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
|
|
|
|
// Setup JS binding -- only possible on JavaScriptCore
|
|
_exglCtxId = 0;
|
|
[_viewManager.bridge dispatchBlock:^{
|
|
_exglCtxId = EX_UNVERSIONED(EXGLContextCreate(_viewManager.bridge.jsContextRef));
|
|
_onSurfaceCreate(@{ @"exglCtxId": @(_exglCtxId) });
|
|
} queue:RCTJSThread];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void)layoutSubviews
|
|
{
|
|
[self resizeViewBuffersToWidth:self.frame.size.width*self.contentScaleFactor height:self.frame.size.height*self.contentScaleFactor];
|
|
}
|
|
|
|
- (void)deleteViewBuffers
|
|
{
|
|
[EAGLContext setCurrentContext:_eaglCtx];
|
|
if (_viewDepthStencilbuffer != 0) {
|
|
glDeleteRenderbuffers(1, &_viewDepthStencilbuffer);
|
|
_viewDepthStencilbuffer = 0;
|
|
}
|
|
if (_viewColorbuffer != 0) {
|
|
glDeleteRenderbuffers(1, &_viewColorbuffer);
|
|
_viewColorbuffer = 0;
|
|
}
|
|
if (_viewFramebuffer != 0) {
|
|
glDeleteFramebuffers(1, &_viewFramebuffer);
|
|
_viewFramebuffer = 0;
|
|
}
|
|
if (_msaaRenderbuffer != 0) {
|
|
glDeleteRenderbuffers(1, &_msaaRenderbuffer);
|
|
_msaaRenderbuffer = 0;
|
|
}
|
|
if (_msaaFramebuffer != 0) {
|
|
glDeleteFramebuffers(1, &_msaaFramebuffer);
|
|
_msaaFramebuffer = 0;
|
|
}
|
|
}
|
|
|
|
- (void)resizeViewBuffersToWidth:(short)width height:(short)height
|
|
{
|
|
[EAGLContext setCurrentContext:_eaglCtx];
|
|
|
|
// Save surrounding framebuffer/renderbuffer
|
|
GLint prevFramebuffer;
|
|
GLint prevRenderbuffer;
|
|
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &prevFramebuffer);
|
|
glGetIntegerv(GL_RENDERBUFFER_BINDING, &prevRenderbuffer);
|
|
if (prevFramebuffer == _viewFramebuffer) {
|
|
prevFramebuffer = 0;
|
|
}
|
|
|
|
// Delete old buffers if they exist
|
|
[self deleteViewBuffers];
|
|
|
|
// Set up view framebuffer
|
|
glGenFramebuffers(1, &_viewFramebuffer);
|
|
glBindFramebuffer(GL_FRAMEBUFFER, _viewFramebuffer);
|
|
|
|
// Set up new color renderbuffer
|
|
glGenRenderbuffers(1, &_viewColorbuffer);
|
|
glBindRenderbuffer(GL_RENDERBUFFER, _viewColorbuffer);
|
|
[_eaglCtx renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer *)self.layer];
|
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
|
GL_RENDERBUFFER, _viewColorbuffer);
|
|
GLint realWidth, realHeight;
|
|
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &realWidth);
|
|
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &realHeight);
|
|
|
|
// Set up MSAA framebuffer/renderbuffer
|
|
glGenFramebuffers(1, &_msaaFramebuffer);
|
|
glGenRenderbuffers(1, &_msaaRenderbuffer);
|
|
glBindFramebuffer(GL_FRAMEBUFFER, _msaaFramebuffer);
|
|
glBindRenderbuffer(GL_RENDERBUFFER, _msaaRenderbuffer);
|
|
glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER, self.msaaSamples.intValue, GL_RGBA8_OES,
|
|
realWidth, realHeight);
|
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
|
GL_RENDERBUFFER, _msaaRenderbuffer);
|
|
|
|
// Set up new depth+stencil renderbuffer
|
|
glGenRenderbuffers(1, &_viewDepthStencilbuffer);
|
|
glBindRenderbuffer(GL_RENDERBUFFER, _viewDepthStencilbuffer);
|
|
glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER, self.msaaSamples.intValue, GL_DEPTH24_STENCIL8_OES,
|
|
realWidth, realHeight);
|
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
|
|
GL_RENDERBUFFER, _viewDepthStencilbuffer);
|
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
|
|
GL_RENDERBUFFER, _viewDepthStencilbuffer);
|
|
|
|
// Resize viewport
|
|
glViewport(0, 0, width, height);
|
|
|
|
// Restore surrounding framebuffer/renderbuffer
|
|
if (prevFramebuffer != 0) {
|
|
glBindFramebuffer(GL_FRAMEBUFFER, prevFramebuffer);
|
|
}
|
|
glBindRenderbuffer(GL_RENDERBUFFER, prevRenderbuffer);
|
|
|
|
// TODO(nikki): Notify JS component of resize
|
|
}
|
|
|
|
// TODO(nikki): Should all this be done in `dealloc` instead?
|
|
- (void)removeFromSuperview
|
|
{
|
|
// Destroy JS binding
|
|
EX_UNVERSIONED(EXGLContextDestroy(_exglCtxId));
|
|
|
|
// Destroy GL objects owned by us
|
|
[self deleteViewBuffers];
|
|
|
|
// Stop draw loop
|
|
[_displayLink invalidate];
|
|
_displayLink = nil;
|
|
|
|
[super removeFromSuperview];
|
|
}
|
|
|
|
- (void)draw
|
|
{
|
|
// _exglCtxId may be unset if we get here (on the UI thread) before EXGLContextCreate(...) is
|
|
// called on the JS thread to create the EXGL context and save its id (see init method). In
|
|
// this case no GL work has been sent yet so we skip this frame.
|
|
//
|
|
// _viewFramebuffer may be 0 if we haven't had a layout event yet and so the size of the
|
|
// framebuffer to create is unknown. In this case we have nowhere to render to so we skip
|
|
// this frame (the GL work to run remains on the queue for next time).
|
|
if (_exglCtxId != 0 && _viewFramebuffer != 0) {
|
|
[EAGLContext setCurrentContext:_eaglCtx];
|
|
EX_UNVERSIONED(EXGLContextSetDefaultFramebuffer(_exglCtxId, _msaaFramebuffer));
|
|
EX_UNVERSIONED(EXGLContextFlush(_exglCtxId));
|
|
|
|
// Present current state of view buffers
|
|
// TODO(nikki): This should happen exactly at `gl.endFrameEXP()` in the queue
|
|
if (_viewColorbuffer != 0)
|
|
{
|
|
// Save surrounding framebuffer/renderbuffer
|
|
GLint prevFramebuffer;
|
|
GLint prevRenderbuffer;
|
|
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &prevFramebuffer);
|
|
glGetIntegerv(GL_RENDERBUFFER_BINDING, &prevRenderbuffer);
|
|
if (prevFramebuffer == _viewFramebuffer) {
|
|
prevFramebuffer = 0;
|
|
}
|
|
|
|
// Resolve multisampling and present
|
|
glBindFramebuffer(GL_READ_FRAMEBUFFER_APPLE, _msaaFramebuffer);
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER_APPLE, _viewFramebuffer);
|
|
glResolveMultisampleFramebufferAPPLE();
|
|
glBindRenderbuffer(GL_RENDERBUFFER, _viewColorbuffer);
|
|
[_eaglCtx presentRenderbuffer:GL_RENDERBUFFER];
|
|
|
|
// Restore surrounding framebuffer/renderbuffer
|
|
if (prevFramebuffer != 0) {
|
|
glBindFramebuffer(GL_FRAMEBUFFER, prevFramebuffer);
|
|
}
|
|
glBindRenderbuffer(GL_RENDERBUFFER, prevRenderbuffer);
|
|
}
|
|
}
|
|
}
|
|
|
|
@end
|