mirror of
https://github.com/gpujs/gpu.js.git
synced 2025-12-08 20:35:56 +00:00
- 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
855 lines
30 KiB
JavaScript
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');
|
|
}); |