1'use strict'; 2import { Asset } from 'expo-asset'; 3import { GLView } from 'expo-gl'; 4import { Platform } from 'expo-modules-core'; 5import React from 'react'; 6 7import { mountAndWaitFor } from './helpers'; 8 9export const name = 'GLView'; 10const style = { width: 200, height: 200 }; 11 12export async function test( 13 { it, describe, beforeAll, jasmine, afterAll, expect, afterEach, beforeEach }, 14 { setPortalChild, cleanupPortal } 15) { 16 let instance = null; 17 let originalTimeout; 18 19 const refSetter = (ref) => { 20 instance = ref; 21 }; 22 23 beforeAll(async () => { 24 originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL; 25 jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout * 3; 26 }); 27 28 afterAll(() => { 29 jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout; 30 }); 31 32 afterEach(async () => { 33 instance = null; 34 await cleanupPortal(); 35 }); 36 37 function getContextAsync() { 38 return new Promise(async (resolve) => { 39 await mountAndWaitFor( 40 <GLView onContextCreate={(context) => resolve(context)} ref={refSetter} style={style} />, 41 'onContextCreate', 42 setPortalChild 43 ); 44 }); 45 } 46 47 describe('GLView', () => { 48 it('gets a valid context', async () => { 49 const context = await getContextAsync(); 50 expect( 51 context instanceof WebGLRenderingContext || context instanceof WebGL2RenderingContext 52 ).toBe(true); 53 }); 54 55 it('takes a snapshot', async () => { 56 await getContextAsync(); 57 58 const snapshot = await instance.takeSnapshotAsync({ format: 'png' }); 59 expect(snapshot).toBeDefined(); 60 if (Platform.OS === 'web') { 61 expect(snapshot.uri instanceof Blob).toBe(true); 62 } else { 63 expect(snapshot.uri).toMatch(/^file:\/\//); 64 expect(snapshot.localUri).toMatch(/^file:\/\//); 65 } 66 }); 67 68 describe('context', () => { 69 const vertexShader = ` 70 precision highp float; 71 attribute vec2 position; 72 varying vec2 uv; 73 void main () { 74 uv = position; 75 gl_Position = vec4(1.0 - 2.0 * position, 0, 1); 76 }`; 77 const fragShader = ` 78 precision highp float; 79 uniform sampler2D texture; 80 varying vec2 uv; 81 void main () { 82 gl_FragColor = texture2D(texture, vec2(uv.x, uv.y)); 83 } 84 `; 85 it('has Expo methods', async () => { 86 const context = await getContextAsync(); 87 expect(typeof context.endFrameEXP).toBe('function'); 88 }); 89 it('clears to blue without throwing', async () => { 90 const gl = await getContextAsync(); 91 gl.clearColor(0, 0, 1, 1); 92 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 93 gl.endFrameEXP(); 94 }); 95 96 it(`draws a texture`, async () => { 97 const gl = await getContextAsync(); 98 99 const vert = gl.createShader(gl.VERTEX_SHADER); 100 gl.shaderSource(vert, vertexShader); 101 gl.compileShader(vert); 102 const frag = gl.createShader(gl.FRAGMENT_SHADER); 103 gl.shaderSource(frag, fragShader); 104 gl.compileShader(frag); 105 106 const program = gl.createProgram(); 107 gl.attachShader(program, vert); 108 gl.attachShader(program, frag); 109 gl.linkProgram(program); 110 gl.useProgram(program); 111 112 const buffer = gl.createBuffer(); 113 gl.bindBuffer(gl.ARRAY_BUFFER, buffer); 114 const verts = new Float32Array([-2, 0, 0, -2, 2, 2]); 115 gl.bufferData(gl.ARRAY_BUFFER, verts, gl.STATIC_DRAW); 116 const positionAttrib = gl.getAttribLocation(program, 'position'); 117 gl.enableVertexAttribArray(positionAttrib); 118 gl.vertexAttribPointer(positionAttrib, 2, gl.FLOAT, false, 0, 0); 119 120 const asset = Asset.fromModule(require('../assets/qrcode_expo.jpg')); 121 await asset.downloadAsync(); 122 const texture = gl.createTexture(); 123 gl.activeTexture(gl.TEXTURE0); 124 gl.bindTexture(gl.TEXTURE_2D, texture); 125 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); 126 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); 127 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, asset); 128 gl.uniform1i(gl.getUniformLocation(program, 'texture'), 0); 129 130 gl.clearColor(0, 0, 1, 1); 131 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 132 gl.drawArrays(gl.TRIANGLES, 0, verts.length / 2); 133 gl.endFrameEXP(); 134 }); 135 136 it(`draws a texture with a TypedArray`, async () => { 137 const gl = await getContextAsync(); 138 139 const vert = gl.createShader(gl.VERTEX_SHADER); 140 gl.shaderSource(vert, vertexShader); 141 142 gl.compileShader(vert); 143 const frag = gl.createShader(gl.FRAGMENT_SHADER); 144 gl.shaderSource(frag, fragShader); 145 gl.compileShader(frag); 146 147 const program = gl.createProgram(); 148 gl.attachShader(program, vert); 149 gl.attachShader(program, frag); 150 gl.linkProgram(program); 151 gl.useProgram(program); 152 153 const buffer = gl.createBuffer(); 154 gl.bindBuffer(gl.ARRAY_BUFFER, buffer); 155 const verts = new Float32Array([-2, 0, 0, -2, 2, 2]); 156 gl.bufferData(gl.ARRAY_BUFFER, verts, gl.STATIC_DRAW); 157 const positionAttrib = gl.getAttribLocation(program, 'position'); 158 gl.enableVertexAttribArray(positionAttrib); 159 gl.vertexAttribPointer(positionAttrib, 2, gl.FLOAT, false, 0, 0); 160 161 const texture = gl.createTexture(); 162 gl.activeTexture(gl.TEXTURE0); 163 gl.bindTexture(gl.TEXTURE_2D, texture); 164 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); 165 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); 166 167 // Use below to test using a `TypedArray` parameter 168 gl.texSubImage2D( 169 gl.TEXTURE_2D, 170 0, 171 32, 172 32, 173 2, 174 2, 175 gl.RGBA, 176 gl.UNSIGNED_BYTE, 177 new Uint8Array([255, 0, 0, 255, 0, 255, 0, 255, 0, 0, 255, 255, 128, 128, 0, 255]) 178 ); 179 180 gl.uniform1i(gl.getUniformLocation(program, 'texture'), 0); 181 182 gl.clearColor(0, 0, 1, 1); 183 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 184 gl.drawArrays(gl.TRIANGLES, 0, verts.length / 2); 185 gl.endFrameEXP(); 186 }); 187 }); 188 189 describe('static', () => { 190 it('creates a static context', async () => { 191 const context = await GLView.createContextAsync(); 192 expect( 193 context instanceof WebGLRenderingContext || context instanceof WebGL2RenderingContext 194 ).toBe(true); 195 }); 196 197 it('takes a snapshot', async () => { 198 const context = await getContextAsync(); 199 200 const snapshot = await GLView.takeSnapshotAsync(context, { format: 'png' }); 201 expect(snapshot).toBeDefined(); 202 203 if (Platform.OS === 'web') { 204 expect(snapshot.uri instanceof Blob).toBe(true); 205 } else { 206 expect(snapshot.uri).toMatch(/^file:\/\//); 207 expect(snapshot.localUri).toMatch(/^file:\/\//); 208 } 209 }); 210 }); 211 }); 212} 213