UNPKG

11.5 kBJavaScriptView Raw
1'use strict';
2
3import React, { PropTypes } from 'react';
4import { View, Platform, requireNativeComponent } from 'react-native';
5
6import * as Constants from './Constants';
7
8
9// A component that acts as an OpenGL render target.
10
11export default class GLView extends React.Component {
12 static propTypes = {
13 // Called when the OpenGL context is created, with the context object as a
14 // parameter. The context object has an API mirroring WebGL's
15 // WebGLRenderingContext.
16 onContextCreate: PropTypes.func,
17
18 // [iOS only] Number of samples for Apple's built-in multisampling.
19 msaaSamples: PropTypes.number,
20
21 ...View.propTypes,
22 }
23
24 static defaultProps = {
25 msaaSamples: 4,
26 };
27
28 render() {
29 // eslint-disable-next-line no-unused-vars
30 const { onContextCreate, msaaSamples, ...viewProps } = this.props;
31
32 // NOTE: Removing `backgroundColor: 'transparent'` causes a performance
33 // regression. Not sure why yet...
34 return (
35 <View {...viewProps}>
36 <GLView.NativeView
37 style={{ flex: 1, backgroundColor: 'transparent' }}
38 onSurfaceCreate={this._onSurfaceCreate}
39 msaaSamples={Platform.OS === 'ios' ? msaaSamples : undefined}
40 />
41 </View>
42 );
43 }
44
45 _onSurfaceCreate = ({ nativeEvent: { exglCtxId } }) => {
46 const gl = getGl(exglCtxId);
47 if (this.props.onContextCreate) {
48 this.props.onContextCreate(gl);
49 }
50 }
51
52 static NativeView = requireNativeComponent('ExponentGLView', GLView, {
53 nativeOnly: { onSurfaceCreate: true },
54 });
55}
56
57
58// JavaScript WebGL types to wrap around native objects
59
60global.WebGLRenderingContext = class WebGLRenderingContext {};
61
62global.WebGLObject = class WebGLObject {
63 constructor (id) {
64 this.id = id; // Native GL object id
65 }
66 toString() {
67 return `[WebGLObject ${this.id}]`;
68 }
69}
70global.WebGLBuffer = class WebGLBuffer extends WebGLObject {}
71global.WebGLFramebuffer = class WebGLFramebuffer extends WebGLObject {}
72global.WebGLProgram = class WebGLProgram extends WebGLObject {}
73global.WebGLRenderbuffer = class WebGLRenderbuffer extends WebGLObject {}
74global.WebGLShader = class WebGLShader extends WebGLObject {}
75global.WebGLTexture = class WebGLTexture extends WebGLObject {}
76
77const idToObject = {};
78const wrapObject = (type, id) => {
79 const found = idToObject[id];
80 if (found) {
81 return found;
82 }
83 return idToObject[id] = new type(id);
84}
85
86global.WebGLUniformLocation = class WebGLUniformLocation {
87 constructor (id) {
88 this.id = id; // Native GL object id
89 }
90}
91
92global.WebGLActiveInfo = class WebGLActiveInfo {
93 constructor (obj) {
94 Object.assign(this, obj);
95 }
96}
97global.WebGLShaderPrecisionFormat = class WebGLShaderPrecisionFormat {
98 constructor (obj) {
99 Object.assign(this, obj);
100 }
101}
102
103// Many functions need wrapping/unwrapping of arguments and return value. We
104// handle each case specifically so we can write the tightest code for
105// better performance.
106const wrapMethods = (gl) => {
107 const wrap = (methodNames, wrapper) =>
108 (Array.isArray(methodNames) ? methodNames : [methodNames]).forEach(
109 (methodName) => gl[methodName] = wrapper(gl[methodName]));
110
111 // We can be slow in `gl.getParameter(...)` since it's a blocking call anyways
112 const getParameterTypes = {
113 [gl.ARRAY_BUFFER_BINDING]: WebGLBuffer,
114 [gl.ELEMENT_ARRAY_BUFFER_BINDING]: WebGLBuffer,
115 [gl.CURRENT_PROGRAM]: WebGLProgram,
116 [gl.FRAMEBUFFER_BINDING]: WebGLFramebuffer,
117 [gl.RENDERBUFFER_BINDING]: WebGLRenderbuffer,
118 [gl.TEXTURE_BINDING_2D]: WebGLTexture,
119 [gl.TEXTURE_BINDING_CUBE_MAP]: WebGLTexture,
120 };
121 wrap('getParameter', (orig) => (pname) => {
122 let ret = orig.call(gl, pname);
123 if (pname === gl.VERSION) {
124 // Wrap native version name
125 ret = `WebGL 1.0 (Expo-${Platform.OS}-${Constants.expoVersion}) (${ret})`;
126 }
127 const type = getParameterTypes[pname];
128 return type ? wrapObject(type, ret) : ret;
129 });
130
131 // Buffers
132 wrap('bindBuffer', (orig) => (target, buffer) =>
133 orig.call(gl, target, buffer && buffer.id));
134 wrap('createBuffer', (orig) => () =>
135 wrapObject(WebGLBuffer, orig.call(gl)));
136 wrap('deleteBuffer', (orig) => (buffer) =>
137 orig.call(gl, buffer && buffer.id));
138 wrap('isBuffer', (orig) => (buffer) =>
139 buffer instanceof WebGLBuffer && orig.call(gl, buffer.id));
140
141 // Framebuffers
142 wrap('bindFramebuffer', (orig) => (target, framebuffer) =>
143 orig.call(gl, target, framebuffer && framebuffer.id));
144 wrap('createFramebuffer', (orig) => () =>
145 wrapObject(WebGLFramebuffer, orig.call(gl)));
146 wrap('deleteFramebuffer', (orig) => (framebuffer) =>
147 orig.call(gl, framebuffer && framebuffer.id));
148 wrap('framebufferRenderbuffer', (orig) => (target, attachment, rbtarget, rb) =>
149 orig.call(gl, target, attachment, rbtarget, rb && rb.id));
150 wrap('framebufferTexture2D', (orig) => (target, attachment, textarget, tex, level) =>
151 orig.call(gl, target, attachment, textarget, tex && tex.id, level));
152 wrap('isFramebuffer', (orig) => (framebuffer) =>
153 framebuffer instanceof WebGLFramebuffer && orig.call(gl, framebuffer.id));
154
155 // Renderbuffers
156 wrap('bindRenderbuffer', (orig) => (target, renderbuffer) =>
157 orig.call(gl, target, renderbuffer && renderbuffer.id));
158 wrap('createRenderbuffer', (orig) => () =>
159 wrapObject(WebGLRenderbuffer, orig.call(gl)));
160 wrap('deleteRenderbuffer', (orig) => (renderbuffer) =>
161 orig.call(gl, renderbuffer && renderbuffer.id));
162 wrap('isRenderbuffer', (orig) => (renderbuffer) =>
163 renderbuffer instanceof WebGLRenderbuffer && orig.call(gl, renderbuffer.id));
164
165 // Textures
166 wrap('bindTexture', (orig) => (target, texture) =>
167 orig.call(gl, target, texture && texture.id));
168 wrap('createTexture', (orig) => () =>
169 wrapObject(WebGLTexture, orig.call(gl)));
170 wrap('deleteTexture', (orig) => (texture) =>
171 orig.call(gl, texture && texture.id));
172 wrap('isTexture', (orig) => (texture) =>
173 texture instanceof WebGLTexture && orig.call(gl, texture.id));
174
175 // Programs and shaders
176 wrap('attachShader', (orig) => (program, shader) =>
177 orig.call(gl, program && program.id, shader && shader.id));
178 wrap('bindAttribLocation', (orig) => (program, index, name) =>
179 orig.call(gl, program && program.id, index, name));
180 wrap('compileShader', (orig) => (shader) =>
181 orig.call(gl, shader && shader.id));
182 wrap('createProgram', (orig) => () =>
183 wrapObject(WebGLProgram, orig.call(gl)));
184 wrap('createShader', (orig) => (type) =>
185 wrapObject(WebGLShader, orig.call(gl, type)));
186 wrap('deleteProgram', (orig) => (program) =>
187 orig.call(gl, program && program.id));
188 wrap('deleteShader', (orig) => (shader) =>
189 orig.call(gl, shader && shader.id));
190 wrap('detachShader', (orig) => (program, shader) =>
191 orig.call(gl, program && program.id, shader && shader.id));
192 wrap('getAttachedShaders', (orig) => (program) =>
193 orig.call(gl, program && program.id).map((id) => wrapObject(WebGLShader, id)));
194 wrap('getProgramParameter', (orig) => (program, pname) =>
195 orig.call(gl, program && program.id, pname));
196 wrap('getProgramInfoLog', (orig) => (program) =>
197 orig.call(gl, program && program.id));
198 wrap('getShaderParameter', (orig) => (shader, pname) =>
199 orig.call(gl, shader && shader.id, pname));
200 wrap('getShaderPrecisionFormat', (orig) => (shadertype, precisiontype) =>
201 new WebGLShaderPrecisionFormat(orig.call(gl, shadertype, precisiontype)))
202 wrap('getShaderInfoLog', (orig) => (shader) =>
203 orig.call(gl, shader && shader.id));
204 wrap('getShaderSource', (orig) => (shader) =>
205 orig.call(gl, shader && shader.id));
206 wrap('linkProgram', (orig) => (program) =>
207 orig.call(gl, program && program.id));
208 wrap('shaderSource', (orig) => (shader, source) =>
209 orig.call(gl, shader && shader.id, source));
210 wrap('useProgram', (orig) => (program) =>
211 orig.call(gl, program && program.id));
212 wrap('validateProgram', (orig) => (program) =>
213 orig.call(gl, program && program.id));
214 wrap('isShader', (orig) => (shader) =>
215 shader instanceof WebGLShader && orig.call(gl, shader.id));
216 wrap('isProgram', (orig) => (program) =>
217 program instanceof WebGLProgram && orig.call(gl, program.id));
218
219 // Uniforms and attributes
220 wrap('getActiveAttrib', (orig) => (program, index) =>
221 new WebGLActiveInfo(orig.call(gl, program && program.id, index)));
222 wrap('getActiveUniform', (orig) => (program, index) =>
223 new WebGLActiveInfo(orig.call(gl, program && program.id, index)));
224 wrap('getAttribLocation', (orig) => (program, name) =>
225 orig.call(gl, program && program.id, name));
226 wrap('getUniform', (orig) => (program, location) =>
227 orig.call(gl, program && program.id, location && location.id));
228 wrap('getUniformLocation', (orig) => (program, name) =>
229 new WebGLUniformLocation(orig.call(gl, program && program.id, name)));
230 wrap(['uniform1f', 'uniform1i'], (orig) => (loc, x) =>
231 orig.call(gl, loc && loc.id, x));
232 wrap(['uniform2f', 'uniform2i'], (orig) => (loc, x, y) =>
233 orig.call(gl, loc && loc.id, x, y));
234 wrap(['uniform3f', 'uniform3i'], (orig) => (loc, x, y, z) =>
235 orig.call(gl, loc && loc.id, x, y, z));
236 wrap(['uniform4f', 'uniform4i'], (orig) => (loc, x, y, z, w) =>
237 orig.call(gl, loc && loc.id, x, y, z, w));
238 wrap(['uniform1fv', 'uniform2fv', 'uniform3fv', 'uniform4fv'],
239 (orig) => (loc, val) => orig.call(gl, loc && loc.id, new Float32Array(val)));
240 wrap(['uniform1iv', 'uniform2iv', 'uniform3iv', 'uniform4iv'],
241 (orig) => (loc, val) => orig.call(gl, loc && loc.id, new Int32Array(val)));
242 wrap(['uniformMatrix2fv', 'uniformMatrix3fv', 'uniformMatrix4fv'],
243 (orig) => (loc, transpose, val) =>
244 orig.call(gl, loc && loc.id, transpose, new Float32Array(val)));
245 wrap(['vertexAttrib1fv', 'vertexAttrib2fv', 'vertexAttrib3fv', 'vertexAttrib4fv'],
246 (orig) => (index, val) => orig.call(gl, index, new Float32Array(val)));
247}
248
249// Get the GL interface from an EXGLContextID and do JS-side setup
250const getGl = (exglCtxId) => {
251 const gl = global.__EXGLContexts[exglCtxId];
252 delete global.__EXGLContexts[exglCtxId];
253 if (Object.setPrototypeOf) {
254 Object.setPrototypeOf(gl, global.WebGLRenderingContext.prototype);
255 } else {
256 gl.__proto__ = global.WebGLRenderingContext.prototype;
257 }
258
259 wrapMethods(gl);
260
261 // No canvas yet...
262 gl.canvas = null;
263
264 // Drawing buffer width/height
265 // TODO(nikki): Make this dynamic
266 const viewport = gl.getParameter(gl.VIEWPORT);
267 gl.drawingBufferWidth = viewport[2];
268 gl.drawingBufferHeight = viewport[3];
269
270 // Enable/disable logging of all GL function calls
271 let enableLogging = false;
272 Object.defineProperty(gl, 'enableLogging', {
273 configurable: true,
274 get() {
275 return enableLogging;
276 },
277 set(enable) {
278 if (enable === enableLogging) {
279 return;
280 }
281 if (enable) {
282 Object.keys(gl).forEach((key) => {
283 if (typeof gl[key] === 'function') {
284 const original = gl[key];
285 gl[key] = (...args) => {
286 console.log(`EXGL: ${key}(${args.join(', ')})`);
287 const r = original.apply(gl, args);
288 console.log(`EXGL: = ${r}`);
289 return r;
290 };
291 gl[key].original = original;
292 }
293 });
294 } else {
295 Object.keys(gl).forEach((key) => {
296 if (typeof gl[key] === 'function' && gl[key].original) {
297 gl[key] = gl[key].original;
298 }
299 });
300 }
301 enableLogging = enable;
302 },
303 });
304
305 return gl;
306};