playcanvas
Version:
PlayCanvas WebGL game engine
97 lines (95 loc) • 3.14 kB
JavaScript
class WebglUploadStream {
constructor(uploadStream){
this.availablePBOs = [];
this.pendingPBOs = [];
this.uploadStream = uploadStream;
this.useSingleBuffer = uploadStream.useSingleBuffer;
}
destroy() {
const gl = this.uploadStream.device.gl;
this.availablePBOs.forEach((info)=>gl.deleteBuffer(info.pbo));
this.pendingPBOs.forEach((item)=>{
if (item.sync) gl.deleteSync(item.sync);
gl.deleteBuffer(item.pbo);
});
}
_onDeviceLost() {
this.availablePBOs.length = 0;
this.pendingPBOs.length = 0;
}
update(minByteSize) {
const gl = this.uploadStream.device.gl;
const pending = this.pendingPBOs;
for(let i = pending.length - 1; i >= 0; i--){
const item = pending[i];
const result = gl.clientWaitSync(item.sync, 0, 0);
if (result === gl.CONDITION_SATISFIED || result === gl.ALREADY_SIGNALED) {
gl.deleteSync(item.sync);
this.availablePBOs.push({
pbo: item.pbo,
size: item.size
});
pending.splice(i, 1);
}
}
const available = this.availablePBOs;
for(let i = available.length - 1; i >= 0; i--){
if (available[i].size < minByteSize) {
gl.deleteBuffer(available[i].pbo);
available.splice(i, 1);
}
}
}
upload(data, target, offset, size) {
if (this.useSingleBuffer) {
this.uploadDirect(data, target, offset, size);
} else {
this.uploadPBO(data, target, offset, size);
}
}
uploadDirect(data, target, offset, size) {
target._levels[0] = data;
target.upload();
}
uploadPBO(data, target, offset, size) {
const device = this.uploadStream.device;
const gl = device.gl;
const width = target.width;
const byteSize = size * data.BYTES_PER_ELEMENT;
this.update(byteSize);
const startY = offset / width;
const height = size / width;
const pboInfo = this.availablePBOs.pop() ?? (()=>{
const pbo = gl.createBuffer();
return {
pbo,
size: byteSize
};
})();
gl.bindBuffer(gl.PIXEL_UNPACK_BUFFER, pboInfo.pbo);
gl.bufferData(gl.PIXEL_UNPACK_BUFFER, byteSize, gl.STREAM_DRAW);
gl.bufferSubData(gl.PIXEL_UNPACK_BUFFER, 0, new Uint8Array(data.buffer, data.byteOffset, byteSize));
gl.bindBuffer(gl.PIXEL_UNPACK_BUFFER, null);
device.setTexture(target, 0);
device.activeTexture(0);
device.bindTexture(target);
gl.bindBuffer(gl.PIXEL_UNPACK_BUFFER, pboInfo.pbo);
device.setUnpackFlipY(false);
device.setUnpackPremultiplyAlpha(false);
gl.pixelStorei(gl.UNPACK_ALIGNMENT, data.BYTES_PER_ELEMENT);
gl.pixelStorei(gl.UNPACK_ROW_LENGTH, 0);
gl.pixelStorei(gl.UNPACK_SKIP_ROWS, 0);
gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, 0);
const impl = target.impl;
gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, startY, width, height, impl._glFormat, impl._glPixelType, 0);
gl.bindBuffer(gl.PIXEL_UNPACK_BUFFER, null);
const sync = gl.fenceSync(gl.SYNC_GPU_COMMANDS_COMPLETE, 0);
this.pendingPBOs.push({
pbo: pboInfo.pbo,
size: byteSize,
sync
});
gl.flush();
}
}
export { WebglUploadStream };