From 400859ebb89828d9bc8a3ae86d8a6eb817baef92 Mon Sep 17 00:00:00 2001 From: pandamicro Date: Sun, 10 Apr 2016 00:13:12 +0800 Subject: [PATCH 01/16] Draft implementation of auto batch --- CCBoot.js | 3 + cocos2d/core/CCDirectorWebGL.js | 2 +- .../core/base-nodes/CCNodeCanvasRenderCmd.js | 48 +- .../core/base-nodes/CCNodeWebGLRenderCmd.js | 33 +- cocos2d/core/platform/CCConfig.js | 2 +- cocos2d/core/platform/CCMacro.js | 32 +- cocos2d/core/platform/CCTypes.js | 581 +++++++++++++++++- cocos2d/core/platform/CCTypesWebGL.js | 563 ----------------- cocos2d/core/renderer/RendererCanvas.js | 3 + cocos2d/core/renderer/RendererWebGL.js | 300 ++++++++- cocos2d/core/sprites/CCSprite.js | 20 +- .../core/sprites/CCSpriteWebGLRenderCmd.js | 31 +- cocos2d/core/utils/CCProfiler.js | 4 + cocos2d/node-grid/CCNodeGrid.js | 40 -- cocos2d/shaders/CCGLProgram.js | 4 + cocos2d/shaders/CCShaderCache.js | 132 ++-- cocos2d/shaders/CCShaders.js | 16 + .../CCProtectedNodeWebGLRenderCmd.js | 6 +- .../UIScale9SpriteWebGLRenderCmd.js | 2 + .../UIScrollViewWebGLRenderCmd.js | 2 +- 20 files changed, 1055 insertions(+), 769 deletions(-) diff --git a/CCBoot.js b/CCBoot.js index 7d690a1276..e1d708c564 100644 --- a/CCBoot.js +++ b/CCBoot.js @@ -2595,6 +2595,9 @@ cc.game = /** @lends cc.game# */{ cc.shaderCache._init(); cc._drawingUtil = new cc.DrawingPrimitiveWebGL(this._renderContext); cc.textureCache._initializingRenderer(); + // cc.glExt = {}; + // cc.glExt.instanced_arrays = gl.getExtension("ANGLE_instanced_arrays"); + // cc.glExt.element_uint = gl.getExtension("OES_element_index_uint"); } else { cc.renderer = cc.rendererCanvas; this._renderContext = cc._renderContext = new cc.CanvasContextWrapper(localCanvas.getContext("2d")); diff --git a/cocos2d/core/CCDirectorWebGL.js b/cocos2d/core/CCDirectorWebGL.js index bad8b75e21..7abc42a32d 100644 --- a/cocos2d/core/CCDirectorWebGL.js +++ b/cocos2d/core/CCDirectorWebGL.js @@ -82,7 +82,7 @@ cc.game.addEventListener(cc.game.EVENT_RENDERER_INITED, function () { size.width - ox, -oy, size.height - oy, - -1024, 1024); + -1, 1); cc.kmGLMultMatrix(orthoMatrix); cc.kmGLMatrixMode(cc.KM_GL_MODELVIEW); cc.kmGLLoadIdentity(); diff --git a/cocos2d/core/base-nodes/CCNodeCanvasRenderCmd.js b/cocos2d/core/base-nodes/CCNodeCanvasRenderCmd.js index bc802ed5f0..23f63a60e7 100644 --- a/cocos2d/core/base-nodes/CCNodeCanvasRenderCmd.js +++ b/cocos2d/core/base-nodes/CCNodeCanvasRenderCmd.js @@ -32,7 +32,7 @@ cc.CustomRenderCmd = function (target, func) { if (!this._callback) return; this._callback.call(this._target, ctx, scaleX, scaleY); - } + }; }; cc.Node._dirtyFlags = {transformDirty: 1 << 0, visibleDirty: 1 << 1, colorDirty: 1 << 2, opacityDirty: 1 << 3, cacheDirty: 1 << 4, @@ -56,6 +56,9 @@ cc.Node.RenderCmd = function(renderable){ this._cascadeOpacityEnabledDirty = false; this._curLevel = -1; + + this._minZ = 0; + this._maxZ = 0; }; cc.Node.RenderCmd.prototype = { @@ -368,24 +371,50 @@ cc.Node.RenderCmd.prototype = { }, visitChildren: function(){ + var renderer = cc.renderer; var node = this._node; - var i, children = node._children, child; + var i, children = node._children, child, cmd; var len = children.length; + var minZ = Number.MAX_VALUE; + var maxZ = -Number.MAX_VALUE; if (len > 0) { node.sortAllChildren(); // draw children zOrder < 0 for (i = 0; i < len; i++) { child = children[i]; - if (child._localZOrder < 0) - child._renderCmd.visit(this); - else + if (child._localZOrder < 0) { + cmd = child._renderCmd; + cmd.visit(this); + // minZ = Math.min(minZ, child._minZ); + // maxZ = Math.max(maxZ, child._maxZ); + } + else { break; + } } - cc.renderer.pushRenderCommand(this); - for (; i < len; i++) - children[i]._renderCmd.visit(this); + + var z = renderer.assignedZ; + node._vertexZ = z; + renderer.assignedZ += renderer.assignedZStep; + + // minZ = Math.min(minZ,z); + // maxZ = Math.max(maxZ,z); + + renderer.pushRenderCommand(this); + for (; i < len; i++) { + child = children[i]; + child._renderCmd.visit(this); + // minZ = Math.min(minZ, child._minZ); + // maxZ = Math.max(maxZ, child._maxZ); + } + + // node._minZ = minZ; + // node._maxZ = maxZ; } else { - cc.renderer.pushRenderCommand(this); + node._vertexZ = renderer.assignedZ; + renderer.assignedZ += renderer.assignedZStep; + + renderer.pushRenderCommand(this); } this._dirtyFlag = 0; } @@ -399,7 +428,6 @@ cc.Node.RenderCmd.prototype = { cc.Node.RenderCmd.call(this, renderable); this._cachedParent = null; this._cacheDirty = false; - }; var proto = cc.Node.CanvasRenderCmd.prototype = Object.create(cc.Node.RenderCmd.prototype); diff --git a/cocos2d/core/base-nodes/CCNodeWebGLRenderCmd.js b/cocos2d/core/base-nodes/CCNodeWebGLRenderCmd.js index 88cab78044..82036b70e7 100644 --- a/cocos2d/core/base-nodes/CCNodeWebGLRenderCmd.js +++ b/cocos2d/core/base-nodes/CCNodeWebGLRenderCmd.js @@ -80,37 +80,16 @@ t4x4Mat[5] = trans.d; t4x4Mat[13] = trans.ty; - // Update Z vertex manually - t4x4Mat[14] = node._vertexZ; - //optimize performance for Javascript cc.kmMat4Multiply(stackMatrix, parentMatrix, t4x4); - // XXX: Expensive calls. Camera should be integrated into the cached affine matrix - if (node._camera !== null && !(node.grid !== null && node.grid.isActive())) { - var apx = this._anchorPointInPoints.x, apy = this._anchorPointInPoints.y; - var translate = (apx !== 0.0 || apy !== 0.0); - if (translate){ - if(!cc.SPRITEBATCHNODE_RENDER_SUBPIXEL) { - apx = 0 | apx; - apy = 0 | apy; - } - //cc.kmGLTranslatef(apx, apy, 0); - var translation = cc.math.Matrix4.createByTranslation(apx, apy, 0, t4x4); //t4x4 as a temp matrix - stackMatrix.multiply(translation); - - node._camera._locateForRenderer(stackMatrix); - - //cc.kmGLTranslatef(-apx, -apy, 0); optimize at here : kmGLTranslatef - translation = cc.math.Matrix4.createByTranslation(-apx, -apy, 0, translation); - stackMatrix.multiply(translation); - t4x4.identity(); //reset t4x4; - } else { - node._camera._locateForRenderer(stackMatrix); - } - } - if(!recursive || !node._children || node._children.length === 0) + // Update Z depth + t4x4Mat[14] = node._vertexZ; + + if (!recursive || !node._children) { return; + } + var i, len, locChildren = node._children; for(i = 0, len = locChildren.length; i< len; i++){ locChildren[i]._renderCmd.transform(this, recursive); diff --git a/cocos2d/core/platform/CCConfig.js b/cocos2d/core/platform/CCConfig.js index cc7d1a957a..06061684fa 100644 --- a/cocos2d/core/platform/CCConfig.js +++ b/cocos2d/core/platform/CCConfig.js @@ -56,7 +56,7 @@ window["CocosEngine"] = cc.ENGINE_VERSION = "Cocos2d-JS v3.11"; * @constant * @type {Number} */ -cc.FIX_ARTIFACTS_BY_STRECHING_TEXEL = 0; +cc.FIX_ARTIFACTS_BY_STRECHING_TEXEL = 1; /** * Position of the FPS (Default: 0,0 (bottom-left corner))
diff --git a/cocos2d/core/platform/CCMacro.js b/cocos2d/core/platform/CCMacro.js index f0e0a7c7f4..8e90a08c2d 100644 --- a/cocos2d/core/platform/CCMacro.js +++ b/cocos2d/core/platform/CCMacro.js @@ -566,7 +566,27 @@ cc.VERTEX_ATTRIB_TEX_COORDS = 2; * @constant * @type {Number} */ -cc.VERTEX_ATTRIB_MAX = 3; +cc.VERTEX_ATTRIB_MVMAT0 = 3; +/** + * @constant + * @type {Number} + */ +cc.VERTEX_ATTRIB_MVMAT1 = 4; +/** + * @constant + * @type {Number} + */ +cc.VERTEX_ATTRIB_MVMAT2 = 5; +/** + * @constant + * @type {Number} + */ +cc.VERTEX_ATTRIB_MVMAT3 = 6; +/** + * @constant + * @type {Number} + */ +cc.VERTEX_ATTRIB_MAX = 7; //------------Uniforms------------------ /** @@ -626,6 +646,11 @@ cc.SHADER_POSITION_TEXTURECOLOR = "ShaderPositionTextureColor"; * @type {String} */ cc.SHADER_POSITION_TEXTURECOLORALPHATEST = "ShaderPositionTextureColorAlphaTest"; +/** + * @constant + * @type {String} + */ +cc.SHADER_POSITION_TEXTURECOLORALPHATEST_BATCHED = "ShaderPositionTextureColorAlphaTestBatched"; /** * @constant * @type {String} @@ -720,6 +745,11 @@ cc.ATTRIBUTE_NAME_POSITION = "a_position"; * @type {String} */ cc.ATTRIBUTE_NAME_TEX_COORD = "a_texCoord"; +/** + * @constant + * @type {String} + */ +cc.ATTRIBUTE_NAME_MVMAT = "a_mvMatrix"; /** diff --git a/cocos2d/core/platform/CCTypes.js b/cocos2d/core/platform/CCTypes.js index 9614e66263..5d49e4b2a2 100644 --- a/cocos2d/core/platform/CCTypes.js +++ b/cocos2d/core/platform/CCTypes.js @@ -100,37 +100,571 @@ cc.Acceleration = function (x, y, z, timestamp) { /** * @class cc.Vertex2F + * @param {Number} x + * @param {Number}y + * @param {Array} arrayBuffer + * @param {Number}offset * @constructor - * @param {Number} x1 - * @param {Number} y1 */ -cc.Vertex2F = function (x1, y1) { - this.x = x1 || 0; - this.y = y1 || 0; +cc.Vertex2F = function (x, y, arrayBuffer, offset) { + this._arrayBuffer = arrayBuffer || new ArrayBuffer(cc.Vertex2F.BYTES_PER_ELEMENT); + this._offset = offset || 0; + + this._xF32 = new Float32Array(this._arrayBuffer, this._offset, 1); + this._yF32 = new Float32Array(this._arrayBuffer, this._offset + 4, 1); + this._xF32[0] = x || 0; + this._yF32[0] = y || 0; +}; +/** + * @constant + * @type {number} + */ +cc.Vertex2F.BYTES_PER_ELEMENT = 8; + +_p = cc.Vertex2F.prototype; +_p._getX = function () { + return this._xF32[0]; +}; +_p._setX = function (xValue) { + this._xF32[0] = xValue; +}; +_p._getY = function () { + return this._yF32[0]; }; +_p._setY = function (yValue) { + this._yF32[0] = yValue; +}; +/** @expose */ +_p.x; +cc.defineGetterSetter(_p, "x", _p._getX, _p._setX); +/** @expose */ +_p.y; +cc.defineGetterSetter(_p, "y", _p._getY, _p._setY); /** - * Helper macro that creates an Vertex2F type composed of 2 floats: x, y - * @function + * @class cc.Vertex3F * @param {Number} x * @param {Number} y - * @return {cc.Vertex2F} + * @param {Number}z + * @param {Array} arrayBuffer + * @param {Number} offset + * @constructor */ -cc.vertex2 = function (x, y) { - return new cc.Vertex2F(x, y); +cc.Vertex3F = function (x, y, z, arrayBuffer, offset) { + this._arrayBuffer = arrayBuffer || new ArrayBuffer(cc.Vertex3F.BYTES_PER_ELEMENT); + this._offset = offset || 0; + + var locArrayBuffer = this._arrayBuffer, locOffset = this._offset; + this._xF32 = new Float32Array(locArrayBuffer, locOffset, 1); + this._xF32[0] = x || 0; + this._yF32 = new Float32Array(locArrayBuffer, locOffset + Float32Array.BYTES_PER_ELEMENT, 1); + this._yF32[0] = y || 0; + this._zF32 = new Float32Array(locArrayBuffer, locOffset + Float32Array.BYTES_PER_ELEMENT * 2, 1); + this._zF32[0] = z || 0; }; +/** + * @constant + * @type {number} + */ +cc.Vertex3F.BYTES_PER_ELEMENT = 12; + +_p = cc.Vertex3F.prototype; +_p._getX = function () { + return this._xF32[0]; +}; +_p._setX = function (xValue) { + this._xF32[0] = xValue; +}; +_p._getY = function () { + return this._yF32[0]; +}; +_p._setY = function (yValue) { + this._yF32[0] = yValue; +}; +_p._getZ = function () { + return this._zF32[0]; +}; +_p._setZ = function (zValue) { + this._zF32[0] = zValue; +}; +/** @expose */ +_p.x; +cc.defineGetterSetter(_p, "x", _p._getX, _p._setX); +/** @expose */ +_p.y; +cc.defineGetterSetter(_p, "y", _p._getY, _p._setY); +/** @expose */ +_p.z; +cc.defineGetterSetter(_p, "z", _p._getZ, _p._setZ); /** - * @class cc.Vertex3F + * @class cc.Tex2F + * @param {Number} u + * @param {Number} v + * @param {Array} arrayBuffer + * @param {Number} offset + * @constructor + */ +cc.Tex2F = function (u, v, arrayBuffer, offset) { + this._arrayBuffer = arrayBuffer || new ArrayBuffer(cc.Tex2F.BYTES_PER_ELEMENT); + this._offset = offset || 0; + + this._uF32 = new Float32Array(this._arrayBuffer, this._offset, 1); + this._vF32 = new Float32Array(this._arrayBuffer, this._offset + 4, 1); + this._uF32[0] = u || 0; + this._vF32[0] = v || 0; +}; +/** + * @constants + * @type {number} + */ +cc.Tex2F.BYTES_PER_ELEMENT = 8; + +_p = cc.Tex2F.prototype; +_p._getU = function () { + return this._uF32[0]; +}; +_p._setU = function (xValue) { + this._uF32[0] = xValue; +}; +_p._getV = function () { + return this._vF32[0]; +}; +_p._setV = function (yValue) { + this._vF32[0] = yValue; +}; +/** @expose */ +_p.u; +cc.defineGetterSetter(_p, "u", _p._getU, _p._setU); +/** @expose */ +_p.v; +cc.defineGetterSetter(_p, "v", _p._getV, _p._setV); + +/** + * @class cc.Quad2 + * @param {cc.Vertex2F} tl + * @param {cc.Vertex2F} tr + * @param {cc.Vertex2F} bl + * @param {cc.Vertex2F} br + * @param {Array} arrayBuffer + * @param {Number} offset + * @constructor + */ +cc.Quad2 = function (tl, tr, bl, br, arrayBuffer, offset) { + this._arrayBuffer = arrayBuffer || new ArrayBuffer(cc.Quad2.BYTES_PER_ELEMENT); + this._offset = offset || 0; + + var locArrayBuffer = this._arrayBuffer, locElementLen = cc.Vertex2F.BYTES_PER_ELEMENT; + this._tl = tl ? new cc.Vertex2F(tl.x, tl.y, locArrayBuffer, 0) : new cc.Vertex2F(0, 0, locArrayBuffer, 0); + this._tr = tr ? new cc.Vertex2F(tr.x, tr.y, locArrayBuffer, locElementLen) : new cc.Vertex2F(0, 0, locArrayBuffer, locElementLen); + this._bl = bl ? new cc.Vertex2F(bl.x, bl.y, locArrayBuffer, locElementLen * 2) : new cc.Vertex2F(0, 0, locArrayBuffer, locElementLen * 2); + this._br = br ? new cc.Vertex2F(br.x, br.y, locArrayBuffer, locElementLen * 3) : new cc.Vertex2F(0, 0, locArrayBuffer, locElementLen * 3); +}; +/** + * @constant + * @type {number} + */ +cc.Quad2.BYTES_PER_ELEMENT = 32; + +_p = cc.Quad2.prototype; +_p._getTL = function () { + return this._tl; +}; +_p._setTL = function (tlValue) { + this._tl.x = tlValue.x; + this._tl.y = tlValue.y; +}; +_p._getTR = function () { + return this._tr; +}; +_p._setTR = function (trValue) { + this._tr.x = trValue.x; + this._tr.y = trValue.y; +}; +_p._getBL = function() { + return this._bl; +}; +_p._setBL = function (blValue) { + this._bl.x = blValue.x; + this._bl.y = blValue.y; +}; +_p._getBR = function () { + return this._br; +}; +_p._setBR = function (brValue) { + this._br.x = brValue.x; + this._br.y = brValue.y; +}; + +/** @expose */ +_p.tl; +cc.defineGetterSetter(_p, "tl", _p._getTL, _p._setTL); +/** @expose */ +_p.tr; +cc.defineGetterSetter(_p, "tr", _p._getTR, _p._setTR); +/** @expose */ +_p.bl; +cc.defineGetterSetter(_p, "bl", _p._getBL, _p._setBL); +/** @expose */ +_p.br; +cc.defineGetterSetter(_p, "br", _p._getBR, _p._setBR); + +/** + * A 3D Quad. 4 * 3 floats + * @Class cc.Quad3 + * @Construct + * @param {cc.Vertex3F} bl1 + * @param {cc.Vertex3F} br1 + * @param {cc.Vertex3F} tl1 + * @param {cc.Vertex3F} tr1 + */ +cc.Quad3 = function (bl1, br1, tl1, tr1) { + this.bl = bl1 || new cc.Vertex3F(0, 0, 0); + this.br = br1 || new cc.Vertex3F(0, 0, 0); + this.tl = tl1 || new cc.Vertex3F(0, 0, 0); + this.tr = tr1 || new cc.Vertex3F(0, 0, 0); +}; + +/** + * @class cc.V3F_C4B_T2F + * @param {cc.Vertex3F} vertices + * @param {cc.Color} colors + * @param {cc.Tex2F} texCoords + * @param {Array} arrayBuffer + * @param {Number} offset + * @constructor + */ +cc.V3F_C4B_T2F = function (vertices, colors, texCoords, arrayBuffer, offset) { + this._arrayBuffer = arrayBuffer || new ArrayBuffer(cc.V3F_C4B_T2F.BYTES_PER_ELEMENT); + this._offset = offset || 0; + + var locArrayBuffer = this._arrayBuffer, locOffset = this._offset, locElementLen = cc.Vertex3F.BYTES_PER_ELEMENT; + this._vertices = vertices ? new cc.Vertex3F(vertices.x, vertices.y, vertices.z, locArrayBuffer, locOffset) : + new cc.Vertex3F(0, 0, 0, locArrayBuffer, locOffset); + this._colors = colors ? cc.color(colors.r, colors.g, colors.b, colors.a, locArrayBuffer, locOffset + locElementLen) : + cc.color(0, 0, 0, 0, locArrayBuffer, locOffset + locElementLen); + this._texCoords = texCoords ? new cc.Tex2F(texCoords.u, texCoords.v, locArrayBuffer, locOffset + locElementLen + cc.Color.BYTES_PER_ELEMENT) : + new cc.Tex2F(0, 0, locArrayBuffer, locOffset + locElementLen + cc.Color.BYTES_PER_ELEMENT); +}; +/** + * @constant + * @type {number} + */ +cc.V3F_C4B_T2F.BYTES_PER_ELEMENT = 24; + +_p = cc.V3F_C4B_T2F.prototype; +_p._getVertices = function () { + return this._vertices; +}; +_p._setVertices = function (verticesValue) { + var locVertices = this._vertices; + locVertices.x = verticesValue.x; + locVertices.y = verticesValue.y; + locVertices.z = verticesValue.z; +}; +_p._getColor = function () { + return this._colors; +}; +_p._setColor = function (colorValue) { + var locColors = this._colors; + locColors.r = colorValue.r; + locColors.g = colorValue.g; + locColors.b = colorValue.b; + locColors.a = colorValue.a; +}; +_p._getTexCoords = function () { + return this._texCoords; +}; +_p._setTexCoords = function (texValue) { + this._texCoords.u = texValue.u; + this._texCoords.v = texValue.v; +}; +/** @expose */ +_p.vertices; +cc.defineGetterSetter(_p, "vertices", _p._getVertices, _p._setVertices); +/** @expose */ +_p.colors; +cc.defineGetterSetter(_p, "colors", _p._getColor, _p._setColor); +/** @expose */ +_p.texCoords; +cc.defineGetterSetter(_p, "texCoords", _p._getTexCoords, _p._setTexCoords); + +/** + * @cc.class cc.V3F_C4B_T2F_Quad + * @param {cc.V3F_C4B_T2F} tl + * @param {cc.V3F_C4B_T2F} bl + * @param {cc.V3F_C4B_T2F} tr + * @param {cc.V3F_C4B_T2F} br + * @param {Array} arrayBuffer + * @param {Number} offset + * @constructor + */ +cc.V3F_C4B_T2F_Quad = function (tl, bl, tr, br, arrayBuffer, offset) { + this._arrayBuffer = arrayBuffer || new ArrayBuffer(cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT); + this._offset = offset || 0; + + var locArrayBuffer = this._arrayBuffer, locOffset = this._offset, locElementLen = cc.V3F_C4B_T2F.BYTES_PER_ELEMENT; + this._tl = tl ? new cc.V3F_C4B_T2F(tl.vertices, tl.colors, tl.texCoords, locArrayBuffer, locOffset) : + new cc.V3F_C4B_T2F(null, null, null, locArrayBuffer, locOffset); + this._bl = bl ? new cc.V3F_C4B_T2F(bl.vertices, bl.colors, bl.texCoords, locArrayBuffer, locOffset + locElementLen) : + new cc.V3F_C4B_T2F(null, null, null, locArrayBuffer, locOffset + locElementLen); + this._tr = tr ? new cc.V3F_C4B_T2F(tr.vertices, tr.colors, tr.texCoords, locArrayBuffer, locOffset + locElementLen * 2) : + new cc.V3F_C4B_T2F(null, null, null, locArrayBuffer, locOffset + locElementLen * 2); + this._br = br ? new cc.V3F_C4B_T2F(br.vertices, br.colors, br.texCoords, locArrayBuffer, locOffset + locElementLen * 3) : + new cc.V3F_C4B_T2F(null, null, null, locArrayBuffer, locOffset + locElementLen * 3); +}; +/** + * @constant + * @type {number} + */ +cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT = 96; +_p = cc.V3F_C4B_T2F_Quad.prototype; +_p._getTL = function () { + return this._tl; +}; +_p._setTL = function (tlValue) { + var locTl = this._tl; + locTl.vertices = tlValue.vertices; + locTl.colors = tlValue.colors; + locTl.texCoords = tlValue.texCoords; +}; +_p._getBL = function () { + return this._bl; +}; +_p._setBL = function (blValue) { + var locBl = this._bl; + locBl.vertices = blValue.vertices; + locBl.colors = blValue.colors; + locBl.texCoords = blValue.texCoords; +}; +_p._getTR = function () { + return this._tr; +}; +_p._setTR = function (trValue) { + var locTr = this._tr; + locTr.vertices = trValue.vertices; + locTr.colors = trValue.colors; + locTr.texCoords = trValue.texCoords; +}; +_p._getBR = function () { + return this._br; +}; +_p._setBR = function (brValue) { + var locBr = this._br; + locBr.vertices = brValue.vertices; + locBr.colors = brValue.colors; + locBr.texCoords = brValue.texCoords; +}; +_p._getArrayBuffer = function () { + return this._arrayBuffer; +}; + +/** @expose */ +_p.tl; +cc.defineGetterSetter(_p, "tl", _p._getTL, _p._setTL); +/** @expose */ +_p.tr; +cc.defineGetterSetter(_p, "tr", _p._getTR, _p._setTR); +/** @expose */ +_p.bl; +cc.defineGetterSetter(_p, "bl", _p._getBL, _p._setBL); +/** @expose */ +_p.br; +cc.defineGetterSetter(_p, "br", _p._getBR, _p._setBR); +/** @expose */ +_p.arrayBuffer; +cc.defineGetterSetter(_p, "arrayBuffer", _p._getArrayBuffer, null); + +/** + * @function + * @returns {cc.V3F_C4B_T2F_Quad} + */ +cc.V3F_C4B_T2F_QuadZero = function () { + return new cc.V3F_C4B_T2F_Quad(); +}; + +/** + * @function + * @param {cc.V3F_C4B_T2F_Quad} sourceQuad + * @return {cc.V3F_C4B_T2F_Quad} + */ +cc.V3F_C4B_T2F_QuadCopy = function (sourceQuad) { + if (!sourceQuad) + return cc.V3F_C4B_T2F_QuadZero(); + + //return new cc.V3F_C4B_T2F_Quad(sourceQuad,tl,sourceQuad,bl,sourceQuad.tr,sourceQuad.br,null,0); + var srcTL = sourceQuad.tl, srcBL = sourceQuad.bl, srcTR = sourceQuad.tr, srcBR = sourceQuad.br; + return { + tl: {vertices: {x: srcTL.vertices.x, y: srcTL.vertices.y, z: srcTL.vertices.z}, + colors: {r: srcTL.colors.r, g: srcTL.colors.g, b: srcTL.colors.b, a: srcTL.colors.a}, + texCoords: {u: srcTL.texCoords.u, v: srcTL.texCoords.v}}, + bl: {vertices: {x: srcBL.vertices.x, y: srcBL.vertices.y, z: srcBL.vertices.z}, + colors: {r: srcBL.colors.r, g: srcBL.colors.g, b: srcBL.colors.b, a: srcBL.colors.a}, + texCoords: {u: srcBL.texCoords.u, v: srcBL.texCoords.v}}, + tr: {vertices: {x: srcTR.vertices.x, y: srcTR.vertices.y, z: srcTR.vertices.z}, + colors: {r: srcTR.colors.r, g: srcTR.colors.g, b: srcTR.colors.b, a: srcTR.colors.a}, + texCoords: {u: srcTR.texCoords.u, v: srcTR.texCoords.v}}, + br: {vertices: {x: srcBR.vertices.x, y: srcBR.vertices.y, z: srcBR.vertices.z}, + colors: {r: srcBR.colors.r, g: srcBR.colors.g, b: srcBR.colors.b, a: srcBR.colors.a}, + texCoords: {u: srcBR.texCoords.u, v: srcBR.texCoords.v}} + }; +}; + +/** + * @function + * @param {Array} sourceQuads + * @returns {Array} + */ +cc.V3F_C4B_T2F_QuadsCopy = function (sourceQuads) { + if (!sourceQuads) + return []; + + var retArr = []; + for (var i = 0; i < sourceQuads.length; i++) { + retArr.push(cc.V3F_C4B_T2F_QuadCopy(sourceQuads[i])); + } + return retArr; +}; + +//redefine cc.V2F_C4B_T2F +/** + * @class cc.V2F_C4B_T2F + * @param {cc.Vertex2F} vertices + * @param {cc.Color} colors + * @param {cc.Tex2F} texCoords + * @param {Array} arrayBuffer + * @param {Number} offset + * @constructor + */ +cc.V2F_C4B_T2F = function (vertices, colors, texCoords, arrayBuffer, offset) { + this._arrayBuffer = arrayBuffer || new ArrayBuffer(cc.V2F_C4B_T2F.BYTES_PER_ELEMENT); + this._offset = offset || 0; + + var locArrayBuffer = this._arrayBuffer, locOffset = this._offset, locElementLen = cc.Vertex2F.BYTES_PER_ELEMENT; + this._vertices = vertices ? new cc.Vertex2F(vertices.x, vertices.y, locArrayBuffer, locOffset) : + new cc.Vertex2F(0, 0, locArrayBuffer, locOffset); + this._colors = colors ? cc.color(colors.r, colors.g, colors.b, colors.a, locArrayBuffer, locOffset + locElementLen) : + cc.color(0, 0, 0, 0, locArrayBuffer, locOffset + locElementLen); + this._texCoords = texCoords ? new cc.Tex2F(texCoords.u, texCoords.v, locArrayBuffer, locOffset + locElementLen + cc.Color.BYTES_PER_ELEMENT) : + new cc.Tex2F(0, 0, locArrayBuffer, locOffset + locElementLen + cc.Color.BYTES_PER_ELEMENT); +}; + +/** + * @constant + * @type {number} + */ +cc.V2F_C4B_T2F.BYTES_PER_ELEMENT = 20; +_p = cc.V2F_C4B_T2F.prototype; +_p._getVertices = function () { + return this._vertices; +}; +_p._setVertices = function (verticesValue) { + this._vertices.x = verticesValue.x; + this._vertices.y = verticesValue.y; +}; +_p._getColor = function () { + return this._colors; +}; +_p._setColor = function (colorValue) { + var locColors = this._colors; + locColors.r = colorValue.r; + locColors.g = colorValue.g; + locColors.b = colorValue.b; + locColors.a = colorValue.a; +}; +_p._getTexCoords = function () { + return this._texCoords; +}; +_p._setTexCoords = function (texValue) { + this._texCoords.u = texValue.u; + this._texCoords.v = texValue.v; +}; + +/** @expose */ +_p.vertices; +cc.defineGetterSetter(_p, "vertices", _p._getVertices, _p._setVertices); +/** @expose */ +_p.colors; +cc.defineGetterSetter(_p, "colors", _p._getColor, _p._setColor); +/** @expose */ +_p.texCoords; +cc.defineGetterSetter(_p, "texCoords", _p._getTexCoords, _p._setTexCoords); + +//redefine cc.V2F_C4B_T2F_Triangle +/** + * @class cc.V2F_C4B_T2F_Triangle + * @param {cc.V2F_C4B_T2F} a + * @param {cc.V2F_C4B_T2F} b + * @param {cc.V2F_C4B_T2F} c + * @param {Array} arrayBuffer + * @param {Number} offset * @constructor - * @param {Number} x1 - * @param {Number} y1 - * @param {Number} z1 */ -cc.Vertex3F = function (x1, y1, z1) { - this.x = x1 || 0; - this.y = y1 || 0; - this.z = z1 || 0; +cc.V2F_C4B_T2F_Triangle = function (a, b, c, arrayBuffer, offset) { + this._arrayBuffer = arrayBuffer || new ArrayBuffer(cc.V2F_C4B_T2F_Triangle.BYTES_PER_ELEMENT); + this._offset = offset || 0; + + var locArrayBuffer = this._arrayBuffer, locOffset = this._offset, locElementLen = cc.V2F_C4B_T2F.BYTES_PER_ELEMENT; + this._a = a ? new cc.V2F_C4B_T2F(a.vertices, a.colors, a.texCoords, locArrayBuffer, locOffset) : + new cc.V2F_C4B_T2F(null, null, null, locArrayBuffer, locOffset); + this._b = b ? new cc.V2F_C4B_T2F(b.vertices, b.colors, b.texCoords, locArrayBuffer, locOffset + locElementLen) : + new cc.V2F_C4B_T2F(null, null, null, locArrayBuffer, locOffset + locElementLen); + this._c = c ? new cc.V2F_C4B_T2F(c.vertices, c.colors, c.texCoords, locArrayBuffer, locOffset + locElementLen * 2) : + new cc.V2F_C4B_T2F(null, null, null, locArrayBuffer, locOffset + locElementLen * 2); +}; +/** + * @constant + * @type {number} + */ +cc.V2F_C4B_T2F_Triangle.BYTES_PER_ELEMENT = 60; +_p = cc.V2F_C4B_T2F_Triangle.prototype; +_p._getA = function () { + return this._a; +}; +_p._setA = function (aValue) { + var locA = this._a; + locA.vertices = aValue.vertices; + locA.colors = aValue.colors; + locA.texCoords = aValue.texCoords; +}; +_p._getB = function () { + return this._b; +}; +_p._setB = function (bValue) { + var locB = this._b; + locB.vertices = bValue.vertices; + locB.colors = bValue.colors; + locB.texCoords = bValue.texCoords; +}; +_p._getC = function () { + return this._c; +}; +_p._setC = function (cValue) { + var locC = this._c; + locC.vertices = cValue.vertices; + locC.colors = cValue.colors; + locC.texCoords = cValue.texCoords; +}; + +/** @expose */ +_p.a; +cc.defineGetterSetter(_p, "a", _p._getA, _p._setA); +/** @expose */ +_p.b; +cc.defineGetterSetter(_p, "b", _p._getB, _p._setB); +/** @expose */ +_p.c; +cc.defineGetterSetter(_p, "c", _p._getC, _p._setC); + +/** + * Helper macro that creates an Vertex2F type composed of 2 floats: x, y + * @function + * @param {Number} x + * @param {Number} y + * @return {cc.Vertex2F} + */ +cc.vertex2 = function (x, y) { + return new cc.Vertex2F(x, y); }; /** @@ -145,17 +679,6 @@ cc.vertex3 = function (x, y, z) { return new cc.Vertex3F(x, y, z); }; -/** - * @class cc.Tex2F - * @constructor - * @param {Number} u1 - * @param {Number} v1 - */ -cc.Tex2F = function (u1, v1) { - this.u = u1 || 0; - this.v = v1 || 0; -}; - /** * Helper macro that creates an Tex2F type: A texcoord composed of 2 floats: u, y * @function diff --git a/cocos2d/core/platform/CCTypesWebGL.js b/cocos2d/core/platform/CCTypesWebGL.js index 136ba6755b..cd66a5c439 100644 --- a/cocos2d/core/platform/CCTypesWebGL.js +++ b/cocos2d/core/platform/CCTypesWebGL.js @@ -130,567 +130,4 @@ cc.game.addEventListener(cc.game.EVENT_RENDERER_INITED, function () { cc._tmp.PrototypeColor(); delete cc._tmp.PrototypeColor; - //redefine cc.Vertex2F - /** - * @class cc.Vertex2F - * @param {Number} x - * @param {Number}y - * @param {Array} arrayBuffer - * @param {Number}offset - * @constructor - */ - cc.Vertex2F = function (x, y, arrayBuffer, offset) { - this._arrayBuffer = arrayBuffer || new ArrayBuffer(cc.Vertex2F.BYTES_PER_ELEMENT); - this._offset = offset || 0; - - this._xF32 = new Float32Array(this._arrayBuffer, this._offset, 1); - this._yF32 = new Float32Array(this._arrayBuffer, this._offset + 4, 1); - this._xF32[0] = x || 0; - this._yF32[0] = y || 0; - }; - /** - * @constant - * @type {number} - */ - cc.Vertex2F.BYTES_PER_ELEMENT = 8; - - _p = cc.Vertex2F.prototype; - _p._getX = function () { - return this._xF32[0]; - }; - _p._setX = function (xValue) { - this._xF32[0] = xValue; - }; - _p._getY = function () { - return this._yF32[0]; - }; - _p._setY = function (yValue) { - this._yF32[0] = yValue; - }; - /** @expose */ - _p.x; - cc.defineGetterSetter(_p, "x", _p._getX, _p._setX); - /** @expose */ - _p.y; - cc.defineGetterSetter(_p, "y", _p._getY, _p._setY); - - // redefine cc.Vertex3F - /** - * @class cc.Vertex3F - * @param {Number} x - * @param {Number} y - * @param {Number}z - * @param {Array} arrayBuffer - * @param {Number} offset - * @constructor - */ - cc.Vertex3F = function (x, y, z, arrayBuffer, offset) { - this._arrayBuffer = arrayBuffer || new ArrayBuffer(cc.Vertex3F.BYTES_PER_ELEMENT); - this._offset = offset || 0; - - var locArrayBuffer = this._arrayBuffer, locOffset = this._offset; - this._xF32 = new Float32Array(locArrayBuffer, locOffset, 1); - this._xF32[0] = x || 0; - this._yF32 = new Float32Array(locArrayBuffer, locOffset + Float32Array.BYTES_PER_ELEMENT, 1); - this._yF32[0] = y || 0; - this._zF32 = new Float32Array(locArrayBuffer, locOffset + Float32Array.BYTES_PER_ELEMENT * 2, 1); - this._zF32[0] = z || 0; - }; - /** - * @constant - * @type {number} - */ - cc.Vertex3F.BYTES_PER_ELEMENT = 12; - - _p = cc.Vertex3F.prototype; - _p._getX = function () { - return this._xF32[0]; - }; - _p._setX = function (xValue) { - this._xF32[0] = xValue; - }; - _p._getY = function () { - return this._yF32[0]; - }; - _p._setY = function (yValue) { - this._yF32[0] = yValue; - }; - _p._getZ = function () { - return this._zF32[0]; - }; - _p._setZ = function (zValue) { - this._zF32[0] = zValue; - }; - /** @expose */ - _p.x; - cc.defineGetterSetter(_p, "x", _p._getX, _p._setX); - /** @expose */ - _p.y; - cc.defineGetterSetter(_p, "y", _p._getY, _p._setY); - /** @expose */ - _p.z; - cc.defineGetterSetter(_p, "z", _p._getZ, _p._setZ); - - // redefine cc.Tex2F - /** - * @class cc.Tex2F - * @param {Number} u - * @param {Number} v - * @param {Array} arrayBuffer - * @param {Number} offset - * @constructor - */ - cc.Tex2F = function (u, v, arrayBuffer, offset) { - this._arrayBuffer = arrayBuffer || new ArrayBuffer(cc.Tex2F.BYTES_PER_ELEMENT); - this._offset = offset || 0; - - this._uF32 = new Float32Array(this._arrayBuffer, this._offset, 1); - this._vF32 = new Float32Array(this._arrayBuffer, this._offset + 4, 1); - this._uF32[0] = u || 0; - this._vF32[0] = v || 0; - }; - /** - * @constants - * @type {number} - */ - cc.Tex2F.BYTES_PER_ELEMENT = 8; - - _p = cc.Tex2F.prototype; - _p._getU = function () { - return this._uF32[0]; - }; - _p._setU = function (xValue) { - this._uF32[0] = xValue; - }; - _p._getV = function () { - return this._vF32[0]; - }; - _p._setV = function (yValue) { - this._vF32[0] = yValue; - }; - /** @expose */ - _p.u; - cc.defineGetterSetter(_p, "u", _p._getU, _p._setU); - /** @expose */ - _p.v; - cc.defineGetterSetter(_p, "v", _p._getV, _p._setV); - - //redefine cc.Quad2 - /** - * @class cc.Quad2 - * @param {cc.Vertex2F} tl - * @param {cc.Vertex2F} tr - * @param {cc.Vertex2F} bl - * @param {cc.Vertex2F} br - * @param {Array} arrayBuffer - * @param {Number} offset - * @constructor - */ - cc.Quad2 = function (tl, tr, bl, br, arrayBuffer, offset) { - this._arrayBuffer = arrayBuffer || new ArrayBuffer(cc.Quad2.BYTES_PER_ELEMENT); - this._offset = offset || 0; - - var locArrayBuffer = this._arrayBuffer, locElementLen = cc.Vertex2F.BYTES_PER_ELEMENT; - this._tl = tl ? new cc.Vertex2F(tl.x, tl.y, locArrayBuffer, 0) : new cc.Vertex2F(0, 0, locArrayBuffer, 0); - this._tr = tr ? new cc.Vertex2F(tr.x, tr.y, locArrayBuffer, locElementLen) : new cc.Vertex2F(0, 0, locArrayBuffer, locElementLen); - this._bl = bl ? new cc.Vertex2F(bl.x, bl.y, locArrayBuffer, locElementLen * 2) : new cc.Vertex2F(0, 0, locArrayBuffer, locElementLen * 2); - this._br = br ? new cc.Vertex2F(br.x, br.y, locArrayBuffer, locElementLen * 3) : new cc.Vertex2F(0, 0, locArrayBuffer, locElementLen * 3); - }; - /** - * @constant - * @type {number} - */ - cc.Quad2.BYTES_PER_ELEMENT = 32; - - _p = cc.Quad2.prototype; - _p._getTL = function () { - return this._tl; - }; - _p._setTL = function (tlValue) { - this._tl.x = tlValue.x; - this._tl.y = tlValue.y; - }; - _p._getTR = function () { - return this._tr; - }; - _p._setTR = function (trValue) { - this._tr.x = trValue.x; - this._tr.y = trValue.y; - }; - _p._getBL = function() { - return this._bl; - }; - _p._setBL = function (blValue) { - this._bl.x = blValue.x; - this._bl.y = blValue.y; - }; - _p._getBR = function () { - return this._br; - }; - _p._setBR = function (brValue) { - this._br.x = brValue.x; - this._br.y = brValue.y; - }; - - /** @expose */ - _p.tl; - cc.defineGetterSetter(_p, "tl", _p._getTL, _p._setTL); - /** @expose */ - _p.tr; - cc.defineGetterSetter(_p, "tr", _p._getTR, _p._setTR); - /** @expose */ - _p.bl; - cc.defineGetterSetter(_p, "bl", _p._getBL, _p._setBL); - /** @expose */ - _p.br; - cc.defineGetterSetter(_p, "br", _p._getBR, _p._setBR); - - /** - * A 3D Quad. 4 * 3 floats - * @Class cc.Quad3 - * @Construct - * @param {cc.Vertex3F} bl1 - * @param {cc.Vertex3F} br1 - * @param {cc.Vertex3F} tl1 - * @param {cc.Vertex3F} tr1 - */ - cc.Quad3 = function (bl1, br1, tl1, tr1) { - this.bl = bl1 || new cc.Vertex3F(0, 0, 0); - this.br = br1 || new cc.Vertex3F(0, 0, 0); - this.tl = tl1 || new cc.Vertex3F(0, 0, 0); - this.tr = tr1 || new cc.Vertex3F(0, 0, 0); - }; - - //redefine cc.V3F_C4B_T2F - /** - * @class cc.V3F_C4B_T2F - * @param {cc.Vertex3F} vertices - * @param {cc.Color} colors - * @param {cc.Tex2F} texCoords - * @param {Array} arrayBuffer - * @param {Number} offset - * @constructor - */ - cc.V3F_C4B_T2F = function (vertices, colors, texCoords, arrayBuffer, offset) { - this._arrayBuffer = arrayBuffer || new ArrayBuffer(cc.V3F_C4B_T2F.BYTES_PER_ELEMENT); - this._offset = offset || 0; - - var locArrayBuffer = this._arrayBuffer, locOffset = this._offset, locElementLen = cc.Vertex3F.BYTES_PER_ELEMENT; - this._vertices = vertices ? new cc.Vertex3F(vertices.x, vertices.y, vertices.z, locArrayBuffer, locOffset) : - new cc.Vertex3F(0, 0, 0, locArrayBuffer, locOffset); - this._colors = colors ? cc.color(colors.r, colors.g, colors.b, colors.a, locArrayBuffer, locOffset + locElementLen) : - cc.color(0, 0, 0, 0, locArrayBuffer, locOffset + locElementLen); - this._texCoords = texCoords ? new cc.Tex2F(texCoords.u, texCoords.v, locArrayBuffer, locOffset + locElementLen + cc.Color.BYTES_PER_ELEMENT) : - new cc.Tex2F(0, 0, locArrayBuffer, locOffset + locElementLen + cc.Color.BYTES_PER_ELEMENT); - }; - /** - * @constant - * @type {number} - */ - cc.V3F_C4B_T2F.BYTES_PER_ELEMENT = 24; - - _p = cc.V3F_C4B_T2F.prototype; - _p._getVertices = function () { - return this._vertices; - }; - _p._setVertices = function (verticesValue) { - var locVertices = this._vertices; - locVertices.x = verticesValue.x; - locVertices.y = verticesValue.y; - locVertices.z = verticesValue.z; - }; - _p._getColor = function () { - return this._colors; - }; - _p._setColor = function (colorValue) { - var locColors = this._colors; - locColors.r = colorValue.r; - locColors.g = colorValue.g; - locColors.b = colorValue.b; - locColors.a = colorValue.a; - }; - _p._getTexCoords = function () { - return this._texCoords; - }; - _p._setTexCoords = function (texValue) { - this._texCoords.u = texValue.u; - this._texCoords.v = texValue.v; - }; - /** @expose */ - _p.vertices; - cc.defineGetterSetter(_p, "vertices", _p._getVertices, _p._setVertices); - /** @expose */ - _p.colors; - cc.defineGetterSetter(_p, "colors", _p._getColor, _p._setColor); - /** @expose */ - _p.texCoords; - cc.defineGetterSetter(_p, "texCoords", _p._getTexCoords, _p._setTexCoords); - - //redefine cc.V3F_C4B_T2F_Quad - /** - * @cc.class cc.V3F_C4B_T2F_Quad - * @param {cc.V3F_C4B_T2F} tl - * @param {cc.V3F_C4B_T2F} bl - * @param {cc.V3F_C4B_T2F} tr - * @param {cc.V3F_C4B_T2F} br - * @param {Array} arrayBuffer - * @param {Number} offset - * @constructor - */ - cc.V3F_C4B_T2F_Quad = function (tl, bl, tr, br, arrayBuffer, offset) { - this._arrayBuffer = arrayBuffer || new ArrayBuffer(cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT); - this._offset = offset || 0; - - var locArrayBuffer = this._arrayBuffer, locOffset = this._offset, locElementLen = cc.V3F_C4B_T2F.BYTES_PER_ELEMENT; - this._tl = tl ? new cc.V3F_C4B_T2F(tl.vertices, tl.colors, tl.texCoords, locArrayBuffer, locOffset) : - new cc.V3F_C4B_T2F(null, null, null, locArrayBuffer, locOffset); - this._bl = bl ? new cc.V3F_C4B_T2F(bl.vertices, bl.colors, bl.texCoords, locArrayBuffer, locOffset + locElementLen) : - new cc.V3F_C4B_T2F(null, null, null, locArrayBuffer, locOffset + locElementLen); - this._tr = tr ? new cc.V3F_C4B_T2F(tr.vertices, tr.colors, tr.texCoords, locArrayBuffer, locOffset + locElementLen * 2) : - new cc.V3F_C4B_T2F(null, null, null, locArrayBuffer, locOffset + locElementLen * 2); - this._br = br ? new cc.V3F_C4B_T2F(br.vertices, br.colors, br.texCoords, locArrayBuffer, locOffset + locElementLen * 3) : - new cc.V3F_C4B_T2F(null, null, null, locArrayBuffer, locOffset + locElementLen * 3); - }; - /** - * @constant - * @type {number} - */ - cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT = 96; - _p = cc.V3F_C4B_T2F_Quad.prototype; - _p._getTL = function () { - return this._tl; - }; - _p._setTL = function (tlValue) { - var locTl = this._tl; - locTl.vertices = tlValue.vertices; - locTl.colors = tlValue.colors; - locTl.texCoords = tlValue.texCoords; - }; - _p._getBL = function () { - return this._bl; - }; - _p._setBL = function (blValue) { - var locBl = this._bl; - locBl.vertices = blValue.vertices; - locBl.colors = blValue.colors; - locBl.texCoords = blValue.texCoords; - }; - _p._getTR = function () { - return this._tr; - }; - _p._setTR = function (trValue) { - var locTr = this._tr; - locTr.vertices = trValue.vertices; - locTr.colors = trValue.colors; - locTr.texCoords = trValue.texCoords; - }; - _p._getBR = function () { - return this._br; - }; - _p._setBR = function (brValue) { - var locBr = this._br; - locBr.vertices = brValue.vertices; - locBr.colors = brValue.colors; - locBr.texCoords = brValue.texCoords; - }; - _p._getArrayBuffer = function () { - return this._arrayBuffer; - }; - - /** @expose */ - _p.tl; - cc.defineGetterSetter(_p, "tl", _p._getTL, _p._setTL); - /** @expose */ - _p.tr; - cc.defineGetterSetter(_p, "tr", _p._getTR, _p._setTR); - /** @expose */ - _p.bl; - cc.defineGetterSetter(_p, "bl", _p._getBL, _p._setBL); - /** @expose */ - _p.br; - cc.defineGetterSetter(_p, "br", _p._getBR, _p._setBR); - /** @expose */ - _p.arrayBuffer; - cc.defineGetterSetter(_p, "arrayBuffer", _p._getArrayBuffer, null); - - /** - * @function - * @returns {cc.V3F_C4B_T2F_Quad} - */ - cc.V3F_C4B_T2F_QuadZero = function () { - return new cc.V3F_C4B_T2F_Quad(); - }; - - /** - * @function - * @param {cc.V3F_C4B_T2F_Quad} sourceQuad - * @return {cc.V3F_C4B_T2F_Quad} - */ - cc.V3F_C4B_T2F_QuadCopy = function (sourceQuad) { - if (!sourceQuad) - return cc.V3F_C4B_T2F_QuadZero(); - - //return new cc.V3F_C4B_T2F_Quad(sourceQuad,tl,sourceQuad,bl,sourceQuad.tr,sourceQuad.br,null,0); - var srcTL = sourceQuad.tl, srcBL = sourceQuad.bl, srcTR = sourceQuad.tr, srcBR = sourceQuad.br; - return { - tl: {vertices: {x: srcTL.vertices.x, y: srcTL.vertices.y, z: srcTL.vertices.z}, - colors: {r: srcTL.colors.r, g: srcTL.colors.g, b: srcTL.colors.b, a: srcTL.colors.a}, - texCoords: {u: srcTL.texCoords.u, v: srcTL.texCoords.v}}, - bl: {vertices: {x: srcBL.vertices.x, y: srcBL.vertices.y, z: srcBL.vertices.z}, - colors: {r: srcBL.colors.r, g: srcBL.colors.g, b: srcBL.colors.b, a: srcBL.colors.a}, - texCoords: {u: srcBL.texCoords.u, v: srcBL.texCoords.v}}, - tr: {vertices: {x: srcTR.vertices.x, y: srcTR.vertices.y, z: srcTR.vertices.z}, - colors: {r: srcTR.colors.r, g: srcTR.colors.g, b: srcTR.colors.b, a: srcTR.colors.a}, - texCoords: {u: srcTR.texCoords.u, v: srcTR.texCoords.v}}, - br: {vertices: {x: srcBR.vertices.x, y: srcBR.vertices.y, z: srcBR.vertices.z}, - colors: {r: srcBR.colors.r, g: srcBR.colors.g, b: srcBR.colors.b, a: srcBR.colors.a}, - texCoords: {u: srcBR.texCoords.u, v: srcBR.texCoords.v}} - }; - }; - - /** - * @function - * @param {Array} sourceQuads - * @returns {Array} - */ - cc.V3F_C4B_T2F_QuadsCopy = function (sourceQuads) { - if (!sourceQuads) - return []; - - var retArr = []; - for (var i = 0; i < sourceQuads.length; i++) { - retArr.push(cc.V3F_C4B_T2F_QuadCopy(sourceQuads[i])); - } - return retArr; - }; - - //redefine cc.V2F_C4B_T2F - /** - * @class cc.V2F_C4B_T2F - * @param {cc.Vertex2F} vertices - * @param {cc.Color} colors - * @param {cc.Tex2F} texCoords - * @param {Array} arrayBuffer - * @param {Number} offset - * @constructor - */ - cc.V2F_C4B_T2F = function (vertices, colors, texCoords, arrayBuffer, offset) { - this._arrayBuffer = arrayBuffer || new ArrayBuffer(cc.V2F_C4B_T2F.BYTES_PER_ELEMENT); - this._offset = offset || 0; - - var locArrayBuffer = this._arrayBuffer, locOffset = this._offset, locElementLen = cc.Vertex2F.BYTES_PER_ELEMENT; - this._vertices = vertices ? new cc.Vertex2F(vertices.x, vertices.y, locArrayBuffer, locOffset) : - new cc.Vertex2F(0, 0, locArrayBuffer, locOffset); - this._colors = colors ? cc.color(colors.r, colors.g, colors.b, colors.a, locArrayBuffer, locOffset + locElementLen) : - cc.color(0, 0, 0, 0, locArrayBuffer, locOffset + locElementLen); - this._texCoords = texCoords ? new cc.Tex2F(texCoords.u, texCoords.v, locArrayBuffer, locOffset + locElementLen + cc.Color.BYTES_PER_ELEMENT) : - new cc.Tex2F(0, 0, locArrayBuffer, locOffset + locElementLen + cc.Color.BYTES_PER_ELEMENT); - }; - - /** - * @constant - * @type {number} - */ - cc.V2F_C4B_T2F.BYTES_PER_ELEMENT = 20; - _p = cc.V2F_C4B_T2F.prototype; - _p._getVertices = function () { - return this._vertices; - }; - _p._setVertices = function (verticesValue) { - this._vertices.x = verticesValue.x; - this._vertices.y = verticesValue.y; - }; - _p._getColor = function () { - return this._colors; - }; - _p._setColor = function (colorValue) { - var locColors = this._colors; - locColors.r = colorValue.r; - locColors.g = colorValue.g; - locColors.b = colorValue.b; - locColors.a = colorValue.a; - }; - _p._getTexCoords = function () { - return this._texCoords; - }; - _p._setTexCoords = function (texValue) { - this._texCoords.u = texValue.u; - this._texCoords.v = texValue.v; - }; - - /** @expose */ - _p.vertices; - cc.defineGetterSetter(_p, "vertices", _p._getVertices, _p._setVertices); - /** @expose */ - _p.colors; - cc.defineGetterSetter(_p, "colors", _p._getColor, _p._setColor); - /** @expose */ - _p.texCoords; - cc.defineGetterSetter(_p, "texCoords", _p._getTexCoords, _p._setTexCoords); - - //redefine cc.V2F_C4B_T2F_Triangle - /** - * @class cc.V2F_C4B_T2F_Triangle - * @param {cc.V2F_C4B_T2F} a - * @param {cc.V2F_C4B_T2F} b - * @param {cc.V2F_C4B_T2F} c - * @param {Array} arrayBuffer - * @param {Number} offset - * @constructor - */ - cc.V2F_C4B_T2F_Triangle = function (a, b, c, arrayBuffer, offset) { - this._arrayBuffer = arrayBuffer || new ArrayBuffer(cc.V2F_C4B_T2F_Triangle.BYTES_PER_ELEMENT); - this._offset = offset || 0; - - var locArrayBuffer = this._arrayBuffer, locOffset = this._offset, locElementLen = cc.V2F_C4B_T2F.BYTES_PER_ELEMENT; - this._a = a ? new cc.V2F_C4B_T2F(a.vertices, a.colors, a.texCoords, locArrayBuffer, locOffset) : - new cc.V2F_C4B_T2F(null, null, null, locArrayBuffer, locOffset); - this._b = b ? new cc.V2F_C4B_T2F(b.vertices, b.colors, b.texCoords, locArrayBuffer, locOffset + locElementLen) : - new cc.V2F_C4B_T2F(null, null, null, locArrayBuffer, locOffset + locElementLen); - this._c = c ? new cc.V2F_C4B_T2F(c.vertices, c.colors, c.texCoords, locArrayBuffer, locOffset + locElementLen * 2) : - new cc.V2F_C4B_T2F(null, null, null, locArrayBuffer, locOffset + locElementLen * 2); - }; - /** - * @constant - * @type {number} - */ - cc.V2F_C4B_T2F_Triangle.BYTES_PER_ELEMENT = 60; - _p = cc.V2F_C4B_T2F_Triangle.prototype; - _p._getA = function () { - return this._a; - }; - _p._setA = function (aValue) { - var locA = this._a; - locA.vertices = aValue.vertices; - locA.colors = aValue.colors; - locA.texCoords = aValue.texCoords; - }; - _p._getB = function () { - return this._b; - }; - _p._setB = function (bValue) { - var locB = this._b; - locB.vertices = bValue.vertices; - locB.colors = bValue.colors; - locB.texCoords = bValue.texCoords; - }; - _p._getC = function () { - return this._c; - }; - _p._setC = function (cValue) { - var locC = this._c; - locC.vertices = cValue.vertices; - locC.colors = cValue.colors; - locC.texCoords = cValue.texCoords; - }; - - /** @expose */ - _p.a; - cc.defineGetterSetter(_p, "a", _p._getA, _p._setA); - /** @expose */ - _p.b; - cc.defineGetterSetter(_p, "b", _p._getB, _p._setB); - /** @expose */ - _p.c; - cc.defineGetterSetter(_p, "c", _p._getC, _p._setC); }); diff --git a/cocos2d/core/renderer/RendererCanvas.js b/cocos2d/core/renderer/RendererCanvas.js index f55dbd581f..fe1143a9d9 100644 --- a/cocos2d/core/renderer/RendererCanvas.js +++ b/cocos2d/core/renderer/RendererCanvas.js @@ -24,6 +24,9 @@ cc.rendererCanvas = { childrenOrderDirty: true, + assignedZ: 0, + assignedZStep: 1/10000, + _transformNodePool: [], //save nodes transform dirty _renderCmds: [], //save renderer commands diff --git a/cocos2d/core/renderer/RendererWebGL.js b/cocos2d/core/renderer/RendererWebGL.js index af7e354eea..974d437f80 100644 --- a/cocos2d/core/renderer/RendererWebGL.js +++ b/cocos2d/core/renderer/RendererWebGL.js @@ -22,8 +22,40 @@ THE SOFTWARE. ****************************************************************************/ -cc.rendererWebGL = { +cc.rendererWebGL = (function () { + +function objEqual (a, b) { + if (!a || !b) return false; + + var keys = Object.keys(a); + for (var i = 0; i < keys.length; ++i) { + var key = keys[i]; + if (!b[key] || a[key] !== b[key]) { + return false; + } + } + return true; +} + +function removeByLastSwap (array, i) { + if (array.length > 0) + array[i] = array[array.length - 1]; + array.length--; +} + +// Internal variables +var _batchedInfo = {}, + _batchedCount, + _batchBuffer, + _batchElementBuffer, + _batchBufferPool = [], + _pooledBuffer; + +return { childrenOrderDirty: true, + assignedZ: 0, + assignedZStep: 1/10000, + _transformNodePool: [], //save nodes transform dirty _renderCmds: [], //save renderer commands @@ -42,15 +74,15 @@ cc.rendererWebGL = { * drawing all renderer command to context (default is cc._renderContext) * @param {WebGLRenderingContext} [ctx=cc._renderContext] */ - rendering: function (ctx) { - var locCmds = this._renderCmds, - i, - len; - var context = ctx || cc._renderContext; - for (i = 0, len = locCmds.length; i < len; i++) { - locCmds[i].rendering(context); - } - }, + // rendering: function (ctx) { + // var locCmds = this._renderCmds, + // i, + // len; + // var context = ctx || cc._renderContext; + // for (i = 0, len = locCmds.length; i < len; i++) { + // locCmds[i].rendering(context); + // } + // }, _turnToCacheMode: function (renderTextureID) { this._isCacheToBufferOn = true; @@ -150,5 +182,253 @@ cc.rendererWebGL = { if (this._renderCmds.indexOf(cmd) === -1) this._renderCmds.push(cmd); } + }, + + createBatchBuffer: function (bufferSize, indiceSize) { + var arrayBuffer = gl.createBuffer(); + var elementBuffer = gl.createBuffer(); + + this.initBatchBuffers(arrayBuffer, elementBuffer, bufferSize, indiceSize); + + return {arrayBuffer: arrayBuffer, elementBuffer: elementBuffer, bufferSize: bufferSize, indiceSize: indiceSize}; + }, + + initBatchBuffers: function (arrayBuffer, elementBuffer, bufferSize, indiceSize) { + gl.bindBuffer(gl.ARRAY_BUFFER, arrayBuffer); + gl.bufferData(gl.ARRAY_BUFFER, bufferSize, gl.DYNAMIC_DRAW); + + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elementBuffer); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indiceSize, gl.DYNAMIC_DRAW); + }, + + // Returns an object with {arrayBuffer, elementBuffer, size}, + // where size denotes how many unit fit in the buffer (no need for bufferData if it's already big enough, bufferSubData enough) + getBatchBuffer: function(bufferSize, indiceSize) + { + var pool = _batchBufferPool; + + if (pool.length <= 0) { + return this.createBatchBuffer(bufferSize, indiceSize); + } + else { + var minBuf = null; //we also track the smallest found buffer because that one will be re-initialized and returned if no fitting buffer can be found + var minSize = Number.MAX_VALUE; + var minBufIndex = -1; + for (var i = pool.length - 1; i >= 0; --i) { + var buf = pool[i]; + if (buf.bufferSize >= bufferSize && buf.indiceSize >= indiceSize) { + removeByLastSwap(pool, i); + this.initBatchBuffers(buf.arrayBuffer, buf.elementBuffer, bufferSize, indiceSize); + buf.bufferSize = bufferSize; + buf.indiceSize = indiceSize; + return buf; + } + + if (buf.bufferSize < minSize) + { + minSize = buf.bufferSize; + minBuf = buf; + minBufIndex = i; + } + } + + // we only get here if no properly sized buffer was found + // in that case, take smallest buffer in pool, resize it and return it + removeByLastSwap(pool, minBufIndex); + this.initBatchBuffers(minBuf.arrayBuffer, minBuf.elementBuffer, bufferSize, indiceSize); + minBuf.bufferSize = bufferSize; + minBuf.indiceSize = indiceSize; + return minBuf; + } + }, + + storeBatchBuffer: function(buffer) { + var pool = _batchBufferPool; + pool.push(buffer); + }, + + + _forwardBatch: function (first) { + var renderCmds = this._renderCmds, + cmd = renderCmds[first]; + if (!cmd || !cmd._supportBatch) return 0; + + var info = {}, last; + + // Initialize batched info + cmd.getBatchInfo(_batchedInfo); + _batchedInfo.totalVertexData = 0; + _batchedInfo.totalBufferSize = 0; + _batchedInfo.totalIndiceSize = 0; + + for (last = first; last < renderCmds.length; ++last) { + cmd = renderCmds[last]; + if (cmd._supportBatch) { + cmd.getBatchInfo(info); + } + else { + break; + } + // Batch info don't match, break batching + if (!objEqual(info, _batchedInfo)) { + break; + } + else { + _batchedInfo.totalVertexData += cmd.vertexDataPerUnit; + _batchedInfo.totalBufferSize += cmd.bytesPerUnit; + _batchedInfo.totalIndiceSize += cmd.indicesPerUnit; + } + } + + var count = last - first; + if (count <= 1) { + return count; + } + + _batchedCount = count; + + var bufferSize = _batchedInfo.totalBufferSize; + var indiceSize = _batchedInfo.totalIndiceSize * 2; // *2 because we use shorts for indices + _pooledBuffer = this.getBatchBuffer(bufferSize, indiceSize); + _batchBuffer = _pooledBuffer.arrayBuffer; + _batchElementBuffer = _pooledBuffer.elementBuffer; + + //all of the divisions by 4 are just because we work with uint32arrays instead of uint8 arrays so all indexes need to be shortened by the factor of 4 + var totalVertexData = _batchedInfo.totalVertexData / 4; + var totalBufferSize = _batchedInfo.totalBufferSize / 4; + var vertexDataOffset = 0; + var matrixDataOffset = 0; + + var uploadBuffer = new Uint32Array(totalBufferSize); + + gl.bindBuffer(gl.ARRAY_BUFFER, _batchBuffer); + + var i, j; + for (i = first; i < last; ++i) { + cmd = renderCmds[i]; + var matrixData = cmd.matrixByteSize / 4; + var vertexDataPerUnit = cmd.vertexDataPerUnit / 4; + + var source = cmd._quadBufferView; + var len = source.length; + for (j = 0; j < len; ++j) { + uploadBuffer[vertexDataOffset + j] = source[j]; + } + + var matData = new Uint32Array(cmd._stackMatrix.mat.buffer); + + source = matData; + len = source.length; + + var base = totalVertexData + matrixDataOffset; + var offset0 = base + matrixData * 0; + var offset1 = base + matrixData * 1; + var offset2 = base + matrixData * 2; + var offset3 = base + matrixData * 3; + + for (j = 0; j < len; ++j) { + var val = source[j]; + uploadBuffer[offset0 + j] = val; + uploadBuffer[offset1 + j] = val; + uploadBuffer[offset2 + j] = val; + uploadBuffer[offset3 + j] = val; + } + + vertexDataOffset += vertexDataPerUnit; + matrixDataOffset += matrixData * 4; + } + + gl.bufferSubData(gl.ARRAY_BUFFER, 0, uploadBuffer); + + //create element buffer + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, _batchElementBuffer); + + var indices = new Uint16Array(_batchedInfo.totalIndiceSize); + + var currentQuad = 0; + var indiceI = 0; + for (i = first; i < last; ++i) { + cmd = renderCmds[i]; + indices[indiceI] = currentQuad + 0; + indices[indiceI + 1] = currentQuad + 1; + indices[indiceI + 2] = currentQuad + 2; + indices[indiceI + 3] = currentQuad + 3; + indices[indiceI + 4] = currentQuad + 2; + indices[indiceI + 5] = currentQuad + 1; + currentQuad += 4; + indiceI += cmd.indicesPerUnit; + } + + gl.bufferSubData(gl.ELEMENT_ARRAY_BUFFER, 0, indices); + return count; + }, + + _batchRendering: function () { + // var node = this._node; + var texture = _batchedInfo.texture; + var shader = _batchedInfo.shader; + var count = _batchedCount; + + var bytesPerRow = 16; //4 floats with 4 bytes each + var matrixData = this.matrixByteSize; + var totalVertexData = _batchedInfo.totalVertexData / 4; + + shader.use(); + shader._updateProjectionUniform(); + + cc.glBlendFunc(_batchedInfo.blendSrc, _batchedInfo.blendDst); + cc.glBindTexture2DN(0, texture); // = cc.glBindTexture2D(texture); + + gl.bindBuffer(gl.ARRAY_BUFFER, _batchBuffer); + + cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POS_COLOR_TEX); + + gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 24, 0); //cc.VERTEX_ATTRIB_POSITION + gl.vertexAttribPointer(1, 4, gl.UNSIGNED_BYTE, true, 24, 12); //cc.VERTEX_ATTRIB_COLOR + gl.vertexAttribPointer(2, 2, gl.FLOAT, false, 24, 16); //cc.VERTEX_ATTRIB_TEX_COORDS + + var i; + //enable matrix vertex attribs + for (i = 0; i < 4; ++i) { + gl.enableVertexAttribArray(cc.VERTEX_ATTRIB_MVMAT0 + i); + gl.vertexAttribPointer(cc.VERTEX_ATTRIB_MVMAT0 + i, 4, gl.FLOAT, false, bytesPerRow * 4, totalVertexData + bytesPerRow * i); //stride is one row + } + + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, _batchElementBuffer); + gl.drawElements(gl.TRIANGLES, count * 6, gl.UNSIGNED_SHORT, 0); + + for (i = 0; i < 4; ++i) { + gl.disableVertexAttribArray(cc.VERTEX_ATTRIB_MVMAT0 + i); + } + + this.storeBatchBuffer(_pooledBuffer); + + cc.g_NumberOfDraws++; + }, + + /** + * drawing all renderer command to context (default is cc._renderContext) + * @param {WebGLRenderingContext} [ctx=cc._renderContext] + */ + rendering: function (ctx) { + var locCmds = this._renderCmds, + i, len, cmd, + context = ctx || cc._renderContext; + + for (i = 0, len = locCmds.length; i < len; i++) { + cmd = locCmds[i]; + + // Batching or direct rendering + var batchCount = this._forwardBatch(i); + if (batchCount > 1) { + this._batchRendering(); + // i will increase by 1 each loop + i += batchCount - 1; + } + else { + cmd.rendering(context); + } + } } }; +})(); \ No newline at end of file diff --git a/cocos2d/core/sprites/CCSprite.js b/cocos2d/core/sprites/CCSprite.js index ef8ff0a1fc..c38a2f3788 100644 --- a/cocos2d/core/sprites/CCSprite.js +++ b/cocos2d/core/sprites/CCSprite.js @@ -314,25 +314,7 @@ cc.Sprite = cc.Node.extend(/** @lends cc.Sprite# */{ if (this._reorderChildDirty) { var _children = this._children; - // insertion sort - var len = _children.length, i, j, tmp; - for(i=1; i= 0){ - if(tmp._localZOrder < _children[j]._localZOrder){ - _children[j+1] = _children[j]; - }else if(tmp._localZOrder === _children[j]._localZOrder && tmp.arrivalOrder < _children[j].arrivalOrder){ - _children[j+1] = _children[j]; - }else{ - break; - } - j--; - } - _children[j+1] = tmp; - } + cc.Node.prototype.sortAllChildren.call(this); if (this._batchNode) { this._arrayMakeObjectsPerformSelector(_children, cc.Node._stateCallbackType.sortAllChildren); diff --git a/cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js b/cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js index dd2f33e712..b6b7fba0de 100644 --- a/cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js +++ b/cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js @@ -29,15 +29,37 @@ this._needDraw = true; this._quad = new cc.V3F_C4B_T2F_Quad(); + this._quadBufferView = new Uint32Array(this._quad.arrayBuffer); this._quadWebBuffer = cc._renderContext.createBuffer(); this._quadDirty = true; this._dirty = false; this._recursiveDirty = false; + + this._supportBatch = true; + if (!proto.batchShader) { + proto.batchShader = cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURECOLORALPHATEST_BATCHED); + } }; var proto = cc.Sprite.WebGLRenderCmd.prototype = Object.create(cc.Node.WebGLRenderCmd.prototype); proto.constructor = cc.Sprite.WebGLRenderCmd; + proto.vertexDataPerUnit = cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT; + proto.matrixByteSize = 4 * 4 * 4; //4 rows of 4 floats, 4 bytes each + proto.bytesPerUnit = proto.vertexDataPerUnit + proto.matrixByteSize * 4; + proto.indicesPerUnit = 6; + proto.batchShader = null; + + proto.getBatchInfo = function (info) { + info.texture = this._node._texture; + info.blendSrc = this._node._blendFunc.src; + info.blendDst = this._node._blendFunc.dst; + info.shader = this.batchShader; + info.vertexDataPerUnit = this.vertexDataPerUnit; + info.matrixByteSize = this.matrixByteSize; + info.bytesPerUnit = this.bytesPerUnit; + }; + proto.updateBlendFunc = function (blendFunc) {}; proto.setDirtyFlag = function(dirtyFlag){ @@ -425,10 +447,11 @@ var gl = ctx || cc._renderContext ; //cc.assert(!_t._batchNode, "If cc.Sprite is being rendered by cc.SpriteBatchNode, cc.Sprite#draw SHOULD NOT be called"); + var program = this._shaderProgram; if (locTexture) { if (locTexture._textureLoaded) { - this._shaderProgram.use(); - this._shaderProgram._setUniformForMVPMatrixWithMat4(this._stackMatrix); + program.use(); + program._setUniformForMVPMatrixWithMat4(this._stackMatrix); cc.glBlendFunc(node._blendFunc.src, node._blendFunc.dst); //optimize performance for javascript @@ -446,8 +469,8 @@ gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); } } else { - this._shaderProgram.use(); - this._shaderProgram._setUniformForMVPMatrixWithMat4(this._stackMatrix); + program.use(); + program._setUniformForMVPMatrixWithMat4(this._stackMatrix); cc.glBlendFunc(node._blendFunc.src, node._blendFunc.dst); cc.glBindTexture2D(null); diff --git a/cocos2d/core/utils/CCProfiler.js b/cocos2d/core/utils/CCProfiler.js index 9e1925b55d..bdbd7b9ae4 100644 --- a/cocos2d/core/utils/CCProfiler.js +++ b/cocos2d/core/utils/CCProfiler.js @@ -21,6 +21,10 @@ cc.profiler = (function () { _SPFLabel = new cc.LabelTTF("0.000", "Arial", fontSize); _drawsLabel = new cc.LabelTTF("0000", "Arial", fontSize); + _FPSLabel.setVertexZ(0.9999); + _SPFLabel.setVertexZ(0.9999); + _SPFLabel.setVertexZ(0.9999); + _drawsLabel.setPosition(_drawsLabel.width / 2 + locStatsPosition.x, _drawsLabel.height * 5 / 2 + locStatsPosition.y); _SPFLabel.setPosition(_SPFLabel.width / 2 + locStatsPosition.x, _SPFLabel.height * 3 / 2 + locStatsPosition.y); _FPSLabel.setPosition(_FPSLabel.width / 2 + locStatsPosition.x, _FPSLabel.height / 2 + locStatsPosition.y); diff --git a/cocos2d/node-grid/CCNodeGrid.js b/cocos2d/node-grid/CCNodeGrid.js index ae767e0ce6..684b3886b8 100644 --- a/cocos2d/node-grid/CCNodeGrid.js +++ b/cocos2d/node-grid/CCNodeGrid.js @@ -82,46 +82,6 @@ cc.NodeGrid = cc.Node.extend(/** @lends cc.NodeGrid# */{ this._target = target; }, - _transformForWebGL: function () { - //optimize performance for javascript - var t4x4 = this._transform4x4, topMat4 = cc.current_stack.top; - - // Convert 3x3 into 4x4 matrix - var trans = this.getNodeToParentTransform(); - var t4x4Mat = t4x4.mat; - t4x4Mat[0] = trans.a; - t4x4Mat[4] = trans.c; - t4x4Mat[12] = trans.tx; - t4x4Mat[1] = trans.b; - t4x4Mat[5] = trans.d; - t4x4Mat[13] = trans.ty; - - // Update Z vertex manually - //this._transform4x4.mat[14] = this._vertexZ; - t4x4Mat[14] = this._vertexZ; - - //optimize performance for Javascript - topMat4.multiply(t4x4) ; // = cc.kmGLMultMatrix(this._transform4x4); - - // XXX: Expensive calls. Camera should be integrated into the cached affine matrix - if (this._camera !== null && !(this.grid && this.grid.isActive())) { - var app = this._renderCmd._anchorPointInPoints, - apx = app.x, apy = app.y, - translate = (apx !== 0.0 || apy !== 0.0); - if (translate) { - if(!cc.SPRITEBATCHNODE_RENDER_SUBPIXEL) { - apx = 0 | apx; - apy = 0 | apy; - } - cc.kmGLTranslatef(apx, apy, 0); - this._camera.locate(); - cc.kmGLTranslatef(-apx, -apy, 0); - } else { - this._camera.locate(); - } - } - }, - _createRenderCmd: function(){ if (cc._renderType === cc.game.RENDER_TYPE_WEBGL) return new cc.NodeGrid.WebGLRenderCmd(this); diff --git a/cocos2d/shaders/CCGLProgram.js b/cocos2d/shaders/CCGLProgram.js index 1f55478576..737085d5ee 100644 --- a/cocos2d/shaders/CCGLProgram.js +++ b/cocos2d/shaders/CCGLProgram.js @@ -630,6 +630,10 @@ cc.GLProgram = cc.Class.extend(/** @lends cc.GLProgram# */{ this._glContext.uniformMatrix4fv(this._uniforms[cc.UNIFORM_PMATRIX], false, cc.projection_matrix_stack.top.mat); }, + _updateProjectionUniform: function(){ + this._glContext.uniformMatrix4fv(this._uniforms[cc.UNIFORM_PMATRIX], false, cc.projection_matrix_stack.top.mat); + }, + /** * returns the vertexShader error log * @return {String} diff --git a/cocos2d/shaders/CCShaderCache.js b/cocos2d/shaders/CCShaderCache.js index f78780ddb6..ab8061aa97 100644 --- a/cocos2d/shaders/CCShaderCache.js +++ b/cocos2d/shaders/CCShaderCache.js @@ -31,60 +31,66 @@ */ cc.shaderCache = /** @lends cc.shaderCache# */{ - /** - * @public - * @constant - * @type {Number} - */ - TYPE_POSITION_TEXTURECOLOR: 0, - /** - * @public - * @constant - * @type {Number} - */ - TYPE_POSITION_TEXTURECOLOR_ALPHATEST: 1, - /** - * @public - * @constant - * @type {Number} - */ - TYPE_POSITION_COLOR: 2, - /** - * @public - * @constant - * @type {Number} - */ - TYPE_POSITION_TEXTURE: 3, - /** - * @public - * @constant - * @type {Number} - */ - TYPE_POSITION_TEXTURE_UCOLOR: 4, - /** - * @public - * @constant - * @type {Number} - */ - TYPE_POSITION_TEXTURE_A8COLOR: 5, - /** - * @public - * @constant - * @type {Number} - */ - TYPE_POSITION_UCOLOR: 6, - /** - * @public - * @constant - * @type {Number} - */ - TYPE_POSITION_LENGTH_TEXTURECOLOR: 7, - /** - * @public - * @constant - * @type {Number} - */ - TYPE_MAX: 8, + /** + * @public + * @constant + * @type {Number} + */ + TYPE_POSITION_TEXTURECOLOR: 0, + /** + * @public + * @constant + * @type {Number} + */ + TYPE_POSITION_TEXTURECOLOR_ALPHATEST: 1, + /** + * @public + * @constant + * @type {Number} + */ + TYPE_POSITION_COLOR: 2, + /** + * @public + * @constant + * @type {Number} + */ + TYPE_POSITION_TEXTURE: 3, + /** + * @public + * @constant + * @type {Number} + */ + TYPE_POSITION_TEXTURE_UCOLOR: 4, + /** + * @public + * @constant + * @type {Number} + */ + TYPE_POSITION_TEXTURE_A8COLOR: 5, + /** + * @public + * @constant + * @type {Number} + */ + TYPE_POSITION_UCOLOR: 6, + /** + * @public + * @constant + * @type {Number} + */ + TYPE_POSITION_LENGTH_TEXTURECOLOR: 7, + /** + * @public + * @constant + * @type {Number} + */ + TYPE_POSITION_TEXTURECOLOR_ALPHATEST_BATCHED: 8, + /** + * @public + * @constant + * @type {Number} + */ + TYPE_MAX: 9, _programs: {}, @@ -97,7 +103,6 @@ cc.shaderCache = /** @lends cc.shaderCache# */{ switch (type) { case this.TYPE_POSITION_TEXTURECOLOR: program.initWithVertexShaderByteArray(cc.SHADER_POSITION_TEXTURE_COLOR_VERT, cc.SHADER_POSITION_TEXTURE_COLOR_FRAG); - program.addAttribute(cc.ATTRIBUTE_NAME_POSITION, cc.VERTEX_ATTRIB_POSITION); program.addAttribute(cc.ATTRIBUTE_NAME_COLOR, cc.VERTEX_ATTRIB_COLOR); program.addAttribute(cc.ATTRIBUTE_NAME_TEX_COORD, cc.VERTEX_ATTRIB_TEX_COORDS); @@ -105,31 +110,33 @@ cc.shaderCache = /** @lends cc.shaderCache# */{ case this.TYPE_POSITION_TEXTURECOLOR_ALPHATEST: program.initWithVertexShaderByteArray(cc.SHADER_POSITION_TEXTURE_COLOR_VERT, cc.SHADER_POSITION_TEXTURE_COLOR_ALPHATEST_FRAG); + program.addAttribute(cc.ATTRIBUTE_NAME_POSITION, cc.VERTEX_ATTRIB_POSITION); + program.addAttribute(cc.ATTRIBUTE_NAME_COLOR, cc.VERTEX_ATTRIB_COLOR); + program.addAttribute(cc.ATTRIBUTE_NAME_TEX_COORD, cc.VERTEX_ATTRIB_TEX_COORDS); + break; + case this.TYPE_POSITION_TEXTURECOLOR_ALPHATEST_BATCHED: + program.initWithVertexShaderByteArray(cc.SHADER_POSITION_TEXTURE_COLOR_VERT_BATCHED, cc.SHADER_POSITION_TEXTURE_COLOR_ALPHATEST_FRAG); program.addAttribute(cc.ATTRIBUTE_NAME_POSITION, cc.VERTEX_ATTRIB_POSITION); program.addAttribute(cc.ATTRIBUTE_NAME_COLOR, cc.VERTEX_ATTRIB_COLOR); program.addAttribute(cc.ATTRIBUTE_NAME_TEX_COORD, cc.VERTEX_ATTRIB_TEX_COORDS); break; case this.TYPE_POSITION_COLOR: program.initWithVertexShaderByteArray(cc.SHADER_POSITION_COLOR_VERT, cc.SHADER_POSITION_COLOR_FRAG); - program.addAttribute(cc.ATTRIBUTE_NAME_POSITION, cc.VERTEX_ATTRIB_POSITION); program.addAttribute(cc.ATTRIBUTE_NAME_COLOR, cc.VERTEX_ATTRIB_COLOR); break; case this.TYPE_POSITION_TEXTURE: program.initWithVertexShaderByteArray(cc.SHADER_POSITION_TEXTURE_VERT, cc.SHADER_POSITION_TEXTURE_FRAG); - program.addAttribute(cc.ATTRIBUTE_NAME_POSITION, cc.VERTEX_ATTRIB_POSITION); program.addAttribute(cc.ATTRIBUTE_NAME_TEX_COORD, cc.VERTEX_ATTRIB_TEX_COORDS); break; case this.TYPE_POSITION_TEXTURE_UCOLOR: program.initWithVertexShaderByteArray(cc.SHADER_POSITION_TEXTURE_UCOLOR_VERT, cc.SHADER_POSITION_TEXTURE_UCOLOR_FRAG); - program.addAttribute(cc.ATTRIBUTE_NAME_POSITION, cc.VERTEX_ATTRIB_POSITION); program.addAttribute(cc.ATTRIBUTE_NAME_TEX_COORD, cc.VERTEX_ATTRIB_TEX_COORDS); break; case this.TYPE_POSITION_TEXTURE_A8COLOR: program.initWithVertexShaderByteArray(cc.SHADER_POSITION_TEXTURE_A8COLOR_VERT, cc.SHADER_POSITION_TEXTURE_A8COLOR_FRAG); - program.addAttribute(cc.ATTRIBUTE_NAME_POSITION, cc.VERTEX_ATTRIB_POSITION); program.addAttribute(cc.ATTRIBUTE_NAME_COLOR, cc.VERTEX_ATTRIB_COLOR); program.addAttribute(cc.ATTRIBUTE_NAME_TEX_COORD, cc.VERTEX_ATTRIB_TEX_COORDS); @@ -140,7 +147,6 @@ cc.shaderCache = /** @lends cc.shaderCache# */{ break; case this.TYPE_POSITION_LENGTH_TEXTURECOLOR: program.initWithVertexShaderByteArray(cc.SHADER_POSITION_COLOR_LENGTH_TEXTURE_VERT, cc.SHADER_POSITION_COLOR_LENGTH_TEXTURE_FRAG); - program.addAttribute(cc.ATTRIBUTE_NAME_POSITION, cc.VERTEX_ATTRIB_POSITION); program.addAttribute(cc.ATTRIBUTE_NAME_TEX_COORD, cc.VERTEX_ATTRIB_TEX_COORDS); program.addAttribute(cc.ATTRIBUTE_NAME_COLOR, cc.VERTEX_ATTRIB_COLOR); @@ -172,6 +178,12 @@ cc.shaderCache = /** @lends cc.shaderCache# */{ this._programs[cc.SHADER_POSITION_TEXTURECOLORALPHATEST] = program; this._programs["ShaderPositionTextureColorAlphaTest"] = program; + // Position Texture Color alpha batched test + program = new cc.GLProgram(); + this._loadDefaultShader(program, this.TYPE_POSITION_TEXTURECOLOR_ALPHATEST_BATCHED); + this._programs[cc.SHADER_POSITION_TEXTURECOLORALPHATEST_BATCHED] = program; + this._programs["ShaderPositionTextureColorAlphaTestBatched"] = program; + // // Position, Color shader // diff --git a/cocos2d/shaders/CCShaders.js b/cocos2d/shaders/CCShaders.js index 437ce589aa..7403022575 100644 --- a/cocos2d/shaders/CCShaders.js +++ b/cocos2d/shaders/CCShaders.js @@ -260,6 +260,22 @@ cc.SHADER_POSITION_TEXTURE_COLOR_ALPHATEST_FRAG = + " gl_FragColor = texColor * v_fragmentColor; \n" + "}"; +//-----------------------Shader_PositionTextureColorVertBatchedTest_frag Shader Source---------------------------- +cc.SHADER_POSITION_TEXTURE_COLOR_VERT_BATCHED = + "attribute vec4 a_position; \n" + + "attribute vec2 a_texCoord; \n" + + "attribute vec4 a_color; \n" + + "attribute mat4 a_mvMatrix;" + + "varying lowp vec4 v_fragmentColor; \n" + + "varying mediump vec2 v_texCoord; \n" + + "void main() \n" + + "{ \n" + //+ " gl_Position = CC_MVPMatrix * a_position; \n" + + " gl_Position = (CC_PMatrix * a_mvMatrix) * a_position; \n" + + " v_fragmentColor = a_color; \n" + + " v_texCoord = a_texCoord; \n" + + "}"; + //-----------------------ShaderEx_SwitchMask_frag Shader Source---------------------------- /** * @constant diff --git a/extensions/ccui/base-classes/CCProtectedNodeWebGLRenderCmd.js b/extensions/ccui/base-classes/CCProtectedNodeWebGLRenderCmd.js index 0855611009..864ef2ee82 100644 --- a/extensions/ccui/base-classes/CCProtectedNodeWebGLRenderCmd.js +++ b/extensions/ccui/base-classes/CCProtectedNodeWebGLRenderCmd.js @@ -113,12 +113,12 @@ t4x4Mat[5] = trans.d; t4x4Mat[13] = trans.ty; - // Update Z vertex manually - t4x4Mat[14] = node._vertexZ; - //optimize performance for Javascript cc.kmMat4Multiply(stackMatrix, parentMatrix, t4x4); + // Update Z depth + t4x4Mat[14] = node._vertexZ; + // XXX: Expensive calls. Camera should be integrated into the cached affine matrix if (node._camera !== null && !(node.grid !== null && node.grid.isActive())) { var apx = this._anchorPointInPoints.x, apy = this._anchorPointInPoints.y; diff --git a/extensions/ccui/base-classes/UIScale9SpriteWebGLRenderCmd.js b/extensions/ccui/base-classes/UIScale9SpriteWebGLRenderCmd.js index a288c05841..d0dc5d0d49 100644 --- a/extensions/ccui/base-classes/UIScale9SpriteWebGLRenderCmd.js +++ b/extensions/ccui/base-classes/UIScale9SpriteWebGLRenderCmd.js @@ -76,6 +76,7 @@ proto.transform = function(parentCmd, recursive){ var node = this._node; + parentCmd = parentCmd || this.getParentRenderCmd(); cc.Node.WebGLRenderCmd.prototype.transform.call(this, parentCmd, recursive); if (node._positionsAreDirty) { node._updatePositions(); @@ -87,6 +88,7 @@ for(var j=0; j < protectChildLen; j++) { var pchild = locRenderers[j]; if(pchild) { + pchild._vertexZ = parentCmd._node._vertexZ; var tempCmd = pchild._renderCmd; tempCmd.transform(this, true); } diff --git a/extensions/ccui/uiwidgets/scroll-widget/UIScrollViewWebGLRenderCmd.js b/extensions/ccui/uiwidgets/scroll-widget/UIScrollViewWebGLRenderCmd.js index 43b7cf5237..ef1f2c8e41 100644 --- a/extensions/ccui/uiwidgets/scroll-widget/UIScrollViewWebGLRenderCmd.js +++ b/extensions/ccui/uiwidgets/scroll-widget/UIScrollViewWebGLRenderCmd.js @@ -42,5 +42,5 @@ continue; locCmds[i].rendering(context); } - } + }; })(); From b845a16efdcd7bb17dcb6345b3643913bc97e4e5 Mon Sep 17 00:00:00 2001 From: pandamicro Date: Mon, 11 Apr 2016 15:11:28 +0800 Subject: [PATCH 02/16] Bind attribute MVMAT and Fix matrix attribute set issue --- cocos2d/core/renderer/RendererWebGL.js | 8 ++++---- cocos2d/shaders/CCShaderCache.js | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/cocos2d/core/renderer/RendererWebGL.js b/cocos2d/core/renderer/RendererWebGL.js index 974d437f80..c347fd8510 100644 --- a/cocos2d/core/renderer/RendererWebGL.js +++ b/cocos2d/core/renderer/RendererWebGL.js @@ -369,9 +369,9 @@ return { var shader = _batchedInfo.shader; var count = _batchedCount; + var matrixBytes = this.matrixByteSize; var bytesPerRow = 16; //4 floats with 4 bytes each - var matrixData = this.matrixByteSize; - var totalVertexData = _batchedInfo.totalVertexData / 4; + var totalVertexBytes = _batchedInfo.totalVertexData; shader.use(); shader._updateProjectionUniform(); @@ -388,10 +388,10 @@ return { gl.vertexAttribPointer(2, 2, gl.FLOAT, false, 24, 16); //cc.VERTEX_ATTRIB_TEX_COORDS var i; - //enable matrix vertex attribs + //enable matrix vertex attribs row by row for (i = 0; i < 4; ++i) { gl.enableVertexAttribArray(cc.VERTEX_ATTRIB_MVMAT0 + i); - gl.vertexAttribPointer(cc.VERTEX_ATTRIB_MVMAT0 + i, 4, gl.FLOAT, false, bytesPerRow * 4, totalVertexData + bytesPerRow * i); //stride is one row + gl.vertexAttribPointer(cc.VERTEX_ATTRIB_MVMAT0 + i, 4, gl.FLOAT, false, bytesPerRow, totalVertexBytes + bytesPerRow * i); //stride is one row } gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, _batchElementBuffer); diff --git a/cocos2d/shaders/CCShaderCache.js b/cocos2d/shaders/CCShaderCache.js index ab8061aa97..0fb30350b5 100644 --- a/cocos2d/shaders/CCShaderCache.js +++ b/cocos2d/shaders/CCShaderCache.js @@ -119,6 +119,7 @@ cc.shaderCache = /** @lends cc.shaderCache# */{ program.addAttribute(cc.ATTRIBUTE_NAME_POSITION, cc.VERTEX_ATTRIB_POSITION); program.addAttribute(cc.ATTRIBUTE_NAME_COLOR, cc.VERTEX_ATTRIB_COLOR); program.addAttribute(cc.ATTRIBUTE_NAME_TEX_COORD, cc.VERTEX_ATTRIB_TEX_COORDS); + program.addAttribute(cc.ATTRIBUTE_NAME_MVMAT, cc.VERTEX_ATTRIB_MVMAT0); break; case this.TYPE_POSITION_COLOR: program.initWithVertexShaderByteArray(cc.SHADER_POSITION_COLOR_VERT, cc.SHADER_POSITION_COLOR_FRAG); From 66231d5a46f790d7222de02e2a02c6161f9bdbd0 Mon Sep 17 00:00:00 2001 From: pandamicro Date: Mon, 11 Apr 2016 22:07:12 +0800 Subject: [PATCH 03/16] Improve auto batch implementation --- cocos2d/core/renderer/RendererWebGL.js | 55 ++++--------------- .../core/sprites/CCSpriteWebGLRenderCmd.js | 52 ++++++++++++++++-- cocos2d/shaders/CCGLProgram.js | 2 +- 3 files changed, 59 insertions(+), 50 deletions(-) diff --git a/cocos2d/core/renderer/RendererWebGL.js b/cocos2d/core/renderer/RendererWebGL.js index c347fd8510..79bd035d12 100644 --- a/cocos2d/core/renderer/RendererWebGL.js +++ b/cocos2d/core/renderer/RendererWebGL.js @@ -274,7 +274,7 @@ return { break; } else { - _batchedInfo.totalVertexData += cmd.vertexDataPerUnit; + _batchedInfo.totalVertexData += cmd.vertexBytesPerUnit; _batchedInfo.totalBufferSize += cmd.bytesPerUnit; _batchedInfo.totalIndiceSize += cmd.indicesPerUnit; } @@ -303,39 +303,13 @@ return { gl.bindBuffer(gl.ARRAY_BUFFER, _batchBuffer); - var i, j; + var i; for (i = first; i < last; ++i) { cmd = renderCmds[i]; - var matrixData = cmd.matrixByteSize / 4; - var vertexDataPerUnit = cmd.vertexDataPerUnit / 4; - - var source = cmd._quadBufferView; - var len = source.length; - for (j = 0; j < len; ++j) { - uploadBuffer[vertexDataOffset + j] = source[j]; - } - - var matData = new Uint32Array(cmd._stackMatrix.mat.buffer); - - source = matData; - len = source.length; + cmd.batchVertexBuffer(uploadBuffer, vertexDataOffset, totalVertexData, matrixDataOffset); - var base = totalVertexData + matrixDataOffset; - var offset0 = base + matrixData * 0; - var offset1 = base + matrixData * 1; - var offset2 = base + matrixData * 2; - var offset3 = base + matrixData * 3; - - for (j = 0; j < len; ++j) { - var val = source[j]; - uploadBuffer[offset0 + j] = val; - uploadBuffer[offset1 + j] = val; - uploadBuffer[offset2 + j] = val; - uploadBuffer[offset3 + j] = val; - } - - vertexDataOffset += vertexDataPerUnit; - matrixDataOffset += matrixData * 4; + vertexDataOffset += cmd.vertexBytesPerUnit / 4; + matrixDataOffset += cmd.matrixDataPerUnit / 4; } gl.bufferSubData(gl.ARRAY_BUFFER, 0, uploadBuffer); @@ -345,18 +319,14 @@ return { var indices = new Uint16Array(_batchedInfo.totalIndiceSize); - var currentQuad = 0; - var indiceI = 0; + var currentVertex = 0; + var index = 0; for (i = first; i < last; ++i) { cmd = renderCmds[i]; - indices[indiceI] = currentQuad + 0; - indices[indiceI + 1] = currentQuad + 1; - indices[indiceI + 2] = currentQuad + 2; - indices[indiceI + 3] = currentQuad + 3; - indices[indiceI + 4] = currentQuad + 2; - indices[indiceI + 5] = currentQuad + 1; - currentQuad += 4; - indiceI += cmd.indicesPerUnit; + cmd.batchIndexBuffer(indices, index, currentVertex); + + currentVertex += cmd.verticesPerUnit; + index += cmd.indicesPerUnit; } gl.bufferSubData(gl.ELEMENT_ARRAY_BUFFER, 0, indices); @@ -369,7 +339,6 @@ return { var shader = _batchedInfo.shader; var count = _batchedCount; - var matrixBytes = this.matrixByteSize; var bytesPerRow = 16; //4 floats with 4 bytes each var totalVertexBytes = _batchedInfo.totalVertexData; @@ -391,7 +360,7 @@ return { //enable matrix vertex attribs row by row for (i = 0; i < 4; ++i) { gl.enableVertexAttribArray(cc.VERTEX_ATTRIB_MVMAT0 + i); - gl.vertexAttribPointer(cc.VERTEX_ATTRIB_MVMAT0 + i, 4, gl.FLOAT, false, bytesPerRow, totalVertexBytes + bytesPerRow * i); //stride is one row + gl.vertexAttribPointer(cc.VERTEX_ATTRIB_MVMAT0 + i, 4, gl.FLOAT, false, bytesPerRow * 4, totalVertexBytes + bytesPerRow * i); //stride is one row } gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, _batchElementBuffer); diff --git a/cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js b/cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js index b6b7fba0de..f0d4e70203 100644 --- a/cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js +++ b/cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js @@ -24,6 +24,8 @@ //Sprite's WebGL render command (function() { + var matrixByteSize = 4 * 4 * 4; //4 rows of 4 floats, 4 bytes each + cc.Sprite.WebGLRenderCmd = function (renderable) { cc.Node.WebGLRenderCmd.call(this, renderable); this._needDraw = true; @@ -44,10 +46,13 @@ var proto = cc.Sprite.WebGLRenderCmd.prototype = Object.create(cc.Node.WebGLRenderCmd.prototype); proto.constructor = cc.Sprite.WebGLRenderCmd; - proto.vertexDataPerUnit = cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT; - proto.matrixByteSize = 4 * 4 * 4; //4 rows of 4 floats, 4 bytes each - proto.bytesPerUnit = proto.vertexDataPerUnit + proto.matrixByteSize * 4; + // The following static properties must be provided for a auto batchable command + proto.vertexBytesPerUnit = cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT; + proto.matrixBytesPerUnit = matrixByteSize * 4; + proto.bytesPerUnit = proto.vertexBytesPerUnit + proto.matrixBytesPerUnit; proto.indicesPerUnit = 6; + proto.verticesPerUnit = 4; + proto.batchShader = null; proto.getBatchInfo = function (info) { @@ -55,9 +60,6 @@ info.blendSrc = this._node._blendFunc.src; info.blendDst = this._node._blendFunc.dst; info.shader = this.batchShader; - info.vertexDataPerUnit = this.vertexDataPerUnit; - info.matrixByteSize = this.matrixByteSize; - info.bytesPerUnit = this.bytesPerUnit; }; proto.updateBlendFunc = function (blendFunc) {}; @@ -516,4 +518,42 @@ } // CC_SPRITE_DEBUG_DRAW cc.current_stack.top = cc.current_stack.stack.pop(); }; + + proto.batchVertexBuffer = function (buffer, vertexDataOffset, totalVertexData, matrixDataOffset) { + var matrixData = matrixByteSize / 4; + + var source = this._quadBufferView; + var len = source.length; + for (j = 0; j < len; ++j) { + buffer[vertexDataOffset + j] = source[j]; + } + + var matData = new Uint32Array(this._stackMatrix.mat.buffer); + + source = matData; + len = source.length; + + var base = totalVertexData + matrixDataOffset; + var offset0 = base + matrixData * 0; + var offset1 = base + matrixData * 1; + var offset2 = base + matrixData * 2; + var offset3 = base + matrixData * 3; + + for (j = 0; j < len; ++j) { + var val = source[j]; + buffer[offset0 + j] = val; + buffer[offset1 + j] = val; + buffer[offset2 + j] = val; + buffer[offset3 + j] = val; + } + }; + + proto.batchIndexBuffer = function (indices, index, vertexIndex) { + indices[index] = vertexIndex + 0; + indices[index + 1] = vertexIndex + 1; + indices[index + 2] = vertexIndex + 2; + indices[index + 3] = vertexIndex + 1; + indices[index + 4] = vertexIndex + 2; + indices[index + 5] = vertexIndex + 3; + }; })(); \ No newline at end of file diff --git a/cocos2d/shaders/CCGLProgram.js b/cocos2d/shaders/CCGLProgram.js index 737085d5ee..6213f58cc5 100644 --- a/cocos2d/shaders/CCGLProgram.js +++ b/cocos2d/shaders/CCGLProgram.js @@ -631,7 +631,7 @@ cc.GLProgram = cc.Class.extend(/** @lends cc.GLProgram# */{ }, _updateProjectionUniform: function(){ - this._glContext.uniformMatrix4fv(this._uniforms[cc.UNIFORM_PMATRIX], false, cc.projection_matrix_stack.top.mat); + this._glContext.uniformMatrix4fv(this._uniforms[cc.UNIFORM_PMATRIX], false, cc.projection_matrix_stack.top.mat); }, /** From cf37f563dec6e254a471e7f7ac13e802e421b61f Mon Sep 17 00:00:00 2001 From: pandamicro Date: Mon, 11 Apr 2016 22:15:03 +0800 Subject: [PATCH 04/16] Improve details and add comments & Add reference to @Heishe --- cocos2d/core/CCDirector.js | 1 + .../core/base-nodes/CCNodeCanvasRenderCmd.js | 4 +- cocos2d/core/renderer/RendererWebGL.js | 81 ++++++++++++------- .../core/sprites/CCSpriteWebGLRenderCmd.js | 49 ++++++----- cocos2d/shaders/CCShaders.js | 2 + 5 files changed, 84 insertions(+), 53 deletions(-) diff --git a/cocos2d/core/CCDirector.js b/cocos2d/core/CCDirector.js index 93c767678f..e1e043800e 100644 --- a/cocos2d/core/CCDirector.js +++ b/cocos2d/core/CCDirector.js @@ -233,6 +233,7 @@ cc.Director = cc.Class.extend(/** @lends cc.Director# */{ if (this._runningScene) { if (renderer.childrenOrderDirty === true) { cc.renderer.clearRenderCommands(); + cc.renderer.assignedZ = 0; this._runningScene._renderCmd._curLevel = 0; //level start from 0; this._runningScene.visit(); renderer.resetFlag(); diff --git a/cocos2d/core/base-nodes/CCNodeCanvasRenderCmd.js b/cocos2d/core/base-nodes/CCNodeCanvasRenderCmd.js index 23f63a60e7..a9e6e73a2a 100644 --- a/cocos2d/core/base-nodes/CCNodeCanvasRenderCmd.js +++ b/cocos2d/core/base-nodes/CCNodeCanvasRenderCmd.js @@ -57,8 +57,8 @@ cc.Node.RenderCmd = function(renderable){ this._curLevel = -1; - this._minZ = 0; - this._maxZ = 0; + // this._minZ = 0; + // this._maxZ = 0; }; cc.Node.RenderCmd.prototype = { diff --git a/cocos2d/core/renderer/RendererWebGL.js b/cocos2d/core/renderer/RendererWebGL.js index 79bd035d12..ac47feb7b8 100644 --- a/cocos2d/core/renderer/RendererWebGL.js +++ b/cocos2d/core/renderer/RendererWebGL.js @@ -27,9 +27,7 @@ cc.rendererWebGL = (function () { function objEqual (a, b) { if (!a || !b) return false; - var keys = Object.keys(a); - for (var i = 0; i < keys.length; ++i) { - var key = keys[i]; + for (var key in a) { if (!b[key] || a[key] !== b[key]) { return false; } @@ -38,13 +36,30 @@ function objEqual (a, b) { } function removeByLastSwap (array, i) { - if (array.length > 0) + if (array.length > 0) { array[i] = array[array.length - 1]; - array.length--; + array.length--; + } } // Internal variables -var _batchedInfo = {}, + // Batching general informations +var _batchedInfo = { + // Total vertex array buffer size, including vertex data and matrix data, in bytes + totalBufferSize: 0, + // All vertex data size for the current batch, in bytes + totalVertexData: 0, + // Index array size + totalIndexSize: 0, + // The batched texture, all batching element should have the same texture + texture: null, + // The batched blend source, all batching element should have the same blend source + blendSrc: null, + // The batched blend destination, all batching element should have the same blend destination + blendDst: null, + // The batched shader, all batching element should have the same shader + shader: null + }, _batchedCount, _batchBuffer, _batchElementBuffer, @@ -70,20 +85,6 @@ return { return renderableObject._createRenderCmd(); }, - /** - * drawing all renderer command to context (default is cc._renderContext) - * @param {WebGLRenderingContext} [ctx=cc._renderContext] - */ - // rendering: function (ctx) { - // var locCmds = this._renderCmds, - // i, - // len; - // var context = ctx || cc._renderContext; - // for (i = 0, len = locCmds.length; i < len; i++) { - // locCmds[i].rendering(context); - // } - // }, - _turnToCacheMode: function (renderTextureID) { this._isCacheToBufferOn = true; renderTextureID = renderTextureID || 0; @@ -184,6 +185,8 @@ return { } }, + // Auto batch implementation inspired from @Heishe 's PR + // Ref: https://github.com/cocos2d/cocos2d-html5/pull/3248 createBatchBuffer: function (bufferSize, indiceSize) { var arrayBuffer = gl.createBuffer(); var elementBuffer = gl.createBuffer(); @@ -193,6 +196,8 @@ return { return {arrayBuffer: arrayBuffer, elementBuffer: elementBuffer, bufferSize: bufferSize, indiceSize: indiceSize}; }, + // Auto batch implementation inspired from @Heishe 's PR + // Ref: https://github.com/cocos2d/cocos2d-html5/pull/3248 initBatchBuffers: function (arrayBuffer, elementBuffer, bufferSize, indiceSize) { gl.bindBuffer(gl.ARRAY_BUFFER, arrayBuffer); gl.bufferData(gl.ARRAY_BUFFER, bufferSize, gl.DYNAMIC_DRAW); @@ -201,6 +206,9 @@ return { gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indiceSize, gl.DYNAMIC_DRAW); }, + // Auto batch implementation inspired from @Heishe 's PR + // Ref: https://github.com/cocos2d/cocos2d-html5/pull/3248 + // Returns an object with {arrayBuffer, elementBuffer, size}, // where size denotes how many unit fit in the buffer (no need for bufferData if it's already big enough, bufferSubData enough) getBatchBuffer: function(bufferSize, indiceSize) @@ -242,12 +250,16 @@ return { } }, + // Auto batch implementation inspired from @Heishe 's PR + // Ref: https://github.com/cocos2d/cocos2d-html5/pull/3248 storeBatchBuffer: function(buffer) { var pool = _batchBufferPool; pool.push(buffer); }, - + // Auto batch implementation inspired from @Heishe 's PR + // Ref: https://github.com/cocos2d/cocos2d-html5/pull/3248 + // Forward search commands that can be batched together _forwardBatch: function (first) { var renderCmds = this._renderCmds, cmd = renderCmds[first]; @@ -259,8 +271,9 @@ return { cmd.getBatchInfo(_batchedInfo); _batchedInfo.totalVertexData = 0; _batchedInfo.totalBufferSize = 0; - _batchedInfo.totalIndiceSize = 0; + _batchedInfo.totalIndexSize = 0; + // Forward search and collect batch informations for (last = first; last < renderCmds.length; ++last) { cmd = renderCmds[last]; if (cmd._supportBatch) { @@ -276,11 +289,12 @@ return { else { _batchedInfo.totalVertexData += cmd.vertexBytesPerUnit; _batchedInfo.totalBufferSize += cmd.bytesPerUnit; - _batchedInfo.totalIndiceSize += cmd.indicesPerUnit; + _batchedInfo.totalIndexSize += cmd.indicesPerUnit; } } var count = last - first; + // Can't batch, fall back to original render command if (count <= 1) { return count; } @@ -288,7 +302,7 @@ return { _batchedCount = count; var bufferSize = _batchedInfo.totalBufferSize; - var indiceSize = _batchedInfo.totalIndiceSize * 2; // *2 because we use shorts for indices + var indiceSize = _batchedInfo.totalIndexSize * 2; // *2 because we use shorts for indices _pooledBuffer = this.getBatchBuffer(bufferSize, indiceSize); _batchBuffer = _pooledBuffer.arrayBuffer; _batchElementBuffer = _pooledBuffer.elementBuffer; @@ -301,30 +315,34 @@ return { var uploadBuffer = new Uint32Array(totalBufferSize); + // Bind vertex data buffer gl.bindBuffer(gl.ARRAY_BUFFER, _batchBuffer); + // Fill in vertex data command by command var i; for (i = first; i < last; ++i) { cmd = renderCmds[i]; cmd.batchVertexBuffer(uploadBuffer, vertexDataOffset, totalVertexData, matrixDataOffset); vertexDataOffset += cmd.vertexBytesPerUnit / 4; - matrixDataOffset += cmd.matrixDataPerUnit / 4; + matrixDataOffset += cmd.matrixBytesPerUnit / 4; } + // Submit vertex data in one bufferSubData call gl.bufferSubData(gl.ARRAY_BUFFER, 0, uploadBuffer); - //create element buffer + // Bind element buffer gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, _batchElementBuffer); - var indices = new Uint16Array(_batchedInfo.totalIndiceSize); + var indices = new Uint16Array(_batchedInfo.totalIndexSize); + // Fill in element buffer command by command var currentVertex = 0; var index = 0; for (i = first; i < last; ++i) { cmd = renderCmds[i]; cmd.batchIndexBuffer(indices, index, currentVertex); - + currentVertex += cmd.verticesPerUnit; index += cmd.indicesPerUnit; } @@ -333,6 +351,9 @@ return { return count; }, + // Auto batch implementation inspired from @Heishe 's PR + // Ref: https://github.com/cocos2d/cocos2d-html5/pull/3248 + // Batch rendering using result collected in `_forwardBatch` _batchRendering: function () { // var node = this._node; var texture = _batchedInfo.texture; @@ -357,14 +378,14 @@ return { gl.vertexAttribPointer(2, 2, gl.FLOAT, false, 24, 16); //cc.VERTEX_ATTRIB_TEX_COORDS var i; - //enable matrix vertex attribs row by row + // Enable matrix vertex attribs row by row (vec4 * 4) for (i = 0; i < 4; ++i) { gl.enableVertexAttribArray(cc.VERTEX_ATTRIB_MVMAT0 + i); gl.vertexAttribPointer(cc.VERTEX_ATTRIB_MVMAT0 + i, 4, gl.FLOAT, false, bytesPerRow * 4, totalVertexBytes + bytesPerRow * i); //stride is one row } gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, _batchElementBuffer); - gl.drawElements(gl.TRIANGLES, count * 6, gl.UNSIGNED_SHORT, 0); + gl.drawElements(gl.TRIANGLES, _batchedInfo.totalIndexSize, gl.UNSIGNED_SHORT, 0); for (i = 0; i < 4; ++i) { gl.disableVertexAttribArray(cc.VERTEX_ATTRIB_MVMAT0 + i); diff --git a/cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js b/cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js index f0d4e70203..e47eda611f 100644 --- a/cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js +++ b/cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js @@ -520,35 +520,42 @@ }; proto.batchVertexBuffer = function (buffer, vertexDataOffset, totalVertexData, matrixDataOffset) { - var matrixData = matrixByteSize / 4; - - var source = this._quadBufferView; - var len = source.length; - for (j = 0; j < len; ++j) { - buffer[vertexDataOffset + j] = source[j]; + // buffer is a Uint32 view typed array, not necessarily the same view with render command vertex data, + // but it's ok, it's just for copy data easier + + // Fill in vertex data with quad information (4 vertices for sprite) + var vertexData = this._quadBufferView; + var i, len = vertexData.length; + for (i = 0; i < len; ++i) { + buffer[vertexDataOffset + i] = vertexData[i]; } - var matData = new Uint32Array(this._stackMatrix.mat.buffer); - - source = matData; - len = source.length; + // Fill in matrix data, matrix data is also wrapped into a Uint32 view, + // you may see strange big values, but it's also ok. + var matrixData = new Uint32Array(this._stackMatrix.mat.buffer); + len = matrixData.length; + // We need four matrix data into the buffer, one for each vertex. + // Otherwise the shader won't work var base = totalVertexData + matrixDataOffset; - var offset0 = base + matrixData * 0; - var offset1 = base + matrixData * 1; - var offset2 = base + matrixData * 2; - var offset3 = base + matrixData * 3; - - for (j = 0; j < len; ++j) { - var val = source[j]; - buffer[offset0 + j] = val; - buffer[offset1 + j] = val; - buffer[offset2 + j] = val; - buffer[offset3 + j] = val; + var matrixDataSize = matrixByteSize / 4; + var offset0 = base; + var offset1 = offset0 + matrixDataSize; + var offset2 = offset1 + matrixDataSize; + var offset3 = offset2 + matrixDataSize; + + for (i = 0; i < len; ++i) { + var val = matrixData[i]; + buffer[offset0 + i] = val; + buffer[offset1 + i] = val; + buffer[offset2 + i] = val; + buffer[offset3 + i] = val; } }; proto.batchIndexBuffer = function (indices, index, vertexIndex) { + // Fill in index buffer, we split quad into two triangles + // because only triangles can be batched indices[index] = vertexIndex + 0; indices[index + 1] = vertexIndex + 1; indices[index + 2] = vertexIndex + 2; diff --git a/cocos2d/shaders/CCShaders.js b/cocos2d/shaders/CCShaders.js index 7403022575..0668304525 100644 --- a/cocos2d/shaders/CCShaders.js +++ b/cocos2d/shaders/CCShaders.js @@ -261,6 +261,8 @@ cc.SHADER_POSITION_TEXTURE_COLOR_ALPHATEST_FRAG = + "}"; //-----------------------Shader_PositionTextureColorVertBatchedTest_frag Shader Source---------------------------- +// Auto batch implementation inspired from @Heishe 's PR +// Ref: https://github.com/cocos2d/cocos2d-html5/pull/3248 cc.SHADER_POSITION_TEXTURE_COLOR_VERT_BATCHED = "attribute vec4 a_position; \n" + "attribute vec2 a_texCoord; \n" From f8f873e64aa2ffd666780778341423310c426efa Mon Sep 17 00:00:00 2001 From: pandamicro Date: Tue, 12 Apr 2016 18:52:23 +0800 Subject: [PATCH 05/16] Small performance improvement for auto batching --- .../core/labelttf/CCLabelTTFWebGLRenderCmd.js | 4 +- cocos2d/core/renderer/RendererWebGL.js | 54 ++++++++++++------- .../core/sprites/CCSpriteWebGLRenderCmd.js | 2 +- 3 files changed, 39 insertions(+), 21 deletions(-) diff --git a/cocos2d/core/labelttf/CCLabelTTFWebGLRenderCmd.js b/cocos2d/core/labelttf/CCLabelTTFWebGLRenderCmd.js index 5e08a3e213..9d6de1e586 100644 --- a/cocos2d/core/labelttf/CCLabelTTFWebGLRenderCmd.js +++ b/cocos2d/core/labelttf/CCLabelTTFWebGLRenderCmd.js @@ -30,10 +30,12 @@ this.setShaderProgram(cc.shaderCache.programForKey(cc.LabelTTF._SHADER_PROGRAM)); }; var proto = cc.LabelTTF.WebGLRenderCmd.prototype = Object.create(cc.Sprite.WebGLRenderCmd.prototype); + proto._supportBatch = false; + cc.inject(cc.LabelTTF.CacheRenderCmd.prototype, proto); proto.constructor = cc.LabelTTF.WebGLRenderCmd; proto._updateColor = function () { this._updateTexture(); cc.Sprite.WebGLRenderCmd.prototype._updateColor.call(this); - } + }; })(); \ No newline at end of file diff --git a/cocos2d/core/renderer/RendererWebGL.js b/cocos2d/core/renderer/RendererWebGL.js index ac47feb7b8..469d94d7e8 100644 --- a/cocos2d/core/renderer/RendererWebGL.js +++ b/cocos2d/core/renderer/RendererWebGL.js @@ -60,6 +60,13 @@ var _batchedInfo = { // The batched shader, all batching element should have the same shader shader: null }, + // to compare with the batched info + _currentInfo = { + texture: null, + blendSrc: null, + blendDst: null, + shader: null + }, _batchedCount, _batchBuffer, _batchElementBuffer, @@ -262,28 +269,32 @@ return { // Forward search commands that can be batched together _forwardBatch: function (first) { var renderCmds = this._renderCmds, - cmd = renderCmds[first]; - if (!cmd || !cmd._supportBatch) return 0; + cmd = renderCmds[first], + last = first + 1, length = renderCmds.length; - var info = {}, last; + if (!cmd || !cmd._supportBatch) + return 0; // Initialize batched info cmd.getBatchInfo(_batchedInfo); - _batchedInfo.totalVertexData = 0; - _batchedInfo.totalBufferSize = 0; - _batchedInfo.totalIndexSize = 0; + _batchedInfo.totalVertexData = cmd.vertexBytesPerUnit; + _batchedInfo.totalBufferSize = cmd.bytesPerUnit; + _batchedInfo.totalIndexSize = cmd.indicesPerUnit; // Forward search and collect batch informations - for (last = first; last < renderCmds.length; ++last) { - cmd = renderCmds[last]; + cmd = renderCmds[last]; + while (cmd) { if (cmd._supportBatch) { - cmd.getBatchInfo(info); + cmd.getBatchInfo(_currentInfo); } else { break; } // Batch info don't match, break batching - if (!objEqual(info, _batchedInfo)) { + if (_currentInfo.texture !== _batchedInfo.texture || + _currentInfo.blendSrc !== _batchedInfo.blendSrc || + _currentInfo.blendDst !== _batchedInfo.blendDst || + _currentInfo.shader !== _batchedInfo.shader) { break; } else { @@ -291,6 +302,8 @@ return { _batchedInfo.totalBufferSize += cmd.bytesPerUnit; _batchedInfo.totalIndexSize += cmd.indicesPerUnit; } + ++last; + cmd = renderCmds[last]; } var count = last - first; @@ -402,22 +415,25 @@ return { */ rendering: function (ctx) { var locCmds = this._renderCmds, - i, len, cmd, + i, len, cmd, next, batchCount, context = ctx || cc._renderContext; for (i = 0, len = locCmds.length; i < len; i++) { cmd = locCmds[i]; + next = locCmds[i+1]; // Batching or direct rendering - var batchCount = this._forwardBatch(i); - if (batchCount > 1) { - this._batchRendering(); - // i will increase by 1 each loop - i += batchCount - 1; - } - else { - cmd.rendering(context); + if (cmd._supportBatch && next && next._supportBatch) { + batchCount = this._forwardBatch(i); + if (batchCount > 1) { + this._batchRendering(); + // i will increase by 1 each loop + i += batchCount - 1; + continue; + } } + + cmd.rendering(context); } } }; diff --git a/cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js b/cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js index e47eda611f..5f6cf9619a 100644 --- a/cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js +++ b/cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js @@ -37,7 +37,6 @@ this._dirty = false; this._recursiveDirty = false; - this._supportBatch = true; if (!proto.batchShader) { proto.batchShader = cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURECOLORALPHATEST_BATCHED); } @@ -52,6 +51,7 @@ proto.bytesPerUnit = proto.vertexBytesPerUnit + proto.matrixBytesPerUnit; proto.indicesPerUnit = 6; proto.verticesPerUnit = 4; + proto._supportBatch = true; proto.batchShader = null; From d6ec37dc717582c1bfe5ab49744d574741ffe039 Mon Sep 17 00:00:00 2001 From: pandamicro Date: Wed, 13 Apr 2016 22:18:31 +0800 Subject: [PATCH 06/16] Refactor auto batching implementation (support buffer cache) --- cocos2d/core/renderer/RendererWebGL.js | 289 ++++++++++++------ .../core/sprites/CCSpriteWebGLRenderCmd.js | 3 + .../UIScale9SpriteWebGLRenderCmd.js | 53 +++- 3 files changed, 252 insertions(+), 93 deletions(-) diff --git a/cocos2d/core/renderer/RendererWebGL.js b/cocos2d/core/renderer/RendererWebGL.js index 469d94d7e8..3beac80eaf 100644 --- a/cocos2d/core/renderer/RendererWebGL.js +++ b/cocos2d/core/renderer/RendererWebGL.js @@ -24,17 +24,6 @@ cc.rendererWebGL = (function () { -function objEqual (a, b) { - if (!a || !b) return false; - - for (var key in a) { - if (!b[key] || a[key] !== b[key]) { - return false; - } - } - return true; -} - function removeByLastSwap (array, i) { if (array.length > 0) { array[i] = array[array.length - 1]; @@ -45,12 +34,6 @@ function removeByLastSwap (array, i) { // Internal variables // Batching general informations var _batchedInfo = { - // Total vertex array buffer size, including vertex data and matrix data, in bytes - totalBufferSize: 0, - // All vertex data size for the current batch, in bytes - totalVertexData: 0, - // Index array size - totalIndexSize: 0, // The batched texture, all batching element should have the same texture texture: null, // The batched blend source, all batching element should have the same blend source @@ -67,11 +50,46 @@ var _batchedInfo = { blendDst: null, shader: null }, - _batchedCount, - _batchBuffer, - _batchElementBuffer, + // The current virtual buffer + _currentBuffer = null, _batchBufferPool = [], - _pooledBuffer; + _virtualBuffers = [], + _needUpdateBuffer = true; + +function createVirtualBuffer (buffer, vertexOffset, matrixOffset, indexOffset, totalBufferSize, totalIndexSize, count) { + var data = new Uint32Array(totalBufferSize / 4); + var vBuf = { + // The object contains real WebGL buffers, it's created or retrieved via getBatchBuffer + buffer: buffer, + // The vertex data array (Uint32Array) + dataArray: data, + // The start offset in the vertex buffer, in bytes + vertexOffset: vertexOffset, + // The start offset of matrix data in the vertex buffer, in bytes + matrixOffset: matrixOffset, + // The start offset in the index buffer + indexOffset: indexOffset, + // Total vertex array buffer size, including vertex data and matrix data, in bytes + totalBufferSize: totalBufferSize, + // Index array size + totalIndexSize: totalIndexSize, + // Render command count + count: count + }; + + _virtualBuffers.push(vBuf); + return vBuf; +} + +function clearVirtualBuffers () { + for (var i = _virtualBuffers.length-1; i >= 0; --i) { + var vbuffer = _virtualBuffers[i]; + _batchBufferPool.push(vbuffer.buffer); + vbuffer.buffer = null; + vbuffer.dataArray = null; + } + _virtualBuffers.length = 0; +} return { childrenOrderDirty: true, @@ -127,6 +145,10 @@ return { //reset renderer's flag resetFlag: function () { + // Straight forward buffer update logic, order dirty then update + if (this.childrenOrderDirty) { + _needUpdateBuffer = true; + } this.childrenOrderDirty = false; this._transformNodePool.length = 0; }, @@ -137,8 +159,32 @@ return { //sort the pool locPool.sort(this._sortNodeByLevelAsc); //transform node - for (var i = 0, len = locPool.length; i < len; i++) { - locPool[i].updateStatus(); + var i, len, cmd, currVBuffer, totalVertexData; + for (i = 0, len = locPool.length; i < len; i++) { + cmd = locPool[i]; + cmd.updateStatus(); + + if (cmd._vBuffer && cmd._vBuffer.buffer) { + if (currVBuffer !== cmd._vBuffer) { + // Send previous buffer to WebGLBuffer + if (currVBuffer) { + gl.bufferSubData(gl.ARRAY_BUFFER, 0, currVBuffer.dataArray); + } + // Bind buffer + currVBuffer = cmd._vBuffer; + totalVertexData = currVBuffer.matrixOffset / 4; + gl.bindBuffer(gl.ARRAY_BUFFER, currVBuffer.buffer.arrayBuffer); + } + cmd.batchVertexBuffer(currVBuffer.dataArray, cmd._vertexOffset, totalVertexData, cmd._matrixOffset); + } + // Ugly work around for Node type like Scale9Sprite + else if (cmd._customUpdateBuffer) { + currVBuffer = cmd._customUpdateBuffer(currVBuffer); + } + } + // Send last buffer to WebGLBuffer + if (currVBuffer) { + gl.bufferSubData(gl.ARRAY_BUFFER, 0, currVBuffer.dataArray); } locPool.length = 0; }, @@ -194,23 +240,23 @@ return { // Auto batch implementation inspired from @Heishe 's PR // Ref: https://github.com/cocos2d/cocos2d-html5/pull/3248 - createBatchBuffer: function (bufferSize, indiceSize) { + createBatchBuffer: function (bufferSize, indexSize) { var arrayBuffer = gl.createBuffer(); var elementBuffer = gl.createBuffer(); - this.initBatchBuffers(arrayBuffer, elementBuffer, bufferSize, indiceSize); + this.initBatchBuffers(arrayBuffer, elementBuffer, bufferSize, indexSize); - return {arrayBuffer: arrayBuffer, elementBuffer: elementBuffer, bufferSize: bufferSize, indiceSize: indiceSize}; + return {arrayBuffer: arrayBuffer, elementBuffer: elementBuffer, bufferSize: bufferSize, indexSize: indexSize}; }, // Auto batch implementation inspired from @Heishe 's PR // Ref: https://github.com/cocos2d/cocos2d-html5/pull/3248 - initBatchBuffers: function (arrayBuffer, elementBuffer, bufferSize, indiceSize) { + initBatchBuffers: function (arrayBuffer, elementBuffer, bufferSize, indexSize) { gl.bindBuffer(gl.ARRAY_BUFFER, arrayBuffer); gl.bufferData(gl.ARRAY_BUFFER, bufferSize, gl.DYNAMIC_DRAW); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elementBuffer); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indiceSize, gl.DYNAMIC_DRAW); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indexSize, gl.DYNAMIC_DRAW); }, // Auto batch implementation inspired from @Heishe 's PR @@ -218,50 +264,81 @@ return { // Returns an object with {arrayBuffer, elementBuffer, size}, // where size denotes how many unit fit in the buffer (no need for bufferData if it's already big enough, bufferSubData enough) - getBatchBuffer: function(bufferSize, indiceSize) + getBatchBuffer: function(bufferSize, indexSize) { - var pool = _batchBufferPool; - - if (pool.length <= 0) { - return this.createBatchBuffer(bufferSize, indiceSize); + if (_batchBufferPool.length <= 0) { + return this.createBatchBuffer(bufferSize, indexSize); } - else { - var minBuf = null; //we also track the smallest found buffer because that one will be re-initialized and returned if no fitting buffer can be found - var minSize = Number.MAX_VALUE; - var minBufIndex = -1; - for (var i = pool.length - 1; i >= 0; --i) { - var buf = pool[i]; - if (buf.bufferSize >= bufferSize && buf.indiceSize >= indiceSize) { - removeByLastSwap(pool, i); - this.initBatchBuffers(buf.arrayBuffer, buf.elementBuffer, bufferSize, indiceSize); - buf.bufferSize = bufferSize; - buf.indiceSize = indiceSize; - return buf; - } - if (buf.bufferSize < minSize) - { - minSize = buf.bufferSize; - minBuf = buf; - minBufIndex = i; - } + var minBuf = null; //we also track the smallest found buffer because that one will be re-initialized and returned if no fitting buffer can be found + var minSize = Number.MAX_VALUE; + var minBufIndex = -1; + for (var i = _batchBufferPool.length - 1; i >= 0; --i) { + var buf = _batchBufferPool[i]; + // Find available buffer with suitable size + if (buf.bufferSize >= bufferSize && buf.indexSize >= indexSize) { + removeByLastSwap(_batchBufferPool, i); + this.initBatchBuffers(buf.arrayBuffer, buf.elementBuffer, bufferSize, indexSize); + buf.bufferSize = bufferSize; + buf.indexSize = indexSize; + return buf; } - // we only get here if no properly sized buffer was found - // in that case, take smallest buffer in pool, resize it and return it - removeByLastSwap(pool, minBufIndex); - this.initBatchBuffers(minBuf.arrayBuffer, minBuf.elementBuffer, bufferSize, indiceSize); - minBuf.bufferSize = bufferSize; - minBuf.indiceSize = indiceSize; - return minBuf; + if (buf.bufferSize < minSize) + { + minSize = buf.bufferSize; + minBuf = buf; + minBufIndex = i; + } } + + // we only get here if no properly sized buffer was found + // in that case, take smallest buffer in pool, resize it and return it + removeByLastSwap(_batchBufferPool, minBufIndex); + this.initBatchBuffers(minBuf.arrayBuffer, minBuf.elementBuffer, bufferSize, indexSize); + minBuf.bufferSize = bufferSize; + minBuf.indexSize = indexSize; + return minBuf; }, - // Auto batch implementation inspired from @Heishe 's PR - // Ref: https://github.com/cocos2d/cocos2d-html5/pull/3248 - storeBatchBuffer: function(buffer) { - var pool = _batchBufferPool; - pool.push(buffer); + // Forward search commands that are in the same virtual buffer, + // If size match then no problem to render + // Otherwise, the virtual buffer need to be updated + _forwardCheck: function (first) { + var renderCmds = this._renderCmds, + cmd = renderCmds[first], + last = first + 1, length = renderCmds.length, + vbuffer = cmd._vBuffer; + + // A simple solution temporarily + cmd.getBatchInfo(_batchedInfo); + _currentBuffer = vbuffer; + return vbuffer.count; + + for (; last < length; ++last) { + cmd = renderCmds[last]; + if (vbuffer !== cmd._vBuffer) { + break; + } + } + + var size = last - first; + // no problem + if (vbuffer.count === size) { + return size; + } + // New render commands have been inserted, + // need to create new virtual buffer for the current frame + // and if the inserted command can be batched, mark _needUpdateBuffer as true + // because we need to update the buffers by rerun _forwardBatch + else if (vbuffer.count > size) { + + } + // Render commands removed + // need to split virtual buffer into separate parts without touch the buffer itself + else { + + } }, // Auto batch implementation inspired from @Heishe 's PR @@ -277,9 +354,9 @@ return { // Initialize batched info cmd.getBatchInfo(_batchedInfo); - _batchedInfo.totalVertexData = cmd.vertexBytesPerUnit; - _batchedInfo.totalBufferSize = cmd.bytesPerUnit; - _batchedInfo.totalIndexSize = cmd.indicesPerUnit; + var matrixOffset = cmd.vertexBytesPerUnit; + var totalBufferSize = cmd.bytesPerUnit; + var totalIndexSize = cmd.indicesPerUnit; // Forward search and collect batch informations cmd = renderCmds[last]; @@ -298,9 +375,9 @@ return { break; } else { - _batchedInfo.totalVertexData += cmd.vertexBytesPerUnit; - _batchedInfo.totalBufferSize += cmd.bytesPerUnit; - _batchedInfo.totalIndexSize += cmd.indicesPerUnit; + matrixOffset += cmd.vertexBytesPerUnit; + totalBufferSize += cmd.bytesPerUnit; + totalIndexSize += cmd.indicesPerUnit; } ++last; cmd = renderCmds[last]; @@ -312,24 +389,27 @@ return { return count; } - _batchedCount = count; + var buffer = this.getBatchBuffer(totalBufferSize, totalIndexSize * 2); // *2 because we use shorts for indices - var bufferSize = _batchedInfo.totalBufferSize; - var indiceSize = _batchedInfo.totalIndexSize * 2; // *2 because we use shorts for indices - _pooledBuffer = this.getBatchBuffer(bufferSize, indiceSize); - _batchBuffer = _pooledBuffer.arrayBuffer; - _batchElementBuffer = _pooledBuffer.elementBuffer; + // Create a virtual buffer + var vbuffer = createVirtualBuffer(buffer, + 0, + matrixOffset, + 0, + totalBufferSize, + totalIndexSize, + count); + _currentBuffer = vbuffer; + var uploadBuffer = vbuffer.dataArray; //all of the divisions by 4 are just because we work with uint32arrays instead of uint8 arrays so all indexes need to be shortened by the factor of 4 - var totalVertexData = _batchedInfo.totalVertexData / 4; - var totalBufferSize = _batchedInfo.totalBufferSize / 4; + var totalVertexData = matrixOffset / 4; + var totalBufferData = totalBufferSize / 4; var vertexDataOffset = 0; var matrixDataOffset = 0; - var uploadBuffer = new Uint32Array(totalBufferSize); - // Bind vertex data buffer - gl.bindBuffer(gl.ARRAY_BUFFER, _batchBuffer); + gl.bindBuffer(gl.ARRAY_BUFFER, vbuffer.buffer.arrayBuffer); // Fill in vertex data command by command var i; @@ -337,6 +417,10 @@ return { cmd = renderCmds[i]; cmd.batchVertexBuffer(uploadBuffer, vertexDataOffset, totalVertexData, matrixDataOffset); + cmd._vBuffer = vbuffer; + cmd._vertexOffset = vertexDataOffset; + cmd._matrixOffset = matrixDataOffset; + vertexDataOffset += cmd.vertexBytesPerUnit / 4; matrixDataOffset += cmd.matrixBytesPerUnit / 4; } @@ -345,9 +429,9 @@ return { gl.bufferSubData(gl.ARRAY_BUFFER, 0, uploadBuffer); // Bind element buffer - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, _batchElementBuffer); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, vbuffer.buffer.elementBuffer); - var indices = new Uint16Array(_batchedInfo.totalIndexSize); + var indices = new Uint16Array(totalIndexSize); // Fill in element buffer command by command var currentVertex = 0; @@ -371,10 +455,9 @@ return { // var node = this._node; var texture = _batchedInfo.texture; var shader = _batchedInfo.shader; - var count = _batchedCount; + var count = _currentBuffer.count; var bytesPerRow = 16; //4 floats with 4 bytes each - var totalVertexBytes = _batchedInfo.totalVertexData; shader.use(); shader._updateProjectionUniform(); @@ -382,7 +465,7 @@ return { cc.glBlendFunc(_batchedInfo.blendSrc, _batchedInfo.blendDst); cc.glBindTexture2DN(0, texture); // = cc.glBindTexture2D(texture); - gl.bindBuffer(gl.ARRAY_BUFFER, _batchBuffer); + gl.bindBuffer(gl.ARRAY_BUFFER, _currentBuffer.buffer.arrayBuffer); cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POS_COLOR_TEX); @@ -394,18 +477,16 @@ return { // Enable matrix vertex attribs row by row (vec4 * 4) for (i = 0; i < 4; ++i) { gl.enableVertexAttribArray(cc.VERTEX_ATTRIB_MVMAT0 + i); - gl.vertexAttribPointer(cc.VERTEX_ATTRIB_MVMAT0 + i, 4, gl.FLOAT, false, bytesPerRow * 4, totalVertexBytes + bytesPerRow * i); //stride is one row + gl.vertexAttribPointer(cc.VERTEX_ATTRIB_MVMAT0 + i, 4, gl.FLOAT, false, bytesPerRow * 4, _currentBuffer.matrixOffset + bytesPerRow * i); //stride is one row } - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, _batchElementBuffer); - gl.drawElements(gl.TRIANGLES, _batchedInfo.totalIndexSize, gl.UNSIGNED_SHORT, 0); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, _currentBuffer.buffer.elementBuffer); + gl.drawElements(gl.TRIANGLES, _currentBuffer.totalIndexSize, gl.UNSIGNED_SHORT, 0); for (i = 0; i < 4; ++i) { gl.disableVertexAttribArray(cc.VERTEX_ATTRIB_MVMAT0 + i); } - this.storeBatchBuffer(_pooledBuffer); - cc.g_NumberOfDraws++; }, @@ -418,9 +499,32 @@ return { i, len, cmd, next, batchCount, context = ctx || cc._renderContext; + // Need to rebuild all virtual buffers in forward batching + if (_needUpdateBuffer) { + clearVirtualBuffers(); + } + for (i = 0, len = locCmds.length; i < len; i++) { cmd = locCmds[i]; next = locCmds[i+1]; + + // Already batched in buffer + if (cmd._vBuffer) { + // No need to update buffer then try to check and reuse the buffer + if (!_needUpdateBuffer) { + batchCount = this._forwardCheck(i); + if (batchCount > 1) { + this._batchRendering(); + // i will increase by 1 each loop + i += batchCount - 1; + continue; + } + } + // Need to update buffer, clear vBuffer in commands + else { + cmd._vBuffer = null; + } + } // Batching or direct rendering if (cmd._supportBatch && next && next._supportBatch) { @@ -435,6 +539,9 @@ return { cmd.rendering(context); } + if (_needUpdateBuffer) { + _needUpdateBuffer = false; + } } }; })(); \ No newline at end of file diff --git a/cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js b/cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js index 5f6cf9619a..876bb79bc6 100644 --- a/cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js +++ b/cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js @@ -36,6 +36,9 @@ this._quadDirty = true; this._dirty = false; this._recursiveDirty = false; + this._vBuffer = null; + this._vertexOffset = 0; + this._matrixOffset = 0; if (!proto.batchShader) { proto.batchShader = cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURECOLORALPHATEST_BATCHED); diff --git a/extensions/ccui/base-classes/UIScale9SpriteWebGLRenderCmd.js b/extensions/ccui/base-classes/UIScale9SpriteWebGLRenderCmd.js index d0dc5d0d49..c6d823ee4b 100644 --- a/extensions/ccui/base-classes/UIScale9SpriteWebGLRenderCmd.js +++ b/extensions/ccui/base-classes/UIScale9SpriteWebGLRenderCmd.js @@ -92,8 +92,9 @@ var tempCmd = pchild._renderCmd; tempCmd.transform(this, true); } - else + else { break; + } } } else { @@ -101,7 +102,6 @@ node._adjustScale9ImagePosition(); node._scale9Image._renderCmd.transform(this, true); } - }; proto.setDirtyFlag = function (dirtyFlag, child) { @@ -166,6 +166,55 @@ } }; + proto._customUpdateBuffer = function (currVBuffer) { + var vbuffer, cmd, + node = this._node, + locRenderers = node._renderers, + totalVertexData; + if(node._scale9Enabled) { + cmd = locRenderers[0]; + if (cmd) { + vbuffer = cmd._renderCmd._vBuffer; + } + } + else { + vbuffer = node._scale9Image._renderCmd._vBuffer; + } + + if (vbuffer) { + if (currVBuffer !== vbuffer) { + // Send previous buffer to WebGLBuffer + if (currVBuffer) { + gl.bufferSubData(gl.ARRAY_BUFFER, 0, currVBuffer.dataArray); + } + // Bind buffer + currVBuffer = vbuffer; + totalVertexData = currVBuffer.matrixOffset / 4; + gl.bindBuffer(gl.ARRAY_BUFFER, currVBuffer.buffer.arrayBuffer); + } + else { + totalVertexData = currVBuffer.matrixOffset / 4; + } + + if(node._scale9Enabled) { + var protectChildLen = locRenderers.length; + for(var i = 0; i < protectChildLen; i++) { + var pchild = locRenderers[i]; + if(pchild) { + cmd = pchild._renderCmd; + cmd.batchVertexBuffer(currVBuffer.dataArray, cmd._vertexOffset, totalVertexData, cmd._matrixOffset); + } + } + } + else { + cmd = node._scale9Image._renderCmd; + cmd.batchVertexBuffer(currVBuffer.dataArray, cmd._vertexOffset, totalVertexData, cmd._matrixOffset); + } + } + + return currVBuffer; + }; + ccui.Scale9Sprite.WebGLRenderCmd._grayShaderProgram = null; ccui.Scale9Sprite.WebGLRenderCmd._getGrayShaderProgram = function(){ var grayShader = ccui.Scale9Sprite.WebGLRenderCmd._grayShaderProgram; From 95aed33d819809ed22db5f64f48751d471b0b55d Mon Sep 17 00:00:00 2001 From: pandamicro Date: Thu, 14 Apr 2016 01:25:41 +0800 Subject: [PATCH 07/16] Auto batch v3 and v4 with activation flags --- cocos2d/core/renderer/RendererWebGL.js | 150 ++++++++++++++++++++++--- 1 file changed, 132 insertions(+), 18 deletions(-) diff --git a/cocos2d/core/renderer/RendererWebGL.js b/cocos2d/core/renderer/RendererWebGL.js index 3beac80eaf..d414eac338 100644 --- a/cocos2d/core/renderer/RendererWebGL.js +++ b/cocos2d/core/renderer/RendererWebGL.js @@ -25,12 +25,19 @@ cc.rendererWebGL = (function () { function removeByLastSwap (array, i) { - if (array.length > 0) { - array[i] = array[array.length - 1]; + var len = array.length; + if (len > 0 && i >= 0 && i < len) { + array[i] = array[len - 1]; array.length--; } } +var ACTIVATE_V4 = false; +var CACHING_BUFFER = false; +if (ACTIVATE_V4) { + CACHING_BUFFER = true; +} + // Internal variables // Batching general informations var _batchedInfo = { @@ -54,10 +61,11 @@ var _batchedInfo = { _currentBuffer = null, _batchBufferPool = [], _virtualBuffers = [], - _needUpdateBuffer = true; + _needUpdateBuffer = CACHING_BUFFER ? true : false, + _updateBufferNextFrame = false; -function createVirtualBuffer (buffer, vertexOffset, matrixOffset, indexOffset, totalBufferSize, totalIndexSize, count) { - var data = new Uint32Array(totalBufferSize / 4); +function createVirtualBuffer (buffer, vertexOffset, matrixOffset, indexOffset, totalBufferSize, totalIndexSize, count, data) { + data = data || new Uint32Array(totalBufferSize / 4); var vBuf = { // The object contains real WebGL buffers, it's created or retrieved via getBatchBuffer buffer: buffer, @@ -77,7 +85,9 @@ function createVirtualBuffer (buffer, vertexOffset, matrixOffset, indexOffset, t count: count }; - _virtualBuffers.push(vBuf); + if (CACHING_BUFFER) { + _virtualBuffers.push(vBuf); + } return vBuf; } @@ -146,7 +156,7 @@ return { //reset renderer's flag resetFlag: function () { // Straight forward buffer update logic, order dirty then update - if (this.childrenOrderDirty) { + if (!ACTIVATE_V4 && CACHING_BUFFER && this.childrenOrderDirty) { _needUpdateBuffer = true; } this.childrenOrderDirty = false; @@ -160,6 +170,15 @@ return { locPool.sort(this._sortNodeByLevelAsc); //transform node var i, len, cmd, currVBuffer, totalVertexData; + if (!CACHING_BUFFER) { + for (i = 0, len = locPool.length; i < len; i++) { + cmd = locPool[i]; + cmd.updateStatus(); + } + locPool.length = 0; + return; + } + for (i = 0, len = locPool.length; i < len; i++) { cmd = locPool[i]; cmd.updateStatus(); @@ -308,18 +327,37 @@ return { var renderCmds = this._renderCmds, cmd = renderCmds[first], last = first + 1, length = renderCmds.length, - vbuffer = cmd._vBuffer; + vbuffer = cmd._vBuffer, + indexSize = cmd.indicesPerUnit; // A simple solution temporarily - cmd.getBatchInfo(_batchedInfo); - _currentBuffer = vbuffer; - return vbuffer.count; + if (!ACTIVATE_V4) { + cmd.getBatchInfo(_batchedInfo); + _currentBuffer = vbuffer; + return vbuffer.count; + } + // Protection, vbuffer doesn't match the command + if (cmd._vertexOffset !== vbuffer.vertexOffset) { + cmd._vBuffer = null; + for (; last < length; ++last) { + cmd = renderCmds[last]; + if (vbuffer !== cmd._vBuffer) { + break; + } + cmd._vBuffer = null; + } + this._updateBufferNextFrame = true; + return 0; + } + + // Forward check for (; last < length; ++last) { cmd = renderCmds[last]; if (vbuffer !== cmd._vBuffer) { break; } + indexSize += cmd.indicesPerUnit; } var size = last - first; @@ -332,12 +370,72 @@ return { // and if the inserted command can be batched, mark _needUpdateBuffer as true // because we need to update the buffers by rerun _forwardBatch else if (vbuffer.count > size) { + // Roll back to previous command + cmd = renderCmds[last-1]; + var secondIndexSize = vbuffer.totalIndexSize - indexSize; + var secondCount = vbuffer.count - size; + if (size > 1) { + // Update first part vbuffer + vbuffer.count = size; + vbuffer.totalIndexSize = indexSize; + } + else { + // First part only contain 1 command, remove the virtual buffer + cmd._vBuffer = null; + removeByLastSwap(_virtualBuffers, _virtualBuffers.indexOf(vbuffer)); + } + var newBuffer; + if (secondCount > 1) { + // Create second part vbuffer reusing the same buffer and data array + newBuffer = createVirtualBuffer(vbuffer.buffer, + cmd._vertexOffset + cmd.vertexBytesPerUnit / 4, + cmd._matrixOffset + cmd.matrixBytesPerUnit / 4, + vbuffer.indexOffset + indexSize, + vbuffer.totalBufferSize, + secondIndexSize, + secondCount, + vbuffer.data); + + } + else { + // Second part only contain 1 command + newBuffer = null; + } + // Update second part commands _vBuffer + for (last = last+1; last < length; ++last) { + cmd = renderCmds[last]; + if (vbuffer !== cmd._vBuffer) { + break; + } + cmd._vBuffer = newBuffer; + } + + // The breaking command + cmd = renderCmds[first+size]; + if (cmd._supportBatch) { + cmd.getBatchInfo(_currentInfo); + cmd = renderCmds[first]; + cmd.getBatchInfo(_batchedInfo); + // Can be batched together, update buffer in next frame + if (_currentInfo.texture === _batchedInfo.texture && + _currentInfo.blendSrc === _batchedInfo.blendSrc && + _currentInfo.blendDst === _batchedInfo.blendDst && + _currentInfo.shader === _batchedInfo.shader) { + this._updateBufferNextFrame = true; + } + } + return size; } // Render commands removed - // need to split virtual buffer into separate parts without touch the buffer itself + // need to update all commands and update buffer in next frame else { - + for (last = first; last < first + size; ++last) { + cmd = renderCmds[last]; + cmd._vBuffer = null; + } + this._updateBufferNextFrame = true; + return 0; } }, @@ -374,6 +472,12 @@ return { _currentInfo.shader !== _batchedInfo.shader) { break; } + // Some render command inserted before existing batching cmds + // Need to rebuild virtual buffers + else if (CACHING_BUFFER && cmd._vBuffer) { + this._updateBufferNextFrame = true; + break; + } else { matrixOffset += cmd.vertexBytesPerUnit; totalBufferSize += cmd.bytesPerUnit; @@ -404,7 +508,6 @@ return { //all of the divisions by 4 are just because we work with uint32arrays instead of uint8 arrays so all indexes need to be shortened by the factor of 4 var totalVertexData = matrixOffset / 4; - var totalBufferData = totalBufferSize / 4; var vertexDataOffset = 0; var matrixDataOffset = 0; @@ -417,9 +520,11 @@ return { cmd = renderCmds[i]; cmd.batchVertexBuffer(uploadBuffer, vertexDataOffset, totalVertexData, matrixDataOffset); - cmd._vBuffer = vbuffer; - cmd._vertexOffset = vertexDataOffset; - cmd._matrixOffset = matrixDataOffset; + if (CACHING_BUFFER) { + cmd._vBuffer = vbuffer; + cmd._vertexOffset = vertexDataOffset; + cmd._matrixOffset = matrixDataOffset; + } vertexDataOffset += cmd.vertexBytesPerUnit / 4; matrixDataOffset += cmd.matrixBytesPerUnit / 4; @@ -445,6 +550,10 @@ return { } gl.bufferSubData(gl.ELEMENT_ARRAY_BUFFER, 0, indices); + + if (!CACHING_BUFFER) { + _batchBufferPool.push(buffer); + } return count; }, @@ -481,7 +590,7 @@ return { } gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, _currentBuffer.buffer.elementBuffer); - gl.drawElements(gl.TRIANGLES, _currentBuffer.totalIndexSize, gl.UNSIGNED_SHORT, 0); + gl.drawElements(gl.TRIANGLES, _currentBuffer.totalIndexSize, gl.UNSIGNED_SHORT, _currentBuffer.indexOffset); for (i = 0; i < 4; ++i) { gl.disableVertexAttribArray(cc.VERTEX_ATTRIB_MVMAT0 + i); @@ -542,6 +651,11 @@ return { if (_needUpdateBuffer) { _needUpdateBuffer = false; } + // Update virtual buffers in next frame + if (_updateBufferNextFrame) { + _needUpdateBuffer = true; + _updateBufferNextFrame = false; + } } }; })(); \ No newline at end of file From 8e2507d93d4aa3abee1663accbfed73efe693723 Mon Sep 17 00:00:00 2001 From: pandamicro Date: Thu, 14 Apr 2016 16:59:04 +0800 Subject: [PATCH 08/16] Fix Autobatch v4 --- cocos2d/core/renderer/RendererWebGL.js | 118 +++++++++--------- .../core/sprites/CCSpriteWebGLRenderCmd.js | 2 + .../UIScale9SpriteWebGLRenderCmd.js | 49 -------- 3 files changed, 59 insertions(+), 110 deletions(-) diff --git a/cocos2d/core/renderer/RendererWebGL.js b/cocos2d/core/renderer/RendererWebGL.js index d414eac338..d72cb36922 100644 --- a/cocos2d/core/renderer/RendererWebGL.js +++ b/cocos2d/core/renderer/RendererWebGL.js @@ -32,7 +32,7 @@ function removeByLastSwap (array, i) { } } -var ACTIVATE_V4 = false; +var ACTIVATE_V4 = true; var CACHING_BUFFER = false; if (ACTIVATE_V4) { CACHING_BUFFER = true; @@ -64,7 +64,7 @@ var _batchedInfo = { _needUpdateBuffer = CACHING_BUFFER ? true : false, _updateBufferNextFrame = false; -function createVirtualBuffer (buffer, vertexOffset, matrixOffset, indexOffset, totalBufferSize, totalIndexSize, count, data) { +function createVirtualBuffer (buffer, vertexOffset, matrixOrigin, matrixOffset, indexOffset, totalBufferSize, totalIndexSize, count, data) { data = data || new Uint32Array(totalBufferSize / 4); var vBuf = { // The object contains real WebGL buffers, it's created or retrieved via getBatchBuffer @@ -73,7 +73,9 @@ function createVirtualBuffer (buffer, vertexOffset, matrixOffset, indexOffset, t dataArray: data, // The start offset in the vertex buffer, in bytes vertexOffset: vertexOffset, - // The start offset of matrix data in the vertex buffer, in bytes + // The offset of all matrix data, in bytes + matrixOrigin: matrixOrigin, + // The start offset after the origin of matrix data in the vertex buffer, in bytes matrixOffset: matrixOffset, // The start offset in the index buffer indexOffset: indexOffset, @@ -94,7 +96,9 @@ function createVirtualBuffer (buffer, vertexOffset, matrixOffset, indexOffset, t function clearVirtualBuffers () { for (var i = _virtualBuffers.length-1; i >= 0; --i) { var vbuffer = _virtualBuffers[i]; - _batchBufferPool.push(vbuffer.buffer); + if (vbuffer.buffer) { + _batchBufferPool.push(vbuffer.buffer); + } vbuffer.buffer = null; vbuffer.dataArray = null; } @@ -169,41 +173,10 @@ return { //sort the pool locPool.sort(this._sortNodeByLevelAsc); //transform node - var i, len, cmd, currVBuffer, totalVertexData; - if (!CACHING_BUFFER) { - for (i = 0, len = locPool.length; i < len; i++) { - cmd = locPool[i]; - cmd.updateStatus(); - } - locPool.length = 0; - return; - } - + var i, len, cmd; for (i = 0, len = locPool.length; i < len; i++) { cmd = locPool[i]; cmd.updateStatus(); - - if (cmd._vBuffer && cmd._vBuffer.buffer) { - if (currVBuffer !== cmd._vBuffer) { - // Send previous buffer to WebGLBuffer - if (currVBuffer) { - gl.bufferSubData(gl.ARRAY_BUFFER, 0, currVBuffer.dataArray); - } - // Bind buffer - currVBuffer = cmd._vBuffer; - totalVertexData = currVBuffer.matrixOffset / 4; - gl.bindBuffer(gl.ARRAY_BUFFER, currVBuffer.buffer.arrayBuffer); - } - cmd.batchVertexBuffer(currVBuffer.dataArray, cmd._vertexOffset, totalVertexData, cmd._matrixOffset); - } - // Ugly work around for Node type like Scale9Sprite - else if (cmd._customUpdateBuffer) { - currVBuffer = cmd._customUpdateBuffer(currVBuffer); - } - } - // Send last buffer to WebGLBuffer - if (currVBuffer) { - gl.bufferSubData(gl.ARRAY_BUFFER, 0, currVBuffer.dataArray); } locPool.length = 0; }, @@ -330,15 +303,18 @@ return { vbuffer = cmd._vBuffer, indexSize = cmd.indicesPerUnit; + // Reset current buffer and batched info + cmd.getBatchInfo(_batchedInfo); + _currentBuffer = null; + // A simple solution temporarily if (!ACTIVATE_V4) { - cmd.getBatchInfo(_batchedInfo); _currentBuffer = vbuffer; return vbuffer.count; } - // Protection, vbuffer doesn't match the command - if (cmd._vertexOffset !== vbuffer.vertexOffset) { + // Protection, vbuffer invalid or doesn't match the command + if (cmd._vertexOffset !== vbuffer.vertexOffset || !vbuffer.buffer) { cmd._vBuffer = null; for (; last < length; ++last) { cmd = renderCmds[last]; @@ -347,22 +323,40 @@ return { } cmd._vBuffer = null; } - this._updateBufferNextFrame = true; + _updateBufferNextFrame = true; return 0; } // Forward check + var matrixBuffer, martixOrigin; for (; last < length; ++last) { cmd = renderCmds[last]; if (vbuffer !== cmd._vBuffer) { break; } + + // Lazy update transform matrix in buffer + if (cmd._matrixDirty) { + if (!matrixBuffer) { + // Bind buffer + matrixBuffer = vbuffer; + martixOrigin = matrixBuffer.matrixOrigin / 4; + gl.bindBuffer(gl.ARRAY_BUFFER, matrixBuffer.buffer.arrayBuffer); + } + cmd.batchVertexBuffer(matrixBuffer.dataArray, cmd._vertexOffset, martixOrigin, cmd._matrixOffset); + cmd._matrixDirty = false; + } indexSize += cmd.indicesPerUnit; } + // Send last buffer to WebGLBuffer + if (matrixBuffer) { + gl.bufferSubData(gl.ARRAY_BUFFER, 0, matrixBuffer.dataArray); + } var size = last - first; // no problem if (vbuffer.count === size) { + _currentBuffer = vbuffer; return size; } // New render commands have been inserted, @@ -378,6 +372,7 @@ return { // Update first part vbuffer vbuffer.count = size; vbuffer.totalIndexSize = indexSize; + _currentBuffer = vbuffer; } else { // First part only contain 1 command, remove the virtual buffer @@ -389,8 +384,9 @@ return { if (secondCount > 1) { // Create second part vbuffer reusing the same buffer and data array newBuffer = createVirtualBuffer(vbuffer.buffer, - cmd._vertexOffset + cmd.vertexBytesPerUnit / 4, - cmd._matrixOffset + cmd.matrixBytesPerUnit / 4, + cmd._vertexOffset * 4 + cmd.vertexBytesPerUnit, + vbuffer.matrixOrigin, + cmd._matrixOffset * 4 + cmd.matrixBytesPerUnit, vbuffer.indexOffset + indexSize, vbuffer.totalBufferSize, secondIndexSize, @@ -422,7 +418,7 @@ return { _currentInfo.blendSrc === _batchedInfo.blendSrc && _currentInfo.blendDst === _batchedInfo.blendDst && _currentInfo.shader === _batchedInfo.shader) { - this._updateBufferNextFrame = true; + _updateBufferNextFrame = true; } } return size; @@ -434,7 +430,7 @@ return { cmd = renderCmds[last]; cmd._vBuffer = null; } - this._updateBufferNextFrame = true; + _updateBufferNextFrame = true; return 0; } }, @@ -452,7 +448,9 @@ return { // Initialize batched info cmd.getBatchInfo(_batchedInfo); - var matrixOffset = cmd.vertexBytesPerUnit; + if (!_batchedInfo.texture) + return 0; + var matrixOrigin = cmd.vertexBytesPerUnit; var totalBufferSize = cmd.bytesPerUnit; var totalIndexSize = cmd.indicesPerUnit; @@ -475,11 +473,11 @@ return { // Some render command inserted before existing batching cmds // Need to rebuild virtual buffers else if (CACHING_BUFFER && cmd._vBuffer) { - this._updateBufferNextFrame = true; + _updateBufferNextFrame = true; break; } else { - matrixOffset += cmd.vertexBytesPerUnit; + matrixOrigin += cmd.vertexBytesPerUnit; totalBufferSize += cmd.bytesPerUnit; totalIndexSize += cmd.indicesPerUnit; } @@ -498,7 +496,8 @@ return { // Create a virtual buffer var vbuffer = createVirtualBuffer(buffer, 0, - matrixOffset, + matrixOrigin, + 0, 0, totalBufferSize, totalIndexSize, @@ -507,7 +506,7 @@ return { var uploadBuffer = vbuffer.dataArray; //all of the divisions by 4 are just because we work with uint32arrays instead of uint8 arrays so all indexes need to be shortened by the factor of 4 - var totalVertexData = matrixOffset / 4; + var totalVertexData = matrixOrigin / 4; var vertexDataOffset = 0; var matrixDataOffset = 0; @@ -578,15 +577,17 @@ return { cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POS_COLOR_TEX); - gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 24, 0); //cc.VERTEX_ATTRIB_POSITION - gl.vertexAttribPointer(1, 4, gl.UNSIGNED_BYTE, true, 24, 12); //cc.VERTEX_ATTRIB_COLOR - gl.vertexAttribPointer(2, 2, gl.FLOAT, false, 24, 16); //cc.VERTEX_ATTRIB_TEX_COORDS + var vertexOffset = _currentBuffer.vertexOffset; + gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 24, vertexOffset); + gl.vertexAttribPointer(1, 4, gl.UNSIGNED_BYTE, true, 24, vertexOffset + 12); + gl.vertexAttribPointer(2, 2, gl.FLOAT, false, 24, vertexOffset + 16); var i; + var matrixOffset = _currentBuffer.matrixOrigin + _currentBuffer.matrixOffset; // Enable matrix vertex attribs row by row (vec4 * 4) for (i = 0; i < 4; ++i) { gl.enableVertexAttribArray(cc.VERTEX_ATTRIB_MVMAT0 + i); - gl.vertexAttribPointer(cc.VERTEX_ATTRIB_MVMAT0 + i, 4, gl.FLOAT, false, bytesPerRow * 4, _currentBuffer.matrixOffset + bytesPerRow * i); //stride is one row + gl.vertexAttribPointer(cc.VERTEX_ATTRIB_MVMAT0 + i, 4, gl.FLOAT, false, bytesPerRow * 4, matrixOffset + bytesPerRow * i); //stride is one row } gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, _currentBuffer.buffer.elementBuffer); @@ -617,10 +618,9 @@ return { cmd = locCmds[i]; next = locCmds[i+1]; - // Already batched in buffer - if (cmd._vBuffer) { - // No need to update buffer then try to check and reuse the buffer - if (!_needUpdateBuffer) { + if (!_needUpdateBuffer) { + // Already batched in buffer + if (cmd._vBuffer) { batchCount = this._forwardCheck(i); if (batchCount > 1) { this._batchRendering(); @@ -629,10 +629,6 @@ return { continue; } } - // Need to update buffer, clear vBuffer in commands - else { - cmd._vBuffer = null; - } } // Batching or direct rendering diff --git a/cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js b/cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js index 876bb79bc6..23292081a8 100644 --- a/cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js +++ b/cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js @@ -39,6 +39,7 @@ this._vBuffer = null; this._vertexOffset = 0; this._matrixOffset = 0; + this._matrixDirty = false; if (!proto.batchShader) { proto.batchShader = cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURECOLORALPHATEST_BATCHED); @@ -257,6 +258,7 @@ proto.transform = function(parentCmd, recursive){ cc.Node.WebGLRenderCmd.prototype.transform.call(this, parentCmd, recursive); this._dirty = true; //use for batching + this._matrixDirty = true; }; proto._setColorDirty = function () {}; diff --git a/extensions/ccui/base-classes/UIScale9SpriteWebGLRenderCmd.js b/extensions/ccui/base-classes/UIScale9SpriteWebGLRenderCmd.js index c6d823ee4b..a7b8b7a9fc 100644 --- a/extensions/ccui/base-classes/UIScale9SpriteWebGLRenderCmd.js +++ b/extensions/ccui/base-classes/UIScale9SpriteWebGLRenderCmd.js @@ -166,55 +166,6 @@ } }; - proto._customUpdateBuffer = function (currVBuffer) { - var vbuffer, cmd, - node = this._node, - locRenderers = node._renderers, - totalVertexData; - if(node._scale9Enabled) { - cmd = locRenderers[0]; - if (cmd) { - vbuffer = cmd._renderCmd._vBuffer; - } - } - else { - vbuffer = node._scale9Image._renderCmd._vBuffer; - } - - if (vbuffer) { - if (currVBuffer !== vbuffer) { - // Send previous buffer to WebGLBuffer - if (currVBuffer) { - gl.bufferSubData(gl.ARRAY_BUFFER, 0, currVBuffer.dataArray); - } - // Bind buffer - currVBuffer = vbuffer; - totalVertexData = currVBuffer.matrixOffset / 4; - gl.bindBuffer(gl.ARRAY_BUFFER, currVBuffer.buffer.arrayBuffer); - } - else { - totalVertexData = currVBuffer.matrixOffset / 4; - } - - if(node._scale9Enabled) { - var protectChildLen = locRenderers.length; - for(var i = 0; i < protectChildLen; i++) { - var pchild = locRenderers[i]; - if(pchild) { - cmd = pchild._renderCmd; - cmd.batchVertexBuffer(currVBuffer.dataArray, cmd._vertexOffset, totalVertexData, cmd._matrixOffset); - } - } - } - else { - cmd = node._scale9Image._renderCmd; - cmd.batchVertexBuffer(currVBuffer.dataArray, cmd._vertexOffset, totalVertexData, cmd._matrixOffset); - } - } - - return currVBuffer; - }; - ccui.Scale9Sprite.WebGLRenderCmd._grayShaderProgram = null; ccui.Scale9Sprite.WebGLRenderCmd._getGrayShaderProgram = function(){ var grayShader = ccui.Scale9Sprite.WebGLRenderCmd._grayShaderProgram; From 8201f3e1f9060aaa6be419b35a37424d15389095 Mon Sep 17 00:00:00 2001 From: pandamicro Date: Thu, 14 Apr 2016 18:03:29 +0800 Subject: [PATCH 09/16] Fix transform issue with first sprite batched --- cocos2d/core/renderer/RendererWebGL.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cocos2d/core/renderer/RendererWebGL.js b/cocos2d/core/renderer/RendererWebGL.js index d72cb36922..9d06c8bc8d 100644 --- a/cocos2d/core/renderer/RendererWebGL.js +++ b/cocos2d/core/renderer/RendererWebGL.js @@ -299,9 +299,9 @@ return { _forwardCheck: function (first) { var renderCmds = this._renderCmds, cmd = renderCmds[first], - last = first + 1, length = renderCmds.length, + last = first, length = renderCmds.length, vbuffer = cmd._vBuffer, - indexSize = cmd.indicesPerUnit; + indexSize = 0; // Reset current buffer and batched info cmd.getBatchInfo(_batchedInfo); From eb590d73c813cb90d4eb367c3cff360510b2135d Mon Sep 17 00:00:00 2001 From: pandamicro Date: Fri, 15 Apr 2016 00:02:03 +0800 Subject: [PATCH 10/16] Disable autobatch v4 on mobile devices --- cocos2d/core/renderer/RendererWebGL.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cocos2d/core/renderer/RendererWebGL.js b/cocos2d/core/renderer/RendererWebGL.js index 9d06c8bc8d..dffbdb4c17 100644 --- a/cocos2d/core/renderer/RendererWebGL.js +++ b/cocos2d/core/renderer/RendererWebGL.js @@ -32,7 +32,10 @@ function removeByLastSwap (array, i) { } } -var ACTIVATE_V4 = true; +var ACTIVATE_V4 = false; +if (!cc.sys.isMobile) { + ACTIVATE_V4 = true; +} var CACHING_BUFFER = false; if (ACTIVATE_V4) { CACHING_BUFFER = true; From a459947563ea520a52bc106cdf49dd6f86a2b48d Mon Sep 17 00:00:00 2001 From: pandamicro Date: Fri, 15 Apr 2016 11:57:21 +0800 Subject: [PATCH 11/16] Rearrange auto batch flags: ACTIVATE_AUTO_BATCH + CACHING_BUFFER --- .../core/base-nodes/CCNodeCanvasRenderCmd.js | 4 +- cocos2d/core/renderer/RendererWebGL.js | 49 +++++++------------ 2 files changed, 21 insertions(+), 32 deletions(-) diff --git a/cocos2d/core/base-nodes/CCNodeCanvasRenderCmd.js b/cocos2d/core/base-nodes/CCNodeCanvasRenderCmd.js index a9e6e73a2a..86ee939abe 100644 --- a/cocos2d/core/base-nodes/CCNodeCanvasRenderCmd.js +++ b/cocos2d/core/base-nodes/CCNodeCanvasRenderCmd.js @@ -375,8 +375,8 @@ cc.Node.RenderCmd.prototype = { var node = this._node; var i, children = node._children, child, cmd; var len = children.length; - var minZ = Number.MAX_VALUE; - var maxZ = -Number.MAX_VALUE; + // var minZ = Number.MAX_VALUE; + // var maxZ = -Number.MAX_VALUE; if (len > 0) { node.sortAllChildren(); // draw children zOrder < 0 diff --git a/cocos2d/core/renderer/RendererWebGL.js b/cocos2d/core/renderer/RendererWebGL.js index dffbdb4c17..e4bef95a8a 100644 --- a/cocos2d/core/renderer/RendererWebGL.js +++ b/cocos2d/core/renderer/RendererWebGL.js @@ -32,14 +32,11 @@ function removeByLastSwap (array, i) { } } -var ACTIVATE_V4 = false; -if (!cc.sys.isMobile) { - ACTIVATE_V4 = true; -} var CACHING_BUFFER = false; -if (ACTIVATE_V4) { +if (!cc.sys.isMobile) { CACHING_BUFFER = true; } +var ACTIVATE_AUTO_BATCH = true; // Internal variables // Batching general informations @@ -162,10 +159,6 @@ return { //reset renderer's flag resetFlag: function () { - // Straight forward buffer update logic, order dirty then update - if (!ACTIVATE_V4 && CACHING_BUFFER && this.childrenOrderDirty) { - _needUpdateBuffer = true; - } this.childrenOrderDirty = false; this._transformNodePool.length = 0; }, @@ -310,12 +303,6 @@ return { cmd.getBatchInfo(_batchedInfo); _currentBuffer = null; - // A simple solution temporarily - if (!ACTIVATE_V4) { - _currentBuffer = vbuffer; - return vbuffer.count; - } - // Protection, vbuffer invalid or doesn't match the command if (cmd._vertexOffset !== vbuffer.vertexOffset || !vbuffer.buffer) { cmd._vBuffer = null; @@ -621,10 +608,23 @@ return { cmd = locCmds[i]; next = locCmds[i+1]; - if (!_needUpdateBuffer) { - // Already batched in buffer - if (cmd._vBuffer) { - batchCount = this._forwardCheck(i); + if (ACTIVATE_AUTO_BATCH) { + if (!_needUpdateBuffer) { + // Already batched in buffer + if (cmd._vBuffer) { + batchCount = this._forwardCheck(i); + if (batchCount > 1) { + this._batchRendering(); + // i will increase by 1 each loop + i += batchCount - 1; + continue; + } + } + } + + // Batching or direct rendering + if (cmd._supportBatch && next && next._supportBatch) { + batchCount = this._forwardBatch(i); if (batchCount > 1) { this._batchRendering(); // i will increase by 1 each loop @@ -633,17 +633,6 @@ return { } } } - - // Batching or direct rendering - if (cmd._supportBatch && next && next._supportBatch) { - batchCount = this._forwardBatch(i); - if (batchCount > 1) { - this._batchRendering(); - // i will increase by 1 each loop - i += batchCount - 1; - continue; - } - } cmd.rendering(context); } From 3b9b7122ca1dc0f87d24b21c891827c5994fdf2b Mon Sep 17 00:00:00 2001 From: pandamicro Date: Sat, 16 Apr 2016 10:09:32 +0800 Subject: [PATCH 12/16] Add SimplePool and fix status sync issue with sprite in auto batch --- .../core/base-nodes/CCNodeCanvasRenderCmd.js | 6 +- cocos2d/core/renderer/RendererWebGL.js | 113 +++++++++++------- .../core/sprites/CCSpriteCanvasRenderCmd.js | 1 - .../core/sprites/CCSpriteWebGLRenderCmd.js | 10 +- cocos2d/core/utils/CCSimplePool.js | 74 ++++++++++++ extensions/ccpool/CCPool.js | 8 +- moduleConfig.json | 1 + tools/build.xml | 2 + 8 files changed, 158 insertions(+), 57 deletions(-) create mode 100644 cocos2d/core/utils/CCSimplePool.js diff --git a/cocos2d/core/base-nodes/CCNodeCanvasRenderCmd.js b/cocos2d/core/base-nodes/CCNodeCanvasRenderCmd.js index 86ee939abe..7c3930003b 100644 --- a/cocos2d/core/base-nodes/CCNodeCanvasRenderCmd.js +++ b/cocos2d/core/base-nodes/CCNodeCanvasRenderCmd.js @@ -41,6 +41,7 @@ cc.Node._dirtyFlags = {transformDirty: 1 << 0, visibleDirty: 1 << 1, colorDirty: //-------------------------Base ------------------------- cc.Node.RenderCmd = function(renderable){ this._dirtyFlag = 1; //need update the transform at first. + this._savedDirtyFlag = 0; this._node = renderable; this._needDraw = false; @@ -88,7 +89,7 @@ cc.Node.RenderCmd.prototype = { }, getParentToNodeTransform: function(){ - if(this._dirtyFlag & cc.Node._dirtyFlags.transformDirty) + if (this._dirtyFlag & cc.Node._dirtyFlags.transformDirty) this._inverse = cc.affineTransformInvert(this.getNodeToParentTransform()); return this._inverse; }, @@ -220,6 +221,8 @@ cc.Node.RenderCmd.prototype = { var flags = cc.Node._dirtyFlags, locFlag = this._dirtyFlag; var colorDirty = locFlag & flags.colorDirty, opacityDirty = locFlag & flags.opacityDirty; + this._savedDirtyFlag = locFlag; + if(colorDirty) this._updateDisplayColor(); @@ -330,6 +333,7 @@ cc.Node.RenderCmd.prototype = { // Because child elements need parent's _dirtyFlag to change himself var flags = cc.Node._dirtyFlags, locFlag = this._dirtyFlag; var parentNode = parentCmd ? parentCmd._node : null; + this._savedDirtyFlag = locFlag; // There is a possibility: // The parent element changed color, child element not change diff --git a/cocos2d/core/renderer/RendererWebGL.js b/cocos2d/core/renderer/RendererWebGL.js index e4bef95a8a..dca83c27d0 100644 --- a/cocos2d/core/renderer/RendererWebGL.js +++ b/cocos2d/core/renderer/RendererWebGL.js @@ -59,10 +59,11 @@ var _batchedInfo = { }, // The current virtual buffer _currentBuffer = null, - _batchBufferPool = [], + _batchBufferPool = new cc.SimplePool(), _virtualBuffers = [], - _needUpdateBuffer = CACHING_BUFFER ? true : false, - _updateBufferNextFrame = false; + _needUpdateBuffer = false, + _updateBufferNextFrame = false, + _orderDirtyInFrame = false; function createVirtualBuffer (buffer, vertexOffset, matrixOrigin, matrixOffset, indexOffset, totalBufferSize, totalIndexSize, count, data) { data = data || new Uint32Array(totalBufferSize / 4); @@ -97,7 +98,7 @@ function clearVirtualBuffers () { for (var i = _virtualBuffers.length-1; i >= 0; --i) { var vbuffer = _virtualBuffers[i]; if (vbuffer.buffer) { - _batchBufferPool.push(vbuffer.buffer); + _batchBufferPool.put(vbuffer.buffer); } vbuffer.buffer = null; vbuffer.dataArray = null; @@ -159,6 +160,9 @@ return { //reset renderer's flag resetFlag: function () { + if (this.childrenOrderDirty) { + _orderDirtyInFrame = true; + } this.childrenOrderDirty = false; this._transformNodePool.length = 0; }, @@ -254,39 +258,35 @@ return { // where size denotes how many unit fit in the buffer (no need for bufferData if it's already big enough, bufferSubData enough) getBatchBuffer: function(bufferSize, indexSize) { - if (_batchBufferPool.length <= 0) { - return this.createBatchBuffer(bufferSize, indexSize); - } + if (_batchBufferPool.size() > 0) { + var minSize = Number.MAX_VALUE; + var minBufIndex = -1; + + var buf = _batchBufferPool.find(function (i, buf) { + // Find available buffer with suitable size + if (buf.bufferSize >= bufferSize && buf.indexSize >= indexSize) { + return true; + } - var minBuf = null; //we also track the smallest found buffer because that one will be re-initialized and returned if no fitting buffer can be found - var minSize = Number.MAX_VALUE; - var minBufIndex = -1; - for (var i = _batchBufferPool.length - 1; i >= 0; --i) { - var buf = _batchBufferPool[i]; - // Find available buffer with suitable size - if (buf.bufferSize >= bufferSize && buf.indexSize >= indexSize) { - removeByLastSwap(_batchBufferPool, i); + // Track the smallest found buffer because that one will be re-initialized and returned if no fitting buffer can be found + if (buf.bufferSize < minSize) + { + minSize = buf.bufferSize; + minBufIndex = i; + } + }, function () { + return minBufIndex; + }); + + if (buf) { this.initBatchBuffers(buf.arrayBuffer, buf.elementBuffer, bufferSize, indexSize); buf.bufferSize = bufferSize; buf.indexSize = indexSize; return buf; } - - if (buf.bufferSize < minSize) - { - minSize = buf.bufferSize; - minBuf = buf; - minBufIndex = i; - } } - // we only get here if no properly sized buffer was found - // in that case, take smallest buffer in pool, resize it and return it - removeByLastSwap(_batchBufferPool, minBufIndex); - this.initBatchBuffers(minBuf.arrayBuffer, minBuf.elementBuffer, bufferSize, indexSize); - minBuf.bufferSize = bufferSize; - minBuf.indexSize = indexSize; - return minBuf; + return this.createBatchBuffer(bufferSize, indexSize); }, // Forward search commands that are in the same virtual buffer, @@ -326,7 +326,7 @@ return { } // Lazy update transform matrix in buffer - if (cmd._matrixDirty) { + if (cmd._savedDirtyFlag) { if (!matrixBuffer) { // Bind buffer matrixBuffer = vbuffer; @@ -334,7 +334,7 @@ return { gl.bindBuffer(gl.ARRAY_BUFFER, matrixBuffer.buffer.arrayBuffer); } cmd.batchVertexBuffer(matrixBuffer.dataArray, cmd._vertexOffset, martixOrigin, cmd._matrixOffset); - cmd._matrixDirty = false; + cmd._savedDirtyFlag = false; } indexSize += cmd.indicesPerUnit; } @@ -346,6 +346,22 @@ return { var size = last - first; // no problem if (vbuffer.count === size) { + // If children order dirty in this frame, then need to check the next command + // to see whether buffer should be updated + if (_orderDirtyInFrame) { + cmd = renderCmds[last]; + if (cmd && cmd._supportBatch) { + cmd.getBatchInfo(_currentInfo); + // Can be batched together, update buffer in next frame + if (_currentInfo.texture === _batchedInfo.texture && + _currentInfo.blendSrc === _batchedInfo.blendSrc && + _currentInfo.blendDst === _batchedInfo.blendDst && + _currentInfo.shader === _batchedInfo.shader) { + _updateBufferNextFrame = true; + } + } + } + _currentBuffer = vbuffer; return size; } @@ -440,6 +456,7 @@ return { cmd.getBatchInfo(_batchedInfo); if (!_batchedInfo.texture) return 0; + var matrixOrigin = cmd.vertexBytesPerUnit; var totalBufferSize = cmd.bytesPerUnit; var totalIndexSize = cmd.indicesPerUnit; @@ -462,7 +479,7 @@ return { } // Some render command inserted before existing batching cmds // Need to rebuild virtual buffers - else if (CACHING_BUFFER && cmd._vBuffer) { + else if (!_needUpdateBuffer && cmd._vBuffer) { _updateBufferNextFrame = true; break; } @@ -541,7 +558,7 @@ return { gl.bufferSubData(gl.ELEMENT_ARRAY_BUFFER, 0, indices); if (!CACHING_BUFFER) { - _batchBufferPool.push(buffer); + _batchBufferPool.put(buffer); } return count; }, @@ -609,9 +626,9 @@ return { next = locCmds[i+1]; if (ACTIVATE_AUTO_BATCH) { - if (!_needUpdateBuffer) { - // Already batched in buffer - if (cmd._vBuffer) { + // Already batched in buffer + if (cmd._vBuffer) { + if (!_needUpdateBuffer) { batchCount = this._forwardCheck(i); if (batchCount > 1) { this._batchRendering(); @@ -620,16 +637,21 @@ return { continue; } } + else { + cmd._vBuffer = null; + } } - // Batching or direct rendering - if (cmd._supportBatch && next && next._supportBatch) { - batchCount = this._forwardBatch(i); - if (batchCount > 1) { - this._batchRendering(); - // i will increase by 1 each loop - i += batchCount - 1; - continue; + if (!CACHING_BUFFER || _needUpdateBuffer || _orderDirtyInFrame) { + // Batching or direct rendering + if (cmd._supportBatch && next && next._supportBatch) { + batchCount = this._forwardBatch(i); + if (batchCount > 1) { + this._batchRendering(); + // i will increase by 1 each loop + i += batchCount - 1; + continue; + } } } } @@ -644,6 +666,9 @@ return { _needUpdateBuffer = true; _updateBufferNextFrame = false; } + if (_orderDirtyInFrame) { + _orderDirtyInFrame = false; + } } }; })(); \ No newline at end of file diff --git a/cocos2d/core/sprites/CCSpriteCanvasRenderCmd.js b/cocos2d/core/sprites/CCSpriteCanvasRenderCmd.js index eca55cd61d..0c1fe6d9f9 100644 --- a/cocos2d/core/sprites/CCSpriteCanvasRenderCmd.js +++ b/cocos2d/core/sprites/CCSpriteCanvasRenderCmd.js @@ -109,7 +109,6 @@ proto.rendering = function (ctx, scaleX, scaleY) { var node = this._node; var locTextureCoord = this._textureCoord, alpha = (this._displayedOpacity / 255); - var texture = this._textureToRender || node._texture; if ((texture && (locTextureCoord.width === 0 || locTextureCoord.height === 0|| !texture._textureLoaded)) || alpha === 0) diff --git a/cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js b/cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js index 23292081a8..a0464f79cb 100644 --- a/cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js +++ b/cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js @@ -39,7 +39,6 @@ this._vBuffer = null; this._vertexOffset = 0; this._matrixOffset = 0; - this._matrixDirty = false; if (!proto.batchShader) { proto.batchShader = cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURECOLORALPHATEST_BATCHED); @@ -258,7 +257,6 @@ proto.transform = function(parentCmd, recursive){ cc.Node.WebGLRenderCmd.prototype.transform.call(this, parentCmd, recursive); this._dirty = true; //use for batching - this._matrixDirty = true; }; proto._setColorDirty = function () {}; @@ -281,15 +279,13 @@ // renders using Sprite Manager if (node._batchNode) { if (node.atlasIndex !== cc.Sprite.INDEX_NOT_INITIALIZED) { - node.textureAtlas.updateQuad(locQuad, node.atlasIndex) + node.textureAtlas.updateQuad(locQuad, node.atlasIndex); } else { // no need to set it recursively // update dirty_, don't update recursiveDirty_ this._dirty = true; } } - // self render - // do nothing this._quadDirty = true; }; @@ -448,10 +444,10 @@ proto.rendering = function (ctx) { var node = this._node, locTexture = node._texture; - if ((locTexture &&!locTexture._textureLoaded) || this._displayedOpacity === 0) + if ((locTexture && (!locTexture._textureLoaded || !node._rect.width || !node._rect.height)) || !this._displayedOpacity) return; - var gl = ctx || cc._renderContext ; + var gl = ctx || cc._renderContext; //cc.assert(!_t._batchNode, "If cc.Sprite is being rendered by cc.SpriteBatchNode, cc.Sprite#draw SHOULD NOT be called"); var program = this._shaderProgram; diff --git a/cocos2d/core/utils/CCSimplePool.js b/cocos2d/core/utils/CCSimplePool.js new file mode 100644 index 0000000000..f196ea0d1a --- /dev/null +++ b/cocos2d/core/utils/CCSimplePool.js @@ -0,0 +1,74 @@ +/**************************************************************************** + Copyright (c) 2016 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +cc.SimplePool = function () { + this._pool = []; +}; +cc.SimplePool.prototype = { + constructor: cc.SimplePool, + + size: function () { + return this._pool.length; + }, + + put: function (obj) { + if (obj && this._pool.indexOf(obj) !== -1) { + this._pool.unshift(obj); + } + }, + + get: function () { + var last = this._pool.length-1; + if (last < 0) { + return null; + } + else { + var obj = this._pool[last]; + this._pool.length = last; + return obj; + } + }, + + find: function (finder, end) { + var found, i, obj, pool = this._pool, last = pool.length-1; + for (i = pool.length; i >= 0; --i) { + obj = pool[i]; + found = finder(i, obj); + if (found) { + pool[i] = pool[last]; + pool.length = last; + return obj; + } + } + if (end) { + var index = end(); + if (index >= 0) { + pool[index] = pool[last]; + pool.length = last; + return obj; + } + } + return null; + } +}; \ No newline at end of file diff --git a/extensions/ccpool/CCPool.js b/extensions/ccpool/CCPool.js index 205c8a2295..489ae1d9c6 100644 --- a/extensions/ccpool/CCPool.js +++ b/extensions/ccpool/CCPool.js @@ -60,7 +60,7 @@ cc.pool = /** @lends cc.pool# */{ * @param obj */ putInPool: function (obj) { - var pid = obj.constructor.prototype.__pid; + var pid = obj.constructor.prototype['__pid']; if (!pid) { var desc = { writable: true, enumerable: false, configurable: true }; desc.value = ClassManager.getNewID(); @@ -82,7 +82,7 @@ cc.pool = /** @lends cc.pool# */{ * @returns {boolean} if this kind of obj is already in pool return true,else return false; */ hasObject: function (objClass) { - var pid = objClass.prototype.__pid; + var pid = objClass.prototype['__pid']; var list = this._pool[pid]; if (!list || list.length === 0) { return false; @@ -95,7 +95,7 @@ cc.pool = /** @lends cc.pool# */{ * @param obj */ removeObject: function (obj) { - var pid = obj.constructor.prototype.__pid; + var pid = obj.constructor.prototype['__pid']; if (pid) { var list = this._pool[pid]; if (list) { @@ -117,7 +117,7 @@ cc.pool = /** @lends cc.pool# */{ */ getFromPool: function (objClass/*,args*/) { if (this.hasObject(objClass)) { - var pid = objClass.prototype.__pid; + var pid = objClass.prototype['__pid']; var list = this._pool[pid]; var args = Array.prototype.slice.call(arguments); args.shift(); diff --git a/moduleConfig.json b/moduleConfig.json index 4fc43ccfef..96371dd66c 100644 --- a/moduleConfig.json +++ b/moduleConfig.json @@ -42,6 +42,7 @@ "core" : [ "cocos2d/core/event-manager/CCEventHelper.js", "CCDebugger.js", + "cocos2d/core/utils/CCSimplePool.js", "cocos2d/core/utils/BinaryLoader.js", "Base64Images.js", "cocos2d/core/platform/CCClass.js", diff --git a/tools/build.xml b/tools/build.xml index 73ad792f7f..7077802358 100644 --- a/tools/build.xml +++ b/tools/build.xml @@ -12,6 +12,7 @@ + @@ -307,6 +308,7 @@ debug="false" output="./../lib/cocos2d-js-v3.11-core-min.js"> + From 4cf480e7b7a532bee406592a54217ac8939429a8 Mon Sep 17 00:00:00 2001 From: pandamicro Date: Sun, 17 Apr 2016 23:10:40 +0800 Subject: [PATCH 13/16] Auto batch v5 --- .../core/base-nodes/CCNodeCanvasRenderCmd.js | 15 +- .../core/base-nodes/CCNodeWebGLRenderCmd.js | 3 + cocos2d/core/renderer/RendererWebGL.js | 307 +++++++++--------- .../core/sprites/CCSpriteWebGLRenderCmd.js | 2 + 4 files changed, 160 insertions(+), 167 deletions(-) diff --git a/cocos2d/core/base-nodes/CCNodeCanvasRenderCmd.js b/cocos2d/core/base-nodes/CCNodeCanvasRenderCmd.js index 7c3930003b..dbf2dc4652 100644 --- a/cocos2d/core/base-nodes/CCNodeCanvasRenderCmd.js +++ b/cocos2d/core/base-nodes/CCNodeCanvasRenderCmd.js @@ -41,7 +41,7 @@ cc.Node._dirtyFlags = {transformDirty: 1 << 0, visibleDirty: 1 << 1, colorDirty: //-------------------------Base ------------------------- cc.Node.RenderCmd = function(renderable){ this._dirtyFlag = 1; //need update the transform at first. - this._savedDirtyFlag = 0; + this._savedDirtyFlag = true; this._node = renderable; this._needDraw = false; @@ -221,7 +221,7 @@ cc.Node.RenderCmd.prototype = { var flags = cc.Node._dirtyFlags, locFlag = this._dirtyFlag; var colorDirty = locFlag & flags.colorDirty, opacityDirty = locFlag & flags.opacityDirty; - this._savedDirtyFlag = locFlag; + this._savedDirtyFlag = this._savedDirtyFlag || locFlag; if(colorDirty) this._updateDisplayColor(); @@ -331,9 +331,14 @@ cc.Node.RenderCmd.prototype = { _syncStatus: function (parentCmd) { // In the visit logic does not restore the _dirtyFlag // Because child elements need parent's _dirtyFlag to change himself - var flags = cc.Node._dirtyFlags, locFlag = this._dirtyFlag; - var parentNode = parentCmd ? parentCmd._node : null; - this._savedDirtyFlag = locFlag; + var flags = cc.Node._dirtyFlags, locFlag = this._dirtyFlag, parentNode = null; + if (parentCmd) { + parentNode = parentCmd._node; + this._savedDirtyFlag = this._savedDirtyFlag || parentCmd._savedDirtyFlag || locFlag; + } + else { + this._savedDirtyFlag = this._savedDirtyFlag || locFlag; + } // There is a possibility: // The parent element changed color, child element not change diff --git a/cocos2d/core/base-nodes/CCNodeWebGLRenderCmd.js b/cocos2d/core/base-nodes/CCNodeWebGLRenderCmd.js index 82036b70e7..83ed88a986 100644 --- a/cocos2d/core/base-nodes/CCNodeWebGLRenderCmd.js +++ b/cocos2d/core/base-nodes/CCNodeWebGLRenderCmd.js @@ -34,6 +34,9 @@ this._shaderProgram = null; this._camera = null; + + // Current index in the command list for improving auto batching perf + this._currId = -1; }; var proto = cc.Node.WebGLRenderCmd.prototype = Object.create(cc.Node.RenderCmd.prototype); diff --git a/cocos2d/core/renderer/RendererWebGL.js b/cocos2d/core/renderer/RendererWebGL.js index dca83c27d0..c4249bbcbd 100644 --- a/cocos2d/core/renderer/RendererWebGL.js +++ b/cocos2d/core/renderer/RendererWebGL.js @@ -60,10 +60,9 @@ var _batchedInfo = { // The current virtual buffer _currentBuffer = null, _batchBufferPool = new cc.SimplePool(), - _virtualBuffers = [], - _needUpdateBuffer = false, - _updateBufferNextFrame = false, - _orderDirtyInFrame = false; + _orderDirtyInFrame = false, + _bufferError = false, + _prevRenderCmds = []; function createVirtualBuffer (buffer, vertexOffset, matrixOrigin, matrixOffset, indexOffset, totalBufferSize, totalIndexSize, count, data) { data = data || new Uint32Array(totalBufferSize / 4); @@ -87,25 +86,9 @@ function createVirtualBuffer (buffer, vertexOffset, matrixOrigin, matrixOffset, // Render command count count: count }; - - if (CACHING_BUFFER) { - _virtualBuffers.push(vBuf); - } return vBuf; } -function clearVirtualBuffers () { - for (var i = _virtualBuffers.length-1; i >= 0; --i) { - var vbuffer = _virtualBuffers[i]; - if (vbuffer.buffer) { - _batchBufferPool.put(vbuffer.buffer); - } - vbuffer.buffer = null; - vbuffer.dataArray = null; - } - _virtualBuffers.length = 0; -} - return { childrenOrderDirty: true, assignedZ: 0, @@ -162,8 +145,8 @@ return { resetFlag: function () { if (this.childrenOrderDirty) { _orderDirtyInFrame = true; + this.childrenOrderDirty = false; } - this.childrenOrderDirty = false; this._transformNodePool.length = 0; }, @@ -195,6 +178,17 @@ return { }, clearRenderCommands: function () { + // Copy previous command list for late check in rendering + if (CACHING_BUFFER) { + var locCmds = this._renderCmds; + var i, len = locCmds.length, cmd; + for (i = 0; i < len; ++i) { + cmd = locCmds[i]; + cmd._currId = -1; + _prevRenderCmds[i] = cmd; + } + _prevRenderCmds.length = len; + } this._renderCmds.length = 0; }, @@ -225,8 +219,10 @@ return { if (cmdList.indexOf(cmd) === -1) cmdList.push(cmd); } else { - if (this._renderCmds.indexOf(cmd) === -1) + if (this._renderCmds.indexOf(cmd) === -1) { + cmd._currId = this._renderCmds.length; this._renderCmds.push(cmd); + } } }, @@ -289,6 +285,112 @@ return { return this.createBatchBuffer(bufferSize, indexSize); }, + _refreshVirtualBuffers: function () { + var renderCmds = this._renderCmds, + len = _prevRenderCmds.length, + i = 0, j = 0, end, cmd1, cmd2, next, + newBuf, currBuf, + startId, count; + + // Loop previous render command list to compare with current command list + for (; i < len; ++i) { + cmd1 = _prevRenderCmds[i]; + currBuf = cmd1._vBuffer; + matched = false; + // Check to update virtual buffer + if (currBuf) { + j = cmd1._currId; + // Removed from the command list + if (j < 0) { + cmd1._vBuffer = null; + continue; + } + + cmd1.getBatchInfo(_batchedInfo); + startId = i; + count = 0; + // Remains in the command list + cmd2 = renderCmds[j]; + while (cmd1 === cmd2 && cmd1._vBuffer === currBuf) { + ++count; + ++j; + cmd1 = _prevRenderCmds[i+count]; + cmd2 = renderCmds[j]; + } + end = i + count; + + // No valid batch + if (count <= 1) { + // Set both in case cmd1 doesn't equal cmd2 + cmd1._vBuffer = cmd2._vBuffer = null; + continue; + } + + // The next command in the current list support batch + if (cmd2._supportBatch) { + cmd2.getBatchInfo(_currentInfo); + if (_currentInfo.texture === _batchedInfo.texture && + _currentInfo.blendSrc === _batchedInfo.blendSrc && + _currentInfo.blendDst === _batchedInfo.blendDst && + _currentInfo.shader === _batchedInfo.shader) { + // Old batch dirty, clean up v buffer properties + for (; i < end; ++i) { + _prevRenderCmds[i]._vBuffer = null; + } + continue; + } + } + + // Perfect match + if (currBuf.count === count) { + i = i + count - 1; + } + // Sub match + else if (count > 1) { + // First command in buffer + cmd1 = _prevRenderCmds[i]; + // Last command in buffer + cmd2 = _prevRenderCmds[end-1]; + newBuf = createVirtualBuffer(currBuf.buffer, + cmd1._vertexOffset, + currBuf.matrixOrigin, + cmd1._matrixOffset, + cmd1._indexOffset, + currBuf.totalBufferSize, + cmd2._indexOffset - cmd1._indexOffset + cmd2.indicesPerUnit, + count, + currBuf.dataArray); + for (; i < end; ++i) { + _prevRenderCmds[i]._vBuffer = newBuf; + } + } + } + } + + // Forward batch other commands + len = renderCmds.length; + for (i = 0; i < len; ++i) { + cmd1 = renderCmds[i]; + // Already batched command, do not update + if (cmd1._vBuffer) { + continue; + } + + next = renderCmds[i+1]; + // Batching + if (cmd1._supportBatch && next && next._supportBatch) { + count = this._forwardBatch(i); + if (count > 1) { + // i will increase by 1 each loop + i += count - 1; + continue; + } + } + } + _prevRenderCmds.length = 0; + _bufferError = false; + }, + // Forward search commands that are in the same virtual buffer, // If size match then no problem to render // Otherwise, the virtual buffer need to be updated @@ -296,8 +398,7 @@ return { var renderCmds = this._renderCmds, cmd = renderCmds[first], last = first, length = renderCmds.length, - vbuffer = cmd._vBuffer, - indexSize = 0; + vbuffer = cmd._vBuffer; // Reset current buffer and batched info cmd.getBatchInfo(_batchedInfo); @@ -305,15 +406,7 @@ return { // Protection, vbuffer invalid or doesn't match the command if (cmd._vertexOffset !== vbuffer.vertexOffset || !vbuffer.buffer) { - cmd._vBuffer = null; - for (; last < length; ++last) { - cmd = renderCmds[last]; - if (vbuffer !== cmd._vBuffer) { - break; - } - cmd._vBuffer = null; - } - _updateBufferNextFrame = true; + _bufferError = true; return 0; } @@ -336,7 +429,6 @@ return { cmd.batchVertexBuffer(matrixBuffer.dataArray, cmd._vertexOffset, martixOrigin, cmd._matrixOffset); cmd._savedDirtyFlag = false; } - indexSize += cmd.indicesPerUnit; } // Send last buffer to WebGLBuffer if (matrixBuffer) { @@ -346,97 +438,17 @@ return { var size = last - first; // no problem if (vbuffer.count === size) { - // If children order dirty in this frame, then need to check the next command - // to see whether buffer should be updated - if (_orderDirtyInFrame) { - cmd = renderCmds[last]; - if (cmd && cmd._supportBatch) { - cmd.getBatchInfo(_currentInfo); - // Can be batched together, update buffer in next frame - if (_currentInfo.texture === _batchedInfo.texture && - _currentInfo.blendSrc === _batchedInfo.blendSrc && - _currentInfo.blendDst === _batchedInfo.blendDst && - _currentInfo.shader === _batchedInfo.shader) { - _updateBufferNextFrame = true; - } - } - } - _currentBuffer = vbuffer; return size; } - // New render commands have been inserted, - // need to create new virtual buffer for the current frame - // and if the inserted command can be batched, mark _needUpdateBuffer as true - // because we need to update the buffers by rerun _forwardBatch - else if (vbuffer.count > size) { - // Roll back to previous command - cmd = renderCmds[last-1]; - var secondIndexSize = vbuffer.totalIndexSize - indexSize; - var secondCount = vbuffer.count - size; - if (size > 1) { - // Update first part vbuffer - vbuffer.count = size; - vbuffer.totalIndexSize = indexSize; - _currentBuffer = vbuffer; - } - else { - // First part only contain 1 command, remove the virtual buffer - cmd._vBuffer = null; - removeByLastSwap(_virtualBuffers, _virtualBuffers.indexOf(vbuffer)); - } - - var newBuffer; - if (secondCount > 1) { - // Create second part vbuffer reusing the same buffer and data array - newBuffer = createVirtualBuffer(vbuffer.buffer, - cmd._vertexOffset * 4 + cmd.vertexBytesPerUnit, - vbuffer.matrixOrigin, - cmd._matrixOffset * 4 + cmd.matrixBytesPerUnit, - vbuffer.indexOffset + indexSize, - vbuffer.totalBufferSize, - secondIndexSize, - secondCount, - vbuffer.data); - - } - else { - // Second part only contain 1 command - newBuffer = null; - } - // Update second part commands _vBuffer - for (last = last+1; last < length; ++last) { - cmd = renderCmds[last]; - if (vbuffer !== cmd._vBuffer) { - break; - } - cmd._vBuffer = newBuffer; - } - - // The breaking command - cmd = renderCmds[first+size]; - if (cmd._supportBatch) { - cmd.getBatchInfo(_currentInfo); - cmd = renderCmds[first]; - cmd.getBatchInfo(_batchedInfo); - // Can be batched together, update buffer in next frame - if (_currentInfo.texture === _batchedInfo.texture && - _currentInfo.blendSrc === _batchedInfo.blendSrc && - _currentInfo.blendDst === _batchedInfo.blendDst && - _currentInfo.shader === _batchedInfo.shader) { - _updateBufferNextFrame = true; - } - } - return size; - } - // Render commands removed - // need to update all commands and update buffer in next frame + // buffer errored + // need to update virtual buffers in next frame else { for (last = first; last < first + size; ++last) { cmd = renderCmds[last]; cmd._vBuffer = null; } - _updateBufferNextFrame = true; + _bufferError = true; return 0; } }, @@ -477,12 +489,6 @@ return { _currentInfo.shader !== _batchedInfo.shader) { break; } - // Some render command inserted before existing batching cmds - // Need to rebuild virtual buffers - else if (!_needUpdateBuffer && cmd._vBuffer) { - _updateBufferNextFrame = true; - break; - } else { matrixOrigin += cmd.vertexBytesPerUnit; totalBufferSize += cmd.bytesPerUnit; @@ -521,7 +527,7 @@ return { gl.bindBuffer(gl.ARRAY_BUFFER, vbuffer.buffer.arrayBuffer); // Fill in vertex data command by command - var i; + var i, index = 0; for (i = first; i < last; ++i) { cmd = renderCmds[i]; cmd.batchVertexBuffer(uploadBuffer, vertexDataOffset, totalVertexData, matrixDataOffset); @@ -530,10 +536,15 @@ return { cmd._vBuffer = vbuffer; cmd._vertexOffset = vertexDataOffset; cmd._matrixOffset = matrixDataOffset; + cmd._indexOffset = index; + } + if (cmd._savedDirtyFlag) { + cmd._savedDirtyFlag = false; } vertexDataOffset += cmd.vertexBytesPerUnit / 4; matrixDataOffset += cmd.matrixBytesPerUnit / 4; + index += cmd.indicesPerUnit; } // Submit vertex data in one bufferSubData call @@ -546,13 +557,11 @@ return { // Fill in element buffer command by command var currentVertex = 0; - var index = 0; for (i = first; i < last; ++i) { cmd = renderCmds[i]; - cmd.batchIndexBuffer(indices, index, currentVertex); + cmd.batchIndexBuffer(indices, cmd._indexOffset, currentVertex); currentVertex += cmd.verticesPerUnit; - index += cmd.indicesPerUnit; } gl.bufferSubData(gl.ELEMENT_ARRAY_BUFFER, 0, indices); @@ -616,9 +625,9 @@ return { i, len, cmd, next, batchCount, context = ctx || cc._renderContext; - // Need to rebuild all virtual buffers in forward batching - if (_needUpdateBuffer) { - clearVirtualBuffers(); + // Only update virtual buffers if children order dirty in the current frame + if (ACTIVATE_AUTO_BATCH && (_orderDirtyInFrame || _bufferError)) { + this._refreshVirtualBuffers(); } for (i = 0, len = locCmds.length; i < len; i++) { @@ -628,44 +637,18 @@ return { if (ACTIVATE_AUTO_BATCH) { // Already batched in buffer if (cmd._vBuffer) { - if (!_needUpdateBuffer) { - batchCount = this._forwardCheck(i); - if (batchCount > 1) { - this._batchRendering(); - // i will increase by 1 each loop - i += batchCount - 1; - continue; - } - } - else { - cmd._vBuffer = null; - } - } - - if (!CACHING_BUFFER || _needUpdateBuffer || _orderDirtyInFrame) { - // Batching or direct rendering - if (cmd._supportBatch && next && next._supportBatch) { - batchCount = this._forwardBatch(i); - if (batchCount > 1) { - this._batchRendering(); - // i will increase by 1 each loop - i += batchCount - 1; - continue; - } + batchCount = this._forwardCheck(i); + if (batchCount > 1) { + this._batchRendering(); + // i will increase by 1 each loop + i += batchCount - 1; + continue; } } } cmd.rendering(context); } - if (_needUpdateBuffer) { - _needUpdateBuffer = false; - } - // Update virtual buffers in next frame - if (_updateBufferNextFrame) { - _needUpdateBuffer = true; - _updateBufferNextFrame = false; - } if (_orderDirtyInFrame) { _orderDirtyInFrame = false; } diff --git a/cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js b/cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js index a0464f79cb..336ea39709 100644 --- a/cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js +++ b/cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js @@ -39,6 +39,7 @@ this._vBuffer = null; this._vertexOffset = 0; this._matrixOffset = 0; + this._indexOffset = 0; if (!proto.batchShader) { proto.batchShader = cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURECOLORALPHATEST_BATCHED); @@ -257,6 +258,7 @@ proto.transform = function(parentCmd, recursive){ cc.Node.WebGLRenderCmd.prototype.transform.call(this, parentCmd, recursive); this._dirty = true; //use for batching + this._savedDirtyFlag = true; }; proto._setColorDirty = function () {}; From d0e3ea6d592cd36639e756ad4b0e83c42109e911 Mon Sep 17 00:00:00 2001 From: pandamicro Date: Sun, 17 Apr 2016 23:39:00 +0800 Subject: [PATCH 14/16] Activate CACHING_BUFFER by default --- cocos2d/core/renderer/RendererWebGL.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/cocos2d/core/renderer/RendererWebGL.js b/cocos2d/core/renderer/RendererWebGL.js index c4249bbcbd..f208187687 100644 --- a/cocos2d/core/renderer/RendererWebGL.js +++ b/cocos2d/core/renderer/RendererWebGL.js @@ -32,10 +32,7 @@ function removeByLastSwap (array, i) { } } -var CACHING_BUFFER = false; -if (!cc.sys.isMobile) { - CACHING_BUFFER = true; -} +var CACHING_BUFFER = true; var ACTIVATE_AUTO_BATCH = true; // Internal variables From c7a0c051c417856c033e9679dd7c1dfd1845c2ec Mon Sep 17 00:00:00 2001 From: pandamicro Date: Mon, 18 Apr 2016 17:02:05 +0800 Subject: [PATCH 15/16] Fix GLProgram's uniform cache issue --- .../CCClippingNodeWebGLRenderCmd.js | 3 +- cocos2d/core/CCDrawingPrimitivesWebGL.js | 26 +- cocos2d/core/platform/CCConfig.js | 2 +- cocos2d/shaders/CCGLProgram.js | 378 ++++++++++-------- cocos2d/tilemap/CCTMXLayer.js | 3 +- 5 files changed, 226 insertions(+), 186 deletions(-) diff --git a/cocos2d/clipping-nodes/CCClippingNodeWebGLRenderCmd.js b/cocos2d/clipping-nodes/CCClippingNodeWebGLRenderCmd.js index 4ade72bde9..a5c4bfe3a9 100644 --- a/cocos2d/clipping-nodes/CCClippingNodeWebGLRenderCmd.js +++ b/cocos2d/clipping-nodes/CCClippingNodeWebGLRenderCmd.js @@ -163,10 +163,9 @@ if (node.alphaThreshold < 1) { //TODO desktop var program = cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURECOLORALPHATEST); - var alphaValueLocation = gl.getUniformLocation(program.getProgram(), cc.UNIFORM_ALPHA_TEST_VALUE_S); // set our alphaThreshold cc.glUseProgram(program.getProgram()); - program.setUniformLocationWith1f(alphaValueLocation, node.alphaThreshold); + program.setUniformLocationWith1f(cc.UNIFORM_ALPHA_TEST_VALUE_S, node.alphaThreshold); cc.setProgram(node._stencil, program); } }; diff --git a/cocos2d/core/CCDrawingPrimitivesWebGL.js b/cocos2d/core/CCDrawingPrimitivesWebGL.js index 710ff80246..ee5d1e4db3 100644 --- a/cocos2d/core/CCDrawingPrimitivesWebGL.js +++ b/cocos2d/core/CCDrawingPrimitivesWebGL.js @@ -33,9 +33,9 @@ cc.DrawingPrimitiveWebGL = cc.Class.extend(/** @lends cc.DrawingPrimitiveWebGL# _renderContext:null, _initialized:false, _shader: null, - _colorLocation:-1, + _colorLocation: "u_color", _colorArray: null, - _pointSizeLocation:-1, + _pointSizeLocation: "u_pointSize", _pointSize:-1, /** * contructor of cc.DrawingPrimitiveWebGL @@ -59,8 +59,8 @@ cc.DrawingPrimitiveWebGL = cc.Class.extend(/** @lends cc.DrawingPrimitiveWebGL# // Position and 1 color passed as a uniform (to similate glColor4ub ) // _t._shader = cc.shaderCache.programForKey(cc.SHADER_POSITION_UCOLOR); - _t._colorLocation = _t._renderContext.getUniformLocation(_t._shader.getProgram(), "u_color"); - _t._pointSizeLocation = _t._renderContext.getUniformLocation(_t._shader.getProgram(), "u_pointSize"); + _t._shader._addUniformLocation(this._colorLocation); + _t._shader._addUniformLocation(this._pointSizeLocation); _t._initialized = true; } @@ -84,7 +84,7 @@ cc.DrawingPrimitiveWebGL = cc.Class.extend(/** @lends cc.DrawingPrimitiveWebGL# this._shader.use(); this._shader.setUniformForModelViewAndProjectionMatrixWithMat4(); cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POSITION); - glContext.uniform4fv(this._colorLocation, this._colorArray); + this._shader.setUniformLocationWith4fv(this._colorLocation, this._colorArray); this._shader.setUniformLocationWith1f(this._pointSizeLocation, this._pointSize); var pointBuffer = glContext.createBuffer(); @@ -113,7 +113,7 @@ cc.DrawingPrimitiveWebGL = cc.Class.extend(/** @lends cc.DrawingPrimitiveWebGL# this._shader.use(); this._shader.setUniformForModelViewAndProjectionMatrixWithMat4(); cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POSITION); - glContext.uniform4fv(this._colorLocation, this._colorArray); + this._shader.setUniformLocationWith4fv(this._colorLocation, this._colorArray); this._shader.setUniformLocationWith1f(this._pointSizeLocation, this._pointSize); var pointBuffer = glContext.createBuffer(); @@ -148,7 +148,7 @@ cc.DrawingPrimitiveWebGL = cc.Class.extend(/** @lends cc.DrawingPrimitiveWebGL# this._shader.use(); this._shader.setUniformForModelViewAndProjectionMatrixWithMat4(); cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POSITION); - glContext.uniform4fv(this._colorLocation, this._colorArray); + this._shader.setUniformLocationWith4fv(this._colorLocation, this._colorArray); var pointBuffer = glContext.createBuffer(); glContext.bindBuffer(glContext.ARRAY_BUFFER, pointBuffer); @@ -203,7 +203,7 @@ cc.DrawingPrimitiveWebGL = cc.Class.extend(/** @lends cc.DrawingPrimitiveWebGL# this._shader.use(); this._shader.setUniformForModelViewAndProjectionMatrixWithMat4(); cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POSITION); - glContext.uniform4fv(this._colorLocation, this._colorArray); + this._shader.setUniformLocationWith4fv(this._colorLocation, this._colorArray); var pointBuffer = glContext.createBuffer(); glContext.bindBuffer(glContext.ARRAY_BUFFER, pointBuffer); @@ -234,7 +234,7 @@ cc.DrawingPrimitiveWebGL = cc.Class.extend(/** @lends cc.DrawingPrimitiveWebGL# this._shader.use(); this._shader.setUniformForModelViewAndProjectionMatrixWithMat4(); cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POSITION); - glContext.uniform4fv(this._colorLocation, this._colorArray); + this._shader.setUniformLocationWith4fv(this._colorLocation, this._colorArray); var pointBuffer = glContext.createBuffer(); glContext.bindBuffer(glContext.ARRAY_BUFFER, pointBuffer); @@ -282,7 +282,7 @@ cc.DrawingPrimitiveWebGL = cc.Class.extend(/** @lends cc.DrawingPrimitiveWebGL# this._shader.use(); this._shader.setUniformForModelViewAndProjectionMatrixWithMat4(); cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POSITION); - glContext.uniform4fv(this._colorLocation, this._colorArray); + this._shader.setUniformLocationWith4fv(this._colorLocation, this._colorArray); var pointBuffer = glContext.createBuffer(); glContext.bindBuffer(glContext.ARRAY_BUFFER, pointBuffer); @@ -320,7 +320,7 @@ cc.DrawingPrimitiveWebGL = cc.Class.extend(/** @lends cc.DrawingPrimitiveWebGL# this._shader.use(); this._shader.setUniformForModelViewAndProjectionMatrixWithMat4(); cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POSITION); - glContext.uniform4fv(this._colorLocation, this._colorArray); + this._shader.setUniformLocationWith4fv(this._colorLocation, this._colorArray); var pointBuffer = glContext.createBuffer(); glContext.bindBuffer(glContext.ARRAY_BUFFER, pointBuffer); @@ -359,7 +359,7 @@ cc.DrawingPrimitiveWebGL = cc.Class.extend(/** @lends cc.DrawingPrimitiveWebGL# this._shader.use(); this._shader.setUniformForModelViewAndProjectionMatrixWithMat4(); cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POSITION); - glContext.uniform4fv(this._colorLocation, this._colorArray); + this._shader.setUniformLocationWith4fv(this._colorLocation, this._colorArray); var pointBuffer = glContext.createBuffer(); glContext.bindBuffer(glContext.ARRAY_BUFFER, pointBuffer); @@ -419,7 +419,7 @@ cc.DrawingPrimitiveWebGL = cc.Class.extend(/** @lends cc.DrawingPrimitiveWebGL# this._shader.use(); this._shader.setUniformForModelViewAndProjectionMatrixWithMat4(); cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POSITION); - glContext.uniform4fv(this._colorLocation, this._colorArray); + this._shader.setUniformLocationWith4fv(this._colorLocation, this._colorArray); var pointBuffer = glContext.createBuffer(); glContext.bindBuffer(glContext.ARRAY_BUFFER, pointBuffer); diff --git a/cocos2d/core/platform/CCConfig.js b/cocos2d/core/platform/CCConfig.js index 06061684fa..cc7d1a957a 100644 --- a/cocos2d/core/platform/CCConfig.js +++ b/cocos2d/core/platform/CCConfig.js @@ -56,7 +56,7 @@ window["CocosEngine"] = cc.ENGINE_VERSION = "Cocos2d-JS v3.11"; * @constant * @type {Number} */ -cc.FIX_ARTIFACTS_BY_STRECHING_TEXEL = 1; +cc.FIX_ARTIFACTS_BY_STRECHING_TEXEL = 0; /** * Position of the FPS (Default: 0,0 (bottom-left corner))
diff --git a/cocos2d/shaders/CCGLProgram.js b/cocos2d/shaders/CCGLProgram.js index 6213f58cc5..50eb817f23 100644 --- a/cocos2d/shaders/CCGLProgram.js +++ b/cocos2d/shaders/CCGLProgram.js @@ -26,12 +26,6 @@ THE SOFTWARE. ****************************************************************************/ -cc.HashUniformEntry = function (value, location, hh) { - this.value = value; - this.location = location; - this.hh = hh || {}; -}; - /** * Class that implements a WebGL program * @class @@ -47,28 +41,31 @@ cc.GLProgram = cc.Class.extend(/** @lends cc.GLProgram# */{ _usesTime: false, // Uniform cache - _updateUniformLocation: function (location, data, bytes) { - if (location == null) + _updateUniformLocation: function (location) { + if (!location) return false; - var updated = true; - var element = null; - for (var i = 0; i < this._hashForUniforms.length; i++) - if (this._hashForUniforms[i].location == location) - element = this._hashForUniforms[i]; + var updated; + var element = this._hashForUniforms[location]; if (!element) { - element = new cc.HashUniformEntry(); - // key - element.location = location; - // value - element.value = data; - this._hashForUniforms.push(element); + element = [ + arguments[1], + arguments[2], + arguments[3], + arguments[4] + ]; + this._hashForUniforms[location] = element; + updated = true; } else { - if (element.value == data) - updated = false; - else - element.value = data; + updated = false; + var count = arguments.length-1; + for (var i = 0; i < count; ++i) { + if (arguments[i+1] !== element[i]) { + element[i] = arguments[i+1]; + updated = true; + } + } } return updated; @@ -116,8 +113,8 @@ cc.GLProgram = cc.Class.extend(/** @lends cc.GLProgram# */{ * @returns {cc.GLProgram} */ ctor: function (vShaderFileName, fShaderFileName, glContext) { - this._uniforms = []; - this._hashForUniforms = []; + this._uniforms = {}; + this._hashForUniforms = {}; this._glContext = glContext || cc._renderContext; vShaderFileName && fShaderFileName && this.init(vShaderFileName, fShaderFileName); @@ -170,7 +167,10 @@ cc.GLProgram = cc.Class.extend(/** @lends cc.GLProgram# */{ if (this._fragShader) locGL.attachShader(this._programObj, this._fragShader); - this._hashForUniforms.length = 0; + + for (var key in this._hashForUniforms) { + delete this._hashForUniforms[key]; + } cc.checkGLErrorDebug(); return true; @@ -267,21 +267,26 @@ cc.GLProgram = cc.Class.extend(/** @lends cc.GLProgram# */{ * cc.UNIFORM_SAMPLER */ updateUniforms: function () { - this._uniforms[cc.UNIFORM_PMATRIX] = this._glContext.getUniformLocation(this._programObj, cc.UNIFORM_PMATRIX_S); - this._uniforms[cc.UNIFORM_MVMATRIX] = this._glContext.getUniformLocation(this._programObj, cc.UNIFORM_MVMATRIX_S); - this._uniforms[cc.UNIFORM_MVPMATRIX] = this._glContext.getUniformLocation(this._programObj, cc.UNIFORM_MVPMATRIX_S); - this._uniforms[cc.UNIFORM_TIME] = this._glContext.getUniformLocation(this._programObj, cc.UNIFORM_TIME_S); - this._uniforms[cc.UNIFORM_SINTIME] = this._glContext.getUniformLocation(this._programObj, cc.UNIFORM_SINTIME_S); - this._uniforms[cc.UNIFORM_COSTIME] = this._glContext.getUniformLocation(this._programObj, cc.UNIFORM_COSTIME_S); + this._uniforms[cc.UNIFORM_PMATRIX_S] = this._glContext.getUniformLocation(this._programObj, cc.UNIFORM_PMATRIX_S); + this._uniforms[cc.UNIFORM_MVMATRIX_S] = this._glContext.getUniformLocation(this._programObj, cc.UNIFORM_MVMATRIX_S); + this._uniforms[cc.UNIFORM_MVPMATRIX_S] = this._glContext.getUniformLocation(this._programObj, cc.UNIFORM_MVPMATRIX_S); + this._uniforms[cc.UNIFORM_TIME_S] = this._glContext.getUniformLocation(this._programObj, cc.UNIFORM_TIME_S); + this._uniforms[cc.UNIFORM_SINTIME_S] = this._glContext.getUniformLocation(this._programObj, cc.UNIFORM_SINTIME_S); + this._uniforms[cc.UNIFORM_COSTIME_S] = this._glContext.getUniformLocation(this._programObj, cc.UNIFORM_COSTIME_S); - this._usesTime = (this._uniforms[cc.UNIFORM_TIME] != null || this._uniforms[cc.UNIFORM_SINTIME] != null || this._uniforms[cc.UNIFORM_COSTIME] != null); + this._usesTime = (this._uniforms[cc.UNIFORM_TIME_S] != null || this._uniforms[cc.UNIFORM_SINTIME_S] != null || this._uniforms[cc.UNIFORM_COSTIME_S] != null); - this._uniforms[cc.UNIFORM_RANDOM01] = this._glContext.getUniformLocation(this._programObj, cc.UNIFORM_RANDOM01_S); - this._uniforms[cc.UNIFORM_SAMPLER] = this._glContext.getUniformLocation(this._programObj, cc.UNIFORM_SAMPLER_S); + this._uniforms[cc.UNIFORM_RANDOM01_S] = this._glContext.getUniformLocation(this._programObj, cc.UNIFORM_RANDOM01_S); + this._uniforms[cc.UNIFORM_SAMPLER_S] = this._glContext.getUniformLocation(this._programObj, cc.UNIFORM_SAMPLER_S); this.use(); // Since sample most probably won't change, set it to 0 now. - this.setUniformLocationWith1i(this._uniforms[cc.UNIFORM_SAMPLER], 0); + this.setUniformLocationWith1i(this._uniforms[cc.UNIFORM_SAMPLER_S], 0); + }, + + _addUniformLocation: function (name) { + var location = this._glContext.getUniformLocation(this._programObj, name); + this._uniforms[name] = location; }, /** @@ -289,13 +294,14 @@ cc.GLProgram = cc.Class.extend(/** @lends cc.GLProgram# */{ * @param {String} name * @returns {Number} */ - getUniformLocationForName:function(name){ - if(!name) + getUniformLocationForName: function (name) { + if (!name) throw new Error("cc.GLProgram.getUniformLocationForName(): uniform name should be non-null"); - if(!this._programObj) + if (!this._programObj) throw new Error("cc.GLProgram.getUniformLocationForName(): Invalid operation. Cannot get uniform location when program is not initialized"); - return this._glContext.getUniformLocation(this._programObj, name); + var location = this._uniforms[name] || this._glContext.getUniformLocation(this._programObj, name); + return location; }, /** @@ -303,7 +309,7 @@ cc.GLProgram = cc.Class.extend(/** @lends cc.GLProgram# */{ * @returns {WebGLUniformLocation} */ getUniformMVPMatrix: function () { - return this._uniforms[cc.UNIFORM_MVPMATRIX]; + return this._uniforms[cc.UNIFORM_MVPMATRIX_S]; }, /** @@ -311,212 +317,251 @@ cc.GLProgram = cc.Class.extend(/** @lends cc.GLProgram# */{ * @returns {WebGLUniformLocation} */ getUniformSampler: function () { - return this._uniforms[cc.UNIFORM_SAMPLER]; + return this._uniforms[cc.UNIFORM_SAMPLER_S]; }, /** * calls glUniform1i only if the values are different than the previous call for this same shader program. - * @param {WebGLUniformLocation} location + * @param {WebGLUniformLocation|String} location * @param {Number} i1 */ setUniformLocationWith1i: function (location, i1) { - var updated = this._updateUniformLocation(location, i1); - if (updated) - this._glContext.uniform1i(location, i1); + var gl = this._glContext; + if (typeof location === 'string') { + var updated = this._updateUniformLocation(location, i1); + if (updated) { + var locObj = this.getUniformLocationForName(location); + gl.uniform1i(locObj, i1); + } + } + else { + gl.uniform1i(location, i1); + } }, /** * calls glUniform2i only if the values are different than the previous call for this same shader program. - * @param {WebGLUniformLocation} location + * @param {WebGLUniformLocation|String} location * @param {Number} i1 * @param {Number} i2 */ - setUniformLocationWith2i:function(location, i1,i2){ - var intArray= [i1,i2]; - var updated = this._updateUniformLocation(location, intArray); - - if( updated ) - this._glContext.uniform2i(location, i1, i2); + setUniformLocationWith2i: function (location, i1, i2) { + var gl = this._glContext; + if (typeof location === 'string') { + var updated = this._updateUniformLocation(location, i1, i2); + if (updated) { + var locObj = this.getUniformLocationForName(location); + gl.uniform2i(locObj, i1, i2); + } + } + else { + gl.uniform2i(location, i1, i2); + } }, /** * calls glUniform3i only if the values are different than the previous call for this same shader program. - * @param {WebGLUniformLocation} location + * @param {WebGLUniformLocation|String} location * @param {Number} i1 * @param {Number} i2 * @param {Number} i3 */ - setUniformLocationWith3i:function(location, i1, i2, i3){ - var intArray = [i1,i2,i3]; - var updated = this._updateUniformLocation(location, intArray); - - if( updated ) - this._glContext.uniform3i(location, i1, i2, i3); + setUniformLocationWith3i: function (location, i1, i2, i3) { + var gl = this._glContext; + if (typeof location === 'string') { + var updated = this._updateUniformLocation(location, i1, i2, i3); + if (updated) { + var locObj = this.getUniformLocationForName(location); + gl.uniform3i(locObj, i1, i2, i3); + } + } + else { + gl.uniform3i(location, i1, i2, i3); + } }, /** * calls glUniform4i only if the values are different than the previous call for this same shader program. - * @param {WebGLUniformLocation} location + * @param {WebGLUniformLocation|String} location * @param {Number} i1 * @param {Number} i2 * @param {Number} i3 * @param {Number} i4 */ - setUniformLocationWith4i:function(location, i1, i2, i3, i4){ - var intArray = [i1,i2,i3,i4]; - var updated = this._updateUniformLocation(location, intArray); - - if( updated ) - this._glContext.uniform4i(location, i1, i2, i3, i4); + setUniformLocationWith4i: function (location, i1, i2, i3, i4) { + var gl = this._glContext; + if (typeof location === 'string') { + var updated = this._updateUniformLocation(location, i1, i2, i3, i4); + if (updated) { + var locObj = this.getUniformLocationForName(location); + gl.uniform4i(locObj, i1, i2, i3, i4); + } + } + else { + gl.uniform4i(location, i1, i2, i3, i4); + } }, /** - * calls glUniform2iv only if the values are different than the previous call for this same shader program. - * @param {WebGLUniformLocation} location + * calls glUniform2iv + * @param {WebGLUniformLocation|String} location * @param {Int32Array} intArray * @param {Number} numberOfArrays */ - setUniformLocationWith2iv:function(location, intArray, numberOfArrays){ - var updated = this._updateUniformLocation(location, intArray); - - if( updated ) - this._glContext.uniform2iv(location, intArray); + setUniformLocationWith2iv: function (location, intArray) { + var locObj = typeof location === 'string' ? this.getUniformLocationForName(location) : location; + this._glContext.uniform2iv(locObj, intArray); }, /** - * calls glUniform3iv only if the values are different than the previous call for this same shader program. - * @param {WebGLUniformLocation} location + * calls glUniform3iv + * @param {WebGLUniformLocation|String} location * @param {Int32Array} intArray - * @param {Number} numberOfArrays */ - setUniformLocationWith3iv:function(location, intArray, numberOfArrays){ - var updated = this._updateUniformLocation(location, intArray); - - if( updated ) - this._glContext.uniform3iv(location, intArray); + setUniformLocationWith3iv:function(location, intArray){ + var locObj = typeof location === 'string' ? this.getUniformLocationForName(location) : location; + this._glContext.uniform3iv(locObj, intArray); }, /** - * calls glUniform4iv only if the values are different than the previous call for this same shader program. - * @param {WebGLUniformLocation} location + * calls glUniform4iv + * @param {WebGLUniformLocation|String} location * @param {Int32Array} intArray - * @param {Number} numberOfArrays */ - setUniformLocationWith4iv:function(location, intArray, numberOfArrays){ - var updated = this._updateUniformLocation(location, intArray); - - if( updated ) - this._glContext.uniform4iv(location, intArray); + setUniformLocationWith4iv:function(location, intArray){ + var locObj = typeof location === 'string' ? this.getUniformLocationForName(location) : location; + this._glContext.uniform4iv(locObj, intArray); }, /** * calls glUniform1i only if the values are different than the previous call for this same shader program. - * @param {WebGLUniformLocation} location + * @param {WebGLUniformLocation|String} location * @param {Number} i1 */ setUniformLocationI32: function (location, i1) { - this.setUniformLocationWith1i(arguments[0], arguments[1]); + this.setUniformLocationWith1i(location, i1); }, /** * calls glUniform1f only if the values are different than the previous call for this same shader program. - * @param {WebGLUniformLocation} location + * @param {WebGLUniformLocation|String} location * @param {Number} f1 */ setUniformLocationWith1f: function (location, f1) { - var updated = this._updateUniformLocation(location, f1); - if (updated) - this._glContext.uniform1f(location, f1); + var gl = this._glContext; + if (typeof location === 'string') { + var updated = this._updateUniformLocation(location, f1); + if (updated) { + var locObj = this.getUniformLocationForName(location); + gl.uniform1f(locObj, f1); + } + } + else { + gl.uniform1f(location, f1); + } }, /** * calls glUniform2f only if the values are different than the previous call for this same shader program. - * @param {WebGLUniformLocation} location + * @param {WebGLUniformLocation|String} location * @param {Number} f1 * @param {Number} f2 */ setUniformLocationWith2f: function (location, f1, f2) { - var floats = [f1, f2]; - var updated = this._updateUniformLocation(location, floats); - if (updated) - this._glContext.uniform2f(location, f1, f2); + var gl = this._glContext; + if (typeof location === 'string') { + var updated = this._updateUniformLocation(location, f1, f2); + if (updated) { + var locObj = this.getUniformLocationForName(location); + gl.uniform2f(locObj, f1, f2); + } + } + else { + gl.uniform2f(location, f1, f2); + } }, /** * calls glUniform3f only if the values are different than the previous call for this same shader program. - * @param {WebGLUniformLocation} location + * @param {WebGLUniformLocation|String} location * @param {Number} f1 * @param {Number} f2 * @param {Number} f3 */ setUniformLocationWith3f: function (location, f1, f2, f3) { - var floats = [f1, f2, f3]; - var updated = this._updateUniformLocation(location, floats); - if (updated) - this._glContext.uniform3f(location, f1, f2, f3); + var gl = this._glContext; + if (typeof location === 'string') { + var updated = this._updateUniformLocation(location, f1, f2, f3); + if (updated) { + var locObj = this.getUniformLocationForName(location); + gl.uniform3f(locObj, f1, f2, f3); + } + } + else { + gl.uniform3f(location, f1, f2, f3); + } }, /** * calls glUniform4f only if the values are different than the previous call for this same shader program. - * @param {WebGLUniformLocation} location + * @param {WebGLUniformLocation|String} location * @param {Number} f1 * @param {Number} f2 * @param {Number} f3 * @param {Number} f4 */ setUniformLocationWith4f: function (location, f1, f2, f3, f4) { - var floats = [f1, f2, f3, f4]; - var updated = this._updateUniformLocation(location, floats); - if (updated) - this._glContext.uniform4f(location, f1, f2, f3, f4); + var gl = this._glContext; + if (typeof location === 'string') { + var updated = this._updateUniformLocation(location, f1, f2, f3, f4); + if (updated) { + var locObj = this.getUniformLocationForName(location); + gl.uniform4f(locObj, f1, f2, f3, f4); + } + } + else { + gl.uniform4f(location, f1, f2, f3, f4); + } }, /** - * calls glUniform2fv only if the values are different than the previous call for this same shader program. - * @param {WebGLUniformLocation} location + * calls glUniform2fv + * @param {WebGLUniformLocation|String} location * @param {Float32Array} floatArray - * @param {Number} numberOfArrays */ - setUniformLocationWith2fv: function (location, floatArray, numberOfArrays) { - var updated = this._updateUniformLocation(location, floatArray); - if (updated) - this._glContext.uniform2fv(location, floatArray); + setUniformLocationWith2fv: function (location, floatArray) { + var locObj = typeof location === 'string' ? this.getUniformLocationForName(location) : location; + this._glContext.uniform2fv(locObj, floatArray); }, /** - * calls glUniform3fv only if the values are different than the previous call for this same shader program. - * @param {WebGLUniformLocation} location + * calls glUniform3fv + * @param {WebGLUniformLocation|String} location * @param {Float32Array} floatArray - * @param {Number} numberOfArrays */ - setUniformLocationWith3fv: function (location, floatArray, numberOfArrays) { - var updated = this._updateUniformLocation(location, floatArray); - if (updated) - this._glContext.uniform3fv(location, floatArray); + setUniformLocationWith3fv: function (location, floatArray) { + var locObj = typeof location === 'string' ? this.getUniformLocationForName(location) : location; + this._glContext.uniform3fv(locObj, floatArray); }, /** - * calls glUniform4fv only if the values are different than the previous call for this same shader program. - * @param {WebGLUniformLocation} location + * calls glUniform4fv + * @param {WebGLUniformLocation|String} location * @param {Float32Array} floatArray - * @param {Number} numberOfArrays */ - setUniformLocationWith4fv: function (location, floatArray, numberOfArrays) { - var updated = this._updateUniformLocation(location, floatArray); - if (updated) - this._glContext.uniform4fv(location, floatArray); + setUniformLocationWith4fv: function (location, floatArray) { + var locObj = typeof location === 'string' ? this.getUniformLocationForName(location) : location; + this._glContext.uniform4fv(locObj, floatArray); }, /** - * calls glUniformMatrix4fv only if the values are different than the previous call for this same shader program. - * @param {WebGLUniformLocation} location + * calls glUniformMatrix4fv + * @param {WebGLUniformLocation|String} location * @param {Float32Array} matrixArray - * @param {Number} numberOfMatrices */ - setUniformLocationWithMatrix4fv: function (location, matrixArray, numberOfMatrices) { - var updated = this._updateUniformLocation(location, matrixArray); - if (updated) - this._glContext.uniformMatrix4fv(location, false, matrixArray); + setUniformLocationWithMatrix4fv: function (location, matrixArray) { + var locObj = typeof location === 'string' ? this.getUniformLocationForName(location) : location; + this._glContext.uniformMatrix4fv(location, false, matrixArray); }, setUniformLocationF32: function () { @@ -552,9 +597,9 @@ cc.GLProgram = cc.Class.extend(/** @lends cc.GLProgram# */{ cc.kmMat4Multiply(matrixMVP, matrixP, matrixMV); - this.setUniformLocationWithMatrix4fv(this._uniforms[cc.UNIFORM_PMATRIX], matrixP.mat, 1); - this.setUniformLocationWithMatrix4fv(this._uniforms[cc.UNIFORM_MVMATRIX], matrixMV.mat, 1); - this.setUniformLocationWithMatrix4fv(this._uniforms[cc.UNIFORM_MVPMATRIX], matrixMVP.mat, 1); + this.setUniformLocationWithMatrix4fv(this._uniforms[cc.UNIFORM_PMATRIX_S], matrixP.mat, 1); + this.setUniformLocationWithMatrix4fv(this._uniforms[cc.UNIFORM_MVMATRIX_S], matrixMV.mat, 1); + this.setUniformLocationWithMatrix4fv(this._uniforms[cc.UNIFORM_MVPMATRIX_S], matrixMVP.mat, 1); if (this._usesTime) { var director = cc.director; @@ -563,13 +608,13 @@ cc.GLProgram = cc.Class.extend(/** @lends cc.GLProgram# */{ // Getting Mach time per frame per shader using time could be extremely expensive. var time = director.getTotalFrames() * director.getAnimationInterval(); - this.setUniformLocationWith4f(this._uniforms[cc.UNIFORM_TIME], time / 10.0, time, time * 2, time * 4); - this.setUniformLocationWith4f(this._uniforms[cc.UNIFORM_SINTIME], time / 8.0, time / 4.0, time / 2.0, Math.sin(time)); - this.setUniformLocationWith4f(this._uniforms[cc.UNIFORM_COSTIME], time / 8.0, time / 4.0, time / 2.0, Math.cos(time)); + this.setUniformLocationWith4f(this._uniforms[cc.UNIFORM_TIME_S], time / 10.0, time, time * 2, time * 4); + this.setUniformLocationWith4f(this._uniforms[cc.UNIFORM_SINTIME_S], time / 8.0, time / 4.0, time / 2.0, Math.sin(time)); + this.setUniformLocationWith4f(this._uniforms[cc.UNIFORM_COSTIME_S], time / 8.0, time / 4.0, time / 2.0, Math.cos(time)); } - if (this._uniforms[cc.UNIFORM_RANDOM01] !== -1) - this.setUniformLocationWith4f(this._uniforms[cc.UNIFORM_RANDOM01], Math.random(), Math.random(), Math.random(), Math.random()); + if (this._uniforms[cc.UNIFORM_RANDOM01_S] !== -1) + this.setUniformLocationWith4f(this._uniforms[cc.UNIFORM_RANDOM01_S], Math.random(), Math.random(), Math.random(), Math.random()); }, _setUniformsForBuiltinsForRenderer: function (node) { @@ -585,9 +630,9 @@ cc.GLProgram = cc.Class.extend(/** @lends cc.GLProgram# */{ cc.kmMat4Multiply(matrixMVP, matrixP, node._renderCmd._stackMatrix); - this.setUniformLocationWithMatrix4fv(this._uniforms[cc.UNIFORM_PMATRIX], matrixP.mat, 1); - this.setUniformLocationWithMatrix4fv(this._uniforms[cc.UNIFORM_MVMATRIX], node._renderCmd._stackMatrix.mat, 1); - this.setUniformLocationWithMatrix4fv(this._uniforms[cc.UNIFORM_MVPMATRIX], matrixMVP.mat, 1); + this.setUniformLocationWithMatrix4fv(this._uniforms[cc.UNIFORM_PMATRIX_S], matrixP.mat, 1); + this.setUniformLocationWithMatrix4fv(this._uniforms[cc.UNIFORM_MVMATRIX_S], node._renderCmd._stackMatrix.mat, 1); + this.setUniformLocationWithMatrix4fv(this._uniforms[cc.UNIFORM_MVPMATRIX_S], matrixMVP.mat, 1); if (this._usesTime) { var director = cc.director; @@ -596,42 +641,42 @@ cc.GLProgram = cc.Class.extend(/** @lends cc.GLProgram# */{ // Getting Mach time per frame per shader using time could be extremely expensive. var time = director.getTotalFrames() * director.getAnimationInterval(); - this.setUniformLocationWith4f(this._uniforms[cc.UNIFORM_TIME], time / 10.0, time, time * 2, time * 4); - this.setUniformLocationWith4f(this._uniforms[cc.UNIFORM_SINTIME], time / 8.0, time / 4.0, time / 2.0, Math.sin(time)); - this.setUniformLocationWith4f(this._uniforms[cc.UNIFORM_COSTIME], time / 8.0, time / 4.0, time / 2.0, Math.cos(time)); + this.setUniformLocationWith4f(this._uniforms[cc.UNIFORM_TIME_S], time / 10.0, time, time * 2, time * 4); + this.setUniformLocationWith4f(this._uniforms[cc.UNIFORM_SINTIME_S], time / 8.0, time / 4.0, time / 2.0, Math.sin(time)); + this.setUniformLocationWith4f(this._uniforms[cc.UNIFORM_COSTIME_S], time / 8.0, time / 4.0, time / 2.0, Math.cos(time)); } - if (this._uniforms[cc.UNIFORM_RANDOM01] !== -1) - this.setUniformLocationWith4f(this._uniforms[cc.UNIFORM_RANDOM01], Math.random(), Math.random(), Math.random(), Math.random()); + if (this._uniforms[cc.UNIFORM_RANDOM01_S] !== -1) + this.setUniformLocationWith4f(this._uniforms[cc.UNIFORM_RANDOM01_S], Math.random(), Math.random(), Math.random(), Math.random()); }, /** * will update the MVP matrix on the MVP uniform if it is different than the previous call for this same shader program. */ setUniformForModelViewProjectionMatrix: function () { - this._glContext.uniformMatrix4fv(this._uniforms[cc.UNIFORM_MVPMATRIX], false, - cc.getMat4MultiplyValue(cc.projection_matrix_stack.top, cc.modelview_matrix_stack.top)); + this._glContext.uniformMatrix4fv(this._uniforms[cc.UNIFORM_MVPMATRIX_S], false, + cc.getMat4MultiplyValue(cc.projection_matrix_stack.top, cc.modelview_matrix_stack.top)); }, setUniformForModelViewProjectionMatrixWithMat4: function (swapMat4) { cc.kmMat4Multiply(swapMat4, cc.projection_matrix_stack.top, cc.modelview_matrix_stack.top); - this._glContext.uniformMatrix4fv(this._uniforms[cc.UNIFORM_MVPMATRIX], false, swapMat4.mat); + this._glContext.uniformMatrix4fv(this._uniforms[cc.UNIFORM_MVPMATRIX_S], false, swapMat4.mat); }, setUniformForModelViewAndProjectionMatrixWithMat4: function () { - this._glContext.uniformMatrix4fv(this._uniforms[cc.UNIFORM_MVMATRIX], false, cc.modelview_matrix_stack.top.mat); - this._glContext.uniformMatrix4fv(this._uniforms[cc.UNIFORM_PMATRIX], false, cc.projection_matrix_stack.top.mat); + this._glContext.uniformMatrix4fv(this._uniforms[cc.UNIFORM_MVMATRIX_S], false, cc.modelview_matrix_stack.top.mat); + this._glContext.uniformMatrix4fv(this._uniforms[cc.UNIFORM_PMATRIX_S], false, cc.projection_matrix_stack.top.mat); }, _setUniformForMVPMatrixWithMat4: function(modelViewMatrix){ if(!modelViewMatrix) throw new Error("modelView matrix is undefined."); - this._glContext.uniformMatrix4fv(this._uniforms[cc.UNIFORM_MVMATRIX], false, modelViewMatrix.mat); - this._glContext.uniformMatrix4fv(this._uniforms[cc.UNIFORM_PMATRIX], false, cc.projection_matrix_stack.top.mat); + this._glContext.uniformMatrix4fv(this._uniforms[cc.UNIFORM_MVMATRIX_S], false, modelViewMatrix.mat); + this._glContext.uniformMatrix4fv(this._uniforms[cc.UNIFORM_PMATRIX_S], false, cc.projection_matrix_stack.top.mat); }, _updateProjectionUniform: function(){ - this._glContext.uniformMatrix4fv(this._uniforms[cc.UNIFORM_PMATRIX], false, cc.projection_matrix_stack.top.mat); + this._glContext.uniformMatrix4fv(this._uniforms[cc.UNIFORM_PMATRIX_S], false, cc.projection_matrix_stack.top.mat); }, /** @@ -697,12 +742,9 @@ cc.GLProgram = cc.Class.extend(/** @lends cc.GLProgram# */{ this._programObj = null; // Purge uniform hash - for (var i = 0; i < this._hashForUniforms.length; i++) { - this._hashForUniforms[i].value = null; - this._hashForUniforms[i] = null; + for (var key in this._hashForUniforms) { + delete this._hashForUniforms[key]; } - - this._hashForUniforms.length = 0; }, /** diff --git a/cocos2d/tilemap/CCTMXLayer.js b/cocos2d/tilemap/CCTMXLayer.js index a2877df2c3..6ac6979c93 100644 --- a/cocos2d/tilemap/CCTMXLayer.js +++ b/cocos2d/tilemap/CCTMXLayer.js @@ -771,10 +771,9 @@ cc.TMXLayer = cc.SpriteBatchNode.extend(/** @lends cc.TMXLayer# */{ if (cc._renderType === cc.game.RENDER_TYPE_WEBGL) { //todo: need move to WebGL render cmd this.shaderProgram = cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURECOLORALPHATEST); - var alphaValueLocation = cc._renderContext.getUniformLocation(this.shaderProgram.getProgram(), cc.UNIFORM_ALPHA_TEST_VALUE_S); // NOTE: alpha test shader is hard-coded to use the equivalent of a glAlphaFunc(GL_GREATER) comparison this.shaderProgram.use(); - this.shaderProgram.setUniformLocationWith1f(alphaValueLocation, alphaFuncValue); + this.shaderProgram.setUniformLocationWith1f(cc.UNIFORM_ALPHA_TEST_VALUE_S, alphaFuncValue); } } else this._vertexZvalue = parseInt(vertexz, 10); From 5c252e62def276e7e2e952eb42890f702e1b2d49 Mon Sep 17 00:00:00 2001 From: pandamicro Date: Tue, 19 Apr 2016 02:25:52 +0800 Subject: [PATCH 16/16] Use only one index buffer for auto batching --- .../core/base-nodes/CCNodeCanvasRenderCmd.js | 15 --- cocos2d/core/renderer/RendererWebGL.js | 106 +++++++++--------- .../core/sprites/CCSpriteWebGLRenderCmd.js | 12 -- cocos2d/core/utils/CCProfiler.js | 2 +- cocos2d/core/utils/CCSimplePool.js | 2 +- 5 files changed, 58 insertions(+), 79 deletions(-) diff --git a/cocos2d/core/base-nodes/CCNodeCanvasRenderCmd.js b/cocos2d/core/base-nodes/CCNodeCanvasRenderCmd.js index dbf2dc4652..cd2a27aeeb 100644 --- a/cocos2d/core/base-nodes/CCNodeCanvasRenderCmd.js +++ b/cocos2d/core/base-nodes/CCNodeCanvasRenderCmd.js @@ -57,9 +57,6 @@ cc.Node.RenderCmd = function(renderable){ this._cascadeOpacityEnabledDirty = false; this._curLevel = -1; - - // this._minZ = 0; - // this._maxZ = 0; }; cc.Node.RenderCmd.prototype = { @@ -384,8 +381,6 @@ cc.Node.RenderCmd.prototype = { var node = this._node; var i, children = node._children, child, cmd; var len = children.length; - // var minZ = Number.MAX_VALUE; - // var maxZ = -Number.MAX_VALUE; if (len > 0) { node.sortAllChildren(); // draw children zOrder < 0 @@ -394,8 +389,6 @@ cc.Node.RenderCmd.prototype = { if (child._localZOrder < 0) { cmd = child._renderCmd; cmd.visit(this); - // minZ = Math.min(minZ, child._minZ); - // maxZ = Math.max(maxZ, child._maxZ); } else { break; @@ -406,19 +399,11 @@ cc.Node.RenderCmd.prototype = { node._vertexZ = z; renderer.assignedZ += renderer.assignedZStep; - // minZ = Math.min(minZ,z); - // maxZ = Math.max(maxZ,z); - renderer.pushRenderCommand(this); for (; i < len; i++) { child = children[i]; child._renderCmd.visit(this); - // minZ = Math.min(minZ, child._minZ); - // maxZ = Math.max(maxZ, child._maxZ); } - - // node._minZ = minZ; - // node._maxZ = maxZ; } else { node._vertexZ = renderer.assignedZ; renderer.assignedZ += renderer.assignedZStep; diff --git a/cocos2d/core/renderer/RendererWebGL.js b/cocos2d/core/renderer/RendererWebGL.js index f208187687..cfb88374d2 100644 --- a/cocos2d/core/renderer/RendererWebGL.js +++ b/cocos2d/core/renderer/RendererWebGL.js @@ -59,9 +59,49 @@ var _batchedInfo = { _batchBufferPool = new cc.SimplePool(), _orderDirtyInFrame = false, _bufferError = false, - _prevRenderCmds = []; + _prevRenderCmds = [], + _quadIndexBuffer = { + buffer: null, + maxQuads: 0 + }; + +// Inspired from @Heishe's gotta-batch-them-all branch +// https://github.com/Talisca/cocos2d-html5/commit/de731f16414eb9bcaa20480006897ca6576d362c +function updateQuadIndexBuffer (numQuads) { + if (!_quadIndexBuffer.buffer) { + return; + } + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, _quadIndexBuffer.buffer); + + var indices = new Uint16Array(numQuads * 6); + var currentQuad = 0; + for (var i = 0, len = numQuads * 6; i < len; i += 6) { + indices[i] = currentQuad + 0; + indices[i + 1] = currentQuad + 1; + indices[i + 2] = currentQuad + 2; + indices[i + 3] = currentQuad + 1; + indices[i + 4] = currentQuad + 2; + indices[i + 5] = currentQuad + 3; + currentQuad += 4; + } + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW); +} + +// Inspired from @Heishe's gotta-batch-them-all branch +// https://github.com/Talisca/cocos2d-html5/commit/de731f16414eb9bcaa20480006897ca6576d362c +function getQuadIndexBuffer (numQuads) { + if (_quadIndexBuffer.buffer === null) { + _quadIndexBuffer.buffer = gl.createBuffer(); + } + + if (_quadIndexBuffer.maxQuads < numQuads) { + updateQuadIndexBuffer(numQuads); + } + + return _quadIndexBuffer.buffer; +} -function createVirtualBuffer (buffer, vertexOffset, matrixOrigin, matrixOffset, indexOffset, totalBufferSize, totalIndexSize, count, data) { +function createVirtualBuffer (buffer, vertexOffset, matrixOrigin, matrixOffset, totalBufferSize, count, data) { data = data || new Uint32Array(totalBufferSize / 4); var vBuf = { // The object contains real WebGL buffers, it's created or retrieved via getBatchBuffer @@ -74,12 +114,8 @@ function createVirtualBuffer (buffer, vertexOffset, matrixOrigin, matrixOffset, matrixOrigin: matrixOrigin, // The start offset after the origin of matrix data in the vertex buffer, in bytes matrixOffset: matrixOffset, - // The start offset in the index buffer - indexOffset: indexOffset, // Total vertex array buffer size, including vertex data and matrix data, in bytes totalBufferSize: totalBufferSize, - // Index array size - totalIndexSize: totalIndexSize, // Render command count count: count }; @@ -225,31 +261,27 @@ return { // Auto batch implementation inspired from @Heishe 's PR // Ref: https://github.com/cocos2d/cocos2d-html5/pull/3248 - createBatchBuffer: function (bufferSize, indexSize) { + createBatchBuffer: function (bufferSize) { var arrayBuffer = gl.createBuffer(); - var elementBuffer = gl.createBuffer(); - this.initBatchBuffers(arrayBuffer, elementBuffer, bufferSize, indexSize); + this.initBatchBuffers(arrayBuffer, bufferSize); - return {arrayBuffer: arrayBuffer, elementBuffer: elementBuffer, bufferSize: bufferSize, indexSize: indexSize}; + return {arrayBuffer: arrayBuffer, bufferSize: bufferSize}; }, // Auto batch implementation inspired from @Heishe 's PR // Ref: https://github.com/cocos2d/cocos2d-html5/pull/3248 - initBatchBuffers: function (arrayBuffer, elementBuffer, bufferSize, indexSize) { + initBatchBuffers: function (arrayBuffer, bufferSize) { gl.bindBuffer(gl.ARRAY_BUFFER, arrayBuffer); gl.bufferData(gl.ARRAY_BUFFER, bufferSize, gl.DYNAMIC_DRAW); - - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elementBuffer); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indexSize, gl.DYNAMIC_DRAW); }, // Auto batch implementation inspired from @Heishe 's PR // Ref: https://github.com/cocos2d/cocos2d-html5/pull/3248 - // Returns an object with {arrayBuffer, elementBuffer, size}, + // Returns an object with {arrayBuffer, size}, // where size denotes how many unit fit in the buffer (no need for bufferData if it's already big enough, bufferSubData enough) - getBatchBuffer: function(bufferSize, indexSize) + getBatchBuffer: function(bufferSize) { if (_batchBufferPool.size() > 0) { var minSize = Number.MAX_VALUE; @@ -257,7 +289,7 @@ return { var buf = _batchBufferPool.find(function (i, buf) { // Find available buffer with suitable size - if (buf.bufferSize >= bufferSize && buf.indexSize >= indexSize) { + if (buf.bufferSize >= bufferSize) { return true; } @@ -272,14 +304,13 @@ return { }); if (buf) { - this.initBatchBuffers(buf.arrayBuffer, buf.elementBuffer, bufferSize, indexSize); + this.initBatchBuffers(buf.arrayBuffer, bufferSize); buf.bufferSize = bufferSize; - buf.indexSize = indexSize; return buf; } } - return this.createBatchBuffer(bufferSize, indexSize); + return this.createBatchBuffer(bufferSize); }, _refreshVirtualBuffers: function () { @@ -346,15 +377,11 @@ return { else if (count > 1) { // First command in buffer cmd1 = _prevRenderCmds[i]; - // Last command in buffer - cmd2 = _prevRenderCmds[end-1]; newBuf = createVirtualBuffer(currBuf.buffer, cmd1._vertexOffset, currBuf.matrixOrigin, cmd1._matrixOffset, - cmd1._indexOffset, currBuf.totalBufferSize, - cmd2._indexOffset - cmd1._indexOffset + cmd2.indicesPerUnit, count, currBuf.dataArray); for (; i < end; ++i) { @@ -468,7 +495,6 @@ return { var matrixOrigin = cmd.vertexBytesPerUnit; var totalBufferSize = cmd.bytesPerUnit; - var totalIndexSize = cmd.indicesPerUnit; // Forward search and collect batch informations cmd = renderCmds[last]; @@ -489,7 +515,6 @@ return { else { matrixOrigin += cmd.vertexBytesPerUnit; totalBufferSize += cmd.bytesPerUnit; - totalIndexSize += cmd.indicesPerUnit; } ++last; cmd = renderCmds[last]; @@ -501,16 +526,14 @@ return { return count; } - var buffer = this.getBatchBuffer(totalBufferSize, totalIndexSize * 2); // *2 because we use shorts for indices + var buffer = this.getBatchBuffer(totalBufferSize); // Create a virtual buffer var vbuffer = createVirtualBuffer(buffer, 0, matrixOrigin, 0, - 0, totalBufferSize, - totalIndexSize, count); _currentBuffer = vbuffer; var uploadBuffer = vbuffer.dataArray; @@ -524,7 +547,7 @@ return { gl.bindBuffer(gl.ARRAY_BUFFER, vbuffer.buffer.arrayBuffer); // Fill in vertex data command by command - var i, index = 0; + var i; for (i = first; i < last; ++i) { cmd = renderCmds[i]; cmd.batchVertexBuffer(uploadBuffer, vertexDataOffset, totalVertexData, matrixDataOffset); @@ -533,7 +556,6 @@ return { cmd._vBuffer = vbuffer; cmd._vertexOffset = vertexDataOffset; cmd._matrixOffset = matrixDataOffset; - cmd._indexOffset = index; } if (cmd._savedDirtyFlag) { cmd._savedDirtyFlag = false; @@ -541,28 +563,11 @@ return { vertexDataOffset += cmd.vertexBytesPerUnit / 4; matrixDataOffset += cmd.matrixBytesPerUnit / 4; - index += cmd.indicesPerUnit; } // Submit vertex data in one bufferSubData call gl.bufferSubData(gl.ARRAY_BUFFER, 0, uploadBuffer); - // Bind element buffer - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, vbuffer.buffer.elementBuffer); - - var indices = new Uint16Array(totalIndexSize); - - // Fill in element buffer command by command - var currentVertex = 0; - for (i = first; i < last; ++i) { - cmd = renderCmds[i]; - cmd.batchIndexBuffer(indices, cmd._indexOffset, currentVertex); - - currentVertex += cmd.verticesPerUnit; - } - - gl.bufferSubData(gl.ELEMENT_ARRAY_BUFFER, 0, indices); - if (!CACHING_BUFFER) { _batchBufferPool.put(buffer); } @@ -603,8 +608,9 @@ return { gl.vertexAttribPointer(cc.VERTEX_ATTRIB_MVMAT0 + i, 4, gl.FLOAT, false, bytesPerRow * 4, matrixOffset + bytesPerRow * i); //stride is one row } - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, _currentBuffer.buffer.elementBuffer); - gl.drawElements(gl.TRIANGLES, _currentBuffer.totalIndexSize, gl.UNSIGNED_SHORT, _currentBuffer.indexOffset); + var elemBuffer = getQuadIndexBuffer(count); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elemBuffer); + gl.drawElements(gl.TRIANGLES, count * 6, gl.UNSIGNED_SHORT, 0); for (i = 0; i < 4; ++i) { gl.disableVertexAttribArray(cc.VERTEX_ATTRIB_MVMAT0 + i); diff --git a/cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js b/cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js index 336ea39709..7b977e7ba0 100644 --- a/cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js +++ b/cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js @@ -39,7 +39,6 @@ this._vBuffer = null; this._vertexOffset = 0; this._matrixOffset = 0; - this._indexOffset = 0; if (!proto.batchShader) { proto.batchShader = cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURECOLORALPHATEST_BATCHED); @@ -555,15 +554,4 @@ buffer[offset3 + i] = val; } }; - - proto.batchIndexBuffer = function (indices, index, vertexIndex) { - // Fill in index buffer, we split quad into two triangles - // because only triangles can be batched - indices[index] = vertexIndex + 0; - indices[index + 1] = vertexIndex + 1; - indices[index + 2] = vertexIndex + 2; - indices[index + 3] = vertexIndex + 1; - indices[index + 4] = vertexIndex + 2; - indices[index + 5] = vertexIndex + 3; - }; })(); \ No newline at end of file diff --git a/cocos2d/core/utils/CCProfiler.js b/cocos2d/core/utils/CCProfiler.js index bdbd7b9ae4..0aa1493aa4 100644 --- a/cocos2d/core/utils/CCProfiler.js +++ b/cocos2d/core/utils/CCProfiler.js @@ -31,7 +31,7 @@ cc.profiler = (function () { }; var analyseFPS = function (fps) { - var lastId = i = LEVELS.length - 1, ratio, average = 0; + var lastId = LEVELS.length - 1, i = lastId, ratio, average = 0; _analyseCount++; _totalFPS += fps; diff --git a/cocos2d/core/utils/CCSimplePool.js b/cocos2d/core/utils/CCSimplePool.js index f196ea0d1a..30fa22cdbc 100644 --- a/cocos2d/core/utils/CCSimplePool.js +++ b/cocos2d/core/utils/CCSimplePool.js @@ -33,7 +33,7 @@ cc.SimplePool.prototype = { }, put: function (obj) { - if (obj && this._pool.indexOf(obj) !== -1) { + if (obj && this._pool.indexOf(obj) === -1) { this._pool.unshift(obj); } },