Skip to content

Commit

Permalink
fix: Memory recycling bug, and remove no longer needed renderStrategy
Browse files Browse the repository at this point in the history
Also cleanup documentation a bit
  • Loading branch information
robertleeplummerjr committed Dec 25, 2019
1 parent 5738698 commit a3c930d
Show file tree
Hide file tree
Showing 26 changed files with 788 additions and 673 deletions.
250 changes: 110 additions & 140 deletions dist/gpu-browser-core.js

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions dist/gpu-browser-core.min.js

Large diffs are not rendered by default.

252 changes: 111 additions & 141 deletions dist/gpu-browser.js

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions dist/gpu-browser.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "gpu.js",
"version": "2.4.0",
"version": "2.4.1",
"description": "GPU Accelerated JavaScript",
"engines": {
"node": ">=8.0.0"
Expand Down Expand Up @@ -45,7 +45,8 @@
"scripts": {
"test": "qunit",
"setup": "npm i -g gulp-cli",
"make": "gulp make"
"make": "gulp make",
"docs": "doxdox ./src --layout bootstrap --output docs.html"
},
"repository": {
"type": "git",
Expand Down
176 changes: 62 additions & 114 deletions src/backend/gl/kernel.js

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion src/backend/gl/texture/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
const { Texture } = require('../../../texture');

/**
* @class
* @property framebuffer
* @extends Texture
*/
class GLTexture extends Texture {
/**
* @returns {Number}
Expand Down Expand Up @@ -52,7 +57,7 @@ class GLTexture extends Texture {

delete() {
super.delete();
if (this.framebuffer && this.texture && this.texture.refs < 1) {
if (this.framebuffer) {
this.context.deleteFramebuffer(this.framebuffer);
}
}
Expand Down
31 changes: 26 additions & 5 deletions src/backend/kernel.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ class Kernel {
}

/**
* @type {Boolean}
* @abstract
* @returns {Boolean}
*/
static isContextMatch(context) {
throw new Error(`"isContextMatch" not implemented on ${ this.name }`);
Expand Down Expand Up @@ -42,7 +43,7 @@ class Kernel {

/**
*
* @param {string|IJSON} source
* @param {string|IKernelJSON} source
* @param [settings]
*/
constructor(source, settings) {
Expand Down Expand Up @@ -73,7 +74,7 @@ class Kernel {

/**
* The function source
* @type {String|IJSON}
* @type {String|IKernelJSON}
*/
this.source = source;

Expand Down Expand Up @@ -106,9 +107,29 @@ class Kernel {
* @type {Object}
*/
this.constants = null;

/**
*
* @type {Object.<string, string>}
*/
this.constantTypes = null;

/**
*
* @type {Object.<string, number>}
*/
this.constantBitRatios = null;

/**
*
* @type {boolean}
*/
this.dynamicArguments = false;

/**
*
* @type {boolean}
*/
this.dynamicOutput = false;

/**
Expand Down Expand Up @@ -178,7 +199,7 @@ class Kernel {
this.pipeline = false;

/**
* Make GPU use single precison or unsigned. Acceptable values: 'single' or 'unsigned'
* Make GPU use single precision or unsigned. Acceptable values: 'single' or 'unsigned'
* @type {String|null}
* @enum 'single' | 'unsigned'
*/
Expand Down Expand Up @@ -759,7 +780,7 @@ class Kernel {
}

/**
* @return {IJSON}
* @return {IKernelJSON}
*/
toJSON() {
return {
Expand Down
4 changes: 4 additions & 0 deletions src/backend/web-gl/kernel-value/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class WebGLKernelValue extends KernelValue {
this.uploadValue = null;
this.textureSize = null;
this.bitRatio = null;
this.prevArg = null;
}

/**
Expand Down Expand Up @@ -144,6 +145,9 @@ class WebGLKernelValue extends KernelValue {
}

destroy() {
if (this.prevArg) {
this.prevArg.delete();
}
this.context.deleteTexture(this.texture);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class WebGLKernelValueMemoryOptimizedNumberTexture extends WebGLKernelValue {

const { context: gl, kernel } = this;
if (kernel.pipeline) {
kernel.updateTextureArgumentRefs(inputTexture);
kernel.updateTextureArgumentRefs(this, inputTexture);
}

gl.activeTexture(this.contextHandle);
Expand Down
2 changes: 1 addition & 1 deletion src/backend/web-gl/kernel-value/number-texture.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class WebGLKernelValueNumberTexture extends WebGLKernelValue {

const { kernel, context: gl } = this;
if (kernel.pipeline) {
kernel.updateTextureArgumentRefs(inputTexture);
kernel.updateTextureArgumentRefs(this, inputTexture);
}

gl.activeTexture(this.contextHandle);
Expand Down
47 changes: 30 additions & 17 deletions src/backend/web-gl/kernel.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,15 @@ const { glKernelString } = require('../gl/kernel-string');
const { lookupKernelValueType } = require('./kernel-value-maps');

let isSupported = null;
/**
*
* @type {HTMLCanvasElement|OffscreenCanvas|null}
*/
let testCanvas = null;
/**
*
* @type {WebGLRenderingContext|null}
*/
let testContext = null;
let testExtensions = null;
let features = null;
Expand All @@ -24,17 +32,17 @@ const maxTexSizes = {};
* <p>This builds the shaders and runs them on the GPU,
* the outputs the result back as float(enabled by default) and Texture.</p>
*
* @prop {Object} textureCache - webGl Texture cache
* @prop {Object} programUniformLocationCache - Location of program variables in memory
* @prop {Object} framebuffer - Webgl frameBuffer
* @prop {Object} buffer - WebGL buffer
* @prop {Object} program - The webGl Program
* @prop {Object} functionBuilder - Function Builder instance bound to this Kernel
* @prop {Boolean} pipeline - Set output type to FAST mode (GPU to GPU via Textures), instead of float
* @prop {String} endianness - Endian information like Little-endian, Big-endian.
* @prop {Array} argumentTypes - Types of parameters sent to the Kernel
* @prop {String} compiledFragmentShader - Compiled fragment shader string
* @prop {String} compiledVertexShader - Compiled Vertical shader string
* @property {WebGLTexture[]} textureCache - webGl Texture cache
* @property {Object.<string, WebGLUniformLocation>} programUniformLocationCache - Location of program variables in memory
* @property {WebGLFramebuffer} framebuffer - Webgl frameBuffer
* @property {WebGLBuffer} buffer - WebGL buffer
* @property {WebGLProgram} program - The webGl Program
* @property {FunctionBuilder} functionBuilder - Function Builder instance bound to this Kernel
* @property {Boolean} pipeline - Set output type to FAST mode (GPU to GPU via Textures), instead of float
* @property {string} endianness - Endian information like Little-endian, Big-endian.
* @property {string[]} argumentTypes - Types of parameters sent to the Kernel
* @property {string|null} compiledFragmentShader - Compiled fragment shader string
* @property {string|null} compiledVertexShader - Compiled Vertical shader string
* @extends GLKernel
*/
class WebGLKernel extends GLKernel {
Expand Down Expand Up @@ -116,7 +124,7 @@ class WebGLKernel extends GLKernel {

/**
*
* @param {String|IJSON} source
* @param {String|IKernelJSON} source
* @param {IDirectKernelSettings} settings
*/
constructor(source, settings) {
Expand All @@ -130,7 +138,6 @@ class WebGLKernel extends GLKernel {
this.fragShader = null;
this.vertShader = null;
this.drawBuffersMap = null;
this.outputTexture = null;

/**
*
Expand Down Expand Up @@ -717,7 +724,6 @@ class WebGLKernel extends GLKernel {
const texSize = this.texSize;
this.drawBuffersMap = [gl.COLOR_ATTACHMENT0];
this.mappedTextures = [];
this.prevMappedInputs = {};
for (let i = 0; i < this.subKernels.length; i++) {
const texture = this.createTexture();
this.drawBuffersMap.push(gl.COLOR_ATTACHMENT0 + i + 1);
Expand Down Expand Up @@ -1441,9 +1447,6 @@ class WebGLKernel extends GLKernel {
if (this.program) {
this.context.deleteProgram(this.program);
}
if (this.prevInput) {
this.prevInput.delete();
}
if (this.texture) {
this.texture.delete();
const textureCacheIndex = this.textureCache.indexOf(this.texture.texture);
Expand All @@ -1463,6 +1466,16 @@ class WebGLKernel extends GLKernel {
}
this.mappedTextures = null;
}
if (this.kernelArguments) {
for (let i = 0; i < this.kernelArguments.length; i++) {
this.kernelArguments[i].destroy();
}
}
if (this.kernelConstants) {
for (let i = 0; i < this.kernelConstants.length; i++) {
this.kernelConstants[i].destroy();
}
}
while (this.textureCache.length > 0) {
const texture = this.textureCache.pop();
this.context.deleteTexture(texture);
Expand Down
5 changes: 2 additions & 3 deletions src/backend/web-gl2/kernel.js
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ class WebGL2Kernel extends WebGLKernel {
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.texture.texture, 0);
return;
}
const texture = this.outputTexture = gl.createTexture();
const texture = gl.createTexture();
const { texSize } = this;
gl.activeTexture(gl.TEXTURE0 + this.constantTextureCount + this.argumentTextureCount);
gl.bindTexture(gl.TEXTURE_2D, texture);
Expand Down Expand Up @@ -325,7 +325,6 @@ class WebGL2Kernel extends WebGLKernel {
const { texSize } = this;
this.drawBuffersMap = [gl.COLOR_ATTACHMENT0];
this.mappedTextures = [];
this.prevMappedInputs = {};
for (let i = 0; i < this.subKernels.length; i++) {
const texture = this.createTexture();
this.drawBuffersMap.push(gl.COLOR_ATTACHMENT0 + i + 1);
Expand Down Expand Up @@ -602,7 +601,7 @@ class WebGL2Kernel extends WebGLKernel {
}

/**
* @return {IJSON}
* @return {IKernelJSON}
*/
toJSON() {
const json = super.toJSON();
Expand Down
59 changes: 40 additions & 19 deletions src/gpu.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const { kernelRunShortcut } = require('./kernel-run-shortcut');

/**
*
* @type {typeof Kernel[]}
* @type {Array.<Kernel>}
*/
const kernelOrder = [HeadlessGLKernel, WebGL2Kernel, WebGLKernel];

Expand All @@ -29,6 +29,8 @@ let validate = true;

/**
* The GPU.js library class which manages the GPU context for the creating kernels
* @class
* @return {GPU}
*/
class GPU {
static disableValidation() {
Expand Down Expand Up @@ -105,6 +107,7 @@ class GPU {
/**
* Creates an instance of GPU.
* @param {IGPUSettings} [settings] - Settings to set mode, and other properties
* @constructor
*/
constructor(settings) {
settings = settings || {};
Expand Down Expand Up @@ -142,6 +145,10 @@ class GPU {
chooseKernel() {
if (this.Kernel) return;

/**
*
* @type {WebGLKernel|WebGL2Kernel|HeadlessGLKernel|CPUKernel}
*/
let Kernel = null;

if (this.context) {
Expand Down Expand Up @@ -253,7 +260,9 @@ class GPU {
}

function onRequestSwitchKernel(reasons, args, kernel) {
console.warn('Switching kernels');
if (kernel.debug) {
console.warn('Switching kernels');
}
let newOutput = null;
if (kernel.dynamicOutput) {
for (let i = reasons.length - 1; i >= 0; i--) {
Expand Down Expand Up @@ -529,27 +538,39 @@ class GPU {

/**
* @desc Destroys all memory associated with gpu.js & the webGl if we created it
* @return {Promise}
* @resolve {void}
* @reject {Error}
*/
destroy() {
if (!this.kernels) return;
// perform on next run loop - for some reason we dont get lose context events
// if webGl is created and destroyed in the same run loop.
setTimeout(() => {
for (let i = 0; i < this.kernels.length; i++) {
this.kernels[i].destroy(true); // remove canvas if exists
return new Promise((resolve, reject) => {
if (!this.kernels) {
resolve();
}
// all kernels are associated with one context, go ahead and take care of it here
let firstKernel = this.kernels[0];
if (firstKernel) {
// if it is shortcut
if (firstKernel.kernel) {
firstKernel = firstKernel.kernel;
}
if (firstKernel.constructor.destroyContext) {
firstKernel.constructor.destroyContext(this.context);
// perform on next run loop - for some reason we dont get lose context events
// if webGl is created and destroyed in the same run loop.
setTimeout(() => {
try {
for (let i = 0; i < this.kernels.length; i++) {
this.kernels[i].destroy(true); // remove canvas if exists
}
// all kernels are associated with one context, go ahead and take care of it here
let firstKernel = this.kernels[0];
if (firstKernel) {
// if it is shortcut
if (firstKernel.kernel) {
firstKernel = firstKernel.kernel;
}
if (firstKernel.constructor.destroyContext) {
firstKernel.constructor.destroyContext(this.context);
}
}
} catch (e) {
reject(e);
}
}
}, 0);
resolve();
}, 0);
});
}
}

Expand Down
Loading

0 comments on commit a3c930d

Please sign in to comment.