nanogl-sync
Version:
webgl's fence Sync EZPZ and shim for webgl1
240 lines (165 loc) • 4.7 kB
text/typescript
function isWebgl2(gl: WebGLRenderingContext | WebGL2RenderingContext): gl is WebGL2RenderingContext {
return (gl as WebGL2RenderingContext).fenceSync !== undefined;
}
/**
* @class
* @classdesc Wrapper around WebGLSync object. With a promise based completion
*
* @param {WebGLRenderingContext} gl webgl context the sync belongs to
*/
export default class Sync {
gl: WebGLRenderingContext | WebGL2RenderingContext;
readonly promise: Promise<unknown>;
private _sync: ISyncImplementation | null;
private _invalidated: boolean;
private _pooled: boolean;
private _defer : any;
constructor(gl: WebGLRenderingContext | WebGL2RenderingContext) {
this.gl = gl;
this._sync = null;
this._invalidated = false;
this._pooled = false;
this.promise = new Promise<void>((resolve, reject)=>{
this._defer = {resolve,reject};
});
}
static resolve = _resolve;
static auto = _auto;
static stop = _stop;
/**
* actually create a the WebGLSync and insert fence command in the GL command stream
*/
insert() {
if (this._sync === null && !this._invalidated) {
this._sync = _factory(this.gl);
this._pool();
}
}
dispose() {
this._release();
}
/**
* return true if the GL_SYNC_STATUS is GL_SIGNALED
*/
isSignaled() {
if (this._sync === null) {
return false;
}
return this._sync.isSignaled();
}
/**
* shortcut to this.promise.then
*/
then(onFulfilled: (value: unknown) => unknown, onRejected: (reason: any) => unknown) {
this.promise.then(onFulfilled, onRejected);
}
/**
* equivalent to gl.clientWaitSync. Sync must be "insert" before calling this, otherwise return GL_WAIT_FAILED.
* @param {Number} [timeout =1e6] in nanosec, default 100ms
*/
clientWaitSync(timeout:number = 1e6) {
if (this._sync === null) {
return 0x911D;// GL_WAIT_FAILED;
}
return this._sync.clientWaitSync(timeout);
}
_release() {
this._defer.reject(null);
this._unpool();
this._invalidated = true;
if (this._sync !== null) {
this._sync.dispose();
this._sync = null;
}
}
_complete() {
this._defer.resolve();
this._release();
}
_pool() {
if (!this._pooled) {
__pool.push(this);
this._pooled = true;
}
}
_unpool() {
if (this._pooled) {
__pool.splice(__pool.indexOf(this), 1);
this._pooled = false;
}
}
};
interface ISyncImplementation {
isSignaled(): boolean;
dispose(): void;
clientWaitSync(timeout: number): GLenum;
}
// _ _ _ _ ___ _
// | \| |__ _| |_(_)_ _____ |_ _|_ __ _ __| |
// | .` / _` | _| \ V / -_) | || ' \| '_ \ |
// |_|\_\__,_|\__|_|\_/\___| |___|_|_|_| .__/_|
// |_|
class NativeImpl implements ISyncImplementation {
gl: WebGL2RenderingContext;
sync: WebGLSync;
constructor(gl: WebGL2RenderingContext) {
this.gl = gl;
this.sync = <WebGLSync>gl.fenceSync(0x9117, 0);// gl.SYNC_GPU_COMMANDS_COMPLETE
gl.flush();
}
isSignaled() {
//gl.SYNC_STATUS = gl.SIGNALED
return this.gl.getSyncParameter(this.sync, 0x9114) === 0x9119;
}
dispose() {
this.gl.deleteSync(this.sync);
}
clientWaitSync(timeout: number) {
return this.gl.clientWaitSync(this.sync, this.gl.SYNC_FLUSH_COMMANDS_BIT, timeout);
}
}
// ___ _ _ ___ _
// / __| |_ (_)_ __ |_ _|_ __ _ __| |
// \__ \ ' \| | ' \ | || ' \| '_ \ |
// |___/_||_|_|_|_|_| |___|_|_|_| .__/_|
// |_|
class ShimImpl implements ISyncImplementation {
isSignaled() {
return true;
}
dispose() { }
clientWaitSync(timeout: number) {
return 0x911A; //GL_ALREADY_SIGNALED
}
}
// _ _ _
// _ __ ___ ___| | _ _ _ __ __| |__ _| |_ ___
// | '_ \/ _ \/ _ \ | | || | '_ \/ _` / _` | _/ -_)
// | .__/\___/\___/_| \_,_| .__/\__,_\__,_|\__\___|
// |_| |_|
const __pool : Sync[] = [];
let _autoi : number;
function _auto(interval:number) {
if (interval === undefined) {
interval = 32;
}
_stop();
_autoi = setInterval(_resolve, interval);
}
function _stop() {
clearInterval(_autoi);
}
function _resolve() {
for (var i = __pool.length - 1; i >= 0; i--) {
if (__pool[i].isSignaled()) {
__pool[i]._complete();
}
}
}
function _factory(gl: WebGLRenderingContext | WebGL2RenderingContext): ISyncImplementation {
if (isWebgl2(gl)) {
return new NativeImpl(gl);
} else {
return new ShimImpl();
}
}