gpu.js/test/internal/recycling.js
Robert Plummer 4d2f27e596 fix: #590 add Kernel.onActivate
- which is called after gpu.js switches kernels based off need
fix: Consistent error message for values that are not defined
fix: Add recompiled kernels to gpu.js kernels property and test
fix: Move texture deleting to within the GLKernel Texture implementation
fix: Add Texture.clear as an abstract method on the base Texture
fix: Bump and build
2020-04-01 08:24:49 -04:00

855 lines
30 KiB
JavaScript

const sinon = require('sinon');
const { assert, skip, test, module: describe, only } = require('qunit');
const { GPU } = require('../../src');
describe('internal: recycling');
function testImmutableKernelTextureRecycling(precision, mode) {
const gpu = new GPU({ mode });
const kernel = gpu.createKernel(function(v) {
return v[0] + 1;
}, {
output: [1],
pipeline: true,
immutable: true,
precision,
});
let result = kernel([0]);
const newTextureSpy = sinon.spy(kernel.texture.constructor.prototype, 'newTexture');
for (let i = 0; i < 10; i++) {
let lastResult = result;
result = kernel(result);
lastResult.delete();
}
assert.deepEqual(result.toArray(), new Float32Array([11]));
assert.equal(newTextureSpy.callCount, 1);
assert.equal(gpu.kernels.length, 2);
newTextureSpy.restore();
gpu.destroy();
}
test('immutable single precision kernel auto', () => {
testImmutableKernelTextureRecycling('single')
});
test('immutable single precision kernel gpu', () => {
testImmutableKernelTextureRecycling('single', 'gpu');
});
(GPU.isWebGLSupported ? test : skip)('immutable single precision kernel webgl', () => {
testImmutableKernelTextureRecycling('single', 'webgl');
});
(GPU.isWebGL2Supported ? test : skip)('immutable single precision kernel webgl2', () => {
testImmutableKernelTextureRecycling('single', 'webgl2');
});
(GPU.isHeadlessGLSupported ? test : skip)('immutable single precision kernel headlessgl', () => {
testImmutableKernelTextureRecycling('single', 'headlessgl');
});
test('immutable unsigned precision kernel auto', () => {
testImmutableKernelTextureRecycling('unsigned')
});
test('immutable unsigned precision kernel gpu', () => {
testImmutableKernelTextureRecycling('unsigned', 'gpu');
});
(GPU.isWebGLSupported ? test : skip)('immutable unsigned precision kernel webgl', () => {
testImmutableKernelTextureRecycling('unsigned', 'webgl');
});
(GPU.isWebGL2Supported ? test : skip)('immutable unsigned precision kernel webgl2', () => {
testImmutableKernelTextureRecycling('unsigned', 'webgl2');
});
(GPU.isHeadlessGLSupported ? test : skip)('immutable unsigned precision headlessgl', () => {
testImmutableKernelTextureRecycling('unsigned', 'headlessgl');
});
function testImmutableMappedKernelTextureRecycling(precision, mode) {
const gpu = new GPU({ mode });
function oneOff(value) {
return value;
}
const kernel = gpu.createKernelMap({
oneOffValue: oneOff
},function(value1, value2) {
oneOff(value2[0] - 1);
return value1[0] + 1;
}, {
output: [1],
pipeline: true,
immutable: true,
precision,
});
let map = kernel([0], [11]);
const newTextureSpy = sinon.spy(kernel.texture.constructor.prototype, 'newTexture');
for (let i = 0; i < 10; i++) {
let lastResults = map;
map = kernel(map.result, map.oneOffValue);
lastResults.result.delete();
lastResults.oneOffValue.delete();
}
assert.deepEqual(map.result.toArray(), new Float32Array([11]));
assert.deepEqual(map.oneOffValue.toArray(), new Float32Array([0]));
assert.equal(newTextureSpy.callCount, 2);
assert.equal(gpu.kernels.length, 2);
newTextureSpy.restore();
gpu.destroy();
}
test('immutable single precision mapped kernel auto', () => {
testImmutableMappedKernelTextureRecycling('single')
});
test('immutable single precision mapped kernel gpu', () => {
testImmutableMappedKernelTextureRecycling('single', 'gpu');
});
(GPU.isWebGLSupported ? test : skip)('immutable single precision mapped kernel webgl', () => {
testImmutableMappedKernelTextureRecycling('single', 'webgl');
});
(GPU.isWebGL2Supported ? test : skip)('immutable single precision mapped kernel webgl2', () => {
testImmutableMappedKernelTextureRecycling('single', 'webgl2');
});
(GPU.isHeadlessGLSupported ? test : skip)('immutable single precision mapped kernel headlessgl', () => {
testImmutableMappedKernelTextureRecycling('single', 'headlessgl');
});
test('immutable unsigned precision mapped kernel auto', () => {
testImmutableMappedKernelTextureRecycling('unsigned')
});
test('immutable unsigned precision mapped kernel gpu', () => {
testImmutableMappedKernelTextureRecycling('unsigned', 'gpu');
});
(GPU.isWebGLSupported ? test : skip)('immutable unsigned precision mapped kernel webgl', () => {
testImmutableMappedKernelTextureRecycling('unsigned', 'webgl');
});
(GPU.isWebGL2Supported ? test : skip)('immutable unsigned precision mapped kernel webgl2', () => {
testImmutableMappedKernelTextureRecycling('unsigned', 'webgl2');
});
(GPU.isHeadlessGLSupported ? test : skip)('immutable unsigned precision mapped kernel headlessgl', () => {
testImmutableMappedKernelTextureRecycling('unsigned', 'headlessgl');
});
function testImmutableTextureDelete(precision, done, mode) {
const gpu = new GPU({ mode });
const kernel = gpu.createKernel(function() {
return this.thread.x;
}, {
output: [1],
pipeline: true,
immutable: true,
precision,
});
const result = kernel();
assert.equal(result.texture._refs, 2);
const clone1 = result.clone();
assert.equal(result.texture._refs, 3);
const clone2 = result.clone();
assert.equal(result.texture._refs, 4);
const clone3 = result.clone();
assert.equal(result.texture._refs, 5);
const clone4 = result.clone();
assert.equal(result.texture._refs, 6);
const clone5 = result.clone();
assert.equal(result.texture._refs, 7);
clone1.delete();
assert.equal(result.texture._refs, 6);
clone2.delete();
assert.equal(result.texture._refs, 5);
clone3.delete();
assert.equal(result.texture._refs, 4);
clone4.delete();
assert.equal(result.texture._refs, 3);
clone5.delete();
assert.equal(result.texture._refs, 2);
result.delete();
assert.equal(result.texture._refs, 1);
const spy = sinon.spy(kernel.kernel.context, 'deleteTexture');
gpu.destroy()
.then(() => {
assert.equal(result.texture._refs, 0);
assert.equal(spy.callCount, 1);
assert.ok(spy.calledWith(result.texture));
spy.restore();
done();
});
}
test('immutable single precision texture delete auto', t => {
testImmutableTextureDelete('single', t.async());
});
test('immutable single precision texture delete gpu', t => {
testImmutableTextureDelete('single', t.async(), 'gpu');
});
(GPU.isWebGLSupported ? test : skip)('immutable single precision texture delete webgl', t => {
testImmutableTextureDelete('single', t.async(), 'webgl');
});
(GPU.isWebGL2Supported ? test : skip)('immutable single precision texture delete webgl2', t => {
testImmutableTextureDelete('single', t.async(), 'webgl2');
});
(GPU.isHeadlessGLSupported ? test : skip)('immutable single precision texture delete headlessgl', t => {
testImmutableTextureDelete('single', t.async(), 'headlessgl');
});
test('immutable unsigned precision texture delete auto', t => {
testImmutableTextureDelete('unsigned', t.async() );
});
test('immutable unsigned precision texture delete gpu', t => {
testImmutableTextureDelete('unsigned', t.async(), 'gpu');
});
(GPU.isWebGLSupported ? test : skip)('immutable unsigned precision texture delete webgl', t => {
testImmutableTextureDelete('unsigned', t.async(), 'webgl');
});
(GPU.isWebGL2Supported ? test : skip)('immutable unsigned precision texture delete webgl2', t => {
testImmutableTextureDelete('unsigned', t.async(), 'webgl2');
});
(GPU.isHeadlessGLSupported ? test : skip)('immutable unsigned precision texture delete headlessgl', t => {
testImmutableTextureDelete('unsigned', t.async(), 'headlessgl');
});
function testImmutableKernelTextureDoesNotLeak(precision, done, mode) {
const gpu = new GPU({ mode });
const toTexture = gpu.createKernel(function(value) {
return value[this.thread.x];
}, {
output: [1],
pipeline: true,
immutable: true,
precision,
});
const one = toTexture([1]);
assert.equal(one.texture._refs, 2); // one's texture will be used in two places at first, in one and toTexture.texture
assert.equal(toTexture.texture.texture, one.texture); // very important, a clone was mode, but not a deep clone
assert.notEqual(one, toTexture.texture);
const two = toTexture([2]);
assert.equal(one.texture._refs, 1); // was tracked on toTexture.texture, and deleted
assert.equal(toTexture.texture.texture, two.texture);
assert.notEqual(toTexture.texture.texture, one.texture);
assert.equal(two.texture._refs, 2);
one.delete();
two.delete();
assert.equal(one.texture._refs, 0);
assert.equal(two.texture._refs, 1); // still used by toTexture.texture
two.delete(); // already deleted
assert.equal(two.texture._refs, 1); // still used by toTexture
gpu.destroy()
.then(() => {
assert.equal(two.texture._refs, 0);
done();
});
}
test('immutable unsigned precision kernel.texture does not leak auto', t => {
testImmutableKernelTextureDoesNotLeak('unsigned', t.async());
});
test('immutable unsigned precision kernel.texture does not leak gpu', t => {
testImmutableKernelTextureDoesNotLeak('unsigned', t.async(), 'gpu');
});
(GPU.isWebGLSupported ? test : skip)('immutable unsigned precision kernel.texture does not leak webgl', t => {
testImmutableKernelTextureDoesNotLeak('unsigned', t.async(), 'webgl');
});
(GPU.isWebGL2Supported ? test : skip)('immutable unsigned precision kernel.texture does not leak webgl2', t => {
testImmutableKernelTextureDoesNotLeak('unsigned', t.async(), 'webgl2');
});
(GPU.isHeadlessGLSupported ? test : skip)('immutable unsigned precision kernel.texture does not leak headlessgl', t => {
testImmutableKernelTextureDoesNotLeak('unsigned', t.async(), 'headlessgl');
});
test('immutable single precision kernel.texture does not leak auto', t => {
testImmutableKernelTextureDoesNotLeak('single', t.async());
});
test('immutable single precision kernel.texture does not leak gpu', t => {
testImmutableKernelTextureDoesNotLeak('single', t.async(), 'gpu');
});
(GPU.isWebGLSupported ? test : skip)('immutable single precision kernel.texture does not leak webgl', t => {
testImmutableKernelTextureDoesNotLeak('single', t.async(), 'webgl');
});
(GPU.isWebGL2Supported ? test : skip)('immutable single precision kernel.texture does not leak webgl2', t => {
testImmutableKernelTextureDoesNotLeak('single', t.async(), 'webgl2');
});
(GPU.isHeadlessGLSupported ? test : skip)('immutable single precision kernel.texture does not leak headlessgl', t => {
testImmutableKernelTextureDoesNotLeak('single', t.async(), 'headlessgl');
});
function testImmutableKernelMappedTexturesDoesNotLeak(precision, done, mode) {
const gpu = new GPU({ mode });
function saveValue(value) {
return value;
}
const toTextures = gpu.createKernelMap([saveValue],function(value1, value2) {
saveValue(value1[this.thread.x]);
return value2[this.thread.x];
}, {
output: [1],
pipeline: true,
immutable: true,
precision,
});
const { result: one, 0: two } = toTextures([1], [2]);
assert.equal(one.texture._refs, 2); // one's texture will be used in two places at first, in one and toTexture.texture
assert.equal(two.texture._refs, 2); // one's texture will be used in two places at first, in one and toTexture.mappedTextures[0]
assert.equal(toTextures.texture.texture, one.texture); // very important, a clone was mode, but not a deep clone
assert.equal(toTextures.mappedTextures[0].texture, two.texture); // very important, a clone was mode, but not a deep clone
assert.notEqual(one, toTextures.texture);
assert.notEqual(one, toTextures.mappedTextures[0]);
const { result: three, 0: four } = toTextures([3], [4]);
assert.equal(one.texture._refs, 1); // was tracked on toTexture.texture, and deleted
assert.equal(two.texture._refs, 1); // was tracked on toTexture.mappedTextures[0], and deleted
assert.equal(toTextures.texture.texture, three.texture);
assert.equal(toTextures.mappedTextures[0].texture, four.texture);
assert.notEqual(toTextures.texture.texture, one.texture);
assert.notEqual(toTextures.mappedTextures[0].texture, two.texture);
assert.equal(three.texture._refs, 2);
assert.equal(four.texture._refs, 2);
one.delete();
two.delete();
three.delete();
four.delete();
assert.equal(one.texture._refs, 0);
assert.equal(two.texture._refs, 0);
assert.equal(three.texture._refs, 1); // still used by toTexture.texture
assert.equal(four.texture._refs, 1); // still used by toTexture.mappedTextures[0]
three.delete(); // already deleted
four.delete(); // already deleted
assert.equal(three.texture._refs, 1); // still used by toTexture
assert.equal(four.texture._refs, 1); // still used by toTexture
gpu.destroy()
.then(() => {
assert.equal(one.texture._refs, 0);
assert.equal(two.texture._refs, 0);
assert.equal(three.texture._refs, 0);
assert.equal(four.texture._refs, 0);
done();
});
}
test('immutable unsigned precision kernel.mappedTextures does not leak auto', t => {
testImmutableKernelMappedTexturesDoesNotLeak('unsigned', t.async());
});
test('immutable unsigned precision kernel.mappedTextures does not leak gpu', t => {
testImmutableKernelMappedTexturesDoesNotLeak('unsigned', t.async(), 'gpu');
});
(GPU.isWebGLSupported ? test : skip)('immutable unsigned precision kernel.mappedTextures does not leak webgl', t => {
testImmutableKernelMappedTexturesDoesNotLeak('unsigned', t.async(), 'webgl');
});
(GPU.isWebGL2Supported ? test : skip)('immutable unsigned precision kernel.mappedTextures does not leak webgl2', t => {
testImmutableKernelMappedTexturesDoesNotLeak('unsigned', t.async(), 'webgl2');
});
(GPU.isHeadlessGLSupported ? test : skip)('immutable unsigned precision kernel.mappedTextures does not leak headlessgl', t => {
testImmutableKernelMappedTexturesDoesNotLeak('unsigned', t.async(), 'headlessgl');
});
test('immutable single precision kernel.mappedTextures does not leak auto', t => {
testImmutableKernelMappedTexturesDoesNotLeak('single', t.async());
});
test('immutable single precision kernel.mappedTextures does not leak gpu', t => {
testImmutableKernelMappedTexturesDoesNotLeak('single', t.async(), 'gpu');
});
(GPU.isWebGLSupported ? test : skip)('immutable single precision kernel.mappedTextures does not leak webgl', t => {
testImmutableKernelMappedTexturesDoesNotLeak('single', t.async(), 'webgl');
});
(GPU.isWebGL2Supported ? test : skip)('immutable single precision kernel.mappedTextures does not leak webgl2', t => {
testImmutableKernelMappedTexturesDoesNotLeak('single', t.async(), 'webgl2');
});
(GPU.isHeadlessGLSupported ? test : skip)('immutable single precision kernel.mappedTextures does not leak headlessgl', t => {
testImmutableKernelMappedTexturesDoesNotLeak('single', t.async(), 'headlessgl');
});
function testCloning(mode) {
const gpu = new GPU({ mode });
const kernel = gpu.createKernel(function(value) {
return value[0] + 1;
}, { output: [1], pipeline: true });
const texture = kernel([1]);
const { size } = texture;
// set size to something unique, for tracking
texture.size = [size[0] + 0.1, size[1] + 0.2];
texture.cloneTexture();
assert.equal(texture._framebuffer.width, size[0] + 0.1);
assert.equal(texture._framebuffer.height, size[1] + 0.2);
gpu.destroy();
}
(GPU.isWebGLSupported ? test : skip)('cloning sets up framebuffer with correct size webgl', () => {
testCloning('webgl');
});
(GPU.isWebGL2Supported ? test : skip)('cloning sets up framebuffer with correct size webgl2', () => {
testCloning('webgl2');
});
(GPU.isHeadlessGLSupported ? test : skip)('cloning sets up framebuffer with correct size headlessgl', () => {
testCloning('headlessgl');
});
function testMutableLeak(mode) {
const gpu = new GPU({ mode });
const kernel = gpu.createKernel(function() {
return 1;
}, {
output: [1],
pipeline: true
});
kernel.build();
const cloneTextureSpy = sinon.spy(kernel.texture.constructor.prototype, 'beforeMutate');
const texture1 = kernel();
const texture2 = kernel();
assert.equal(cloneTextureSpy.callCount, 0);
assert.equal(texture1.texture._refs, 1);
assert.ok(texture1 === texture2);
cloneTextureSpy.restore();
gpu.destroy();
}
test('test mutable leak auto', () => {
testMutableLeak();
});
test('test mutable leak gpu', () => {
testMutableLeak('gpu');
});
(GPU.isWebGLSupported ? test : skip)('test mutable leak webgl', () => {
testMutableLeak('webgl');
});
(GPU.isWebGL2Supported ? test : skip)('test mutable leak webgl2', () => {
testMutableLeak('webgl2');
});
(GPU.isHeadlessGLSupported ? test : skip)('test mutable leak headlessgl', () => {
testMutableLeak('headlessgl');
});
describe('internal: cpu recycling behaviour');
test('recycle CPU array', () => {
const gpu = new GPU({ mode: 'cpu' });
const kernel = gpu.createKernel(function(v) {
return this.thread.x + v[0];
}, {
output: [1],
pipeline: true,
immutable: false,
});
const result1 = kernel(new Float32Array([1]));
assert.equal(result1[0], 1);
const result2 = kernel(new Float32Array([2]));
assert.equal(result1[0], 2);
assert.equal(result1, result2);
gpu.destroy();
});
test('recycle CPU matrix', () => {
const gpu = new GPU({ mode: 'cpu' });
const kernel = gpu.createKernel(function(v) {
return (this.thread.x + (this.thread.y * this.output.x)) + v[0];
}, {
output: [2, 2],
pipeline: true,
immutable: false,
});
const result1 = kernel(new Float32Array([1]));
assert.equal(result1[0][0], 1);
assert.equal(result1[0][1], 2);
assert.equal(result1[1][0], 3);
assert.equal(result1[1][1], 4);
const result2 = kernel(new Float32Array([2]));
assert.equal(result1[0][0], 2);
assert.equal(result1[0][1], 3);
assert.equal(result1[1][0], 4);
assert.equal(result1[1][1], 5);
assert.equal(result1, result2);
gpu.destroy();
});
test('recycle CPU cube', () => {
const gpu = new GPU({ mode: 'cpu' });
const kernel = gpu.createKernel(function(v) {
return (this.thread.x + (this.thread.y * this.output.x) + (this.thread.z * this.output.y * this.output.x)) + v[0];
}, {
output: [2, 2, 2],
pipeline: true,
immutable: false,
});
const result1 = kernel(new Float32Array([1]));
assert.equal(result1[0][0][0], 1);
assert.equal(result1[0][0][1], 2);
assert.equal(result1[0][1][0], 3);
assert.equal(result1[0][1][1], 4);
assert.equal(result1[1][0][0], 5);
assert.equal(result1[1][0][1], 6);
assert.equal(result1[1][1][0], 7);
assert.equal(result1[1][1][1], 8);
const result2 = kernel(new Float32Array([2]));
assert.equal(result1[0][0][0], 2);
assert.equal(result1[0][0][1], 3);
assert.equal(result1[0][1][0], 4);
assert.equal(result1[0][1][1], 5);
assert.equal(result1[1][0][0], 6);
assert.equal(result1[1][0][1], 7);
assert.equal(result1[1][1][0], 8);
assert.equal(result1[1][1][1], 9);
assert.equal(result1, result2);
gpu.destroy();
});
describe('internal: cpu non-recycling behaviour');
test('non-recycle CPU array', () => {
const gpu = new GPU({ mode: 'cpu' });
const kernel = gpu.createKernel(function(v) {
return this.thread.x + v[0];
}, {
output: [1],
pipeline: true,
immutable: true,
});
const result1 = kernel(new Float32Array([1]));
assert.equal(result1[0], 1);
const result2 = kernel(new Float32Array([2]));
assert.equal(result1[0], 1);
assert.equal(result2[0], 2);
assert.notEqual(result1, result2);
gpu.destroy();
});
test('non-recycle CPU matrix', () => {
const gpu = new GPU({ mode: 'cpu' });
const kernel = gpu.createKernel(function(v) {
return (this.thread.x + (this.thread.y * this.output.x)) + v[0];
}, {
output: [2, 2],
pipeline: true,
immutable: true,
});
const result1 = kernel(new Float32Array([1]));
assert.equal(result1[0][0], 1);
assert.equal(result1[0][1], 2);
assert.equal(result1[1][0], 3);
assert.equal(result1[1][1], 4);
const result2 = kernel(new Float32Array([2]));
// untouched
assert.equal(result1[0][0], 1);
assert.equal(result1[0][1], 2);
assert.equal(result1[1][0], 3);
assert.equal(result1[1][1], 4);
assert.equal(result2[0][0], 2);
assert.equal(result2[0][1], 3);
assert.equal(result2[1][0], 4);
assert.equal(result2[1][1], 5);
assert.notEqual(result1, result2);
gpu.destroy();
});
test('non-recycle CPU cube', () => {
const gpu = new GPU({ mode: 'cpu' });
const kernel = gpu.createKernel(function(v) {
return (this.thread.x + (this.thread.y * this.output.x) + (this.thread.z * this.output.y * this.output.x)) + v[0];
}, {
output: [2, 2, 2],
pipeline: true,
immutable: true,
});
const result1 = kernel(new Float32Array([1]));
assert.equal(result1[0][0][0], 1);
assert.equal(result1[0][0][1], 2);
assert.equal(result1[0][1][0], 3);
assert.equal(result1[0][1][1], 4);
assert.equal(result1[1][0][0], 5);
assert.equal(result1[1][0][1], 6);
assert.equal(result1[1][1][0], 7);
assert.equal(result1[1][1][1], 8);
const result2 = kernel(new Float32Array([2]));
// untouched
assert.equal(result1[0][0][0], 1);
assert.equal(result1[0][0][1], 2);
assert.equal(result1[0][1][0], 3);
assert.equal(result1[0][1][1], 4);
assert.equal(result1[1][0][0], 5);
assert.equal(result1[1][0][1], 6);
assert.equal(result1[1][1][0], 7);
assert.equal(result1[1][1][1], 8);
assert.equal(result2[0][0][0], 2);
assert.equal(result2[0][0][1], 3);
assert.equal(result2[0][1][0], 4);
assert.equal(result2[0][1][1], 5);
assert.equal(result2[1][0][0], 6);
assert.equal(result2[1][0][1], 7);
assert.equal(result2[1][1][0], 8);
assert.equal(result2[1][1][1], 9);
assert.notEqual(result1, result2);
gpu.destroy();
});
function testSameSourceDestinationFromResultThrows(error, precision, mode) {
const gpu = new GPU({ mode });
const kernel = gpu.createKernel(function(value) {
return value[this.thread.x] + 1;
}, {
output: [1],
pipeline: true,
immutable: false,
precision,
});
let result = kernel([0]);
assert.equal((result.toArray ? result.toArray() : result)[0], 1);
assert.throws(() => kernel(result), error);
gpu.destroy();
}
const gpuError = new Error('Source and destination textures are the same. Use immutable = true and manually cleanup kernel output texture memory with texture.delete()');
const cpuError = new Error('Source and destination arrays are the same. Use immutable = true');
test('single precision same source and destination from result mutable throws auto', () => {
testSameSourceDestinationFromResultThrows(gpuError,'single');
});
test('single precision same source and destination from result mutable throws gpu', () => {
testSameSourceDestinationFromResultThrows(gpuError, 'single', 'gpu');
});
(GPU.isWebGLSupported ? test : skip)('single precision same source and destination from result mutable throws webgl', () => {
testSameSourceDestinationFromResultThrows(gpuError, 'single', 'webgl');
});
(GPU.isWebGL2Supported ? test : skip)('single precision same source and destination from result mutable throws webgl2', () => {
testSameSourceDestinationFromResultThrows(gpuError, 'single', 'webgl2');
});
(GPU.isHeadlessGLSupported ? test : skip)('single precision same source and destination from result mutable throws headlessgl', () => {
testSameSourceDestinationFromResultThrows(gpuError, 'single', 'headlessgl');
});
test('single precision same source and destination from result mutable throws cpu', () => {
testSameSourceDestinationFromResultThrows(cpuError, 'single', 'cpu');
});
test('unsigned precision same source and destination from result mutable throws auto', () => {
testSameSourceDestinationFromResultThrows(gpuError, 'unsigned');
});
test('unsigned precision same source and destination from result mutable throws gpu', () => {
testSameSourceDestinationFromResultThrows(gpuError, 'unsigned', 'gpu');
});
(GPU.isWebGLSupported ? test : skip)('unsigned precision same source and destination from result mutable throws webgl', () => {
testSameSourceDestinationFromResultThrows(gpuError, 'unsigned', 'webgl');
});
(GPU.isWebGL2Supported ? test : skip)('unsigned precision same source and destination from result mutable throws webgl2', () => {
testSameSourceDestinationFromResultThrows(gpuError, 'unsigned', 'webgl2');
});
(GPU.isHeadlessGLSupported ? test : skip)('unsigned precision same source and destination from result mutable throws headlessgl', () => {
testSameSourceDestinationFromResultThrows(gpuError, 'unsigned', 'headlessgl');
});
test('unsigned precision same source and destination from result mutable throws cpu', () => {
testSameSourceDestinationFromResultThrows(cpuError, 'unsigned', 'cpu');
});
function testSameSourceDestinationFromMappedResultThrows(error, precision, mode) {
const gpu = new GPU({ mode });
const kernel = gpu.createKernelMap({
mappedResult: function map(v) {
return v;
}
}, function(value) {
return map(value[this.thread.x] + 1);
}, {
output: [1],
pipeline: true,
immutable: false,
precision,
});
let { result, mappedResult } = kernel([0]);
assert.equal((mappedResult.toArray ? mappedResult.toArray() : mappedResult)[0], 1);
assert.throws(() => kernel(mappedResult), error);
assert.throws(() => kernel(result), error);
gpu.destroy();
}
test('single precision same source and destination from mapped result mutable throws auto', () => {
testSameSourceDestinationFromMappedResultThrows(gpuError, 'single');
});
test('single precision same source and destination from mapped result mutable throws gpu', () => {
testSameSourceDestinationFromMappedResultThrows(gpuError, 'single', 'gpu');
});
(GPU.isWebGLSupported ? test : skip)('single precision same source and destination from mapped result mutable throws webgl', () => {
testSameSourceDestinationFromMappedResultThrows(gpuError, 'single', 'webgl');
});
(GPU.isWebGL2Supported ? test : skip)('single precision same source and destination from mapped result mutable throws webgl2', () => {
testSameSourceDestinationFromMappedResultThrows(gpuError, 'single', 'webgl2');
});
(GPU.isHeadlessGLSupported ? test : skip)('single precision same source and destination from mapped result mutable throws headlessgl', () => {
testSameSourceDestinationFromMappedResultThrows(gpuError, 'single', 'headlessgl');
});
test('single precision same source and destination from mapped result mutable throws cpu', () => {
testSameSourceDestinationFromMappedResultThrows(cpuError, 'single', 'cpu');
});
test('unsigned precision same source and destination from mapped result mutable throws auto', () => {
testSameSourceDestinationFromMappedResultThrows(gpuError, 'unsigned');
});
test('unsigned precision same source and destination from mapped result mutable throws gpu', () => {
testSameSourceDestinationFromMappedResultThrows(gpuError, 'unsigned', 'gpu');
});
(GPU.isWebGLSupported ? test : skip)('unsigned precision same source and destination from mapped result mutable throws webgl', () => {
testSameSourceDestinationFromMappedResultThrows(gpuError, 'unsigned', 'webgl');
});
(GPU.isWebGL2Supported ? test : skip)('unsigned precision same source and destination from mapped result mutable throws webgl2', () => {
testSameSourceDestinationFromMappedResultThrows(gpuError, 'unsigned', 'webgl2');
});
(GPU.isHeadlessGLSupported ? test : skip)('unsigned precision same source and destination from mapped result mutable throws headlessgl', () => {
testSameSourceDestinationFromMappedResultThrows(gpuError, 'unsigned', 'headlessgl');
});
test('unsigned precision same source and destination from mapped result mutable throws cpu', () => {
testSameSourceDestinationFromMappedResultThrows(cpuError, 'unsigned', 'cpu');
});
function testOutputTextureIsClonedWhenRecompiling(mode) {
const gpu = new GPU({ mode });
const kernel = gpu.createKernel(function(value) {
return value[this.thread.x] + 1;
}, { output: [1], immutable: true, pipeline: true });
const result1 = kernel([1]);
assert.equal(result1.toArray()[0], 2);
const result2 = kernel(result1);
result1.delete();
assert.equal(result2.toArray()[0], 3);
result2.delete();
const result3 = kernel([3]);
assert.equal(result3.toArray()[0], 4);
const result4 = kernel(result3);
result3.delete();
assert.equal(result4.toArray()[0], 5);
result4.delete();
gpu.destroy();
}
test('output texture is cloned when recompiling auto', () => {
testOutputTextureIsClonedWhenRecompiling();
});
test('output texture is cloned when recompiling gpu', () => {
testOutputTextureIsClonedWhenRecompiling('gpu');
});
(GPU.isWebGLSupported ? test : skip)('output texture is cloned when recompiling webgl', () => {
testOutputTextureIsClonedWhenRecompiling('webgl');
});
(GPU.isWebGL2Supported ? test : skip)('output texture is cloned when recompiling webgl2', () => {
testOutputTextureIsClonedWhenRecompiling('webgl2');
});
(GPU.isHeadlessGLSupported ? test : skip)('output texture is cloned when recompiling headlessgl', () => {
testOutputTextureIsClonedWhenRecompiling('headlessgl');
});
function testMappedOutputTextureIsClonedWhenRecompiling(mode) {
const gpu = new GPU({ mode });
function setValue(value) {
return value * 10;
}
const kernel = gpu.createKernelMap({
value: setValue,
},function(value1, value2) {
setValue(value2[this.thread.x]);
return value1[this.thread.x] + 1;
}, { output: [1], immutable: true, pipeline: true });
const map1 = kernel([1], [1]);
assert.equal(map1.result.toArray()[0], 2);
assert.equal(map1.value.toArray()[0], 10);
const map2 = kernel(map1.result, map1.value);
map1.result.delete();
map1.value.delete();
assert.equal(map2.result.toArray()[0], 3);
assert.equal(map2.value.toArray()[0], 100);
map2.value.delete();
map2.result.delete();
const map3 = kernel([3], [3]);
assert.equal(map3.result.toArray()[0], 4);
assert.equal(map3.value.toArray()[0], 30);
const map4 = kernel(map3.result, map3.value);
map3.result.delete();
map3.value.delete();
assert.equal(map4.result.toArray()[0], 5);
assert.equal(map4.value.toArray()[0], 300);
map4.result.delete();
map4.value.delete();
gpu.destroy();
}
test('mapped output texture is cloned when recompiling auto', () => {
testMappedOutputTextureIsClonedWhenRecompiling();
});
test('mapped output texture is cloned when recompiling gpu', () => {
testMappedOutputTextureIsClonedWhenRecompiling('gpu');
});
(GPU.isWebGLSupported ? test : skip)('mapped output texture is cloned when recompiling webgl', () => {
testMappedOutputTextureIsClonedWhenRecompiling('webgl');
});
(GPU.isWebGL2Supported ? test : skip)('mapped output texture is cloned when recompiling webgl2', () => {
testMappedOutputTextureIsClonedWhenRecompiling('webgl2');
});
(GPU.isHeadlessGLSupported ? test : skip)('mapped output texture is cloned when recompiling headlessgl', () => {
testMappedOutputTextureIsClonedWhenRecompiling('headlessgl');
});