xref: /expo/apps/test-suite/tests/GLView.js (revision 22d1e005)
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