Browse Source

Merge pull request #273 from molstar/shader-tests

add basic unit tests for graphics shaders
David Sehnal 3 years ago
parent
commit
5afdcff6a5

+ 0 - 18
.github/workflows/lint.yml

@@ -1,18 +0,0 @@
-on:
-  push:
-  pull_request:
-
-jobs:
-  eslint:
-    name: eslint
-    runs-on: ubuntu-latest
-    steps:
-    - uses: actions/checkout@v1
-    - name: install node v14
-      uses: actions/setup-node@v1
-      with:
-        node-version: 14
-    - name: yarn install
-      run: yarn install
-    - name: eslint
-      uses: icrawl/action-eslint@v1

+ 20 - 0
.github/workflows/node.yml

@@ -0,0 +1,20 @@
+on:
+  push:
+  pull_request:
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v2
+    - uses: actions/setup-node@v2
+      with:
+        node-version: 14
+    - run: npm ci
+    - run: sudo apt-get install xvfb
+    - name: Lint
+      run: npm run lint
+    - name: Test
+      run: xvfb-run --auto-servernum npm run jest
+    - name: Build
+      run: npm run build

File diff suppressed because it is too large
+ 14613 - 26
package-lock.json


+ 4 - 0
package.json

@@ -96,6 +96,7 @@
     "@graphql-codegen/typescript-graphql-request": "^4.1.4",
     "@graphql-codegen/typescript-operations": "^2.1.6",
     "@types/cors": "^2.8.12",
+    "@types/gl": "^4.1.0",
     "@typescript-eslint/eslint-plugin": "^4.32.0",
     "@typescript-eslint/parser": "^4.32.0",
     "benchmark": "^2.1.4",
@@ -151,5 +152,8 @@
     "tslib": "^2.3.1",
     "util.promisify": "^1.1.1",
     "xhr2": "^0.2.1"
+  },
+  "optionalDependencies": {
+    "gl": "^4.9.2"
   }
 }

+ 36 - 0
src/mol-gl/_spec/cylinders.spec.ts

@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { createRenderObject } from '../render-object';
+import { Scene } from '../scene';
+import { getGLContext, tryGetGLContext } from './gl';
+import { setDebugMode } from '../../mol-util/debug';
+import { ColorNames } from '../../mol-util/color/names';
+import { ParamDefinition as PD } from '../../mol-util/param-definition';
+import { Cylinders } from '../../mol-geo/geometry/cylinders/cylinders';
+
+export function createCylinders() {
+    const cylinders = Cylinders.createEmpty();
+    const props = PD.getDefaultValues(Cylinders.Params);
+    const values = Cylinders.Utils.createValuesSimple(cylinders, props, ColorNames.orange, 1);
+    const state = Cylinders.Utils.createRenderableState(props);
+    return createRenderObject('cylinders', values, state, -1);
+}
+
+describe('cylinders', () => {
+    const ctx = tryGetGLContext(32, 32, { fragDepth: true });
+
+    (ctx ? it : it.skip)('basic', async () => {
+        const ctx = getGLContext(32, 32);
+        const scene = Scene.create(ctx);
+        const cylinders = createCylinders();
+        scene.add(cylinders);
+        setDebugMode(true);
+        expect(() => scene.commit()).not.toThrow();
+        setDebugMode(false);
+        ctx.destroy();
+    });
+});

+ 36 - 0
src/mol-gl/_spec/direct-volume.spec.ts

@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { createRenderObject } from '../render-object';
+import { Scene } from '../scene';
+import { getGLContext, tryGetGLContext } from './gl';
+import { setDebugMode } from '../../mol-util/debug';
+import { ColorNames } from '../../mol-util/color/names';
+import { ParamDefinition as PD } from '../../mol-util/param-definition';
+import { DirectVolume } from '../../mol-geo/geometry/direct-volume/direct-volume';
+
+export function createDirectVolume() {
+    const directVolume = DirectVolume.createEmpty();
+    const props = PD.getDefaultValues(DirectVolume.Params);
+    const values = DirectVolume.Utils.createValuesSimple(directVolume, props, ColorNames.orange, 1);
+    const state = DirectVolume.Utils.createRenderableState(props);
+    return createRenderObject('direct-volume', values, state, -1);
+}
+
+describe('direct-volume', () => {
+    const ctx = tryGetGLContext(32, 32);
+
+    (ctx ? it : it.skip)('basic', async () => {
+        const ctx = getGLContext(32, 32);
+        const scene = Scene.create(ctx);
+        const directVolume = createDirectVolume();
+        scene.add(directVolume);
+        setDebugMode(true);
+        expect(() => scene.commit()).not.toThrow();
+        setDebugMode(false);
+        ctx.destroy();
+    });
+});

