Stop leaking buffer textures

This commit is contained in:
Patrick Walton 2017-10-18 19:47:44 -07:00
parent 03b4ec2cc6
commit a523d71e3c
2 changed files with 74 additions and 40 deletions

View File

@ -11,49 +11,67 @@
import * as glmatrix from 'gl-matrix';
import {setTextureParameters, UniformMap} from './gl-utils';
import {expectNotNull} from './utils';
import {assert, expectNotNull} from './utils';
export default class PathfinderBufferTexture {
readonly texture: WebGLTexture;
readonly uniformName: string;
private size: glmatrix.vec2;
private capacity: glmatrix.vec2;
private glType: number;
private destroyed: boolean;
constructor(gl: WebGLRenderingContext, uniformName: string) {
this.texture = expectNotNull(gl.createTexture(), "Failed to create buffer texture!");
this.size = glmatrix.vec2.create();
this.capacity = glmatrix.vec2.create();
this.uniformName = uniformName;
this.glType = 0;
this.destroyed = false;
}
upload(gl: WebGLRenderingContext, data: Float32Array | Uint8Array) {
destroy(gl: WebGLRenderingContext): void {
assert(!this.destroyed, "Buffer texture destroyed!");
gl.deleteTexture(this.texture);
this.destroyed = true;
}
upload(gl: WebGLRenderingContext, data: Float32Array | Uint8Array): void {
assert(!this.destroyed, "Buffer texture destroyed!");
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, this.texture);
const glType = data instanceof Float32Array ? gl.FLOAT : gl.UNSIGNED_BYTE;
const area = Math.ceil(data.length / 4);
if (glType !== this.glType || area > this.capacityArea) {
const width = Math.ceil(Math.sqrt(area));
const height = Math.ceil(area / width);
this.size = glmatrix.vec2.fromValues(width, height);
this.capacity = this.size;
const areaNeeded = Math.ceil(data.length / 4);
if (glType !== this.glType || areaNeeded > this.area) {
const sideLength = nextPowerOfTwo(Math.ceil(Math.sqrt(areaNeeded)));
this.size = glmatrix.vec2.clone([sideLength, sideLength]);
this.glType = glType;
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, glType, null);
gl.texImage2D(gl.TEXTURE_2D,
0,
gl.RGBA,
sideLength,
sideLength,
0,
gl.RGBA,
glType,
null);
setTextureParameters(gl, gl.NEAREST);
}
const mainDimensions = glmatrix.vec4.fromValues(0,
0,
this.capacity[0],
Math.floor(area / this.capacity[0]));
const remainderDimensions = glmatrix.vec4.fromValues(0,
mainDimensions[3],
area % this.capacity[0],
1);
const mainDimensions = glmatrix.vec4.clone([
0,
0,
this.size[0],
(areaNeeded / this.size[0]) | 0,
]);
const remainderDimensions = glmatrix.vec4.clone([
0,
mainDimensions[3],
areaNeeded % this.size[0],
1,
]);
const splitIndex = mainDimensions[2] * mainDimensions[3] * 4;
if (mainDimensions[2] > 0 && mainDimensions[3] > 0) {
@ -92,20 +110,28 @@ export default class PathfinderBufferTexture {
}
}
bind(gl: WebGLRenderingContext, uniforms: UniformMap, textureUnit: number) {
bind(gl: WebGLRenderingContext, uniforms: UniformMap, textureUnit: number): void {
assert(!this.destroyed, "Buffer texture destroyed!");
gl.activeTexture(gl.TEXTURE0 + textureUnit);
gl.bindTexture(gl.TEXTURE_2D, this.texture);
gl.uniform2i(uniforms[`${this.uniformName}Dimensions`],
this.capacity[0],
this.capacity[1]);
gl.uniform2i(uniforms[`${this.uniformName}Dimensions`], this.size[0], this.size[1]);
gl.uniform1i(uniforms[this.uniformName], textureUnit);
}
private get area() {
private get area(): number {
assert(!this.destroyed, "Buffer texture destroyed!");
return this.size[0] * this.size[1];
}
private get capacityArea() {
return this.capacity[0] * this.capacity[1];
}
}
/// https://stackoverflow.com/a/466242
function nextPowerOfTwo(n: number): number {
n--;
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
return n + 1;
}

View File

@ -211,29 +211,37 @@ export abstract class Renderer {
uploadPathColors(objectCount: number) {
const renderContext = this.renderContext;
this.pathColorsBufferTextures = [];
for (let objectIndex = 0; objectIndex < objectCount; objectIndex++) {
const pathColorsBufferTexture = new PathfinderBufferTexture(renderContext.gl,
'uPathColors');
const pathColors = this.pathColorsForObject(objectIndex);
let pathColorsBufferTexture;
if (objectIndex >= this.pathColorsBufferTextures.length) {
pathColorsBufferTexture = new PathfinderBufferTexture(renderContext.gl,
'uPathColors');
this.pathColorsBufferTextures[objectIndex] = pathColorsBufferTexture;
} else {
pathColorsBufferTexture = this.pathColorsBufferTextures[objectIndex];
}
pathColorsBufferTexture.upload(renderContext.gl, pathColors);
this.pathColorsBufferTextures.push(pathColorsBufferTexture);
}
}
uploadPathTransforms(objectCount: number) {
const renderContext = this.renderContext;
this.pathTransformBufferTextures.splice(0);
for (let objectIndex = 0; objectIndex < objectCount; objectIndex++) {
const pathTransformBufferTexture = new PathfinderBufferTexture(renderContext.gl,
'uPathTransform');
const pathTransforms = this.pathTransformsForObject(objectIndex);
let pathTransformBufferTexture;
if (objectIndex >= this.pathTransformBufferTextures.length) {
pathTransformBufferTexture = new PathfinderBufferTexture(renderContext.gl,
'uPathTransform');
this.pathTransformBufferTextures[objectIndex] = pathTransformBufferTexture;
} else {
pathTransformBufferTexture = this.pathTransformBufferTextures[objectIndex];
}
pathTransformBufferTexture.upload(renderContext.gl, pathTransforms);
this.pathTransformBufferTextures.push(pathTransformBufferTexture);
}
}