import {Buffer, Texture2D} from '@luma.gl/webgl'; import {Transform} from '@luma.gl/engine'; import test from 'tape-catch'; import {fixture} from 'test/setup'; import GL from '@luma.gl/constants'; import {setParameters, getParameters} from '@luma.gl/gltools'; const VS = `\ #version 300 es in float inValue; out float outValue; void main() { outValue = 2.0 * inValue; } `; const VS2 = `\ #version 300 es in float inValue1; in float inValue2; out float doubleValue; out float halfValue; void main() { doubleValue = 2.0 * inValue1; halfValue = 0.5 * inValue2; } `; const VS_NO_SOURCE_BUFFER = `\ varying float outValue; uniform float uValue; void main() { outValue = uValue * 2.; } `; function getResourceCounts() { /* global luma */ const resourceStats = luma.stats.get('Resource Counts'); return { Texture2D: resourceStats.get('Texture2Ds Active').count, Buffer: resourceStats.get('Buffers Active').count }; } function validateResourceCounts(t, startCounts, endCounts) { for (const resourceName in endCounts) { const leakCount = endCounts[resourceName] - startCounts[resourceName]; t.ok(leakCount === 0, `should delete all ${resourceName}, remaining ${leakCount}`); } } test('WebGL#Transform construction', t => { const gl = fixture.gl2; if (!gl) { t.comment('WebGL2 not available, skipping tests'); t.end(); return; } const transform = new Transform(gl, { vs: VS, sourceBuffers: { inValue: new Buffer(gl, {id: 'inValue', data: new Float32Array([0, 2.7, -45])}) }, feedbackBuffers: { outValue: 'inValue' }, elementCount: 3 }); t.ok(transform instanceof Transform, 'should construct Transform object'); t.end(); }); test('WebGL#Transform constructor/delete', t => { const {gl, gl2} = fixture; t.throws(() => new Transform(), 'Transform throws on missing gl context'); t.throws(() => new Transform(gl), 'Transform throws on missing gl context'); if (!gl2) { t.comment('WebGL2 not available, skipping tests'); t.end(); return; } const sourceData = new Float32Array([10, 20, 31, 0, -57]); const sourceBuffer = new Buffer(gl2, {data: sourceData}); const transform = new Transform(gl2, { sourceBuffers: { inValue: sourceBuffer }, vs: VS, feedbackMap: { inValue: 'outValue' }, varyings: ['outValue'], elementCount: 5 }); t.ok(transform instanceof Transform, 'Transform construction successful'); transform.delete(); t.ok(transform instanceof Transform, 'Transform delete successful'); transform.delete(); t.ok(transform instanceof Transform, 'Transform repeated delete successful'); t.end(); }); test('WebGL#Transform run', t => { const {gl2} = fixture; if (!gl2) { t.comment('WebGL2 not available, skipping tests'); t.end(); return; } const sourceData = new Float32Array([10, 20, 31, 0, -57]); const sourceBuffer = new Buffer(gl2, {data: sourceData}); const transform = new Transform(gl2, { sourceBuffers: { inValue: sourceBuffer }, vs: VS, feedbackMap: { inValue: 'outValue' }, varyings: ['outValue'], elementCount: 5 }); transform.run(); const expectedData = sourceData.map(x => x * 2); const outData = transform.getData({varyingName: 'outValue'}); t.deepEqual(outData, expectedData, 'Transform.getData: is successful'); t.end(); }); test('WebGL#Transform run (feedbackBuffer offset)', t => { const {gl2} = fixture; if (!gl2) { t.comment('WebGL2 not available, skipping tests'); t.end(); return; } const sourceData = new Float32Array([10, 20, 31, 0, -57]); const sourceBuffer = new Buffer(gl2, {data: sourceData}); const outBuffer = new Buffer(gl2, 10 * 4); // 10 floats const offset = 3; const transform = new Transform(gl2, { sourceBuffers: { inValue: sourceBuffer }, vs: VS, feedbackBuffers: { outValue: {buffer: outBuffer, byteOffset: 4 * offset} }, varyings: ['outValue'], elementCount: 5 }); transform.run(); const expectedData = sourceData.map(x => x * 2); const outData = transform.getData({varyingName: 'outValue'}).slice(offset, offset + 5); t.deepEqual(outData, expectedData, 'Transform.getData: is successful'); t.end(); }); test('WebGL#Transform run (no source buffer)', t => { const {gl2} = fixture; if (!gl2) { t.comment('WebGL2 not available, skipping tests'); t.end(); return; } const INPUT = 101; const outBuffer = new Buffer(gl2, 4); const transform = new Transform(gl2, { feedbackBuffers: { outValue: outBuffer }, vs: VS_NO_SOURCE_BUFFER, varyings: ['outValue'], elementCount: 1 }); transform.run({uniforms: {uValue: INPUT}}); const expectedData = [INPUT * 2]; const outData = transform.getBuffer('outValue').getData(); t.deepEqual(outData, expectedData, 'Transform.getData: is successful'); t.end(); }); /* TODO Attribute class has been moved out Either remove these tests or create a dummy Attribute with getValue method. If deck.gl is refactoring then we should just remove. test('WebGL#Transform run (Attribute)', t => { const {gl2} = fixture; if (!gl2) { t.comment('WebGL2 not available, skipping tests'); t.end(); return; } const sourceData = new Float32Array([10, 20, 31, 0, -57]); const sourceBuffer = new Attribute(gl2, {value: sourceData}); const feedbackBuffer = new Buffer(gl2, {data: sourceData}); const transform = new Transform(gl2, { sourceBuffers: { inValue: sourceBuffer }, feedbackBuffers: { outValue: feedbackBuffer }, vs: VS, varyings: ['outValue'], elementCount: 5 }); transform.run(); const expectedData = sourceData.map(x => x * 2); const outData = transform.getData({varyingName: 'outValue'}); t.deepEqual(outData, expectedData, 'Transform.getData: is successful'); t.end(); }); // TODO - enabling this test breaks histopyramid.spec.js in headless mode test('WebGL#Transform run (constant Attribute)', t => { const {gl2} = fixture; if (!gl2) { t.comment('WebGL2 not available, skipping tests'); t.end(); return; } const MULTIPLIER = 5; const sourceData = new Float32Array([10, 20, 31, 0, -57]); const sourceBuffer = new Attribute(gl2, {value: sourceData}); const multiplier = new Attribute(gl2, {value: [MULTIPLIER], constant: true}); const feedbackBuffer = new Buffer(gl2, {data: sourceData}); const transform = new Transform(gl2, { sourceBuffers: { inValue: sourceBuffer, multiplier }, feedbackBuffers: { outValue: feedbackBuffer }, vs: VS_CONSTANT_ATTRIBUTE, varyings: ['outValue'], elementCount: 5 }); transform.run(); const expectedData = sourceData.map(x => x * MULTIPLIER); const outData = transform.getData({varyingName: 'outValue'}); t.deepEqual(outData, expectedData, 'Transform.getData: is successful'); t.end(); }); */ test('WebGL#Transform swap', t => { const {gl2} = fixture; if (!gl2) { t.comment('WebGL2 not available, skipping tests'); t.end(); return; } const sourceData = new Float32Array([10, 20, 31, 0, -57]); const sourceBuffer = new Buffer(gl2, {data: sourceData}); const transform = new Transform(gl2, { sourceBuffers: { inValue: sourceBuffer }, vs: VS, feedbackMap: { inValue: 'outValue' }, varyings: ['outValue'], elementCount: 5 }); transform.run(); transform.swap(); transform.run(); const expectedData = sourceData.map(x => x * 4); const outData = transform.getData({varyingName: 'outValue'}); t.deepEqual(outData, expectedData, 'Transform.getData: is successful'); t.end(); }); test('WebGL#Transform swap + update', t => { const {gl2} = fixture; if (!gl2) { t.comment('WebGL2 not available, skipping tests'); t.end(); return; } let sourceData = new Float32Array([10, 20, 31, 0, -57]); let sourceBuffer = new Buffer(gl2, {data: sourceData}); const transform = new Transform(gl2, { sourceBuffers: { inValue: sourceBuffer }, vs: VS, feedbackMap: { inValue: 'outValue' }, varyings: ['outValue'], elementCount: 5 }); transform.run(); transform.swap(); // Increase the buffer size sourceData = new Float32Array([1, 2, 3, 4, 5, 6, 7]); sourceBuffer = new Buffer(gl2, {data: sourceData}); transform.update({ sourceBuffers: { inValue: sourceBuffer }, elementCount: 7 }); transform.run(); let expectedData = sourceData.map(x => x * 2); let outData = transform.getData({varyingName: 'outValue'}); transform.swap(); transform.run(); expectedData = sourceData.map(x => x * 4); outData = transform.getData({varyingName: 'outValue'}); t.deepEqual(outData, expectedData, 'Transform.getData: is successful'); t.end(); }); test('WebGL#Transform swap without varyings', t => { const {gl2} = fixture; if (!gl2) { t.comment('WebGL2 not available, skipping tests'); t.end(); return; } const sourceData1 = new Float32Array([10, 20, 30]); const sourceBuffer1 = new Buffer(gl2, {data: sourceData1}); const sourceData2 = new Float32Array([10, 20, 30]); const sourceBuffer2 = new Buffer(gl2, {data: sourceData2}); // varyings array is dedueced from feedbackMap. const transform = new Transform(gl2, { sourceBuffers: { inValue1: sourceBuffer1, inValue2: sourceBuffer2 }, vs: VS2, feedbackMap: { inValue2: 'halfValue', inValue1: 'doubleValue' }, elementCount: 3 }); transform.run(); transform.swap(); transform.run(); const expectedDoubleData = sourceData1.map(x => x * 4); const expectedHalfData = sourceData2.map(x => x * 0.25); const doubleData = transform.getData({varyingName: 'doubleValue'}); const halfData = transform.getData({varyingName: 'halfValue'}); t.deepEqual(doubleData, expectedDoubleData, 'Transform.getData: is successful'); t.deepEqual(halfData, expectedHalfData, 'Transform.getData: is successful'); t.end(); }); /* eslint-disable max-statements */ test('WebGL#Transform update', t => { const {gl2} = fixture; if (!gl2) { t.comment('WebGL2 not available, skipping tests'); t.end(); return; } let sourceData = new Float32Array([10, 20, 31, 0, -57]); let sourceBuffer = new Buffer(gl2, {data: sourceData}); let expectedData; let outData; const transform = new Transform(gl2, { vs: VS, varyings: ['outValue'] }); t.ok(transform, 'should construct without buffers'); transform.update({ sourceBuffers: { inValue: sourceBuffer }, feedbackMap: { inValue: 'outValue' }, elementCount: 5 }); transform.run(); expectedData = sourceData.map(x => x * 2); outData = transform.getData({varyingName: 'outValue'}); t.deepEqual(outData, expectedData, 'Transform.getData: is successful'); sourceData = new Float32Array([1, 2, 3, 0, -5]); sourceBuffer.delete(); sourceBuffer = new Buffer(gl2, {data: sourceData}); transform.update({ sourceBuffers: { inValue: sourceBuffer } }); t.is(transform.model.vertexCount, 5, 'Transform has correct element count'); transform.run(); expectedData = sourceData.map(x => x * 2); outData = transform.getData({varyingName: 'outValue'}); t.deepEqual(outData, expectedData, 'Transform.getData: is successful'); sourceData = new Float32Array([3, 4, 5, 2, -3, 0]); sourceBuffer.delete(); sourceBuffer = new Buffer(gl2, {data: sourceData}); transform.update({ sourceBuffers: { inValue: sourceBuffer }, feedbackBuffers: { outValue: new Buffer(gl2, {data: new Float32Array(6)}) }, elementCount: 6 }); t.is(transform.model.vertexCount, 6, 'Element count is updated'); transform.run(); expectedData = sourceData.map(x => x * 2); outData = transform.getData({varyingName: 'outValue'}); t.deepEqual(outData, expectedData, 'Transform.getData: is successful'); transform.update({ elementCount: 0 }); t.is(transform.model.vertexCount, 0, 'Element count is updated to 0'); t.end(); }); const VSTexInput = `\ #version 300 es in float inBuffer; in float inTexture; out float outValue; void main() { outValue = inBuffer + inTexture; } `; test('WebGL#Transform run (source texture + feedback buffer)', t => { const {gl2} = fixture; if (!gl2) { t.comment('WebGL2 not available, skipping tests'); t.end(); return; } const sourceData = new Float32Array([20, -31, 0, 23.45]); const sourceBuffer = new Buffer(gl2, {data: sourceData}); const sourceTexture = new Texture2D(gl2, { data: sourceData, format: GL.R32F, dataFormat: GL.RED, type: GL.FLOAT, mipmaps: false, width: 2, height: 2, pixelStore: { [GL.UNPACK_FLIP_Y_WEBGL]: false } }); const transform = new Transform(gl2, { sourceBuffers: { inBuffer: sourceBuffer }, _sourceTextures: { inTexture: sourceTexture }, vs: VSTexInput, feedbackMap: { inBuffer: 'outValue' }, elementCount: sourceData.length }); transform.run(); const expectedData = sourceData.map(x => x * 2); const outData = transform.getData({varyingName: 'outValue'}); t.deepEqual(outData, expectedData, 'Transform.getData: is successful'); t.end(); }); const TEXTURE_BUFFER_TEST_CASES = [ // NOTE: elementCount is equal to width * height // TODO: determine width and height based on elementCount and padding if needed { name: 'RED-FLOAT', sourceData: new Float32Array([0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]), format: GL.R32F, dataFormat: GL.RED, type: GL.FLOAT, width: 4, height: 4, vs: `\ #version 300 es in float inBuffer; in float inTexture; out float outBuffer; out float outTexture; void main() { outBuffer = inTexture + inBuffer; outTexture = inTexture + inBuffer; } ` }, { name: 'RGBA-UNSIGNED_BYTE', sourceData: new Uint8Array([0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]), format: GL.RGBA, dataFormat: GL.RGBA, type: GL.UNSIGNED_BYTE, width: 2, height: 2, vs: `\ #version 300 es in float inBuffer; in vec4 inTexture; out float outBuffer; out vec4 outTexture; void main() { outBuffer = 2. * inBuffer; outTexture = 2. * inTexture; } ` } ]; test('WebGL#Transform run (source&destination texture + feedback buffer)', t => { const {gl2} = fixture; if (!gl2) { t.comment('WebGL2 not available, skipping tests'); t.end(); return; } TEXTURE_BUFFER_TEST_CASES.forEach(testCase => { const {sourceData, format, dataFormat, type, width, height, name, vs} = testCase; const sourceBuffer = new Buffer(gl2, {data: new Float32Array(sourceData)}); const sourceTexture = new Texture2D(gl2, { data: sourceData, format, dataFormat, type, mipmaps: false, width, height, pixelStore: { [GL.UNPACK_FLIP_Y_WEBGL]: false } }); const transform = new Transform(gl2, { sourceBuffers: { inBuffer: sourceBuffer }, _sourceTextures: { inTexture: sourceTexture }, _targetTextureVarying: 'outTexture', _targetTexture: 'inTexture', vs, feedbackMap: { inBuffer: 'outBuffer' }, elementCount: sourceData.length }); transform.run(); const expectedData = sourceData.map(x => x * 2); const outData = transform.getData({varyingName: 'outBuffer'}); t.deepEqual(outData, expectedData, `${name} Transform should write correct data into Buffer`); // By default getData reads data from current Framebuffer. const outTexData = transform.getData({varyingName: 'outTexture', packed: true}); // t.deepEqual(outData, expectedData, 'Transform should write correct data into Buffer'); t.deepEqual( outTexData, expectedData, `${name} Transform should write correct data into Texture` ); }); t.end(); }); const TEXTURE_TEST_CASES = [ // NOTE: elementCount is equal to width * height // TODO: determine width and height based on elementCount and padding if needed { name: 'RGBA-FLOAT', sourceData: new Float32Array([ 0, 0, 0, 0, -1, -2, -3, -4, 2, 3, 4, 5, 10, 20, 30, 40, 5, 6, 7, 8, 51, 61, 71, 81, -15, -16, 70, 81, 50, 100, -2, -5, 9, 10, 11, 12, 0, -20, 52, 78, -3, -4, 2, 3, 8, 51, 61, 71, 3, 14, 15, 16, -4, 2, 3, 4, 11, 12, 0, -20, 0, 0, -1, -2 ]), format: GL.RGBA32F, dataFormat: GL.RGBA, type: GL.FLOAT, width: 4, height: 4, vs: `\ #version 300 es in vec4 inTexture; out vec4 outTexture; void main() { outTexture = 2. * inTexture; } ` }, { name: 'RED-FLOAT', sourceData: new Float32Array([0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]), format: GL.R32F, dataFormat: GL.RED, type: GL.FLOAT, width: 4, height: 4, vs: `\ #version 300 es in float inTexture; out float outTexture; void main() { outTexture = 2. * inTexture; } ` }, { name: 'RGBA-UNSIGNED_BYTE', sourceData: new Uint8Array([0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]), format: GL.RGBA, dataFormat: GL.RGBA, type: GL.UNSIGNED_BYTE, width: 2, height: 2, vs: `\ #version 300 es in vec4 inTexture; out vec4 outTexture; void main() { outTexture = 2. * inTexture; } ` } ]; test('WebGL#Transform run (source&destination texture)', t => { const {gl2} = fixture; if (!gl2) { t.comment('WebGL2 not available, skipping tests'); t.end(); return; } TEXTURE_TEST_CASES.forEach(testCase => { const {sourceData, format, dataFormat, type, width, height, name, vs} = testCase; const sourceTexture = new Texture2D(gl2, { data: sourceData, format, dataFormat, type, mipmaps: false, width, height, pixelStore: { [GL.UNPACK_FLIP_Y_WEBGL]: false } }); const transform = new Transform(gl2, { _sourceTextures: { inTexture: sourceTexture }, _targetTexture: 'inTexture', _targetTextureVarying: 'outTexture', _swapTexture: 'inTexture', vs, elementCount: sourceData.length }); transform.run(); let expectedData = sourceData.map(x => x * 2); // By default getData reads data from current Framebuffer. let outTexData = transform.getData({packed: true}); t.deepEqual( outTexData, expectedData, `${name} Transform should write correct data into Texture` ); transform.swap(); transform.run(); expectedData = sourceData.map(x => x * 4); // By default getData reads data from current Framebuffer. outTexData = transform.getData({packed: true}); t.deepEqual(outTexData, expectedData, `${name} Transform swap Textures`); }); t.end(); }); /* test.only('WebGL#Transform update (source&destination texture)', t => { const {gl2} = fixture; if (!gl2) { t.comment('WebGL2 not available, skipping tests'); t.end(); return; } const {sourceData, format, dataFormat, type, width, height, name, vs} = TEXTURE_TEST_CASES[0]; const sourceTexture = new Texture2D(gl2, { data: sourceData, format, dataFormat, type, mipmaps: false, width, height, pixelStore: { [GL.UNPACK_FLIP_Y_WEBGL]: false } }); const transform = new Transform(gl2, { _targetTextureVarying: 'outTexture', _swapTexture: 'inTexture', vs }); transform.update({ _sourceTextures: { inTexture: sourceTexture }, _targetTexture: 'inTexture', elementCount: sourceData.length }) transform.run(); let expectedData = sourceData.map(x => x * 2); // By default getData reads data from current Framebuffer. let outTexData = transform.getData({packed: true}); t.deepEqual( outTexData, expectedData, `${name} Transform should write correct data into Texture` ); transform.swap(); transform.run(); expectedData = sourceData.map(x => x * 4); // By default getData reads data from current Framebuffer. outTexData = transform.getData({packed: true}); t.deepEqual(outTexData, expectedData, `${name} Transform swap Textures`); t.end(); }); */ test('WebGL#Transform run (source&destination texture update)', t => { const {gl2} = fixture; if (!gl2) { t.comment('WebGL2 not available, skipping tests'); t.end(); return; } TEXTURE_TEST_CASES.forEach(testCase => { const startCounts = getResourceCounts(); const {sourceData, format, dataFormat, type, width, height, name, vs} = testCase; // const sourceBuffer = new Buffer(gl2, {data: new Float32Array(sourceData)}); const sourceTexture = new Texture2D(gl2, { data: sourceData, format, dataFormat, type, mipmaps: false, width, height, pixelStore: { [GL.UNPACK_FLIP_Y_WEBGL]: false } }); const transform = new Transform(gl2, { _sourceTextures: { inTexture: sourceTexture }, _targetTexture: 'inTexture', _targetTextureVarying: 'outTexture', _swapTexture: 'inTexture', vs, elementCount: sourceData.length }); transform.run(); const updateData = sourceData.map(x => x + 3); const updateTexture = new Texture2D(gl2, { data: updateData, format, dataFormat, type, mipmaps: false, width, height, pixelStore: { [GL.UNPACK_FLIP_Y_WEBGL]: false } }); transform.update({_sourceTextures: {inTexture: updateTexture}}); transform.run(); const expectedData = updateData.map(x => x * 2); // By default getData reads data from current Framebuffer. const outTexData = transform.getData({packed: true}); t.deepEqual( outTexData, expectedData, `${name} Transform should write correct data into Texture` ); sourceTexture.delete(); updateTexture.delete(); transform.delete(); const endCounts = getResourceCounts(); validateResourceCounts(t, startCounts, endCounts); }); t.end(); }); const OFFLINE_RENDERING_TEST_CASES = [ { name: 'RED-FLOAT', format: GL.R32F, dataFormat: GL.RED, type: GL.FLOAT, width: 4, height: 4, expected: 123, position: new Float32Array([1, 1, -1, 1, 1, -1, -1, -1]), vs: `\ #version 300 es in vec2 position; out float outTexture; void main() { outTexture = 123.; gl_Position = vec4(position, 0., 1.); } ` }, { name: 'RGBA-UNSIGNED_BYTE', format: GL.RGBA, dataFormat: GL.RGBA, type: GL.UNSIGNED_BYTE, width: 2, height: 2, expected: 255, position: new Float32Array([1, 1, -1, 1, 1, -1, -1, -1]), vs: `\ #version 300 es in vec2 position; out vec4 outTexture; void main() { outTexture = vec4(1.); gl_Position = vec4(position, 0., 1.); } ` } ]; test('WebGL#Transform run (offline rendering)', t => { const {gl2} = fixture; if (!gl2) { t.comment('WebGL2 not available, skipping tests'); t.end(); return; } OFFLINE_RENDERING_TEST_CASES.forEach(testCase => { const {position, format, dataFormat, type, width, height, name, vs, expected} = testCase; const _targetTexture = new Texture2D(gl2, { format, dataFormat, type, mipmaps: false, width, height, pixelStore: { [GL.UNPACK_FLIP_Y_WEBGL]: false } }); const transform = new Transform(gl2, { sourceBuffers: { position: new Buffer(gl2, position) }, _targetTexture, _targetTextureVarying: 'outTexture', vs, drawMode: GL.TRIANGLE_STRIP, elementCount: position.length / 2 }); transform.run(); const outTexData = transform.getData({packed: true}); const testPassed = outTexData.every(item => { return item === expected; }); t.ok(testPassed, `${name} Transform should write correct data into Texture`); transform.delete(); }); t.end(); }); test('WebGL#Transform run with shader injects', t => { const {gl2} = fixture; if (!gl2) { t.comment('WebGL2 not available, skipping tests'); t.end(); return; } const vs = `\ attribute float inValue; varying float outValue; float sum(float a, float b) { return a + b; } void main() { outValue = 2.0 * inValue; } `; const sourceData = new Float32Array([10, 20, 31, 0, -57]); const sourceBuffer = new Buffer(gl2, {data: sourceData}); const inject = { 'vs:#decl': ` attribute float injectedAttribute; varying float injectedVarying; `, 'vs:#main-start': ' if (true) { injectedVarying = sum(1., injectedAttribute); } else {\n', 'vs:#main-end': ' }\n' }; const transform = new Transform(gl2, { sourceBuffers: { injectedAttribute: sourceBuffer }, vs, inject, feedbackMap: { injectedAttribute: 'injectedVarying' }, varyings: ['injectedVarying'], elementCount: 5 }); transform.run(); const expectedData = sourceData.map(x => x + 1); const outData = transform.getData({varyingName: 'injectedVarying'}); t.deepEqual(outData, expectedData, 'Transform.getData: is successful'); t.end(); }); test('WebGL#Transform run (source&destination with custom FS)', t => { const {gl2} = fixture; if (!gl2) { t.comment('WebGL2 not available, skipping tests'); t.end(); return; } const startCounts = getResourceCounts(); const name = 'RGBA-FLOAT'; const sourceData = new Float32Array([ 0, 0, 0, 0, -1, -2, -3, -4, 2, 3, 4, 5, 10, 20, 30, 40, 5, 6, 7, 8, 51, 61, 71, 81, -15, -16, 70, 81, 50, 100, -2, -5, 9, 10, 11, 12, 0, -20, 52, 78, -3, -4, 2, 3, 8, 51, 61, 71, 3, 14, 15, 16, -4, 2, 3, 4, 11, 12, 0, -20, 0, 0, -1, -2 ]); const format = GL.RGBA32F; const dataFormat = GL.RGBA; const type = GL.FLOAT; const width = 4; const height = 4; const vs = `\ #version 300 es in vec4 inTexture; out vec4 outTexture; void main() { outTexture = inTexture; } `; const fs = `\ #version 300 es in vec4 outTexture; out vec4 transform_output; void main() { transform_output = 2. * outTexture; } `; // const {sourceData, format, dataFormat, type, width, height, name, vs} = testCase; const sourceTexture = new Texture2D(gl2, { data: sourceData, format, dataFormat, type, mipmaps: false, width, height, pixelStore: { [GL.UNPACK_FLIP_Y_WEBGL]: false } }); const transform = new Transform(gl2, { _sourceTextures: { inTexture: sourceTexture }, _targetTexture: 'inTexture', _targetTextureVarying: 'outTexture', _swapTexture: 'inTexture', vs, _fs: fs, elementCount: sourceData.length }); transform.run(); let expectedData = sourceData.map(x => x * 2); // By default getData reads data from current Framebuffer. let outTexData = transform.getData({packed: true}); t.deepEqual(outTexData, expectedData, `${name} Transform should write correct data into Texture`); transform.swap(); transform.run(); expectedData = sourceData.map(x => x * 4); // By default getData reads data from current Framebuffer. outTexData = transform.getData({packed: true}); t.deepEqual(outTexData, expectedData, `${name} Transform swap Textures`); sourceTexture.delete(); transform.delete(); const endCounts = getResourceCounts(); validateResourceCounts(t, startCounts, endCounts); t.end(); }); test('WebGL#Transform run (custom parameters)', t => { const {gl2} = fixture; if (!gl2) { t.comment('WebGL2 not available, skipping tests'); t.end(); return; } const {sourceData, format, dataFormat, type, width, height, name, vs} = TEXTURE_TEST_CASES[0]; const sourceTexture = new Texture2D(gl2, { data: sourceData, format, dataFormat, type, mipmaps: false, width, height, pixelStore: { [GL.UNPACK_FLIP_Y_WEBGL]: false } }); // enable blending setParameters(gl2, {blend: true, blendEquation: GL.MIN}); const transform = new Transform(gl2, { _sourceTextures: { inTexture: sourceTexture }, _targetTexture: 'inTexture', _targetTextureVarying: 'outTexture', _swapTexture: 'inTexture', vs, elementCount: sourceData.length }); // disable blending through parameters transform.run({parameters: {blend: false}}); const expectedData = sourceData.map(x => x * 2); const outTexData = transform.getData({packed: true}); t.deepEqual(outTexData, expectedData, `${name} Transform should write correct data into Texture`); t.ok(getParameters(gl2, [GL.BLEND])[GL.BLEND] === true, 'Parameters are properly set'); setParameters(gl2, {blend: false}); t.end(); }); test('WebGL#Transform (Buffer to Texture)', t => { const {gl2} = fixture; if (!gl2) { t.comment('WebGL2 not available, skipping tests'); t.end(); return; } const vs = `\ #define EPSILON 0.00001 attribute vec4 aCur; attribute vec4 aNext; varying float equal; void main() { equal = length(aCur - aNext) > EPSILON ? 0. : 1.; gl_Position = vec4(0, 0, 0, 1.); } `; const fs = `\ varying float equal; void main() { if (equal == 1.) { discard; } gl_FragColor = vec4(1.); } `; const data1 = new Float32Array([10, 20, 31, 0, -57, 28, 100, 53]); const data2 = new Float32Array([10, 20, 31, 0, 7, 10, -10, 43]); const aCur = new Buffer(gl2, {data: data1}); const aNext = new Buffer(gl2, {data: data2}); // buffers contain different data const texture = new Texture2D(gl2, { format: GL.RGBA, dataFormat: GL.RGBA, type: GL.UNSIGNED_BYTE, mipmaps: false }); const transform = new Transform(gl2, { sourceBuffers: { aCur, aNext }, vs, _fs: fs, _targetTexture: texture, _targetTextureVarying: 'outTexture', // dummy varying to enable FB creation elementCount: 2 }); transform.run({clearRenderTarget: true}); let expectedData = [255, 255, 255, 255]; let outData = transform.getData({varyingName: 'outTexture'}); t.deepEqual(outData, expectedData, 'Transform.getData: is successful'); // update aNext to contain same data as aCur aNext.subData(data1); // re-run the tranform transform.run({clearRenderTarget: true}); expectedData = [0, 0, 0, 0]; outData = transform.getData({varyingName: 'outTexture'}); t.deepEqual(outData, expectedData, 'Transform.getData: is successful'); t.end(); });