+ 2 - 2
src/mol-gl/_spec/gl.shim.ts

@@ -760,8 +760,8 @@ export function createGl(width: number, height: number, contextAttributes: WebGL
         validateProgram: function () { },
         generateMipmap: function () { },
         isContextLost: function () { return false; },
-        drawingBufferWidth: 1024,
-        drawingBufferHeight: 1024,
+        drawingBufferWidth: width,
+        drawingBufferHeight: height,
         blendColor: function () { },
         blendEquation: function () { },
         blendEquationSeparate: function () { },

+ 28 - 0
src/mol-gl/_spec/gl.ts

@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { createContext } from '../webgl/context';
+
+export function getGLContext(width: number, height: number) {
+    const gl = require('gl')(width, height, {
+        alpha: true,
+        depth: true,
+        premultipliedAlpha: true,
+        preserveDrawingBuffer: true,
+        antialias: true,
+    });
+    return createContext(gl);
+}
+
+export function tryGetGLContext(width: number, height: number, requiredExtensions?: { fragDepth?: boolean }) {
+    try {
+        const ctx = getGLContext(width, height);
+        if (requiredExtensions?.fragDepth && !ctx.extensions.fragDepth) return;
+        return ctx;
+    } catch (e) {
+        return;
+    }
+}

+ 36 - 0
src/mol-gl/_spec/image.spec.ts

@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { createRenderObject } from '../render-object';
+import { Scene } from '../scene';
+import { getGLContext, tryGetGLContext } from './gl';
+import { setDebugMode } from '../../mol-util/debug';
+import { ColorNames } from '../../mol-util/color/names';
+import { ParamDefinition as PD } from '../../mol-util/param-definition';
+import { Image } from '../../mol-geo/geometry/image/image';
+
+export function createImage() {
+    const image = Image.createEmpty();
+    const props = PD.getDefaultValues(Image.Params);
+    const values = Image.Utils.createValuesSimple(image, props, ColorNames.orange, 1);
+    const state = Image.Utils.createRenderableState(props);
+    return createRenderObject('image', values, state, -1);
+}
+
+describe('image', () => {
+    const ctx = tryGetGLContext(32, 32);
+
+    (ctx ? it : it.skip)('basic', async () => {
+        const ctx = getGLContext(32, 32);
+        const scene = Scene.create(ctx);
+        const image = createImage();
+        scene.add(image);
+        setDebugMode(true);
+        expect(() => scene.commit()).not.toThrow();
+        setDebugMode(false);
+        ctx.destroy();
+    });
+});

+ 36 - 0
src/mol-gl/_spec/lines.spec.ts

@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { createRenderObject } from '../render-object';
+import { Scene } from '../scene';
+import { getGLContext, tryGetGLContext } from './gl';
+import { setDebugMode } from '../../mol-util/debug';
+import { ColorNames } from '../../mol-util/color/names';
+import { ParamDefinition as PD } from '../../mol-util/param-definition';
+import { Lines } from '../../mol-geo/geometry/lines/lines';
+
+export function createLines() {
+    const lines = Lines.createEmpty();
+    const props = PD.getDefaultValues(Lines.Params);
+    const values = Lines.Utils.createValuesSimple(lines, props, ColorNames.orange, 1);
+    const state = Lines.Utils.createRenderableState(props);
+    return createRenderObject('lines', values, state, -1);
+}
+
+describe('lines', () => {
+    const ctx = tryGetGLContext(32, 32);
+
+    (ctx ? it : it.skip)('basic', async () => {
+        const ctx = getGLContext(32, 32);
+        const scene = Scene.create(ctx);
+        const lines = createLines();
+        scene.add(lines);
+        setDebugMode(true);
+        expect(() => scene.commit()).not.toThrow();
+        setDebugMode(false);
+        ctx.destroy();
+    });
+});

+ 36 - 0
src/mol-gl/_spec/mesh.spec.ts

@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { createRenderObject } from '../render-object';
+import { Scene } from '../scene';
+import { getGLContext, tryGetGLContext } from './gl';
+import { setDebugMode } from '../../mol-util/debug';
+import { ColorNames } from '../../mol-util/color/names';
+import { ParamDefinition as PD } from '../../mol-util/param-definition';
+import { Mesh } from '../../mol-geo/geometry/mesh/mesh';
+
+export function createMesh() {
+    const mesh = Mesh.createEmpty();
+    const props = PD.getDefaultValues(Mesh.Params);
+    const values = Mesh.Utils.createValuesSimple(mesh, props, ColorNames.orange, 1);
+    const state = Mesh.Utils.createRenderableState(props);
+    return createRenderObject('mesh', values, state, -1);
+}
+
+describe('mesh', () => {
+    const ctx = tryGetGLContext(32, 32);
+
+    (ctx ? it : it.skip)('basic', async () => {
+        const ctx = getGLContext(32, 32);
+        const scene = Scene.create(ctx);
+        const mesh = createMesh();
+        scene.add(mesh);
+        setDebugMode(true);
+        expect(() => scene.commit()).not.toThrow();
+        setDebugMode(false);
+        ctx.destroy();
+    });
+});

+ 36 - 0
src/mol-gl/_spec/points.spec.ts

@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { createRenderObject } from '../render-object';
+import { Scene } from '../scene';
+import { getGLContext, tryGetGLContext } from './gl';
+import { setDebugMode } from '../../mol-util/debug';
+import { ColorNames } from '../../mol-util/color/names';
+import { ParamDefinition as PD } from '../../mol-util/param-definition';
+import { Points } from '../../mol-geo/geometry/points/points';
+
+export function createPoints() {
+    const points = Points.createEmpty();
+    const props = PD.getDefaultValues(Points.Params);
+    const values = Points.Utils.createValuesSimple(points, props, ColorNames.orange, 1);
+    const state = Points.Utils.createRenderableState(props);
+    return createRenderObject('points', values, state, -1);
+}
+
+describe('points', () => {
+    const ctx = tryGetGLContext(32, 32);
+
+    (ctx ? it : it.skip)('basic', async () => {
+        const ctx = getGLContext(32, 32);
+        const scene = Scene.create(ctx);
+        const points = createPoints();
+        scene.add(points);
+        setDebugMode(true);
+        expect(() => scene.commit()).not.toThrow();
+        setDebugMode(false);
+        ctx.destroy();
+    });
+});

+ 5 - 85
src/mol-gl/_spec/renderer.spec.ts

@@ -5,28 +5,14 @@
  */
 
 import { createGl } from './gl.shim';
-
 import { Camera } from '../../mol-canvas3d/camera';
-import { Vec3, Mat4, Vec4 } from '../../mol-math/linear-algebra';
-import { ValueCell } from '../../mol-util';
-
+import { Vec3 } from '../../mol-math/linear-algebra';
 import { Renderer } from '../renderer';
-import { createValueColor } from '../../mol-geo/geometry/color-data';
-import { createValueSize } from '../../mol-geo/geometry/size-data';
 import { createContext } from '../webgl/context';
-import { RenderableState } from '../renderable';
-import { createRenderObject } from '../render-object';
-import { PointsValues } from '../renderable/points';
 import { Scene } from '../scene';
-import { createEmptyMarkers } from '../../mol-geo/geometry/marker-data';
-import { fillSerial } from '../../mol-util/array';
-import { Color } from '../../mol-util/color';
-import { Sphere3D } from '../../mol-math/geometry';
-import { createEmptyOverpaint } from '../../mol-geo/geometry/overpaint-data';
-import { createEmptyTransparency } from '../../mol-geo/geometry/transparency-data';
-import { createEmptyClipping } from '../../mol-geo/geometry/clipping-data';
+import { createPoints } from './points.spec';
 
-function createRenderer(gl: WebGLRenderingContext) {
+export function createRenderer(gl: WebGLRenderingContext) {
     const ctx = createContext(gl);
     const camera = new Camera({
         position: Vec3.create(0, 0, 50)
@@ -35,80 +21,14 @@ function createRenderer(gl: WebGLRenderingContext) {
     return { ctx, camera, renderer };
 }
 
-function createPoints() {
-    const aPosition = ValueCell.create(new Float32Array([0, -1, 0, -1, 0, 0, 1, 1, 0]));
-    const aGroup = ValueCell.create(fillSerial(new Float32Array(3)));
-    const aInstance = ValueCell.create(fillSerial(new Float32Array(1)));
-    const color = createValueColor(Color(0xFF0000));
-    const size = createValueSize(1);
-    const marker = createEmptyMarkers();
-    const overpaint = createEmptyOverpaint();
-    const transparency = createEmptyTransparency();
-    const clipping = createEmptyClipping();
-
-    const aTransform = ValueCell.create(new Float32Array(16));
-    const m4 = Mat4.identity();
-    Mat4.toArray(m4, aTransform.ref.value, 0);
-    const transform = ValueCell.create(new Float32Array(aTransform.ref.value));
-    const extraTransform = ValueCell.create(new Float32Array(aTransform.ref.value));
-
-    const boundingSphere = ValueCell.create(Sphere3D.create(Vec3.zero(), 2));
-    const invariantBoundingSphere = ValueCell.create(Sphere3D.create(Vec3.zero(), 2));
-
-    const values: PointsValues = {
-        aPosition,
-        aGroup,
-        aTransform,
-        aInstance,
-        ...color,
-        ...marker,
-        ...size,
-        ...overpaint,
-        ...transparency,
-        ...clipping,
-
-        uAlpha: ValueCell.create(1.0),
-        uVertexCount: ValueCell.create(3),
-        uInstanceCount: ValueCell.create(1),
-        uGroupCount: ValueCell.create(3),
-        uInvariantBoundingSphere: ValueCell.create(Vec4.ofSphere(invariantBoundingSphere.ref.value)),
-
-        alpha: ValueCell.create(1.0),
-        drawCount: ValueCell.create(3),
-        instanceCount: ValueCell.create(1),
-        matrix: ValueCell.create(m4),
-        transform,
-        extraTransform,
-        hasReflection: ValueCell.create(false),
-        boundingSphere,
-        invariantBoundingSphere,
-
-        uSizeFactor: ValueCell.create(1),
-        dPointSizeAttenuation: ValueCell.create(true),
-        dPointStyle: ValueCell.create('square'),
-    };
-    const state: RenderableState = {
-        disposed: false,
-        visible: true,
-        alphaFactor: 1,
-        pickable: true,
-        colorOnly: false,
-        opaque: true,
-        writeDepth: true,
-        noClip: false,
-    };
-
-    return createRenderObject('points', values, state, -1);
-}
-
 describe('renderer', () => {
     it('basic', () => {
         const [width, height] = [32, 32];
         const gl = createGl(width, height, { preserveDrawingBuffer: true });
         const { ctx, renderer } = createRenderer(gl);
 
-        expect(ctx.gl.canvas.width).toBe(32);
-        expect(ctx.gl.canvas.height).toBe(32);
+        expect(ctx.gl.drawingBufferWidth).toBe(32);
+        expect(ctx.gl.drawingBufferHeight).toBe(32);
 
         expect(ctx.stats.resourceCounts.attribute).toBe(0);
         expect(ctx.stats.resourceCounts.texture).toBe(0);

+ 36 - 0
src/mol-gl/_spec/spheres.spec.ts

@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { createRenderObject } from '../render-object';
+import { Scene } from '../scene';
+import { getGLContext, tryGetGLContext } from './gl';
+import { setDebugMode } from '../../mol-util/debug';
+import { ColorNames } from '../../mol-util/color/names';
+import { ParamDefinition as PD } from '../../mol-util/param-definition';
+import { Spheres } from '../../mol-geo/geometry/spheres/spheres';
+
+export function createSpheres() {
+    const spheres = Spheres.createEmpty();
+    const props = PD.getDefaultValues(Spheres.Params);
+    const values = Spheres.Utils.createValuesSimple(spheres, props, ColorNames.orange, 1);
+    const state = Spheres.Utils.createRenderableState(props);
+    return createRenderObject('spheres', values, state, -1);
+}
+
+describe('spheres', () => {
+    const ctx = tryGetGLContext(32, 32, { fragDepth: true });
+
+    (ctx ? it : it.skip)('basic', async () => {
+        const ctx = getGLContext(32, 32);
+        const scene = Scene.create(ctx);
+        const spheres = createSpheres();
+        scene.add(spheres);
+        setDebugMode(true);
+        expect(() => scene.commit()).not.toThrow();
+        setDebugMode(false);
+        ctx.destroy();
+    });
+});

+ 36 - 0
src/mol-gl/_spec/text.spec.ts

@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { createRenderObject } from '../render-object';
+import { Scene } from '../scene';
+import { getGLContext, tryGetGLContext } from './gl';
+import { setDebugMode } from '../../mol-util/debug';
+import { ColorNames } from '../../mol-util/color/names';
+import { ParamDefinition as PD } from '../../mol-util/param-definition';
+import { Text } from '../../mol-geo/geometry/text/text';
+
+export function createText() {
+    const text = Text.createEmpty();
+    const props = PD.getDefaultValues(Text.Params);
+    const values = Text.Utils.createValuesSimple(text, props, ColorNames.orange, 1);
+    const state = Text.Utils.createRenderableState(props);
+    return createRenderObject('text', values, state, -1);
+}
+
+describe('text', () => {
+    const ctx = tryGetGLContext(32, 32);
+
+    (ctx ? it : it.skip)('basic', async () => {
+        const ctx = getGLContext(32, 32);
+        const scene = Scene.create(ctx);
+        const text = createText();
+        scene.add(text);
+        setDebugMode(true);
+        expect(() => scene.commit()).not.toThrow();
+        setDebugMode(false);
+        ctx.destroy();
+    });
+});

+ 36 - 0
src/mol-gl/_spec/texture-mesh.spec.ts

@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { createRenderObject } from '../render-object';
+import { Scene } from '../scene';
+import { getGLContext, tryGetGLContext } from './gl';
+import { setDebugMode } from '../../mol-util/debug';
+import { ColorNames } from '../../mol-util/color/names';
+import { ParamDefinition as PD } from '../../mol-util/param-definition';
+import { TextureMesh } from '../../mol-geo/geometry/texture-mesh/texture-mesh';
+
+export function createTextureMesh() {
+    const textureMesh = TextureMesh.createEmpty();
+    const props = PD.getDefaultValues(TextureMesh.Params);
+    const values = TextureMesh.Utils.createValuesSimple(textureMesh, props, ColorNames.orange, 1);
+    const state = TextureMesh.Utils.createRenderableState(props);
+    return createRenderObject('texture-mesh', values, state, -1);
+}
+
+describe('texture-mesh', () => {
+    const ctx = tryGetGLContext(32, 32);
+
+    (ctx ? it : it.skip)('basic', async () => {
+        const ctx = getGLContext(32, 32);
+        const scene = Scene.create(ctx);
+        const textureMesh = createTextureMesh();
+        scene.add(textureMesh);
+        setDebugMode(true);
+        expect(() => scene.commit()).not.toThrow();
+        setDebugMode(false);
+        ctx.destroy();
+    });
+});

+ 4 - 1
src/mol-gl/webgl/context.ts

@@ -358,7 +358,10 @@ export function createContext(gl: GLRenderingContext, props: Partial<{ pixelScal
             unbindResources(gl);
 
             // to aid GC
-            if (!options?.doNotForceWebGLContextLoss) gl.getExtension('WEBGL_lose_context')?.loseContext();
+            if (!options?.doNotForceWebGLContextLoss) {
+                gl.getExtension('WEBGL_lose_context')?.loseContext();
+                gl.getExtension('STACKGL_destroy_context')?.destroy();
+            }
         }
     };
 }

+ 1 - 0
tsconfig.commonjs.json

@@ -14,6 +14,7 @@
         "moduleResolution": "node",
         "importHelpers": true,
         "noEmitHelpers": true,
+        "allowSyntheticDefaultImports": true,
         "jsx": "react-jsx",
         "lib": [ "es6", "dom", "esnext.asynciterable", "es2016" ],
         "rootDir": "src",

+ 1 - 0
tsconfig.json

@@ -14,6 +14,7 @@
         "moduleResolution": "node",
         "importHelpers": true,
         "noEmitHelpers": true,
+        "allowSyntheticDefaultImports": true,
         "jsx": "react-jsx",
         "lib": [ "es6", "dom", "esnext.asynciterable", "es2016" ],
         "rootDir": "src",

Some files were not shown because too many files changed in this diff