blob: 86d05891f2e494a65fafd4f9933932cb5cb463a6 [file] [log] [blame]
/*-------------------------------------------------------------------------
* 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();
};
});