diff options
Diffstat (limited to 'dom/canvas/test/webgl-conf/checkout/deqp/modules/shared/glsFboUtil.js')
-rw-r--r-- | dom/canvas/test/webgl-conf/checkout/deqp/modules/shared/glsFboUtil.js | 1413 |
1 files changed, 1413 insertions, 0 deletions
diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/modules/shared/glsFboUtil.js b/dom/canvas/test/webgl-conf/checkout/deqp/modules/shared/glsFboUtil.js new file mode 100644 index 0000000000..86d05891f2 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/modules/shared/glsFboUtil.js @@ -0,0 +1,1413 @@ +/*------------------------------------------------------------------------- + * drawElements Quality Program OpenGL ES Utilities + * ------------------------------------------------ + * + * Copyright 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +'use strict'; +goog.provide('modules.shared.glsFboUtil'); +goog.require('framework.opengl.gluTextureUtil'); +goog.require('framework.opengl.gluStrUtil'); + +goog.scope(function() { + + var glsFboUtil = modules.shared.glsFboUtil; + var gluTextureUtil = framework.opengl.gluTextureUtil; + var gluStrUtil = framework.opengl.gluStrUtil; + var DE_ASSERT = function(x) { + if (!x) + throw new Error('Assert failed'); + }; + + /** + * @constructor + * @template KeyT + * @template ValueT + * @param {function(!KeyT, !KeyT):boolean} comparefnc + */ + glsFboUtil.Map = function(comparefnc) { + /** @type {Array<{first: !KeyT, second: ValueT}>} */ + this.store = []; + this.compare = comparefnc; + this.length = 0; + }; + + /** + * @param {number} num1 + * @param {number} num2 + * @return {boolean} + */ + glsFboUtil.Map.compareNumber = function(num1, num2) { + return num1 < num2; + }; + + /** + * @param {!KeyT} key + * @param {ValueT} value + * @return {{first: !KeyT, second: ValueT}} + */ + glsFboUtil.Map.prototype.pair = function(key, value) { + return { + first: key, + second: value + }; + }; + + /** + * @protected + * @param {!KeyT} key + * @return {number} + */ + glsFboUtil.Map.prototype.findInsertionPoint = function(key) { + var /** @type {number} */i, /** @type {number} */length; + for (i = 0, length = this.store.length; i < length; ++i) { + if (!this.compare(key, this.store[i].first)) break; + } + return i; + }; + + /** + * index should be a value returned from findInsertionPoint. + * returns true if the compare function returns false reflexively + * (i.e. no matter the order in which the keys are passed as arguments). + * @protected + * @param {!KeyT} key + * @param {number} index + * @return {boolean} + */ + glsFboUtil.Map.prototype.foundIndexMatches = function(key, index) { + return ( + this.store[index] !== undefined && + !this.compare(this.store[index].first, key) + ); + }; + + /** + * @param {!KeyT} key + * @return {boolean} + */ + glsFboUtil.Map.prototype.isset = function(key) { + return this.foundIndexMatches(key, this.findInsertionPoint(key)); + }; + + /** + * @param {!KeyT} key + * @param {ValueT} value + */ + glsFboUtil.Map.prototype.set = function(key, value) { + var index = this.findInsertionPoint(key); + if (this.foundIndexMatches(key, index)) { + this.store[index].second = value; + } else { + this.store.splice(index, 0, this.pair(key, value)); + ++this.length; + } + }; + + /** + * @param {!KeyT} key + * @return {?ValueT} + */ + glsFboUtil.Map.prototype.remove = function(key) { + var index = this.findInsertionPoint(key); + /** @type {?ValueT} */ var ret = null; + if (this.foundIndexMatches(key, index)) { + ret = this.store[index].second; + this.store.splice(index, 1); + --this.length; + } + return ret; + }; + + /** + * @param {KeyT} key + * @return {?{first: KeyT, second: ValueT}} + */ + glsFboUtil.Map.prototype.get = function(key) { + var index = this.findInsertionPoint(key); + if (this.foundIndexMatches(key, index)) return this.store[index]; + return null; + }; + + /** + * @param {KeyT} key + * @return {?ValueT} + */ + glsFboUtil.Map.prototype.getValue = function(key) { + var index = this.findInsertionPoint(key); + if (this.foundIndexMatches(key, index)) return this.store[index].second; + return null; + }; + + /** + * @param {!KeyT} key + * @param {ValueT} fallback + * @return {ValueT} + */ + glsFboUtil.Map.prototype.lookupDefault = function(key, fallback) { + var index = this.findInsertionPoint(key); + if (this.foundIndexMatches(key, index)) return this.store[index].second; + return fallback; + }; + + /** + * @param {number} index + * @return {{first: KeyT, second: ValueT}|undefined} + */ + glsFboUtil.Map.prototype.getIndex = function(index) { + return this.store[index]; + }; + + /** + * Use the callback to set the value to be identified by key. + * If a value is already identified by the key, it will be passed to the callback + * @param {!KeyT} key + * @param {function(ValueT=):!ValueT} callback + */ + glsFboUtil.Map.prototype.transform = function(key, callback) { + var index = this.findInsertionPoint(key); + if (this.foundIndexMatches(key, index)) { + this.store[index].second = callback(this.store[index].second); + } else { + this.store.splice(index, 0, this.pair(key, callback())); + ++this.length; + } + }; + + /** + * removed all elements from the Map + */ + glsFboUtil.Map.prototype.clear = function() { + this.store.splice(0, this.length); + this.length = 0; + }; + + /** + * @constructor + */ + glsFboUtil.FormatDB = function() { + this.m_map = /** @type {glsFboUtil.Map<glsFboUtil.ImageFormat,number>} */( + new glsFboUtil.Map(glsFboUtil.ImageFormat.lessthan) + ); + }; + + /** + * @param {glsFboUtil.ImageFormat} format + * @param {number} newFlags + */ + glsFboUtil.FormatDB.prototype.addFormat = function(format, newFlags) { + this.m_map.transform(format, function(flags) { + return flags | newFlags; + }); + }; + + /** + * @param {number} requirements + * @return {Array<glsFboUtil.ImageFormat>} + */ + glsFboUtil.FormatDB.prototype.getFormats = function(requirements) { + /** @type {Array<glsFboUtil.ImageFormat>} */ var ret = []; + for (var i = 0; i < this.m_map.length; ++i) { + var pair = this.m_map.getIndex(i); + if ((pair.second & requirements) == requirements) + ret.push(pair.first); + } + + return ret; + }; + + /** + * @param {glsFboUtil.ImageFormat} format + * @param {number} fallback + * @return {number} + */ + glsFboUtil.FormatDB.prototype.getFormatInfo = function(format, fallback) { + return this.m_map.lookupDefault(format, fallback); + }; + + /** + * @param {Object} map + * @param {number} key + * @param {number} fallback + * @return {number} + */ + glsFboUtil.lookupDefault = function(map, key, fallback) { + return (map[key] !== undefined) ? map[key] : fallback; + }; + + /** + * @param {Array<number>} array + * @param {number} item + * @return {boolean} + */ + glsFboUtil.contains = function(array, item) { + var l = array.length; + for (var i = 0; i < l; ++i) + if (array[i] == item) return true; + return false; + }; + + /** + * @typedef {Array<(number | glsFboUtil.Range<number>)>} + */ + glsFboUtil.formatT; + + /** + * @param {glsFboUtil.FormatDB} db + * @param {glsFboUtil.Range<glsFboUtil.formatT>} stdFmts + */ + glsFboUtil.addFormats = function(db, stdFmts) { + for (var set = stdFmts.reset(); set = stdFmts.current(); stdFmts.next()) { + for (var fmt = set[1].reset(); fmt = set[1].current(); set[1].next()) { + db.addFormat(glsFboUtil.formatKeyInfo(fmt), set[0]); + } + } + }; + + /** + * @param {glsFboUtil.FormatDB} db + * @param {glsFboUtil.Range} extFmts + * @param {WebGLRenderingContextBase=} gl + * @throws {Error} + */ + glsFboUtil.addExtFormats = function(db, extFmts, gl) { + if (!(gl = gl || window.gl)) throw new Error('Invalid gl object'); + var extensions = gl.getSupportedExtensions(); + + // loop through the range, looking at the extentions. + for (var ext = extFmts.reset(); ext = extFmts.current(); extFmts.next()) { + var tokens = ext.extensions.split(/\s+/); + + var supported = function() { + for (var i = 0, l = tokens.length; i < l; ++i) + if (extensions.indexOf(tokens[i]) === -1) return false; + return true; + }(); + + if (supported) { + for (var format = ext.formats.reset(); format = ext.formats.current(); ext.formats.next()) { + db.addFormat(glsFboUtil.formatKeyInfo(format), ext.flags); + } + } + + } + + }; + + /** + * @param {number} glenum + * @param {WebGLRenderingContextBase=} gl + * @return {number} + * @throws {Error} + */ + glsFboUtil.formatFlag = function(glenum, gl) { + if (!(gl = gl || window.gl)) throw new Error('Invalid gl object'); + + switch (glenum) { + case gl.NONE: + return glsFboUtil.FormatFlags.ANY_FORMAT; + case gl.RENDERBUFFER: + return glsFboUtil.FormatFlags.RENDERBUFFER_VALID; + case gl.TEXTURE: + return glsFboUtil.FormatFlags.TEXTURE_VALID; + case gl.STENCIL_ATTACHMENT: + return glsFboUtil.FormatFlags.STENCIL_RENDERABLE; + case gl.DEPTH_ATTACHMENT: + return glsFboUtil.FormatFlags.DEPTH_RENDERABLE; + default: + if (glenum < gl.COLOR_ATTACHMENT0 || glenum > gl.COLOR_ATTACHMENT15) { + throw new Error('glenum out of range'); + } + } + return glsFboUtil.FormatFlags.COLOR_RENDERABLE; + }; + + /** + * Remove value from array + * @param {Array} array + * @param {number} value + */ + glsFboUtil.remove_from_array = function(array, value) { + var index = 0; + while ((index = array.indexOf(value)) != -1) { + array.splice(index, 1); + } + }; + + /** + * glsFboUtil.FormatExtEntry + * @constructor + * @struct + * @param {string=} extensions + * @param {number=} flags + * @param {glsFboUtil.Range=} formats + */ + glsFboUtil.FormatExtEntry = function(extensions, flags, formats) { + this.extensions = null; + this.flags = null; + this.formats = null; + + if (extensions !== undefined) { + this.extensions = extensions; + if (flags !== undefined) { + this.flags = flags; + if (formats !== undefined) + this.formats = formats; + } + } + + }; + + /** + * glsFboUtil.Range + * @constructor + * @struct + * @template T + * @param {Array<T>} array + * @param {number=} begin + * @param {number=} end + */ + glsFboUtil.Range = function(array, begin, end) { + // @private + this.m_begin = (begin === undefined ? 0 : begin); + // @private + this.m_end = end || array.length; + /** + * @private + * @type {Array<T>} + */ + this.m_array = array; + // @private + this.m_index = this.m_begin; + }; + + /** + * @return {Array<T>} + */ + glsFboUtil.Range.prototype.array = function() { + return this.m_array; + }; + + /** + * @return {number} + */ + glsFboUtil.Range.prototype.begin = function() { + return this.m_begin; + }; + + /** *generated by script* + * @return {number} + */ + glsFboUtil.Range.prototype.end = function() { + return this.m_end; + }; + + /** + * Returns the current pointer index as well as the current object + * @param {number} id + * @return {{first: number, second: T}} + */ + glsFboUtil.Range.prototype.get = function(id) { + return { + first: id, + second: this.m_array[id] + }; + }; + + /** + * Sets the internal pointer to the beginning of the range, and returns the first object. + * @return {T} + */ + glsFboUtil.Range.prototype.reset = function() { + this.m_index = this.m_begin; + return this.current(); + }; + + /** + * returns the current object within the specified range. The internal pointer is unaltered. + * @return {T} + */ + glsFboUtil.Range.prototype.current = function() { + return this.m_index < this.m_end ? this.m_array[this.m_index] : null; + }; + + /** + * Increments the internal pointer + */ + glsFboUtil.Range.prototype.next = function() { + ++this.m_index; + }; + + /** + * glsFboUtil.rangeArray + * replaces the macro GLS_ARRAY_RANGE + * Creates a new Range object from the specified array, spanning the whole array. + * @template T + * @param {Array<T>} array + * @return {glsFboUtil.Range<T>} + */ + glsFboUtil.rangeArray = function(array) { + return new glsFboUtil.Range(array); + }; + + /** + * @constructor + * @struct + * @param {number=} format + * @param {number=} unsizedType + */ + glsFboUtil.ImageFormat = function(format, unsizedType) { + this.format = format || 0; + //! Type if format is unsized, gl.NONE if sized. + this.unsizedType = unsizedType || 0; + + }; + + /** + * @param {!glsFboUtil.ImageFormat} obj1 + * @param {!glsFboUtil.ImageFormat} obj2 + * @return {boolean} + */ + glsFboUtil.ImageFormat.lessthan = function(obj1, obj2) { + return ( + (obj1.format < obj2.format) || + (obj1.format == obj2.format && obj1.unsizedType < obj2.unsizedType) + ); + }; + + /** + * Sets the object's parameters to gl.NONE + */ + glsFboUtil.ImageFormat.prototype.none = function() { + this.format = 0; + this.unsizedType = 0; + }; + + /** + * @return {glsFboUtil.ImageFormat} + */ + glsFboUtil.ImageFormat.none = function() { + var obj = new glsFboUtil.ImageFormat(); + obj.none(); + return obj; + }; + + // where key is a FormatKey, and a FormatKey is a unsigned 32bit int. + + /** + * @param {number} key + * @return {glsFboUtil.ImageFormat} + */ + glsFboUtil.formatKeyInfo = function(key) { + return new glsFboUtil.ImageFormat( + (key & 0x0000ffff), + (key & 0xffff0000) >>> 16 + ); + }; + + /** + * glsFboUtil.Config Class. + * @constructor + */ + glsFboUtil.Config = function() { + this.type = glsFboUtil.Config.s_types.CONFIG; + this.target = glsFboUtil.Config.s_target.NONE; + }; + /** + * @enum {number} + */ + glsFboUtil.Config.s_target = { + NONE: 0, + RENDERBUFFER: 0x8D41, + TEXTURE_2D: 0x0DE1, + TEXTURE_CUBE_MAP: 0x8513, + TEXTURE_3D: 0x806F, + TEXTURE_2D_ARRAY: 0x8C1A, + + FRAMEBUFFER: 0x8D40 + }; + + // the c++ uses dynamic casts to determain if an object inherits from a + // given class. Here, each class' constructor assigns a bit to obj.type. + // look for the bit to see if an object inherits that class. + + /** + * @enum + */ + glsFboUtil.Config.s_types = { + CONFIG: 0x000001, + + IMAGE: 0x000010, + RENDERBUFFER: 0x000020, + TEXTURE: 0x000040, + TEXTURE_FLAT: 0x000080, + TEXTURE_2D: 0x000100, + TEXTURE_CUBE_MAP: 0x000200, + TEXTURE_LAYERED: 0x000400, + TEXTURE_3D: 0x000800, + TEXTURE_2D_ARRAY: 0x001000, + + ATTACHMENT: 0x010000, + ATT_RENDERBUFFER: 0x020000, + ATT_TEXTURE: 0x040000, + ATT_TEXTURE_FLAT: 0x080000, + ATT_TEXTURE_LAYER: 0x100000, + + UNUSED: 0xFFE0E00E + }; + + /** + * glsFboUtil.Image Class. + * @constructor + * @extends {glsFboUtil.Config} + */ + glsFboUtil.Image = function() { + glsFboUtil.Config.call(this); + this.type |= glsFboUtil.Config.s_types.IMAGE; + this.width = 0; + this.height = 0; + this.internalFormat = new glsFboUtil.ImageFormat(); + }; + + /** + * glsFboUtil.Renderbuffer Class. + * @constructor + * @extends {glsFboUtil.Image} + */ + glsFboUtil.Renderbuffer = function() { + glsFboUtil.Image.call(this); + this.type |= glsFboUtil.Config.s_types.RENDERBUFFER; + this.target = glsFboUtil.Config.s_target.RENDERBUFFER; + this.numSamples = 0; + }; + + /** + * glsFboUtil.Texture Class. + * @constructor + * @extends {glsFboUtil.Image} + */ + glsFboUtil.Texture = function() { + glsFboUtil.Image.call(this); + this.type |= glsFboUtil.Config.s_types.TEXTURE; + this.numLevels = 1; + }; + + /** + * glsFboUtil.TextureFlat Class. + * @constructor + * @extends {glsFboUtil.Texture} + */ + glsFboUtil.TextureFlat = function() { + glsFboUtil.Texture.call(this); + this.type |= glsFboUtil.Config.s_types.TEXTURE_FLAT; + }; + + /** + * glsFboUtil.Texture2D Class. + * @constructor + * @extends {glsFboUtil.TextureFlat} + */ + glsFboUtil.Texture2D = function() { + glsFboUtil.TextureFlat.call(this); + this.type |= glsFboUtil.Config.s_types.TEXTURE_2D; + this.target = glsFboUtil.Config.s_target.TEXTURE_2D; + }; + + /** + * glsFboUtil.TextureCubeMap Class. + * @constructor + * @extends {glsFboUtil.TextureFlat} + */ + glsFboUtil.TextureCubeMap = function() { + glsFboUtil.TextureFlat.call(this); + this.type |= glsFboUtil.Config.s_types.TEXTURE_CUBE_MAP; + this.target = glsFboUtil.Config.s_target.TEXTURE_CUBE_MAP; + }; + + /** + * glsFboUtil.TextureLayered Class. + * @constructor + * @extends {glsFboUtil.Texture} + */ + glsFboUtil.TextureLayered = function() { + glsFboUtil.Texture.call(this); + this.type |= glsFboUtil.Config.s_types.TEXTURE_LAYERED; + this.numLayers = 1; + }; + + /** + * glsFboUtil.Texture3D Class. + * @constructor + * @extends {glsFboUtil.TextureLayered} + */ + glsFboUtil.Texture3D = function() { + glsFboUtil.TextureLayered.call(this); + this.type |= glsFboUtil.Config.s_types.TEXTURE_3D; + this.target = glsFboUtil.Config.s_target.TEXTURE_3D; + }; + + /** + * glsFboUtil.Texture2DArray Class. + * @constructor + * @extends {glsFboUtil.TextureLayered} + */ + glsFboUtil.Texture2DArray = function() { + glsFboUtil.TextureLayered.call(this); + this.type |= glsFboUtil.Config.s_types.TEXTURE_2D_ARRAY; + this.target = glsFboUtil.Config.s_target.TEXTURE_2D_ARRAY; + }; + + /** + * glsFboUtil.Attachment Class. + * @constructor + * @extends {glsFboUtil.Config} + */ + glsFboUtil.Attachment = function() { + glsFboUtil.Config.call(this); + + this.type |= glsFboUtil.Config.s_types.ATTACHMENT; + + /** @type {glsFboUtil.Config.s_target} */ + this.target = glsFboUtil.Config.s_target.FRAMEBUFFER; + + /** @type {WebGLObject} */ + this.imageName = null; + }; + + /** + * this function is declared, but has no definition/is unused in the c++ + * @param {number} attPoint + * @param {number} image + * @param {number} vfr + */ + glsFboUtil.Attachment.prototype.isComplete = function(attPoint, image, vfr) { }; + + /** + * glsFboUtil.RenderBufferAttachments Class. + * @constructor + * @extends {glsFboUtil.Attachment} + */ + glsFboUtil.RenderbufferAttachment = function() { + glsFboUtil.Attachment.call(this); + this.type |= glsFboUtil.Config.s_types.ATT_RENDERBUFFER; + this.renderbufferTarget = glsFboUtil.Config.s_target.RENDERBUFFER; + }; + glsFboUtil.RenderbufferAttachment.prototype = Object.create(glsFboUtil.Attachment.prototype); + glsFboUtil.RenderbufferAttachment.prototype.constructor = glsFboUtil.RenderbufferAttachment; + + /** + * glsFboUtil.TextureAttachment Class. + * @constructor + * @extends {glsFboUtil.Attachment} + */ + glsFboUtil.TextureAttachment = function() { + glsFboUtil.Attachment.call(this); + this.type |= glsFboUtil.Config.s_types.ATT_TEXTURE; + this.level = 0; + }; + glsFboUtil.TextureAttachment.prototype = Object.create(glsFboUtil.Attachment.prototype); + glsFboUtil.TextureAttachment.prototype.constructor = glsFboUtil.TextureAttachment; + + /** + * glsFboUtil.TextureFlatAttachment Class. + * @constructor + * @extends {glsFboUtil.TextureAttachment} + */ + glsFboUtil.TextureFlatAttachment = function() { + glsFboUtil.TextureAttachment.call(this); + this.type |= glsFboUtil.Config.s_types.ATT_TEXTURE_FLAT; + this.texTarget = glsFboUtil.Config.s_target.NONE; + }; + glsFboUtil.TextureFlatAttachment.prototype = Object.create(glsFboUtil.TextureAttachment.prototype); + glsFboUtil.TextureFlatAttachment.prototype.constructor = glsFboUtil.TextureFlatAttachment; + + /** + * glsFboUtil.TextureLayerAttachment Class. + * @constructor + * @extends {glsFboUtil.TextureAttachment} + */ + glsFboUtil.TextureLayerAttachment = function() { + glsFboUtil.TextureAttachment.call(this); + this.type |= glsFboUtil.Config.s_types.ATT_TEXTURE_LAYER; + this.layer = 0; + }; + glsFboUtil.TextureLayerAttachment.prototype = Object.create(glsFboUtil.TextureAttachment.prototype); + glsFboUtil.TextureLayerAttachment.prototype.constructor = glsFboUtil.TextureLayerAttachment; + + // these are a collection of helper functions for creating various gl textures. + glsFboUtil.glsup = function() { + + var glInit = function(cfg, gl) { + if ((cfg.type & glsFboUtil.Config.s_types.TEXTURE_2D) != 0) { + glInitFlat(cfg, glTarget(cfg, gl), gl); + + } else if ((cfg.type & glsFboUtil.Config.s_types.TEXTURE_CUBE_MAP) != 0) { + for (var i = gl.TEXTURE_CUBE_MAP_NEGATIVE_X; i <= gl.TEXTURE_CUBE_MAP_POSITIVE_Z; ++i) + glInitFlat(cfg, i, gl); + } else if ((cfg.type & glsFboUtil.Config.s_types.TEXTURE_3D) != 0) { + glInitLayered(cfg, 2, gl); + + } else if ((cfg.type & glsFboUtil.Config.s_types.TEXTURE_2D_ARRAY) != 0) { + glInitLayered(cfg, 1, gl); + } + }; + + var glInitFlat = function(cfg, target, gl) { + var format = glsFboUtil.transferImageFormat(cfg.internalFormat, gl); + var w = cfg.width; + var h = cfg.height; + for (var level = 0; level < cfg.numLevels; ++level) { + gl.texImage2D( + target, level, cfg.internalFormat.format, + w, h, 0, format.format, format.dataType, null + ); + w = Math.max(1, w / 2); + h = Math.max(1, h / 2); + } + }; + + var glInitLayered = function(cfg, depth_divider, gl) { + var format = glsFboUtil.transferImageFormat(cfg.internalFormat, gl); + var w = cfg.width; + var h = cfg.height; + var depth = cfg.numLayers; + for (var level = 0; level < cfg.numLevels; ++level) { + gl.texImage3D( + glTarget(cfg, gl), level, cfg.internalFormat.format, + w, h, depth, 0, format.format, format.dataType, null + ); + w = Math.max(1, w / 2); + h = Math.max(1, h / 2); + depth = Math.max(1, depth / depth_divider); + } + }; + + var glCreate = function(cfg, gl) { + if (!(gl = gl || window.gl)) throw new Error('Invalid gl object'); + + if (cfg.type & glsFboUtil.Config.s_types.RENDERBUFFER) { + var ret = gl.createRenderbuffer(); + gl.bindRenderbuffer(gl.RENDERBUFFER, ret); + + if (cfg.numSamples == 0) { + gl.renderbufferStorage( + gl.RENDERBUFFER, + cfg.internalFormat.format, + cfg.width, cfg.height + ); + } else { + gl.renderbufferStorageMultisample( + gl.RENDERBUFFER, + cfg.numSamples, + cfg.internalFormat.format, + cfg.width, cfg.height + ); + } + gl.bindRenderbuffer(gl.RENDERBUFFER, null); + + } else if (cfg.type & glsFboUtil.Config.s_types.TEXTURE) { + var ret = gl.createTexture(); + gl.bindTexture(glTarget(cfg, gl), ret); + glInit(cfg, gl); + gl.bindTexture(glTarget(cfg, gl), null); + + } else { + throw new Error('Impossible image type'); + } + return ret; + }; + + var glTarget = function(cfg, gl) { + if (!(gl = gl || window.gl)) throw new Error('Invalid gl object'); + var mask = ( + glsFboUtil.Config.s_types.RENDERBUFFER | + glsFboUtil.Config.s_types.TEXTURE_2D | + glsFboUtil.Config.s_types.TEXTURE_CUBE_MAP | + glsFboUtil.Config.s_types.TEXTURE_3D | + glsFboUtil.Config.s_types.TEXTURE_2D_ARRAY + ); + switch (cfg.type & mask) { + case glsFboUtil.Config.s_types.RENDERBUFFER: return gl.RENDERBUFFER; + case glsFboUtil.Config.s_types.TEXTURE_2D: return gl.TEXTURE_2D; + case glsFboUtil.Config.s_types.TEXTURE_CUBE_MAP: return gl.TEXTURE_CUBE_MAP; + case glsFboUtil.Config.s_types.TEXTURE_3D: return gl.TEXTURE_3D; + case glsFboUtil.Config.s_types.TEXTURE_2D_ARRAY: return gl.TEXTURE_2D_ARRAY; + default: break; + } + throw new Error('Impossible image type.'); + }; + + var glDelete = function(cfg, img, gl) { + if (cfg.type & glsFboUtil.Config.s_types.RENDERBUFFER) + gl.deleteRenderbuffer(img); + else if (cfg.type & glsFboUtil.Config.s_types.TEXTURE) + gl.deleteTexture(img); + else + throw new Error('Impossible image type'); + }; + + return { + create: glCreate, + remove: glDelete + }; + + }(); + + /** *generated by script* + * @param {number} img + * @return {number} + */ + glsFboUtil.imageNumSamples = function(img) { + return (img.numSamples != undefined) ? img.numSamples : 0; + }; + + /** *generated by script* + * @param {glsFboUtil.Attachment} att + * @param {number} attPoint + * @param {WebGLRenderingContextBase=} gl + * @throws {Error} + */ + glsFboUtil.attachAttachment = function(att, attPoint, gl) { + if (!(gl = gl || window.gl)) throw new Error('Invalid gl object'); + + var mask = ( + glsFboUtil.Config.s_types.ATT_RENDERBUFFER | + glsFboUtil.Config.s_types.ATT_TEXTURE_FLAT | + glsFboUtil.Config.s_types.ATT_TEXTURE_LAYER + ); + + switch (att.type & mask) { + case glsFboUtil.Config.s_types.ATT_RENDERBUFFER: + gl.framebufferRenderbuffer( + att.target, attPoint, att.renderbufferTarget, /** @type {WebGLRenderbuffer} */(att.imageName) + ); + break; + case glsFboUtil.Config.s_types.ATT_TEXTURE_FLAT: + gl.framebufferTexture2D( + att.target, attPoint, att.texTarget, /** @type {WebGLTexture} */(att.imageName), att.level + ); + break; + case glsFboUtil.Config.s_types.ATT_TEXTURE_LAYER: + gl.framebufferTextureLayer( + att.target, attPoint, /** @type {WebGLTexture} */(att.imageName), att.level, att.layer + ); + break; + default: + throw new Error('Impossible attachment type'); + } + + }; + + /** *generated by script* + * @param {glsFboUtil.Attachment} att + * @param {WebGLRenderingContextBase=} gl + * @return {number} + * @throws {Error} + */ + glsFboUtil.attachmentType = function(att, gl) { + if (!(gl = gl || window.gl)) throw new Error('Invalid gl object'); + + if (att.type & glsFboUtil.Config.s_types.ATT_RENDERBUFFER) { + return gl.RENDERBUFFER; + } + if (att.type & glsFboUtil.Config.s_types.ATT_TEXTURE) { + return gl.TEXTURE; + } + throw new Error('Impossible attachment type.'); + + }; + + /** + * @param {glsFboUtil.Attachment} att + * @return {number} + * @throws {Error} + */ + glsFboUtil.textureLayer = function(att) { + if (att.type & glsFboUtil.Config.s_types.ATT_TEXTURE_FLAT) return 0; + if (att.type & glsFboUtil.Config.s_types.ATT_TEXTURE_LAYER) return att.layer; + throw new Error('Impossible attachment type.'); + }; + + /** + * @param {glsFboUtil.Checker} cctx + * @param {glsFboUtil.Attachment} att + * @param {number} attPoint + * @param {glsFboUtil.Image} image + * @param {glsFboUtil.FormatDB} db + * @param {WebGLRenderingContextBase=} gl + * @throws {Error} + */ + glsFboUtil.checkAttachmentCompleteness = function(cctx, att, attPoint, image, db, gl) { + if (!(gl = gl || window.gl)) throw new Error('Invalid gl object'); + + // GLES2 4.4.5 / GLES3 4.4.4, "glsFboUtil.Framebuffer attachment completeness" + if ( + (att.type & glsFboUtil.Config.s_types.ATT_TEXTURE) && + (image.type & glsFboUtil.Config.s_types.TEXTURE_LAYERED) + ) { + // GLES3: "If the value of FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is + // TEXTURE and the value of FRAMEBUFFER_ATTACHMENT_OBJECT_NAME names a + // three-dimensional texture, then the value of + // FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER must be smaller than the depth + // of the texture. + // + // GLES3: "If the value of FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is + // TEXTURE and the value of FRAMEBUFFER_ATTACHMENT_OBJECT_NAME names a + // two-dimensional array texture, then the value of + // FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER must be smaller than the + // number of layers in the texture. + cctx.addFBOStatus( + glsFboUtil.textureLayer(att) < image.numLayers, + gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT + ); + } + + // "The width and height of image are non-zero." + cctx.addFBOStatus( + image.width > 0 && image.height > 0, + gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT + ); + + // Check for renderability + var flags = db.getFormatInfo(image.internalFormat, glsFboUtil.FormatFlags.ANY_FORMAT); + + // If the format does not have the proper renderability flag, the + // completeness check _must_ fail. + cctx.addFBOStatus( + (flags & glsFboUtil.formatFlag(attPoint)) != 0, + gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT + ); + + // If the format is only optionally renderable, the completeness check _can_ fail. + cctx.addPotentialFBOStatus( + (flags & glsFboUtil.FormatFlags.REQUIRED_RENDERABLE) != 0, + gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT + ); + + }; + + // replaces GLS_UNSIZED_FORMATKEY + + /** + * All params and return types for this function are 32 bit + * @param {number} format + * @param {number} type + * @return {number} + */ + glsFboUtil.formatkey = function(format, type) { + // The formatkey value should be 32-bit unsigned int. + return ((type << 16) >>> 0 | format) & 0xFFFFFFFF; + }; + + /** + * @enum + */ + glsFboUtil.FormatFlags = { + ANY_FORMAT: 0x00, + COLOR_RENDERABLE: 0x01, + DEPTH_RENDERABLE: 0x02, + STENCIL_RENDERABLE: 0x04, + RENDERBUFFER_VALID: 0x08, + TEXTURE_VALID: 0x10, + REQUIRED_RENDERABLE: 0x20 //< Without this, renderability is allowed, not required. + }; + + /** + * A framebuffer configuration + * @constructor + * @param {WebGLRenderingContextBase=} gl + */ + glsFboUtil.Framebuffer = function(gl) { + this.m_gl = gl || window.gl; + this.fbid = 0; + + var fbidCompare = function(obj1, obj2) { + return obj1._fbid < obj2._fbid; + }; + + this.attachments = /** @type {glsFboUtil.Map<number,glsFboUtil.Attachment>} */( + new glsFboUtil.Map(glsFboUtil.Map.compareNumber) + ); + this.textures = /** @type {glsFboUtil.Map<Object,glsFboUtil.Texture>} */( + new glsFboUtil.Map(fbidCompare) + ); + this.rbos = /** @type {glsFboUtil.Map<Object,glsFboUtil.Renderbuffer>} */( + new glsFboUtil.Map(fbidCompare) + ); + }; + + /** + * @param {number} attPoint + * @param {glsFboUtil.Attachment} att + */ + glsFboUtil.Framebuffer.prototype.attach = function(attPoint, att) { + if (!att) { + this.attachments.remove(attPoint); + } else { + this.attachments.set(attPoint, att); + } + }; + + /** + * @param {WebGLTexture} texName + * @param {glsFboUtil.Texture} texCfg + */ + glsFboUtil.Framebuffer.prototype.setTexture = function(texName, texCfg) { + texName._fbid = this.fbid++; + this.textures.set(texName, texCfg); + }; + + /** + * @param {WebGLRenderbuffer} rbName + * @param {glsFboUtil.Renderbuffer} rbCfg + */ + glsFboUtil.Framebuffer.prototype.setRbo = function(rbName, rbCfg) { + rbName._fbid = this.fbid++; + this.rbos.set(rbName, rbCfg); + }; + + /** + * @param {number} type + * @param {WebGLObject} imgName + * @return {glsFboUtil.Image} + * @throws {Error} + */ + glsFboUtil.Framebuffer.prototype.getImage = function(type, imgName) { + switch (type) { + case this.m_gl.TEXTURE: return this.textures.lookupDefault(/** @type {WebGLTexture} */(imgName), null); + case this.m_gl.RENDERBUFFER: return this.rbos.lookupDefault(/** @type {WebGLTexture} */(imgName), null); + default: break; + } + throw new Error('Bad image type.'); + }; + + /** + * @constructor + * @extends {glsFboUtil.Framebuffer} + * @param {WebGLFramebuffer} fbo + * @param {number} target + * @param {WebGLRenderingContextBase=} gl + */ + glsFboUtil.FboBuilder = function(fbo, target, gl) { + glsFboUtil.Framebuffer.call(this, gl); + + this.m_gl = gl || window.gl; + this.m_target = target; + this.m_configs = []; + this.m_error = this.m_gl.NO_ERROR; + + this.m_gl.bindFramebuffer(this.m_target, fbo); + + }; + + glsFboUtil.FboBuilder.prototype = Object.create(glsFboUtil.Framebuffer.prototype); + glsFboUtil.FboBuilder.prototype.constructor = glsFboUtil.FboBuilder; + + glsFboUtil.FboBuilder.prototype.deinit = function() { + + var pair; + for (var i = 0; i < this.textures.length; ++i) { + pair = this.textures.getIndex(i); + glsFboUtil.glsup.remove(pair.second, pair.first, this.m_gl); + } + this.textures.clear(); + + for (var i = 0; i < this.rbos.length; ++i) { + pair = this.rbos.getIndex(i); + glsFboUtil.glsup.remove(pair.second, pair.first, this.m_gl); + } + this.rbos.clear(); + + this.m_gl.bindFramebuffer(this.m_target, null); +/* + for (var i = 0 ; i < this.m_configs.length ; ++i) { + delete this.m_configs[i]; + } +//*/ + }; + + /** + * @param {number} attPoint + * @param {glsFboUtil.Attachment} att + */ + glsFboUtil.FboBuilder.prototype.glAttach = function(attPoint, att) { + if (!att) { + this.m_gl.framebufferRenderbuffer(this.m_target, attPoint, this.m_gl.RENDERBUFFER, null); + } else { + glsFboUtil.attachAttachment(att, attPoint, this.m_gl); + } + this.checkError(); + this.attach(attPoint, att); + }; + + /** + * @param {glsFboUtil.Texture} texCfg + * @return {WebGLTexture} + */ + glsFboUtil.FboBuilder.prototype.glCreateTexture = function(texCfg) { + var texName = glsFboUtil.glsup.create(texCfg, this.m_gl); + this.checkError(); + this.setTexture(texName, texCfg); + return texName; + }; + + /** *generated by script* + * @param {glsFboUtil.Renderbuffer} rbCfg + * @return {WebGLRenderbuffer} + */ + glsFboUtil.FboBuilder.prototype.glCreateRbo = function(rbCfg) { + var rbName = glsFboUtil.glsup.create(rbCfg, this.m_gl); + this.checkError(); + this.setRbo(rbName, rbCfg); + return rbName; + }; + + /** + * @param {function(new:glsFboUtil.Config)} Definition + * @return {glsFboUtil.Config} + */ + glsFboUtil.FboBuilder.prototype.makeConfig = function(Definition) { + var cfg = new Definition(); + this.m_configs.push(cfg); + return cfg; + }; + + /** + */ + glsFboUtil.FboBuilder.prototype.checkError = function() { + var error = this.m_gl.getError(); + if (error != this.m_gl.NO_ERROR && this.m_error == this.m_gl.NO_ERROR) { + this.m_error = error; + } + }; + + /** *generated by script* + * @return {number} + */ + glsFboUtil.FboBuilder.prototype.getError = function() { + return this.m_error; + }; + + glsFboUtil.isFramebufferStatus = function(fboStatus) { + return gluStrUtil.getFramebufferStatusName(fboStatus) != ''; + } + + glsFboUtil.isErrorCode = function(errorCode) { + return gluStrUtil.getErrorName(errorCode) != ''; + } + + /** + * @typedef {funcion(): glsFboUtil.ValidStatusCodes} + */ + glsFboUtil.ValidStatusCodes = function() { + this.m_errorCodes = []; + this.m_errorStatusCodes = []; + this.m_allowComplete = false; + }; + + glsFboUtil.ValidStatusCodes.prototype.isFBOStatusValid = function(fboStatus) { + if (fboStatus == gl.FRAMEBUFFER_COMPLETE) + return this.m_allowComplete; + else { + for(var ndx = 0; ndx < this.m_errorStatusCodes.length; ++ndx) { + if (this.m_errorStatusCodes[ndx] == fboStatus) + return true; + } + return false; + } + }; + + glsFboUtil.ValidStatusCodes.prototype.isFBOStatusRequired = function(fboStatus) { + if (fboStatus == gl.FRAMEBUFFER_COMPLETE) + return this.m_allowComplete && this.m_errorStatusCodes.length == 0; + else + // fboStatus is the only allowed error status and succeeding is forbidden + return !this.m_allowComplete && this.m_errorStatusCodes.length == 1 && this.m_errorStatusCodes[0] == fboStatus; + }; + + glsFboUtil.ValidStatusCodes.prototype.isErrorCodeValid = function(errorCode) { + if (errorCode == gl.NO_ERROR) + return this.m_errorCodes.length == 0; + else { + // rule violation exists? + for (var ndx = 0; ndx < this.m_errorCodes.length; ++ndx) { + if (this.m_errorCodes[ndx] == errorCode) + return true; + } + return false; + } + }; + + glsFboUtil.ValidStatusCodes.prototype.isErrorCodeRequired = function(errorCode) { + if (this.m_errorCodes.length == 0 && errorCode == gl.NO_ERROR) + return true; + else + // only this error code listed + return this.m_errorCodes.length == 1 && merrorCodes[0] == errorCode; + }; + + glsFboUtil.ValidStatusCodes.prototype.addErrorCode = function(error) { + DE_ASSERT(glsFboUtil.isErrorCode(error)); + DE_ASSERT(error != gl.NO_ERROR) + this.m_errorCodes.push(error); + }; + + glsFboUtil.ValidStatusCodes.prototype.addFBOErrorStatus = function(status) { + DE_ASSERT(glsFboUtil.isFramebufferStatus(status)); + DE_ASSERT(status != gl.FRAMEBUFFER_COMPLETE) + this.m_errorStatusCodes.push(status); + }; + + glsFboUtil.ValidStatusCodes.prototype.setAllowComplete = function(b) { + this.m_allowComplete = b; + }; + + /** + * @typedef {function(): glsFboUtil.Checker} + */ + glsFboUtil.CheckerFactory; + + /** + * @constructor + * @param {WebGLRenderingContextBase=} gl + * @throws {Error} + */ + glsFboUtil.Checker = function(gl) { + if (!(gl = gl || window.gl)) throw new Error('Invalid gl object'); + + this.m_statusCodes = new glsFboUtil.ValidStatusCodes(); + this.m_statusCodes.setAllowComplete(true); + + if (typeof(this.check) != 'function') + throw new Error('Constructor called on virtual class: glsFboUtil.Checker'); + }; + + /** + * @param {boolean} condition + * @param {number} error + */ + glsFboUtil.Checker.prototype.addGLError = function(condition, error) { + if (!condition) { + this.m_statusCodes.addErrorCode(error); + this.m_statusCodes.setAllowComplete(false); + } + }; + + /** + * @param {boolean} condition + * @param {number} error + */ + glsFboUtil.Checker.prototype.addPotentialGLError = function(condition, error) { + if (!condition) { + this.m_statusCodes.addErrorCode(error); + } + }; + + /** + * @param {boolean} condition + * @param {number} status + */ + glsFboUtil.Checker.prototype.addFBOStatus = function(condition, status) { + if (!condition) { + this.m_statusCodes.addFBOErrorStatus(status); + this.m_statusCodes.setAllowComplete(false); + } + }; + + /** + * @param {boolean} condition + * @param {number} status + */ + glsFboUtil.Checker.prototype.addPotentialFBOStatus = function(condition, status) { + if (!condition) { + this.m_statusCodes.addFBOErrorStatus(status); + } + }; + + /** + * @return {Array<number>} + */ + glsFboUtil.Checker.prototype.getStatusCodes = function () { + return this.m_statusCodes; + }; + + /** + * @param {glsFboUtil.ImageFormat} imgFormat + * @param {WebGLRenderingContextBase=} gl + * @return {gluTextureUtil.TransferFormat} + * @throws {Error} + */ + glsFboUtil.transferImageFormat = function(imgFormat, gl) { + if (!(gl = gl || window.gl)) throw new Error('Invalid gl object'); + if (imgFormat.unsizedType == gl.NONE) + return gluTextureUtil.getTransferFormat(gluTextureUtil.mapGLInternalFormat(imgFormat.format)); + else + return new gluTextureUtil.TransferFormat(imgFormat.format, imgFormat.unsizedType); + }; + + // FormatDB, CheckerFactory + + /** + * @constructor + * @param {glsFboUtil.FormatDB} formats + * @param {glsFboUtil.CheckerFactory} factory + */ + glsFboUtil.FboVerifier = function(formats, factory) { + this.m_formats = formats; + this.m_factory = factory; + }; + // config::Framebuffer + glsFboUtil.FboVerifier.prototype.validStatusCodes = function(cfg, gl) { + if (!(gl = gl || window.gl)) throw new Error('Invalid gl object'); + + /** @type {glsFboUtil.Checker} */ + var cctx = this.m_factory(); + + for (var id = 0; id < cfg.textures.length; ++id) { + var flags = this.m_formats.getFormatInfo(cfg.textures.getIndex(id).second.internalFormat, glsFboUtil.FormatFlags.ANY_FORMAT); + var textureIsValid = (flags & glsFboUtil.FormatFlags.TEXTURE_VALID) != 0; + cctx.addGLError(textureIsValid, gl.INVALID_ENUM); + cctx.addGLError(textureIsValid, gl.INVALID_OPERATION); + cctx.addGLError(textureIsValid, gl.INVALID_VALUE); + } + + for (var id = 0; id < cfg.rbos.length; ++id) { + var flags = this.m_formats.getFormatInfo(cfg.rbos.getIndex(id).second.internalFormat, glsFboUtil.FormatFlags.ANY_FORMAT); + var rboIsValid = (flags & glsFboUtil.FormatFlags.RENDERBUFFER_VALID) != 0; + cctx.addGLError(rboIsValid, gl.INVALID_ENUM); + } + + var count = 0; + for (var index = 0; index < cfg.attachments.length; ++index) { + var attPoint = cfg.attachments.getIndex(index).first; + var att = cfg.attachments.getIndex(index).second; + /** @type {glsFboUtil.Image}*/ + var image = cfg.getImage(glsFboUtil.attachmentType(att, gl), att.imageName); + glsFboUtil.checkAttachmentCompleteness(cctx, att, attPoint, image, this.m_formats, gl); + cctx.check(attPoint, att, image); + ++count; + } + + // "There is at least one image attached to the framebuffer." + // TODO: support XXX_framebuffer_no_attachments + cctx.addFBOStatus(count > 0, gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT); + + return cctx.getStatusCodes(); + + }; + +}); |