"use strict" var pool = require("typedarray-pool") var ops = require("ndarray-ops") var ndarray = require("ndarray") var SUPPORTED_TYPES = [ "uint8", "uint8_clamped", "uint16", "uint32", "int8", "int16", "int32", "float32" ] function GLBuffer(gl, type, handle, length, usage) { this.gl = gl this.type = type this.handle = handle this.length = length this.usage = usage } var proto = GLBuffer.prototype proto.bind = function() { this.gl.bindBuffer(this.type, this.handle) } proto.unbind = function() { this.gl.bindBuffer(this.type, null) } proto.dispose = function() { this.gl.deleteBuffer(this.handle) } function updateTypeArray(gl, type, len, usage, data, offset) { var dataLen = data.length * data.BYTES_PER_ELEMENT if(offset < 0) { gl.bufferData(type, data, usage) return dataLen } if(dataLen + offset > len) { throw new Error("gl-buffer: If resizing buffer, must not specify offset") } gl.bufferSubData(type, offset, data) return len } function makeScratchTypeArray(array, dtype) { var res = pool.malloc(array.length, dtype) var n = array.length for(var i=0; i=0; --i) { if(stride[i] !== n) { return false } n *= shape[i] } return true } proto.update = function(array, offset) { if(typeof offset !== "number") { offset = -1 } this.bind() if(typeof array === "object" && typeof array.shape !== "undefined") { //ndarray var dtype = array.dtype if(SUPPORTED_TYPES.indexOf(dtype) < 0) { dtype = "float32" } if(this.type === this.gl.ELEMENT_ARRAY_BUFFER) { var ext = gl.getExtension('OES_element_index_uint') if(ext && dtype !== "uint16") { dtype = "uint32" } else { dtype = "uint16" } } if(dtype === array.dtype && isPacked(array.shape, array.stride)) { if(array.offset === 0 && array.data.length === array.shape[0]) { this.length = updateTypeArray(this.gl, this.type, this.length, this.usage, array.data, offset) } else { this.length = updateTypeArray(this.gl, this.type, this.length, this.usage, array.data.subarray(array.offset, array.shape[0]), offset) } } else { var tmp = pool.malloc(array.size, dtype) var ndt = ndarray(tmp, array.shape) ops.assign(ndt, array) if(offset < 0) { this.length = updateTypeArray(this.gl, this.type, this.length, this.usage, tmp, offset) } else { this.length = updateTypeArray(this.gl, this.type, this.length, this.usage, tmp.subarray(0, array.size), offset) } pool.free(tmp) } } else if(Array.isArray(array)) { //Vanilla array var t if(this.type === this.gl.ELEMENT_ARRAY_BUFFER) { t = makeScratchTypeArray(array, "uint16") } else { t = makeScratchTypeArray(array, "float32") } if(offset < 0) { this.length = updateTypeArray(this.gl, this.type, this.length, this.usage, t, offset) } else { this.length = updateTypeArray(this.gl, this.type, this.length, this.usage, t.subarray(0, array.length), offset) } pool.free(t) } else if(typeof array === "object" && typeof array.length === "number") { //Typed array this.length = updateTypeArray(this.gl, this.type, this.length, this.usage, array, offset) } else if(typeof array === "number" || array === undefined) { //Number/default if(offset >= 0) { throw new Error("gl-buffer: Cannot specify offset when resizing buffer") } array = array | 0 if(array <= 0) { array = 1 } this.gl.bufferData(this.type, array|0, this.usage) this.length = array } else { //Error, case should not happen throw new Error("gl-buffer: Invalid data type") } } function createBuffer(gl, data, type, usage) { type = type || gl.ARRAY_BUFFER usage = usage || gl.DYNAMIC_DRAW if(type !== gl.ARRAY_BUFFER && type !== gl.ELEMENT_ARRAY_BUFFER) { throw new Error("gl-buffer: Invalid type for webgl buffer, must be either gl.ARRAY_BUFFER or gl.ELEMENT_ARRAY_BUFFER") } if(usage !== gl.DYNAMIC_DRAW && usage !== gl.STATIC_DRAW && usage !== gl.STREAM_DRAW) { throw new Error("gl-buffer: Invalid usage for buffer, must be either gl.DYNAMIC_DRAW, gl.STATIC_DRAW or gl.STREAM_DRAW") } var handle = gl.createBuffer() var result = new GLBuffer(gl, type, handle, 0, usage) result.update(data) return result } module.exports = createBuffer