UNPKG

nanogl-sync

Version:

webgl's fence Sync EZPZ and shim for webgl1

240 lines (165 loc) 4.7 kB
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(); } }