diff --git a/CCBoot.js b/CCBoot.js index 251b2c5fdd..66ffd6c2d3 100644 --- a/CCBoot.js +++ b/CCBoot.js @@ -1826,6 +1826,7 @@ var _initSys = function () { } } catch (e) {} + tmpCanvas = null; } /** @@ -1936,6 +1937,9 @@ var _initSys = function () { }; _initSys(); +_tmpCanvas1 = null; +_tmpCanvas2 = null; + //to make sure the cc.log, cc.warn, cc.error and cc.assert would not throw error before init by debugger mode. cc.log = cc.warn = cc.error = cc.assert = function () { }; @@ -2068,7 +2072,7 @@ cc.initEngine = function (config, cb) { document.body ? _load(config) : cc._addEventListener(window, 'load', _windowLoaded, false); _engineInitCalled = true; -} +}; })(); //+++++++++++++++++++++++++Engine initialization function end+++++++++++++++++++++++++++++ @@ -2592,12 +2596,10 @@ cc.game = /** @lends cc.game# */{ if (this._renderContext) { cc.renderer = cc.rendererWebGL; win.gl = this._renderContext; // global variable declared in CCMacro.js + cc.renderer.initQuadIndexBuffer(); 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/labelttf/CCLabelTTF.js b/cocos2d/core/labelttf/CCLabelTTF.js index 4e1756e9ad..a67f9e4c5d 100644 --- a/cocos2d/core/labelttf/CCLabelTTF.js +++ b/cocos2d/core/labelttf/CCLabelTTF.js @@ -291,7 +291,7 @@ cc.LabelTTF = cc.Sprite.extend(/** @lends cc.LabelTTF# */{ if (a.r != null && a.g != null && a.b != null && a.a != null) { this._enableShadow(a, b, c); } else { - this._enableShadowNoneColor(a, b, c, d) + this._enableShadowNoneColor(a, b, c, d); } }, diff --git a/cocos2d/core/labelttf/CCLabelTTFCanvasRenderCmd.js b/cocos2d/core/labelttf/CCLabelTTFCanvasRenderCmd.js index a8e0c304a3..d66ea3081c 100644 --- a/cocos2d/core/labelttf/CCLabelTTFCanvasRenderCmd.js +++ b/cocos2d/core/labelttf/CCLabelTTFCanvasRenderCmd.js @@ -211,7 +211,7 @@ cc.LabelTTF._firsrEnglish = /^[a-zA-Z0-9ÄÖÜäöüßéèçàùêâîôû]/; context.setTransform(1, 0, 0, 1, locStatus.contextTransform.x, locStatus.contextTransform.y); var xOffset = locStatus.xOffset; var yOffsetArray = locStatus.OffsetYArray; - this.drawLabels(context, xOffset, yOffsetArray) + this.drawLabels(context, xOffset, yOffsetArray); }; proto._checkWarp = function (strArr, i, maxWidth) { @@ -350,7 +350,7 @@ cc.LabelTTF._firsrEnglish = /^[a-zA-Z0-9ÄÖÜäöüßéèçàùêâîôû]/; context.fillText(line, xOffset, yOffsetArray[i]); } cc.g_NumberOfDraws++; - } + }; })(); (function(){ diff --git a/cocos2d/core/layers/CCLayerCanvasRenderCmd.js b/cocos2d/core/layers/CCLayerCanvasRenderCmd.js index 7ae79ca588..42ac328cc8 100644 --- a/cocos2d/core/layers/CCLayerCanvasRenderCmd.js +++ b/cocos2d/core/layers/CCLayerCanvasRenderCmd.js @@ -97,7 +97,7 @@ for(var i = 0, len = children.length; i < len; i++) children[i]._renderCmd._setCachedParent(this); - if (!this._bakeSprite){ + if (!this._bakeSprite) { this._bakeSprite = new cc.BakeSprite(); this._bakeSprite.setAnchorPoint(0,0); } diff --git a/cocos2d/core/platform/CCMacro.js b/cocos2d/core/platform/CCMacro.js index bcb4e63b1d..11e610b1ee 100644 --- a/cocos2d/core/platform/CCMacro.js +++ b/cocos2d/core/platform/CCMacro.js @@ -562,26 +562,6 @@ cc.VERTEX_ATTRIB_COLOR = 1; * @type {Number} */ cc.VERTEX_ATTRIB_TEX_COORDS = 2; -/** - * @constant - * @type {Number} - */ -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} @@ -650,12 +630,17 @@ cc.SHADER_POSITION_TEXTURECOLORALPHATEST = "ShaderPositionTextureColorAlphaTest" * @constant * @type {String} */ -cc.SHADER_POSITION_TEXTURECOLORALPHATEST_BATCHED = "ShaderPositionTextureColorAlphaTestBatched"; +cc.SHADER_SPRITE_POSITION_TEXTURECOLORALPHATEST = "ShaderSpritePositionTextureColorAlphaTest"; /** * @constant * @type {String} */ cc.SHADER_POSITION_COLOR = "ShaderPositionColor"; +/** + * @constant + * @type {String} + */ +cc.SHADER_SPRITE_POSITION_COLOR = "ShaderSpritePositionColor"; /** * @constant * @type {String} diff --git a/cocos2d/core/renderer/GlobalVertexBuffer.js b/cocos2d/core/renderer/GlobalVertexBuffer.js new file mode 100644 index 0000000000..935c161846 --- /dev/null +++ b/cocos2d/core/renderer/GlobalVertexBuffer.js @@ -0,0 +1,138 @@ +/**************************************************************************** + 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. + ****************************************************************************/ + +var GlobalVertexBuffer = (function () { + +var VERTICES_SIZE = 888; + +var GlobalVertexBuffer = function (gl) { + // WebGL buffer + this.gl = gl; + this.vertexBuffer = gl.createBuffer(); + + this.size = VERTICES_SIZE; + this.byteLength = VERTICES_SIZE * 4 * cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT; + + // buffer data and views + this.data = new ArrayBuffer(this.byteLength); + this.dataArray = new Float32Array(this.data); + + // Init buffer data + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.byteLength, gl.DYNAMIC_DRAW); + + this._dirty = false; + this._spaces = { + 0: this.byteLength + }; +}; +GlobalVertexBuffer.prototype = { + constructor: GlobalVertexBuffer, + + allocBuffer: function (offset, size) { + var space = this._spaces[offset]; + if (space && space >= size) { + // Remove the space + delete this._spaces[offset]; + if (space > size) { + var newOffset = offset + size; + this._spaces[newOffset] = space - size; + } + return true; + } + else { + return false; + } + }, + + requestBuffer: function (size) { + var key, offset, available; + for (key in this._spaces) { + offset = parseInt(key); + available = this._spaces[key]; + if (available >= size && this.allocBuffer(offset, size)) { + return { + buffer: this, + offset: offset, + size: size + }; + } + } + return null; + }, + + freeBuffer: function (offset, size) { + var spaces = this._spaces; + var i, key, end; + // Merge with previous space + for (key in spaces) { + i = parseInt(key); + if (i > offset) { + break; + } + if (i + spaces[key] >= offset) { + size = size + offset - i; + offset = i; + break; + } + } + + end = offset + size; + // Merge with next space + if (this._spaces[end]) { + size += this._spaces[end]; + delete this._spaces[end]; + } + + this._spaces[offset] = size; + }, + + setDirty: function () { + this._dirty = true; + }, + + update: function () { + if (this._dirty) { + this.gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + // Note: Can memorize different dirty zones and update them separately, maybe faster + this.gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.dataArray); + this._dirty = false; + } + }, + + destroy: function () { + this.gl.deleteBuffer(this.vertexBuffer); + + this.data = null; + this.positions = null; + this.colors = null; + this.texCoords = null; + + this.vertexBuffer = null; + } +}; + +return GlobalVertexBuffer; + +})(); \ No newline at end of file diff --git a/cocos2d/core/renderer/RendererWebGL.js b/cocos2d/core/renderer/RendererWebGL.js index cfb88374d2..4f09548a6c 100644 --- a/cocos2d/core/renderer/RendererWebGL.js +++ b/cocos2d/core/renderer/RendererWebGL.js @@ -36,8 +36,10 @@ var CACHING_BUFFER = true; var ACTIVATE_AUTO_BATCH = true; // Internal variables + // Global vertex buffers, shared by sprites +var _gbuffers = [], // Batching general informations -var _batchedInfo = { + _batchedInfo = { // 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 @@ -71,6 +73,7 @@ function updateQuadIndexBuffer (numQuads) { if (!_quadIndexBuffer.buffer) { return; } + var gl = cc._renderContext; gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, _quadIndexBuffer.buffer); var indices = new Uint16Array(numQuads * 6); @@ -85,13 +88,14 @@ function updateQuadIndexBuffer (numQuads) { currentQuad += 4; } gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW); + _quadIndexBuffer.maxQuads = numQuads; } // 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(); + _quadIndexBuffer.buffer = cc._renderContext.createBuffer(); } if (_quadIndexBuffer.maxQuads < numQuads) { @@ -101,20 +105,19 @@ function getQuadIndexBuffer (numQuads) { return _quadIndexBuffer.buffer; } -function createVirtualBuffer (buffer, vertexOffset, matrixOrigin, matrixOffset, totalBufferSize, count, data) { - data = data || new Uint32Array(totalBufferSize / 4); +function createVirtualBuffer (buffer, vertexOffset, totalBufferSize, count, data) { + data = data || new Float32Array(totalBufferSize / 4); + var uint32View = new Uint32Array(data.buffer); 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 vertex data array (Float32Array) + float32View: data, + // Uint32 view + uint32View: uint32View, // The start offset in the vertex buffer, in bytes vertexOffset: vertexOffset, - // 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, - // Total vertex array buffer size, including vertex data and matrix data, in bytes + // Total vertex array buffer size, including vertex data, in bytes totalBufferSize: totalBufferSize, // Render command count count: count @@ -136,6 +139,35 @@ return { _currentID: 0, _clearColor: cc.color(), //background color,default BLACK + initQuadIndexBuffer: function () { + getQuadIndexBuffer(1000); + }, + + requestBuffer: function (size) { + var i, len = _gbuffers.length, buffer, + gl = cc._renderContext, + result; + for (i = 0; i < len; ++i) { + buffer = _gbuffers[i]; + if (buffer.gl === gl) { + result = buffer.requestBuffer(size); + if (result) { + return result; + } + } + } + + if (!result) { + buffer = new GlobalVertexBuffer(gl); + _gbuffers.push(buffer); + result = buffer.requestBuffer(size); + } + if (!result) { + cc.error('Request WebGL buffer failed'); + } + return result; + }, + getRenderCmd: function (renderableObject) { //TODO Add renderCmd pool here return renderableObject._createRenderCmd(); @@ -365,6 +397,8 @@ return { for (; i < end; ++i) { _prevRenderCmds[i]._vBuffer = null; } + // keeping i correct, it should run through all elements + i--; continue; } } @@ -379,14 +413,14 @@ return { cmd1 = _prevRenderCmds[i]; newBuf = createVirtualBuffer(currBuf.buffer, cmd1._vertexOffset, - currBuf.matrixOrigin, - cmd1._matrixOffset, currBuf.totalBufferSize, count, - currBuf.dataArray); + currBuf.float32View); for (; i < end; ++i) { _prevRenderCmds[i]._vBuffer = newBuf; } + // keeping i correct, it should run through all elements + i--; } } } @@ -435,28 +469,27 @@ return { } // Forward check - var matrixBuffer, martixOrigin; + var vertexBuffer; for (; last < length; ++last) { cmd = renderCmds[last]; if (vbuffer !== cmd._vBuffer) { break; } - // Lazy update transform matrix in buffer - if (cmd._savedDirtyFlag) { - if (!matrixBuffer) { + // Lazy update vertex in buffer + if (cmd._bufferDirty) { + if (!vertexBuffer) { // Bind buffer - matrixBuffer = vbuffer; - martixOrigin = matrixBuffer.matrixOrigin / 4; - gl.bindBuffer(gl.ARRAY_BUFFER, matrixBuffer.buffer.arrayBuffer); + vertexBuffer = vbuffer; + gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer.buffer.arrayBuffer); } - cmd.batchVertexBuffer(matrixBuffer.dataArray, cmd._vertexOffset, martixOrigin, cmd._matrixOffset); - cmd._savedDirtyFlag = false; + cmd.batchVertexBuffer(vertexBuffer.float32View, vertexBuffer.uint32View, cmd._vertexOffset); + cmd._bufferDirty = false; } } // Send last buffer to WebGLBuffer - if (matrixBuffer) { - gl.bufferSubData(gl.ARRAY_BUFFER, 0, matrixBuffer.dataArray); + if (vertexBuffer) { + gl.bufferSubData(gl.ARRAY_BUFFER, 0, vertexBuffer.float32View); } var size = last - first; @@ -493,7 +526,6 @@ return { if (!_batchedInfo.texture) return 0; - var matrixOrigin = cmd.vertexBytesPerUnit; var totalBufferSize = cmd.bytesPerUnit; // Forward search and collect batch informations @@ -513,7 +545,6 @@ return { break; } else { - matrixOrigin += cmd.vertexBytesPerUnit; totalBufferSize += cmd.bytesPerUnit; } ++last; @@ -530,18 +561,14 @@ return { // Create a virtual buffer var vbuffer = createVirtualBuffer(buffer, - 0, - matrixOrigin, 0, totalBufferSize, count); _currentBuffer = vbuffer; - var uploadBuffer = vbuffer.dataArray; + var uploadBuffer = vbuffer.float32View; - //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 = matrixOrigin / 4; + //all of the divisions by 4 are just because we work with Float32Arrays instead of uint8 arrays so all indexes need to be shortened by the factor of 4 var vertexDataOffset = 0; - var matrixDataOffset = 0; // Bind vertex data buffer gl.bindBuffer(gl.ARRAY_BUFFER, vbuffer.buffer.arrayBuffer); @@ -550,19 +577,17 @@ return { var i; for (i = first; i < last; ++i) { cmd = renderCmds[i]; - cmd.batchVertexBuffer(uploadBuffer, vertexDataOffset, totalVertexData, matrixDataOffset); + cmd.batchVertexBuffer(uploadBuffer, vbuffer.uint32View, vertexDataOffset); if (CACHING_BUFFER) { cmd._vBuffer = vbuffer; cmd._vertexOffset = vertexDataOffset; - cmd._matrixOffset = matrixDataOffset; } if (cmd._savedDirtyFlag) { cmd._savedDirtyFlag = false; } vertexDataOffset += cmd.vertexBytesPerUnit / 4; - matrixDataOffset += cmd.matrixBytesPerUnit / 4; } // Submit vertex data in one bufferSubData call @@ -599,23 +624,11 @@ return { 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, matrixOffset + bytesPerRow * i); //stride is one row - } 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); - } - cc.g_NumberOfDraws++; }, @@ -628,12 +641,17 @@ return { i, len, cmd, next, batchCount, context = ctx || cc._renderContext; + // Update all global buffers (only invoke bufferData when buffer is dirty) + for (i = 0, len = _gbuffers.length; i < len; ++i) { + _gbuffers[i].update(); + } + // 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++) { + for (i = 0, len = locCmds.length; i < len; ++i) { cmd = locCmds[i]; next = locCmds[i+1]; diff --git a/cocos2d/core/sprites/CCSprite.js b/cocos2d/core/sprites/CCSprite.js index c38a2f3788..dc4b35a637 100644 --- a/cocos2d/core/sprites/CCSprite.js +++ b/cocos2d/core/sprites/CCSprite.js @@ -132,6 +132,20 @@ cc.Sprite = cc.Node.extend(/** @lends cc.Sprite# */{ self._softInit(fileName, rect, rotated); }, + onEnter: function () { + this._super(); + if (cc._renderType === cc.game.RENDER_TYPE_WEBGL) { + this._renderCmd.updateBuffer(); + } + }, + + cleanup: function () { + if (cc._renderType === cc.game.RENDER_TYPE_WEBGL) { + this._renderCmd.freeBuffer(); + } + this._super(); + }, + /** * Returns whether the texture have been loaded * @returns {boolean} @@ -711,7 +725,7 @@ cc.Sprite = cc.Node.extend(/** @lends cc.Sprite# */{ if(_t.texture) _t.texture.removeEventListener("load", _t); texture.addEventListener("load", _t._renderCmd._textureLoadedCallback, _t); - _t.texture = texture; + _t.setTexture(texture); return true; } diff --git a/cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js b/cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js index 7b977e7ba0..65f1370119 100644 --- a/cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js +++ b/cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js @@ -24,24 +24,47 @@ //Sprite's WebGL render command (function() { - var matrixByteSize = 4 * 4 * 4; //4 rows of 4 floats, 4 bytes each + + var _resetPointers = true; cc.Sprite.WebGLRenderCmd = function (renderable) { cc.Node.WebGLRenderCmd.call(this, renderable); 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._vertices = [ + {x: 0, y: 0, z: 0}, + {x: 0, y: 0, z: 0}, + {x: 0, y: 0, z: 0}, + {x: 0, y: 0, z: 0} + ]; + var length = this.vertexBytesPerUnit; + var bufInfo = cc.renderer.requestBuffer(length); + this._buffer = bufInfo.buffer; + this._bufferOffset = bufInfo.offset; + this._quad = new cc.V3F_C4B_T2F_Quad(null, null, null, null, this._buffer.data, this._bufferOffset); + this._float32View = new Float32Array(this._buffer.data, this._bufferOffset, length / 4); + this._uint32View = new Uint32Array(this._buffer.data, this._bufferOffset, length / 4); + + // Separated webgl buffer implementation + // this._buffer = new ArrayBuffer(length); + // this._bufferOffset = 0; + // this._quad = new cc.V3F_C4B_T2F_Quad(null, null, null, null, this._buffer, this._bufferOffset); + // this._float32View = new Float32Array(this._buffer, this._bufferOffset, length / 4); + // this._uint32View = new Uint32Array(this._buffer, this._bufferOffset, length / 4); + // // Init buffer + // var gl = cc._renderContext; + // this._glBuffer = gl.createBuffer(); + // gl.bindBuffer(gl.ARRAY_BUFFER, this._glBuffer); + // gl.bufferData(gl.ARRAY_BUFFER, length, gl.DYNAMIC_DRAW); + this._dirty = false; + this._bufferDirty = 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); + proto.batchShader = cc.shaderCache.programForKey(cc.SHADER_SPRITE_POSITION_TEXTURECOLORALPHATEST); } }; @@ -50,8 +73,7 @@ // 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.bytesPerUnit = proto.vertexBytesPerUnit; proto.indicesPerUnit = 6; proto.verticesPerUnit = 4; proto._supportBatch = true; @@ -65,6 +87,33 @@ info.shader = this.batchShader; }; + proto.updateBuffer = function () { + if (!this._buffer) { + var length = this.vertexBytesPerUnit; + var bufInfo = cc.renderer.requestBuffer(length); + this._buffer = bufInfo.buffer; + this._bufferOffset = bufInfo.offset; + this._quad = new cc.V3F_C4B_T2F_Quad(null, null, null, null, this._buffer.data, this._bufferOffset); + this._float32View = new Float32Array(this._quad.arrayBuffer, this._bufferOffset, length / 4); + this._uint32View = new Uint32Array(this._quad.arrayBuffer, this._bufferOffset, length / 4); + + this._setTextureCoords(this._node._rect); + this._updateColor(); + this._bufferDirty = true; + this._buffer.setDirty(); + } + }; + + proto.freeBuffer = function () { + if (this._buffer) { + this._buffer.freeBuffer(this._bufferOffset, this.vertexBytesPerUnit); + this._buffer = null; + this._bufferOffset = 0; + this._quad = null; + this._float32View = null; + } + }; + proto.updateBlendFunc = function (blendFunc) {}; proto.setDirtyFlag = function(dirtyFlag){ @@ -112,12 +161,14 @@ }; proto._init = function () { + this.updateBuffer(); var tempColor = {r: 255, g: 255, b: 255, a: 255}, quad = this._quad; quad.bl.colors = tempColor; quad.br.colors = tempColor; quad.tl.colors = tempColor; quad.tr.colors = tempColor; - this._quadDirty = true; + this._bufferDirty = true; + this._buffer.setDirty(); }; proto._resetForBatchNode = function () { @@ -126,12 +177,15 @@ var y1 = node._offsetPosition.y; var x2 = x1 + node._rect.width; var y2 = y1 + node._rect.height; - var locQuad = this._quad; - locQuad.bl.vertices = {x: x1, y: y1, z: 0}; - locQuad.br.vertices = {x: x2, y: y1, z: 0}; - locQuad.tl.vertices = {x: x1, y: y2, z: 0}; - locQuad.tr.vertices = {x: x2, y: y2, z: 0}; - this._quadDirty = true; + var vertices = this._vertices; + vertices[0].x = x1; vertices[0].y = y2; // tl + vertices[1].x = x1; vertices[1].y = y1; // bl + vertices[2].x = x2; vertices[2].y = y2; // tr + vertices[3].x = x2; vertices[3].y = y1; // br + this._bufferDirty = true; + if (this._buffer) { + this._buffer.setDirty(); + } }; proto.getQuad = function () { @@ -165,7 +219,6 @@ // by default use "Self Render". // if the sprite is added to a batchnode, then it will automatically switch to "batchnode Render" this.setBatchNode(this._batchNode); - renderCmd._quadDirty = true; this.dispatchEvent("load"); }; @@ -174,16 +227,16 @@ needConvert = true; if (needConvert) rect = cc.rectPointsToPixels(rect); - var node = this._node; + var node = this._node, locQuad = this._quad; var tex = node._batchNode ? node.textureAtlas.texture : node._texture; - if (!tex) + if (!tex || !locQuad) return; var atlasWidth = tex.pixelsWidth; var atlasHeight = tex.pixelsHeight; - var left, right, top, bottom, tempSwap, locQuad = this._quad; + var left, right, top, bottom, tempSwap; if (node._rectRotated) { if (cc.FIX_ARTIFACTS_BY_STRECHING_TEXEL) { left = (2 * rect.x + 1) / (2 * atlasWidth); @@ -251,11 +304,34 @@ locQuad.tr.texCoords.u = right; locQuad.tr.texCoords.v = top; } - this._quadDirty = true; + this._bufferDirty = true; + this._buffer.setDirty(); }; - proto.transform = function(parentCmd, recursive){ + proto.transform = function (parentCmd, recursive) { cc.Node.WebGLRenderCmd.prototype.transform.call(this, parentCmd, recursive); + + if (this._buffer) { + var mat = this._stackMatrix.mat, + vertices = this._vertices, + buffer = this._float32View, + i, x, y, offset = 0, + row = cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT / 16; + + for (i = 0; i < 4; ++i) { + x = vertices[i].x; + y = vertices[i].y; + z = vertices[i].z; + buffer[offset] = x * mat[0] + y * mat[4] + mat[12]; + buffer[offset+1] = x * mat[1] + y * mat[5] + mat[13]; + buffer[offset+2] = mat[14]; + offset += row; + } + + this._bufferDirty = true; + this._buffer.setDirty(); + } + this._dirty = true; //use for batching this._savedDirtyFlag = true; }; @@ -272,10 +348,13 @@ color4.b *= locDisplayedOpacity / 255.0; } var locQuad = this._quad; - locQuad.bl.colors = color4; - locQuad.br.colors = color4; - locQuad.tl.colors = color4; - locQuad.tr.colors = color4; + if (locQuad) { + locQuad.bl.colors = color4; + locQuad.br.colors = color4; + locQuad.tl.colors = color4; + locQuad.tr.colors = color4; + this._buffer.setDirty(); + } // renders using Sprite Manager if (node._batchNode) { @@ -287,7 +366,7 @@ this._dirty = true; } } - this._quadDirty = true; + this._bufferDirty = true; }; proto._updateBlendFunc = function () { @@ -320,7 +399,7 @@ cc.log(cc._LogInfos.Sprite_setTexture); return; } - }else{ + } else { if(node._texture !== texture){ node._textureLoaded = texture ? texture._textureLoaded : false; node._texture = texture; @@ -329,10 +408,9 @@ } if (texture) - this._shaderProgram = cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURECOLOR); + this._shaderProgram = cc.shaderCache.programForKey(cc.SHADER_SPRITE_POSITION_TEXTURECOLORALPHATEST); else - this._shaderProgram = cc.shaderCache.programForKey(cc.SHADER_POSITION_COLOR); - + this._shaderProgram = cc.shaderCache.programForKey(cc.SHADER_SPRITE_POSITION_COLOR); }; proto.updateTransform = function () { //called only at batching. @@ -445,7 +523,7 @@ proto.rendering = function (ctx) { var node = this._node, locTexture = node._texture; - if ((locTexture && (!locTexture._textureLoaded || !node._rect.width || !node._rect.height)) || !this._displayedOpacity) + if (!this._buffer || (locTexture && (!locTexture._textureLoaded || !node._rect.width || !node._rect.height)) || !this._displayedOpacity) return; var gl = ctx || cc._renderContext; @@ -455,40 +533,42 @@ if (locTexture) { if (locTexture._textureLoaded) { program.use(); - program._setUniformForMVPMatrixWithMat4(this._stackMatrix); + program._updateProjectionUniform(); cc.glBlendFunc(node._blendFunc.src, node._blendFunc.dst); //optimize performance for javascript - cc.glBindTexture2DN(0, locTexture); // = cc.glBindTexture2D(locTexture); - cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POS_COLOR_TEX); - - gl.bindBuffer(gl.ARRAY_BUFFER, this._quadWebBuffer); - if (this._quadDirty) { - gl.bufferData(gl.ARRAY_BUFFER, this._quad.arrayBuffer, gl.DYNAMIC_DRAW); - this._quadDirty = false; + cc.glBindTexture2DN(0, locTexture); + + var _bufferchanged = !gl.bindBuffer(gl.ARRAY_BUFFER, this._buffer.vertexBuffer); + // if (this._bufferDirty) { + // gl.bufferSubData(gl.ARRAY_BUFFER, this._bufferOffset, this._float32View); + // this._bufferDirty = false; + // } + if (_resetPointers || _bufferchanged) { + cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POS_COLOR_TEX); + gl.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION, 3, gl.FLOAT, false, 24, 0); + gl.vertexAttribPointer(cc.VERTEX_ATTRIB_COLOR, 4, gl.UNSIGNED_BYTE, true, 24, 12); + gl.vertexAttribPointer(cc.VERTEX_ATTRIB_TEX_COORDS, 2, gl.FLOAT, false, 24, 16); + _resetPointers = false; } - 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 - gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); + gl.drawArrays(gl.TRIANGLE_STRIP, this._bufferOffset / (this.vertexBytesPerUnit/4), 4); } } else { program.use(); program._setUniformForMVPMatrixWithMat4(this._stackMatrix); cc.glBlendFunc(node._blendFunc.src, node._blendFunc.dst); - cc.glBindTexture2D(null); + gl.bindBuffer(gl.ARRAY_BUFFER, this._buffer.vertexBuffer); + // if (this._bufferDirty) { + // gl.bufferSubData(gl.ARRAY_BUFFER, this._bufferOffset, this._float32View); + // this._bufferDirty = false; + // } cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POSITION | cc.VERTEX_ATTRIB_FLAG_COLOR); - - gl.bindBuffer(gl.ARRAY_BUFFER, this._quadWebBuffer); - if (this._quadDirty) { - gl.bufferData(gl.ARRAY_BUFFER, this._quad.arrayBuffer, gl.STATIC_DRAW); - this._quadDirty = false; - } gl.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION, 3, gl.FLOAT, false, 24, 0); gl.vertexAttribPointer(cc.VERTEX_ATTRIB_COLOR, 4, gl.UNSIGNED_BYTE, true, 24, 12); - gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); + gl.drawArrays(gl.TRIANGLE_STRIP, this._bufferOffset / (this.vertexBytesPerUnit/4), 4); + _resetPointers = true; } cc.g_NumberOfDraws++; @@ -502,12 +582,12 @@ if (cc.SPRITE_DEBUG_DRAW === 1 || node._showNode) { // draw bounding box - var locQuad = this._quad; + var vertices = this._vertices; var verticesG1 = [ - cc.p(locQuad.tl.vertices.x, locQuad.tl.vertices.y), - cc.p(locQuad.bl.vertices.x, locQuad.bl.vertices.y), - cc.p(locQuad.br.vertices.x, locQuad.br.vertices.y), - cc.p(locQuad.tr.vertices.x, locQuad.tr.vertices.y) + cc.p(vertices[0].x, vertices[0].y), + cc.p(vertices[2].x, vertices[2].y), + cc.p(vertices[3].x, vertices[3].y), + cc.p(vertices[1].x, vertices[1].y) ]; cc._drawingUtil.drawPoly(verticesG1, 4, true); } else if (cc.SPRITE_DEBUG_DRAW === 2) { @@ -521,37 +601,20 @@ cc.current_stack.top = cc.current_stack.stack.pop(); }; - proto.batchVertexBuffer = function (buffer, vertexDataOffset, totalVertexData, matrixDataOffset) { - // 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 - + proto.batchVertexBuffer = function (f32buffer, int32buffer, vertexDataOffset) { // Fill in vertex data with quad information (4 vertices for sprite) - var vertexData = this._quadBufferView; - var i, len = vertexData.length; + var float32Data = this._float32View; + var uint32Data = this._uint32View; + var i, len = float32Data.length, colorId = 3; for (i = 0; i < len; ++i) { - buffer[vertexDataOffset + i] = vertexData[i]; - } - - // 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 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; + if (i === colorId) { + int32buffer[vertexDataOffset + i] = uint32Data[i]; + // 6 data per index + colorId += 6; + } + else { + f32buffer[vertexDataOffset + i] = float32Data[i]; + } } }; })(); \ No newline at end of file diff --git a/cocos2d/shaders/CCGLStateCache.js b/cocos2d/shaders/CCGLStateCache.js index 1cf4dc2e9e..307157ffbe 100644 --- a/cocos2d/shaders/CCGLStateCache.js +++ b/cocos2d/shaders/CCGLStateCache.js @@ -39,6 +39,25 @@ if (cc.ENABLE_GL_STATE_CACHE) { cc._GLServerState = 0; if(cc.TEXTURE_ATLAS_USE_VAO) cc._uVAO = 0; + + var _currBuffers = {}; + var _currBuffer; + + WebGLRenderingContext.prototype.glBindBuffer = WebGLRenderingContext.prototype.bindBuffer; + WebGLRenderingContext.prototype.bindBuffer = function (target, buffer) { + if (_currBuffers[target] !== buffer) { + _currBuffers[target] = buffer; + this.glBindBuffer(target, buffer); + } + + if (!_currBuffer || _currBuffer !== buffer) { + _currBuffer = buffer; + return false; + } + else { + return true; + } + }; } // GL State Cache functions @@ -81,7 +100,7 @@ cc.glUseProgram = function (program) { if(!cc.ENABLE_GL_STATE_CACHE){ cc.glUseProgram = function (program) { cc._renderContext.useProgram(program); - } + }; } /** @@ -150,9 +169,9 @@ cc.glBlendFuncForParticle = function(sfactor, dfactor) { } }; -if(!cc.ENABLE_GL_STATE_CACHE){ +if (!cc.ENABLE_GL_STATE_CACHE) { cc.glBlendFunc = cc.setBlending; -}; +} /** * Resets the blending mode back to the cached state in case you used glBlendFuncSeparate() or glBlendEquation().
@@ -196,8 +215,6 @@ cc.glEnableVertexAttribs = function (flags) { if (enablePosition !== cc._vertexAttribPosition) { if (enablePosition) ctx.enableVertexAttribArray(cc.VERTEX_ATTRIB_POSITION); - else - ctx.disableVertexAttribArray(cc.VERTEX_ATTRIB_POSITION); cc._vertexAttribPosition = enablePosition; } diff --git a/cocos2d/shaders/CCShaderCache.js b/cocos2d/shaders/CCShaderCache.js index 0fb30350b5..49b786d171 100644 --- a/cocos2d/shaders/CCShaderCache.js +++ b/cocos2d/shaders/CCShaderCache.js @@ -84,7 +84,13 @@ cc.shaderCache = /** @lends cc.shaderCache# */{ * @constant * @type {Number} */ - TYPE_POSITION_TEXTURECOLOR_ALPHATEST_BATCHED: 8, + TYPE_SPRITE_POSITION_TEXTURECOLOR_ALPHATEST: 8, + /** + * @public + * @constant + * @type {Number} + */ + TYPE_SPRITE_POSITION_COLOR: 9, /** * @public * @constant @@ -114,18 +120,22 @@ cc.shaderCache = /** @lends cc.shaderCache# */{ 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); + case this.TYPE_SPRITE_POSITION_TEXTURECOLOR_ALPHATEST: + program.initWithVertexShaderByteArray(cc.SHADER_SPRITE_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); - 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); program.addAttribute(cc.ATTRIBUTE_NAME_POSITION, cc.VERTEX_ATTRIB_POSITION); program.addAttribute(cc.ATTRIBUTE_NAME_COLOR, cc.VERTEX_ATTRIB_COLOR); break; + case this.TYPE_SPRITE_POSITION_COLOR: + program.initWithVertexShaderByteArray(cc.SHADER_SPRITE_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); @@ -179,11 +189,11 @@ cc.shaderCache = /** @lends cc.shaderCache# */{ this._programs[cc.SHADER_POSITION_TEXTURECOLORALPHATEST] = program; this._programs["ShaderPositionTextureColorAlphaTest"] = program; - // Position Texture Color alpha batched test + // Position Texture Color alpha with position precalculated 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; + this._loadDefaultShader(program, this.TYPE_SPRITE_POSITION_TEXTURECOLOR_ALPHATEST); + this._programs[cc.SHADER_SPRITE_POSITION_TEXTURECOLORALPHATEST] = program; + this._programs["ShaderSpritePositionTextureColorAlphaTest"] = program; // // Position, Color shader @@ -193,6 +203,14 @@ cc.shaderCache = /** @lends cc.shaderCache# */{ this._programs[cc.SHADER_POSITION_COLOR] = program; this._programs["ShaderPositionColor"] = program; + // + // Position, Color shader with position precalculated + // + program = new cc.GLProgram(); + this._loadDefaultShader(program, this.TYPE_SPRITE_POSITION_COLOR); + this._programs[cc.SHADER_SPRITE_POSITION_COLOR] = program; + this._programs["ShaderSpritePositionColor"] = program; + // // Position Texture shader // diff --git a/cocos2d/shaders/CCShaders.js b/cocos2d/shaders/CCShaders.js index 0668304525..d0c38c60e7 100644 --- a/cocos2d/shaders/CCShaders.js +++ b/cocos2d/shaders/CCShaders.js @@ -76,11 +76,20 @@ cc.SHADER_POSITION_COLOR_VERT = + "varying lowp vec4 v_fragmentColor;\n" + "void main()\n" + "{\n" - //+ " gl_Position = CC_MVPMatrix * a_position; \n" + " gl_Position = (CC_PMatrix * CC_MVMatrix) * a_position; \n" + " v_fragmentColor = a_color; \n" + "}"; +cc.SHADER_SPRITE_POSITION_COLOR_VERT = + "attribute vec4 a_position;\n" + + "attribute vec4 a_color;\n" + + "varying lowp vec4 v_fragmentColor;\n" + + "void main()\n" + + "{\n" + + " gl_Position = CC_PMatrix * a_position; \n" + + " v_fragmentColor = a_color; \n" + + "}"; + // --------------------- Shader_PositionColorLengthTexture Shader source------------------------ /** * @constant @@ -234,7 +243,6 @@ cc.SHADER_POSITION_TEXTURE_COLOR_VERT = + "varying mediump vec2 v_texCoord; \n" + "void main() \n" + "{ \n" - //+ " gl_Position = CC_MVPMatrix * a_position; \n" + " gl_Position = (CC_PMatrix * CC_MVMatrix) * a_position; \n" + " v_fragmentColor = a_color; \n" + " v_texCoord = a_texCoord; \n" @@ -260,20 +268,16 @@ cc.SHADER_POSITION_TEXTURE_COLOR_ALPHATEST_FRAG = + " gl_FragColor = texColor * v_fragmentColor; \n" + "}"; -//-----------------------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 = +//-----------------------Shader_PositionSpriteTextureColorVertTest_frag Shader Source---------------------------- +cc.SHADER_SPRITE_POSITION_TEXTURE_COLOR_VERT = "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" + + " gl_Position = CC_PMatrix * a_position; \n" + " v_fragmentColor = a_color; \n" + " v_texCoord = a_texCoord; \n" + "}"; diff --git a/moduleConfig.json b/moduleConfig.json index 96371dd66c..b0498b41be 100644 --- a/moduleConfig.json +++ b/moduleConfig.json @@ -74,6 +74,7 @@ "cocos2d/core/event-manager/CCEventManager.js", "cocos2d/core/event-manager/CCEventExtension.js", + "cocos2d/core/renderer/GlobalVertexBuffer.js", "cocos2d/core/renderer/RendererCanvas.js", "cocos2d/core/renderer/RendererWebGL.js", diff --git a/tools/build.xml b/tools/build.xml index 7077802358..f1251f6159 100644 --- a/tools/build.xml +++ b/tools/build.xml @@ -41,6 +41,7 @@ + @@ -336,6 +337,7 @@ +