Skip to content

Commit d16b5ec

Browse files
Merge pull request #637 from gpujs/625-framebuffer-leak
625 framebuffer leak
2 parents 11109c6 + c09e0b6 commit d16b5ec

18 files changed

+805
-174
lines changed

dist/gpu-browser-core.js

Lines changed: 129 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
*
55
* GPU Accelerated JavaScript
66
*
7-
* @version 2.9.5
8-
* @date Sun Jul 19 2020 07:39:18 GMT-0400 (Eastern Daylight Time)
7+
* @version 2.10.0
8+
* @date Tue Aug 25 2020 14:05:30 GMT-0400 (Eastern Daylight Time)
99
*
1010
* @license MIT
1111
* The MIT License
@@ -1043,6 +1043,15 @@ class CPUFunctionNode extends FunctionNode {
10431043
this.astGeneric(mNode.property, retArr);
10441044
retArr.push(']');
10451045
return retArr;
1046+
case 'fn()[][]':
1047+
this.astGeneric(mNode.object.object, retArr);
1048+
retArr.push('[');
1049+
this.astGeneric(mNode.object.property, retArr);
1050+
retArr.push(']');
1051+
retArr.push('[');
1052+
this.astGeneric(mNode.property, retArr);
1053+
retArr.push(']');
1054+
return retArr;
10461055
default:
10471056
throw this.astErrorOutput('Unexpected expression', mNode);
10481057
}
@@ -1064,6 +1073,9 @@ class CPUFunctionNode extends FunctionNode {
10641073
case 'Array(2)':
10651074
case 'Array(3)':
10661075
case 'Array(4)':
1076+
case 'Matrix(2)':
1077+
case 'Matrix(3)':
1078+
case 'Matrix(4)':
10671079
case 'HTMLImageArray':
10681080
case 'ArrayTexture(1)':
10691081
case 'ArrayTexture(2)':
@@ -1165,18 +1177,23 @@ class CPUFunctionNode extends FunctionNode {
11651177
}
11661178

11671179
astArrayExpression(arrNode, retArr) {
1180+
const returnType = this.getType(arrNode);
11681181
const arrLen = arrNode.elements.length;
1169-
1170-
retArr.push('new Float32Array([');
1182+
const elements = [];
11711183
for (let i = 0; i < arrLen; ++i) {
1172-
if (i > 0) {
1173-
retArr.push(', ');
1174-
}
1175-
const subNode = arrNode.elements[i];
1176-
this.astGeneric(subNode, retArr)
1184+
const element = [];
1185+
this.astGeneric(arrNode.elements[i], element);
1186+
elements.push(element.join(''));
1187+
}
1188+
switch (returnType) {
1189+
case 'Matrix(2)':
1190+
case 'Matrix(3)':
1191+
case 'Matrix(4)':
1192+
retArr.push(`[${elements.join(', ')}]`);
1193+
break;
1194+
default:
1195+
retArr.push(`new Float32Array([${elements.join(', ')}])`);
11771196
}
1178-
retArr.push('])');
1179-
11801197
return retArr;
11811198
}
11821199

@@ -1208,6 +1225,9 @@ function constantsToString(constants, types) {
12081225
case 'Array(2)':
12091226
case 'Array(3)':
12101227
case 'Array(4)':
1228+
case 'Matrix(2)':
1229+
case 'Matrix(3)':
1230+
case 'Matrix(4)':
12111231
results.push(`${name}:new ${constant.constructor.name}(${JSON.stringify(Array.from(constant))})`);
12121232
break;
12131233
}
@@ -1347,6 +1367,9 @@ ${ header.join('\n') }
13471367
case 'Array(2)':
13481368
case 'Array(3)':
13491369
case 'Array(4)':
1370+
case 'Matrix(2)':
1371+
case 'Matrix(3)':
1372+
case 'Matrix(4)':
13501373
if (incomingConstants.hasOwnProperty(p)) {
13511374
console.warn('constant ' + p + ' of type ' + type + ' cannot be resigned');
13521375
}
@@ -2843,6 +2866,13 @@ class FunctionNode {
28432866
case 'BlockStatement':
28442867
return this.getType(ast.body);
28452868
case 'ArrayExpression':
2869+
const childType = this.getType(ast.elements[0]);
2870+
switch (childType) {
2871+
case 'Array(2)':
2872+
case 'Array(3)':
2873+
case 'Array(4)':
2874+
return `Matrix(${ast.elements.length})`;
2875+
}
28462876
return `Array(${ ast.elements.length })`;
28472877
case 'Literal':
28482878
const literalKey = this.astKey(ast);
@@ -3774,6 +3804,7 @@ class FunctionNode {
37743804
};
37753805
}
37763806
case 'fn()[]':
3807+
case 'fn()[][]':
37773808
case '[][]':
37783809
return {
37793810
signature: variableSignature,
@@ -3883,6 +3914,9 @@ const typeLookupMap = {
38833914
'Array(2)': 'Number',
38843915
'Array(3)': 'Number',
38853916
'Array(4)': 'Number',
3917+
'Matrix(2)': 'Number',
3918+
'Matrix(3)': 'Number',
3919+
'Matrix(4)': 'Number',
38863920
'Array2D': 'Number',
38873921
'Array3D': 'Number',
38883922
'Input': 'Number',
@@ -4004,8 +4038,21 @@ class FunctionTracer {
40044038
}
40054039

40064040
getDeclaration(name) {
4007-
const { currentContext, currentFunctionContext } = this;
4008-
return currentContext[name] || currentFunctionContext[name] || null;
4041+
const { currentContext, currentFunctionContext, runningContexts } = this;
4042+
const declaration = currentContext[name] || currentFunctionContext[name] || null;
4043+
4044+
if (
4045+
!declaration &&
4046+
currentContext === currentFunctionContext &&
4047+
runningContexts.length > 0
4048+
) {
4049+
const previousRunningContext = runningContexts[runningContexts.length - 2];
4050+
if (previousRunningContext[name]) {
4051+
return previousRunningContext[name];
4052+
}
4053+
}
4054+
4055+
return declaration;
40094056
}
40104057

40114058
scan(ast) {
@@ -4380,14 +4427,15 @@ function glKernelString(Kernel, args, originKernel, setupContextString, destroyC
43804427
result.push(context.toString());
43814428
if (kernel.renderOutput === kernel.renderTexture) {
43824429
context.reset();
4430+
const framebufferName = context.getContextVariableName(kernel.framebuffer);
43834431
if (kernel.renderKernels) {
43844432
const results = kernel.renderKernels();
43854433
const textureName = context.getContextVariableName(kernel.texture.texture);
43864434
result.push(` return {
43874435
result: {
43884436
texture: ${ textureName },
43894437
type: '${ results.result.type }',
4390-
toArray: ${ getToArrayString(results.result, textureName) }
4438+
toArray: ${ getToArrayString(results.result, textureName, framebufferName) }
43914439
},`);
43924440
const { subKernels, mappedTextures } = kernel;
43934441
for (let i = 0; i < subKernels.length; i++) {
@@ -4399,7 +4447,7 @@ function glKernelString(Kernel, args, originKernel, setupContextString, destroyC
43994447
${subKernel.property}: {
44004448
texture: ${ subKernelTextureName },
44014449
type: '${ subKernelResult.type }',
4402-
toArray: ${ getToArrayString(subKernelResult, subKernelTextureName) }
4450+
toArray: ${ getToArrayString(subKernelResult, subKernelTextureName, framebufferName) }
44034451
},`);
44044452
}
44054453
result.push(` };`);
@@ -4409,7 +4457,7 @@ function glKernelString(Kernel, args, originKernel, setupContextString, destroyC
44094457
result.push(` return {
44104458
texture: ${ textureName },
44114459
type: '${ rendered.type }',
4412-
toArray: ${ getToArrayString(rendered, textureName) }
4460+
toArray: ${ getToArrayString(rendered, textureName, framebufferName) }
44134461
};`);
44144462
}
44154463
}
@@ -4424,7 +4472,7 @@ function glKernelString(Kernel, args, originKernel, setupContextString, destroyC
44244472

44254473
let constantsUpload = [];
44264474
kernelConstants.forEach((kernelConstant) => {
4427-
constantsUpload.push(`${ kernelConstant.getStringValueHandler()}`);
4475+
constantsUpload.push(`${kernelConstant.getStringValueHandler()}`);
44284476
});
44294477
return `function kernel(settings) {
44304478
const { context, constants } = settings;
@@ -4468,14 +4516,17 @@ function getGetPixelsString(kernel) {
44684516
});
44694517
}
44704518

4471-
function getToArrayString(kernelResult, textureName) {
4519+
function getToArrayString(kernelResult, textureName, framebufferName) {
44724520
const toArray = kernelResult.toArray.toString();
44734521
const useFunctionKeyword = !/^function/.test(toArray);
44744522
const flattenedFunctions = utils.flattenFunctionToString(`${useFunctionKeyword ? 'function ' : ''}${ toArray }`, {
44754523
findDependency: (object, name) => {
44764524
if (object === 'utils') {
44774525
return `const ${name} = ${utils[name].toString()};`;
44784526
} else if (object === 'this') {
4527+
if (name === 'framebuffer') {
4528+
return '';
4529+
}
44794530
return `${useFunctionKeyword ? 'function ' : ''}${kernelResult[name].toString()}`;
44804531
} else {
44814532
throw new Error('unhandled fromObject');
@@ -4489,17 +4540,14 @@ function getToArrayString(kernelResult, textureName) {
44894540
if (isDeclaration) return null;
44904541
return 'gl';
44914542
}
4492-
if (property === '_framebuffer') {
4493-
return '_framebuffer';
4494-
}
44954543
if (kernelResult.hasOwnProperty(property)) {
44964544
return JSON.stringify(kernelResult[property]);
44974545
}
44984546
throw new Error(`unhandled thisLookup ${ property }`);
44994547
}
45004548
});
45014549
return `() => {
4502-
let _framebuffer;
4550+
function framebuffer() { return ${framebufferName}; };
45034551
${flattenedFunctions}
45044552
return toArray();
45054553
}`;
@@ -5801,18 +5849,12 @@ class GLTexture extends Texture {
58015849
if (this.texture._refs) return;
58025850
}
58035851
this.context.deleteTexture(this.texture);
5804-
if (this.texture._refs === 0 && this._framebuffer) {
5805-
this.context.deleteFramebuffer(this._framebuffer);
5806-
this._framebuffer = null;
5807-
}
58085852
}
58095853

58105854
framebuffer() {
58115855
if (!this._framebuffer) {
5812-
this._framebuffer = this.context.createFramebuffer();
5856+
this._framebuffer = this.kernel.getRawValueFramebuffer(this.size[0], this.size[1]);
58135857
}
5814-
this._framebuffer.width = this.size[0];
5815-
this._framebuffer.height = this.size[1];
58165858
return this._framebuffer;
58175859
}
58185860
}
@@ -5926,8 +5968,7 @@ class GLTextureUnsigned extends GLTexture {
59265968
}
59275969
renderRawOutput() {
59285970
const { context: gl } = this;
5929-
const framebuffer = gl.createFramebuffer();
5930-
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
5971+
gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer());
59315972
gl.framebufferTexture2D(
59325973
gl.FRAMEBUFFER,
59335974
gl.COLOR_ATTACHMENT0,
@@ -7412,6 +7453,9 @@ class WebGLFunctionNode extends FunctionNode {
74127453
case 'Array(4)':
74137454
case 'Array(3)':
74147455
case 'Array(2)':
7456+
case 'Matrix(2)':
7457+
case 'Matrix(3)':
7458+
case 'Matrix(4)':
74157459
case 'Input':
74167460
this.astGeneric(ast.argument, result);
74177461
break;
@@ -7977,7 +8021,7 @@ class WebGLFunctionNode extends FunctionNode {
79778021
}
79788022
const markupType = typeMap[type];
79798023
if (!markupType) {
7980-
throw this.astErrorOutput(`Markup type ${ markupType } not handled`, varDecNode);
8024+
throw this.astErrorOutput(`Markup type ${ type } not handled`, varDecNode);
79818025
}
79828026
const declarationResult = [];
79838027
if (actualType === 'Integer' && type === 'Integer') {
@@ -8275,6 +8319,15 @@ class WebGLFunctionNode extends FunctionNode {
82758319
retArr.push(this.memberExpressionPropertyMarkup(property));
82768320
retArr.push(']');
82778321
return retArr;
8322+
case 'fn()[][]':
8323+
this.astCallExpression(mNode.object.object, retArr);
8324+
retArr.push('[');
8325+
retArr.push(this.memberExpressionPropertyMarkup(mNode.object.property));
8326+
retArr.push(']');
8327+
retArr.push('[');
8328+
retArr.push(this.memberExpressionPropertyMarkup(mNode.property));
8329+
retArr.push(']');
8330+
return retArr;
82788331
case '[][]':
82798332
this.astArrayExpression(mNode.object, retArr);
82808333
retArr.push('[');
@@ -8402,6 +8455,14 @@ class WebGLFunctionNode extends FunctionNode {
84028455
this.memberExpressionXYZ(xProperty, yProperty, zProperty, retArr);
84038456
retArr.push(')');
84048457
break;
8458+
case 'Matrix(2)':
8459+
case 'Matrix(3)':
8460+
case 'Matrix(4)':
8461+
retArr.push(`${markupName}[${this.memberExpressionPropertyMarkup(yProperty)}]`);
8462+
if (yProperty) {
8463+
retArr.push(`[${this.memberExpressionPropertyMarkup(xProperty)}]`);
8464+
}
8465+
break;
84058466
default:
84068467
throw new Error(`unhandled member expression "${ type }"`);
84078468
}
@@ -8574,9 +8635,19 @@ class WebGLFunctionNode extends FunctionNode {
85748635
}
85758636

85768637
astArrayExpression(arrNode, retArr) {
8638+
const returnType = this.getType(arrNode);
8639+
85778640
const arrLen = arrNode.elements.length;
85788641

8579-
retArr.push('vec' + arrLen + '(');
8642+
switch (returnType) {
8643+
case 'Matrix(2)':
8644+
case 'Matrix(3)':
8645+
case 'Matrix(4)':
8646+
retArr.push(`mat${arrLen}(`);
8647+
break;
8648+
default:
8649+
retArr.push(`vec${arrLen}(`);
8650+
}
85808651
for (let i = 0; i < arrLen; ++i) {
85818652
if (i > 0) {
85828653
retArr.push(', ');
@@ -8630,6 +8701,9 @@ const typeMap = {
86308701
'Array(2)': 'vec2',
86318702
'Array(3)': 'vec3',
86328703
'Array(4)': 'vec4',
8704+
'Matrix(2)': 'mat2',
8705+
'Matrix(3)': 'mat3',
8706+
'Matrix(4)': 'mat4',
86338707
'Array2D': 'sampler2D',
86348708
'Array3D': 'sampler2D',
86358709
'Boolean': 'bool',
@@ -10467,6 +10541,7 @@ class WebGLKernel extends GLKernel {
1046710541
this.framebuffer = gl.createFramebuffer();
1046810542
this.framebuffer.width = texSize[0];
1046910543
this.framebuffer.height = texSize[1];
10544+
this.rawValueFramebuffers = {};
1047010545

1047110546
const vertices = new Float32Array([-1, -1,
1047210547
1, -1, -1, 1,
@@ -10999,6 +11074,19 @@ float integerCorrectionModulo(float number, float divisor) {
1099911074
return result.join('');
1100011075
}
1100111076

11077+
getRawValueFramebuffer(width, height) {
11078+
if (!this.rawValueFramebuffers[width]) {
11079+
this.rawValueFramebuffers[width] = {};
11080+
}
11081+
if (!this.rawValueFramebuffers[width][height]) {
11082+
const framebuffer = this.context.createFramebuffer();
11083+
framebuffer.width = width;
11084+
framebuffer.height = height;
11085+
this.rawValueFramebuffers[width][height] = framebuffer;
11086+
}
11087+
return this.rawValueFramebuffers[width][height];
11088+
}
11089+
1100211090
getKernelResultDeclaration() {
1100311091
switch (this.returnType) {
1100411092
case 'Array(2)':
@@ -11329,6 +11417,13 @@ float integerCorrectionModulo(float number, float divisor) {
1132911417
if (this.framebuffer) {
1133011418
this.context.deleteFramebuffer(this.framebuffer);
1133111419
}
11420+
for (const width in this.rawValueFramebuffers) {
11421+
for (const height in this.rawValueFramebuffers[width]) {
11422+
this.context.deleteFramebuffer(this.rawValueFramebuffers[width][height]);
11423+
delete this.rawValueFramebuffers[width][height];
11424+
}
11425+
delete this.rawValueFramebuffers[width];
11426+
}
1133211427
if (this.vertShader) {
1133311428
this.context.deleteShader(this.vertShader);
1133411429
}
@@ -14754,7 +14849,7 @@ const utils = {
1475414849
if (!flattened[functionDependency]) {
1475514850
flattened[functionDependency] = true;
1475614851
}
14757-
flattenedFunctionDependencies.push(utils.flattenFunctionToString(functionDependency, settings) + '\n');
14852+
functionDependency ? flattenedFunctionDependencies.push(utils.flattenFunctionToString(functionDependency, settings) + '\n') : '';
1475814853
}
1475914854
return flattenedFunctionDependencies.join('') + result;
1476014855
}

0 commit comments

Comments
 (0)