/*
* WebGLInspector
* Visit http://createjs.com/ for documentation, updates and examples.
*
* Copyright (c) 2010 gskinner.com, inc.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @module EaselJS
*/
this.createjs = this.createjs||{};
(function() {
"use strict";
/**
* A utility and helper class designed to work with {{#crossLink "StageGL"}}{{/crossLink}} to help investigate and
* test performance or display problems. It contains logging functions to analyze behaviour and performance testing
* utilities.
* @class WebGLInspector
* @constructor
* @param {StageGL} stage The default stage to use when none is supplied.
*/
function WebGLInspector(stage) {
this.EventDispatcher_constructor();
// public properties:
// private properties:
/**
* The internal reference to the default stage this Inspector is for.
* @property _stage
* @protected
* @type {StageGL}
*/
this._stage = stage;
// and begin
this._initializeWebGLInspector();
}
var p = createjs.extend(WebGLInspector, createjs.EventDispatcher);
// static:
/**
* Alternate output for debugging situations where "console" is not available, i.e. Mobile or remote debugging.
* Expects object with a "log" function that takes any number of params.
* @property alternateOutput
* @type {Console}
* @default null
* @static
* @protected
*/
p.alternateOutput = undefined;
// getter / setters:
// ctor:
/**
* @method _initializeWebGL
* @protected
*/
p._initializeWebGLInspector = function() {};
// public methods:
/**
* Perform all of the logging reports at once.
* @method log
* @param {StageGL} [stage=this._stage] The stage to log information for.
*/
p.log = function(stage) {
if(!stage){ stage = this._stage; }
console.log("Batches Per Draw", (stage._batchID/stage._drawID).toFixed(4));
this.logContextInfo(stage._webGLContext);
this.logDepth(stage.children, "");
this.logTextureFill(stage);
};
/**
* Replace the stage's Draw command with an empty draw command. This is useful for testing performance, and ignoring
* rendering.
* @method toggleGPUDraw
* @param {StageGL} [stage=this._stage] The stage to log information for.
* @param {Boolean} enabled Force enabled. If left undefined, it will toggle.
*/
p.toggleGPUDraw = function(stage, enabled) {
if(!stage){ stage = this._stage; }
if(enabled === undefined) {
enabled = !!stage._drawBuffers_;
}
if(enabled) {
if(stage._drawBuffers_) {
stage._drawBuffers = stage._drawBuffers_;
stage._drawBuffers_ = undefined;
}
} else {
stage._drawBuffers_ = stage._drawBuffers;
stage._drawBuffers = function(gl) {
if(this.vocalDebug) {
var output = "BlankDraw["+ this._drawID +":"+ this._batchID +"] : "+ this.batchReason;
this.alternateOutput?this.alternateOutput.log(output):console.log(output);
}
};
}
};
/**
* Recursively walk the entire display tree, log the attached items, and display it in a tree view.
* @method logDepth
* @param {Array} [children=this._stage.children] The children array to walk through.
* @param {String} prepend What to prepend to this output from this point onwards.
* @param {Function} customLog Which logging function to use, mainly for filtering or formatting output.
* Fallback hierarchy is customLog -> alternateOutput -> console.log.
*/
p.logDepth = function(children, prepend, customLog) {
if(!children){ children = this._stage.children; }
if(!prepend){ prepend = ""; }
var l = children.length;
for(var i=0; i<l; i++) {
var child = children[i];
if(customLog !== undefined){
customLog(prepend+"-", child);
} else {
this.alternateOutput?this.alternateOutput.log(prepend+"-", child):console.log(prepend+"-", child);
}
if(child.children && child.children.length) {
p.logDepth(child.children, "|"+prepend, customLog);
}
}
};
/**
* Examine the context and provide information about its capabilities.
* @method logContextInfo
* @param {WebGLRenderingContext} gl The WebGL context to inspect.
*/
p.logContextInfo = function(gl) {
if(!gl) { gl = this._stage._webGLContext; }
var data = "== LOG:\n";
data += "Max textures per draw: " + gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS) +"\n";
data += "Max textures active: " + gl.getParameter(gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS) +"\n";
data += "\n";
data += "Max texture size: " + (gl.getParameter(gl.MAX_TEXTURE_SIZE)/2) +"^2 \n";
data += "Max cache size: " + (gl.getParameter(gl.MAX_RENDERBUFFER_SIZE)/2) +"^2 \n";
data += "\n";
data += "Max attributes per vertex: " + gl.getParameter(gl.MAX_VERTEX_ATTRIBS) +"\n";
data += "WebGL Version string: " + gl.getParameter(gl.VERSION) +"\n";
data += "======";
this.alternateOutput?this.alternateOutput.log(data):console.log(data);
};
/**
* Simulate renders and watch what happens for textures moving around between draw calls. A texture moving between
* slots means it was removed and then re-added to draw calls. Performance may be better if it was allowed to stay
* in place.
* @method logTextureFill
* @param {StageGL} [stage=this._stage] The stage to log information for.
*/
p.logTextureFill = function(stage) {
if(!stage){ stage = this._stage; }
var dict = stage._textureDictionary;
var count = stage._batchTextureCount;
console.log("textureMax:", count);
var output = [];
for(var n in dict) {
var str = n.replace(window.location.origin, "");
var tex = dict[n];
var shifted = tex._lastActiveInde?tex._lastActiveIndex == tex._activeIndex:false;
output.push({src:str, element:tex, shifted:shifted});
tex._lastActiveIndex = tex._activeIndex;
}
output.sort(function(a,b){
if (a.element._drawID == stage._drawID) { return 1; }
if (a.element._drawID < b.element._drawID) { return -1; }
return 0;
});
var l = output.length;
for(var i = 0; i<l; i++) {
var out = output[i];
var active = out.element._drawID == stage._drawID;
var output = "["+out.src+"] "+ (active?"ACTIVE":"stale") +" "+ (out.shifted?"steady":"DRIFT");
this.alternateOutput?this.alternateOutput.log(output, out.element):console.log(output, out.element);
}
};
// protected methods:
// static methods:
/**
* Utility function for use with {{#crossLink "logDepth"))((/crossLink}}. Logs an item's position and registration.
* Useful to see if something is being forced off screen or has an integer position.
* @method dispProps
* @param {String} prepend The string to show before the item, usually formatting for a tree view.
* @param {DisplayObject} item The item we're currently logging about.
* @static
*/
WebGLInspector.dispProps = function(prepend, item){
if(!prepend){ prepend = ""; }
var p = "\tP:"+ item.x.toFixed(2)+"x"+item.y.toFixed(2) +"\t";
var r = "\tR:"+ item.regX.toFixed(2)+"x"+item.regY.toFixed(2) +"\t";
if(!this.alternateOutput) {
console.log(prepend, item.toString()+"\t", p,r);
} else {
this.alternateOutput.log(prepend, item.toString()+"\t", p,r);
}
};
createjs.WebGLInspector = createjs.promote(WebGLInspector, "EventDispatcher");
}());