Files
pixel.horse/src/ts/graphics/baseSpriteBatch.ts
T
2019-08-28 21:15:02 -07:00

203 lines
5.4 KiB
TypeScript

import { Sprite, Batch, SpriteBatchBase, SpriteSheet } from '../common/interfaces';
import { WHITE } from '../common/colors';
import { colorToFloat, colorToFloatAlpha } from '../common/color';
import { BaseStateBatch } from './baseStateBatch';
import { VAO, createVAO } from './webgl/glVao';
import { VAOAttributeDefinition, getVAOAttributesSize, createVAOAttributes } from './webgl/vaoAttributes';
import { timeStart, timeEnd } from '../client/timing';
import { isIdentity } from '../common/mat2d';
// const BATCH_BUFFER_SIZE = 2048; // 8kb
// const BATCH_BUFFER_POOL_SIZE = 5;
// const pool: ArrayBuffer[] = [];
// function aquireBuffer(size: number): Float32Array | undefined {
// if (size <= BATCH_BUFFER_SIZE) {
// if (!pool.length) console.log('alloc');
// const buffer = pool.pop() || new ArrayBuffer(BATCH_BUFFER_SIZE);
// return new Float32Array(buffer, 0, size);
// } else {
// DEVELOPMENT && console.warn(`Failed to aquire buffer of size: ${size}`);
// return undefined;
// }
// }
// function releaseBuffer(buffer: Float32Array) {
// if (pool.length < BATCH_BUFFER_POOL_SIZE) {
// pool.push(buffer.buffer);
// } else {
// console.log('delete');
// }
// }
const WHITE_FLOAT = colorToFloat(WHITE);
export function getColorFloat(color: number, alpha: number) {
return (color === WHITE && alpha === 1) ? WHITE_FLOAT : colorToFloatAlpha(color, alpha);
}
export abstract class BaseSpriteBatch extends BaseStateBatch implements SpriteBatchBase {
tris = 0;
flushes = 0;
index = 0;
spritesCount = 0;
vertices!: Float32Array;
verticesUint32!: Uint32Array;
vao: VAO | undefined = undefined;
rectSprite: Sprite | undefined = undefined;
vertexBuffer: WebGLBuffer | undefined = undefined;
indexBuffer: WebGLBuffer | undefined = undefined;
spriteSheet: SpriteSheet | undefined = undefined;
floatsPerSprite: number;
batching = false;
private startBatchIndex = 0;
private startBatchSprites = 0;
constructor(
public gl: WebGLRenderingContext,
public capacity: number,
buffer: ArrayBuffer,
vertexBuffer: WebGLBuffer,
indexBuffer: WebGLBuffer,
public attributes: VAOAttributeDefinition[],
) {
super();
this.floatsPerSprite = getVAOAttributesSize(gl, attributes);
this.vertices = new Float32Array(buffer, 0, capacity * this.floatsPerSprite);
this.verticesUint32 = new Uint32Array(buffer, 0, capacity * this.floatsPerSprite);
this.vertexBuffer = vertexBuffer;
this.indexBuffer = indexBuffer;
this.vao = createVAO(gl, createVAOAttributes(gl, attributes, vertexBuffer), indexBuffer);
}
dispose() {
disposeBuffers(this.gl, this);
}
begin() {
if (!this.vao) {
throw new Error('Disposed');
}
this.batching = false;
this.vao.bind();
}
end() {
if (!this.vao) {
throw new Error('Disposed');
}
this.flush();
this.vao.unbind();
}
drawBatch(batch: Batch) {
if (DEVELOPMENT && !isIdentity(this.transform)) {
throw new Error('Cannot transform batch');
}
const batchSpriteCount = (batch.length / this.floatsPerSprite) | 0;
if (this.capacity < (this.spritesCount + batchSpriteCount)) {
this.flush();
}
this.vertices.set(batch, this.index);
this.index += batch.length;
this.spritesCount += batchSpriteCount;
this.tris += batchSpriteCount * 2;
}
startBatch() {
if (this.batching) {
throw new Error('Cannot start new batch');
}
this.startBatchIndex = this.index;
this.startBatchSprites = this.spritesCount;
this.batching = true;
}
finishBatch(): Batch | undefined {
if (!this.batching) {
throw new Error('Cannot finish batch');
}
this.batching = false;
try {
// const batch = aquireBuffer(this.index - this.startBatchIndex);
// if (batch) {
// batch.set(this.vertices.subarray(this.startBatchIndex, this.index));
// }
// return batch;
return this.vertices.slice(this.startBatchIndex, this.index);
} catch {
return undefined;
}
}
releaseBatch(_batch: Batch) {
// releaseBuffer(batch);
}
flush() {
if (this.index === 0)
return;
if (!this.vao || !this.vertexBuffer) {
throw new Error('Disposed');
}
const gl = this.gl;
if (this.batching) {
TIMING && timeStart('bufferSubData');
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertices.subarray(0, this.startBatchIndex));
TIMING && timeEnd();
TIMING && timeStart('vao.draw');
this.vao.draw(this.gl.TRIANGLES, this.startBatchSprites * 6, 0);
TIMING && timeEnd();
this.spritesCount -= this.startBatchSprites;
this.index -= this.startBatchIndex;
this.vertices.copyWithin(0, this.startBatchIndex, this.startBatchIndex + this.index);
this.startBatchIndex = 0;
this.startBatchSprites = 0;
} else {
TIMING && timeStart('bufferSubData');
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertices.subarray(0, this.index));
TIMING && timeEnd();
TIMING && timeStart('vao.draw');
this.vao.draw(this.gl.TRIANGLES, this.spritesCount * 6, 0);
TIMING && timeEnd();
this.spritesCount = 0;
this.index = 0;
}
this.flushes++;
}
}
function disposeBuffers(gl: WebGLRenderingContext, batch: BaseSpriteBatch) {
try {
if (batch.vao) {
batch.vao.dispose();
}
if (batch.vertexBuffer) {
gl.deleteBuffer(batch.vertexBuffer);
}
if (batch.indexBuffer) {
gl.deleteBuffer(batch.indexBuffer);
}
} catch (e) {
DEVELOPMENT && console.error(e);
}
batch.vao = undefined;
batch.vertexBuffer = undefined;
batch.indexBuffer = undefined;
}