David Catuhe 9 年之前
父節點
當前提交
a4711b770b

文件差異過大導致無法顯示
+ 20 - 20
dist/preview release/babylon.core.js


文件差異過大導致無法顯示
+ 3829 - 3774
dist/preview release/babylon.d.ts


文件差異過大導致無法顯示
+ 41 - 41
dist/preview release/babylon.js


+ 363 - 47
dist/preview release/babylon.max.js

@@ -5433,6 +5433,13 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(PerfCounter.prototype, "total", {
+            get: function () {
+                return this._totalAccumulated;
+            },
+            enumerable: true,
+            configurable: true
+        });
         /**
          * Call this method to start monitoring a new frame.
          * This scenario is typically used when you accumulate monitoring time many times for a single frame, you call this method at the start of the frame, then beginMonitoring to start recording and endMonitoring(false) to accumulated the recorded time to the PerfCounter or addCount() to accumulate a monitored count.
@@ -5440,6 +5447,7 @@ var BABYLON;
         PerfCounter.prototype.fetchNewFrame = function () {
             this._totalValueCount++;
             this._current = 0;
+            this._lastSecValueCount++;
         };
         /**
          * Call this method to monitor a count of something (e.g. mesh drawn in viewport count)
@@ -5475,14 +5483,16 @@ var BABYLON;
         };
         PerfCounter.prototype._fetchResult = function () {
             this._totalAccumulated += this._current;
+            this._lastSecAccumulated += this._current;
             // Min/Max update
             this._min = Math.min(this._min, this._current);
             this._max = Math.max(this._max, this._current);
             this._average = this._totalAccumulated / this._totalValueCount;
             // Reset last sec?
-            if ((this._startMonitoringTime - this._lastSecTime) > 1000) {
+            var now = Tools.Now;
+            if ((now - this._lastSecTime) > 1000) {
                 this._lastSecAverage = this._lastSecAccumulated / this._lastSecValueCount;
-                this._lastSecTime = this._startMonitoringTime;
+                this._lastSecTime = now;
                 this._lastSecAccumulated = 0;
                 this._lastSecValueCount = 0;
             }
@@ -36773,6 +36783,16 @@ var BABYLON;
             r.extent = this.extent.clone();
             return r;
         };
+        BoundingInfo2D.prototype.clear = function () {
+            this.center.copyFromFloats(0, 0);
+            this.radius = 0;
+            this.extent.copyFromFloats(0, 0);
+        };
+        BoundingInfo2D.prototype.copyFrom = function (src) {
+            this.center.copyFrom(src.center);
+            this.radius = src.radius;
+            this.extent.copyFrom(src.extent);
+        };
         /**
          * return the max extend of the bounding info
          */
@@ -37372,6 +37392,7 @@ var BABYLON;
                     curprim._setFlags(SmartPropertyPrim.flagBoundingInfoDirty);
                     if (curprim.isSizeAuto) {
                         curprim.onPrimitivePropertyDirty(BABYLON.Prim2DBase.sizeProperty.flagId);
+                        curprim._setFlags(SmartPropertyPrim.flagPositioningDirty);
                     }
                     if (curprim instanceof BABYLON.Group2D) {
                         if (curprim.isRenderableGroup) {
@@ -38075,12 +38096,21 @@ var BABYLON;
         }
         /**
          * Set the thickness from a string value
-         * @param thickness format is "top: <value>, left:<value>, right:<value>, bottom:<value>" each are optional, auto will be set if it's omitted.
+         * @param thickness format is "top: <value>, left:<value>, right:<value>, bottom:<value>" or "<value>" (same for all edges) each are optional, auto will be set if it's omitted.
          * Values are: 'auto', 'inherit', 'XX%' for percentage, 'XXpx' or 'XX' for pixels.
          */
         PrimitiveThickness.prototype.fromString = function (thickness) {
             this._clear();
             var m = thickness.trim().split(",");
+            // Special case, one value to apply to all edges
+            if (m.length === 1 && thickness.indexOf(":") === -1) {
+                this._setStringValue(m[0], 0, false);
+                this._setStringValue(m[0], 1, false);
+                this._setStringValue(m[0], 2, false);
+                this._setStringValue(m[0], 3, false);
+                this._changedCallback();
+                return;
+            }
             var res = false;
             for (var _i = 0; _i < m.length; _i++) {
                 var cm = m[_i];
@@ -38742,6 +38772,16 @@ var BABYLON;
             result.width = this.leftPixels + sourceArea.width + this.rightPixels;
             result.height = this.bottomPixels + sourceArea.height + this.topPixels;
         };
+        PrimitiveThickness.prototype.enlarge = function (sourceArea, dstOffset, enlargedArea) {
+            this._computePixels(0, sourceArea, true);
+            this._computePixels(1, sourceArea, true);
+            this._computePixels(2, sourceArea, true);
+            this._computePixels(3, sourceArea, true);
+            dstOffset.x = this.leftPixels;
+            enlargedArea.width = sourceArea.width + (dstOffset.x + this.rightPixels);
+            dstOffset.y = this.bottomPixels;
+            enlargedArea.height = sourceArea.height + (dstOffset.y + this.topPixels);
+        };
         PrimitiveThickness.Auto = 0x1;
         PrimitiveThickness.Inherit = 0x2;
         PrimitiveThickness.Percentage = 0x4;
@@ -38823,8 +38863,10 @@ var BABYLON;
             this._layoutArea = BABYLON.Size.Zero();
             this._layoutAreaPos = BABYLON.Vector2.Zero();
             this._marginOffset = BABYLON.Vector2.Zero();
-            this._parentMargingOffset = BABYLON.Vector2.Zero();
+            this._paddingOffset = BABYLON.Vector2.Zero();
+            this._parentPaddingOffset = BABYLON.Vector2.Zero();
             this._parentContentArea = BABYLON.Size.Zero();
+            this._lastAutoSizeArea = BABYLON.Size.Zero();
             this._contentArea = new BABYLON.Size(null, null);
             this._pointerEventObservable = new BABYLON.Observable();
             this._siblingDepthOffset = this._hierarchyDepthOffset = 0;
@@ -39058,7 +39100,7 @@ var BABYLON;
              * Setting this property may have no effect is specific alignment are in effect.
              */
             get: function () {
-                return this._position;
+                return this._position || Prim2DBase._nullPosition;
             },
             set: function (value) {
                 if (!this._checkPositionChange()) {
@@ -39540,7 +39582,12 @@ var BABYLON;
              */
             get: function () {
                 if (this._isFlagSet(BABYLON.SmartPropertyPrim.flagBoundingInfoDirty)) {
-                    this._boundingInfo = this.levelBoundingInfo.clone();
+                    if (this.isSizedByContent) {
+                        this._boundingInfo.clear();
+                    }
+                    else {
+                        this._boundingInfo.copyFrom(this.levelBoundingInfo);
+                    }
                     var bi = this._boundingInfo;
                     var tps = new BABYLON.BoundingInfo2D();
                     for (var _i = 0, _a = this._children; _i < _a.length; _i++) {
@@ -39570,6 +39617,16 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(Prim2DBase.prototype, "isSizedByContent", {
+            /**
+             * Return true if this prim has an auto size which is set by the children's global bounding box
+             */
+            get: function () {
+                return (this._size == null) && (this._children.length > 0);
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(Prim2DBase.prototype, "isPositionAuto", {
             /**
              * Determine if the position is automatically computed or fixed because manually specified.
@@ -39821,22 +39878,14 @@ var BABYLON;
             this._layoutEngine = engine;
         };
         Prim2DBase.prototype._updateLocalTransform = function () {
-            var parentMarginOffsetChanged = false;
-            var parentMarginOffset = null;
-            if (this._parent) {
-                parentMarginOffset = this._parent._marginOffset;
-                parentMarginOffsetChanged = !parentMarginOffset.equals(this._parentMargingOffset);
-                this._parentMargingOffset.copyFrom(parentMarginOffset);
-            }
-            else {
-                parentMarginOffset = Prim2DBase._v0;
-            }
             var tflags = Prim2DBase.actualPositionProperty.flagId | Prim2DBase.rotationProperty.flagId | Prim2DBase.scaleProperty.flagId | Prim2DBase.originProperty.flagId;
-            if (parentMarginOffsetChanged || this.checkPropertiesDirty(tflags)) {
+            if (this.checkPropertiesDirty(tflags)) {
+                this.owner.addupdateLocalTransformCounter(1);
                 var rot = BABYLON.Quaternion.RotationAxis(new BABYLON.Vector3(0, 0, 1), this._rotation);
                 var local;
+                var pos = this.position;
                 if (this._origin.x === 0 && this._origin.y === 0) {
-                    local = BABYLON.Matrix.Compose(new BABYLON.Vector3(this._scale, this._scale, 1), rot, new BABYLON.Vector3(this.actualPosition.x + parentMarginOffset.x, this.actualPosition.y + parentMarginOffset.y, 0));
+                    local = BABYLON.Matrix.Compose(new BABYLON.Vector3(this._scale, this._scale, 1), rot, new BABYLON.Vector3(pos.x, pos.y, 0));
                     this._localTransform = local;
                 }
                 else {
@@ -39849,8 +39898,8 @@ var BABYLON;
                     // -Origin * rotation * scale
                     BABYLON.Matrix.ScalingToRef(this._scale, this._scale, 1, Prim2DBase._t0);
                     Prim2DBase._t2.multiplyToRef(Prim2DBase._t0, Prim2DBase._t1);
-                    // -Origin * rotation * scale * (Origin + Position + Parent Margin Offset)
-                    BABYLON.Matrix.TranslationToRef((as.width * this._origin.x) + this.actualPosition.x + parentMarginOffset.x, (as.height * this._origin.y) + this.actualPosition.y + parentMarginOffset.y, 0, Prim2DBase._t2);
+                    // -Origin * rotation * scale * (Origin + Position)
+                    BABYLON.Matrix.TranslationToRef((as.width * this._origin.x) + pos.x, (as.height * this._origin.y) + pos.y, 0, Prim2DBase._t2);
                     Prim2DBase._t1.multiplyToRef(Prim2DBase._t2, this._localTransform);
                 }
                 this.clearPropertiesDirty(tflags);
@@ -39862,6 +39911,7 @@ var BABYLON;
             if (this.isDisposed) {
                 return;
             }
+            this.owner.addCachedGroupRenderCounter(1);
             // Check if the parent is synced
             if (this._parent && ((this._parent._globalTransformProcessStep !== this.owner._globalTransformProcessStep) || this._parent._areSomeFlagsSet(BABYLON.SmartPropertyPrim.flagLayoutDirty | BABYLON.SmartPropertyPrim.flagPositioningDirty))) {
                 this._parent.updateCachedStates(false);
@@ -39869,7 +39919,7 @@ var BABYLON;
             // Update actualSize only if there' not positioning to recompute and the size changed
             // Otherwise positioning will take care of it.
             var sizeDirty = this.checkPropertiesDirty(Prim2DBase.sizeProperty.flagId);
-            if (!this._isFlagSet(BABYLON.SmartPropertyPrim.flagLayoutDirty) && sizeDirty) {
+            if (!this._isFlagSet(BABYLON.SmartPropertyPrim.flagLayoutDirty) && !this._isFlagSet(BABYLON.SmartPropertyPrim.flagPositioningDirty) && sizeDirty) {
                 var size = this.size;
                 if (size) {
                     if (this.size.width != null) {
@@ -39884,14 +39934,22 @@ var BABYLON;
             // Check for layout update
             var positioningDirty = this._isFlagSet(BABYLON.SmartPropertyPrim.flagPositioningDirty);
             if (this._isFlagSet(BABYLON.SmartPropertyPrim.flagLayoutDirty)) {
+                this.owner.addUpdateLayoutCounter(1);
                 this._layoutEngine.updateLayout(this);
                 this._clearFlags(BABYLON.SmartPropertyPrim.flagLayoutDirty);
             }
             var positioningComputed = positioningDirty && !this._isFlagSet(BABYLON.SmartPropertyPrim.flagPositioningDirty);
+            var autoContentChanged = false;
+            if (this.isSizeAuto) {
+                autoContentChanged = (!this._lastAutoSizeArea.equals(this.size));
+            }
             // Check for positioning update
-            if (!positioningComputed && (sizeDirty || this._isFlagSet(BABYLON.SmartPropertyPrim.flagPositioningDirty) || (this._parent && !this._parent.contentArea.equals(this._parentContentArea)))) {
+            if (!positioningComputed && (autoContentChanged || sizeDirty || this._isFlagSet(BABYLON.SmartPropertyPrim.flagPositioningDirty) || (this._parent && !this._parent.contentArea.equals(this._parentContentArea)))) {
                 this._updatePositioning();
                 this._clearFlags(BABYLON.SmartPropertyPrim.flagPositioningDirty);
+                if (sizeDirty) {
+                    this.clearPropertiesDirty(Prim2DBase.sizeProperty.flagId);
+                }
                 positioningComputed = true;
             }
             if (positioningComputed && this._parent) {
@@ -39899,18 +39957,30 @@ var BABYLON;
             }
             // Check if we must update this prim
             if (this === this.owner || this._globalTransformProcessStep !== this.owner._globalTransformProcessStep) {
+                this.owner.addUpdateGlobalTransformCounter(1);
                 var curVisibleState = this.isVisible;
                 this.isVisible = (!this._parent || this._parent.isVisible) && this.levelVisible;
                 // Detect a change of visibility
                 this._changeFlags(BABYLON.SmartPropertyPrim.flagVisibilityChanged, curVisibleState !== this.isVisible);
                 // Get/compute the localTransform
                 var localDirty = this._updateLocalTransform();
+                var parentPaddingChanged = false;
+                var parentPaddingOffset = Prim2DBase._v0;
+                if (this._parent) {
+                    parentPaddingOffset = this._parent._paddingOffset;
+                    parentPaddingChanged = !parentPaddingOffset.equals(this._parentPaddingOffset);
+                }
                 // Check if there are changes in the parent that will force us to update the global matrix
                 var parentDirty = (this._parent != null) ? (this._parent._globalTransformStep !== this._parentTransformStep) : false;
                 // Check if we have to update the globalTransform
-                if (!this._globalTransform || localDirty || parentDirty) {
+                if (!this._globalTransform || localDirty || parentDirty || parentPaddingChanged) {
                     var globalTransform = this._parent ? this._parent._globalTransform : null;
-                    this._globalTransform = this._parent ? this._localTransform.multiply(globalTransform) : this._localTransform;
+                    var localTransform;
+                    Prim2DBase._transMtx.copyFrom(this._localTransform);
+                    Prim2DBase._transMtx.m[12] += this._layoutAreaPos.x + this._marginOffset.x + parentPaddingOffset.x;
+                    Prim2DBase._transMtx.m[13] += this._layoutAreaPos.y + this._marginOffset.y + parentPaddingOffset.y;
+                    localTransform = Prim2DBase._transMtx;
+                    this._globalTransform = this._parent ? localTransform.multiply(globalTransform) : localTransform.clone();
                     this._invGlobalTransform = BABYLON.Matrix.Invert(this._globalTransform);
                     this._globalTransformStep = this.owner._globalTransformProcessStep + 1;
                     this._parentTransformStep = this._parent ? this._parent._globalTransformStep : 0;
@@ -39926,6 +39996,7 @@ var BABYLON;
             }
         };
         Prim2DBase.prototype._updatePositioning = function () {
+            this.owner.addUpdatePositioningCounter(1);
             // From this point we assume that the primitive layoutArea is computed and up to date.
             // We know have to :
             //  1. Determine the PaddingArea and the ActualPosition based on the margin/marginAlignment properties, which will also set the size property of the primitive
@@ -39942,31 +40013,44 @@ var BABYLON;
             // Apply margin
             if (this._hasMargin) {
                 this.margin.computeWithAlignment(this.layoutArea, this.size, this.marginAlignment, this._marginOffset, Prim2DBase._size);
-                this.actualPosition = this._marginOffset.add(this._layoutAreaPos);
-                if (this.size.width != null) {
-                    this.size.width = Prim2DBase._size.width;
-                }
-                if (this.size.height != null) {
-                    this.size.height = Prim2DBase._size.height;
-                }
-                this.actualSize.copyFrom(Prim2DBase._size.clone());
+                this.actualSize = Prim2DBase._size.clone();
             }
+            var isSizeAuto = this.isSizeAuto;
             if (this._hasPadding) {
-                this._getInitialContentAreaToRef(this.actualSize, Prim2DBase._icPos, Prim2DBase._icArea);
-                Prim2DBase._icArea.width = Math.max(0, Prim2DBase._icArea.width);
-                Prim2DBase._icArea.height = Math.max(0, Prim2DBase._icArea.height);
-                this.padding.compute(Prim2DBase._icArea, this._marginOffset, Prim2DBase._size);
-                this._marginOffset.x += Prim2DBase._icPos.x;
-                this._marginOffset.y += Prim2DBase._icPos.y;
-                this._contentArea.copyFrom(Prim2DBase._size);
+                // Two cases from here: the size of the Primitive is Auto, its content can't be shrink, so me resize the primitive itself
+                if (isSizeAuto) {
+                    var content = this.size.clone();
+                    this._getActualSizeFromContentToRef(content, Prim2DBase._icArea);
+                    this.padding.enlarge(Prim2DBase._icArea, this._paddingOffset, Prim2DBase._size);
+                    this._contentArea.copyFrom(content);
+                    this.actualSize = Prim2DBase._size.clone();
+                    // Changing the padding has resize the prim, which forces us to recompute margin again
+                    if (this._hasMargin) {
+                        this.margin.computeWithAlignment(this.layoutArea, Prim2DBase._size, this.marginAlignment, this._marginOffset, Prim2DBase._size);
+                    }
+                }
+                else {
+                    this._getInitialContentAreaToRef(this.actualSize, Prim2DBase._icPos, Prim2DBase._icArea);
+                    Prim2DBase._icArea.width = Math.max(0, Prim2DBase._icArea.width);
+                    Prim2DBase._icArea.height = Math.max(0, Prim2DBase._icArea.height);
+                    this.padding.compute(Prim2DBase._icArea, this._paddingOffset, Prim2DBase._size);
+                    this._paddingOffset.x += Prim2DBase._icPos.x;
+                    this._paddingOffset.y += Prim2DBase._icPos.y;
+                    this._contentArea.copyFrom(Prim2DBase._size);
+                }
             }
             else {
                 this._getInitialContentAreaToRef(this.actualSize, Prim2DBase._icPos, Prim2DBase._icArea);
                 Prim2DBase._icArea.width = Math.max(0, Prim2DBase._icArea.width);
                 Prim2DBase._icArea.height = Math.max(0, Prim2DBase._icArea.height);
-                this._marginOffset.copyFrom(Prim2DBase._icPos);
+                this._paddingOffset.copyFrom(Prim2DBase._icPos);
                 this._contentArea.copyFrom(Prim2DBase._icArea);
             }
+            var aPos = new BABYLON.Vector2(this._layoutAreaPos.x + this._marginOffset.x, this._layoutAreaPos.y + this._marginOffset.y);
+            this.actualPosition = aPos;
+            if (isSizeAuto) {
+                this._lastAutoSizeArea = this.size;
+            }
         };
         Object.defineProperty(Prim2DBase.prototype, "contentArea", {
             /**
@@ -40029,10 +40113,18 @@ var BABYLON;
          * @param initialContentArea the size of the initial content area to compute, a valid object is passed, you have to set its properties. PLEASE ROUND the values, we're talking about pixels and fraction of them is not a good thing!
          */
         Prim2DBase.prototype._getInitialContentAreaToRef = function (primSize, initialContentPosition, initialContentArea) {
-            initialContentArea.width = primSize.width;
-            initialContentArea.height = primSize.height;
+            initialContentArea.copyFrom(primSize);
             initialContentPosition.x = initialContentPosition.y = 0;
         };
+        /**
+         * This method is used to calculate the new size of the primitive based on the content which must stay the same
+         * Check the Rectangle2D implementation for a concrete application.
+         * @param primSize the current size of the primitive
+         * @param newPrimSize the new size of the primitive. PLEASE ROUND THE values, we're talking about pixels and fraction of them are not our friends!
+         */
+        Prim2DBase.prototype._getActualSizeFromContentToRef = function (primSize, newPrimSize) {
+            newPrimSize.copyFrom(primSize);
+        };
         Prim2DBase.PRIM2DBASE_PROPCOUNT = 15;
         Prim2DBase._nullPosition = BABYLON.Vector2.Zero();
         Prim2DBase.boundinbBoxReentrency = false;
@@ -40042,6 +40134,7 @@ var BABYLON;
         Prim2DBase._t1 = new BABYLON.Matrix();
         Prim2DBase._t2 = new BABYLON.Matrix();
         Prim2DBase._v0 = BABYLON.Vector2.Zero(); // Must stay with the value 0,0
+        Prim2DBase._transMtx = BABYLON.Matrix.Zero();
         Prim2DBase._icPos = BABYLON.Vector2.Zero();
         Prim2DBase._icArea = BABYLON.Size.Zero();
         Prim2DBase._size = BABYLON.Size.Zero();
@@ -41634,6 +41727,7 @@ var BABYLON;
             this._renderableData._primDirtyList.push(prim);
         };
         Group2D.prototype._renderCachedCanvas = function () {
+            this.owner._addGroupRenderCount(1);
             this.updateCachedStates(true);
             var context = new BABYLON.PrepareRender2DContext();
             this._prepareGroupRender(context);
@@ -41764,6 +41858,7 @@ var BABYLON;
             }
             // Render the primitives if needed: either if we don't cache the content or if the content is cached but has changed
             if (!this.isCachedGroup || this._cacheGroupDirty) {
+                this.owner._addGroupRenderCount(1);
                 if (this.isCachedGroup) {
                     this._bindCacheTarget();
                 }
@@ -41847,6 +41942,7 @@ var BABYLON;
             }
         };
         Group2D.prototype._updateTransparentData = function () {
+            this.owner._addUpdateTransparentDataCount(1);
             var rd = this._renderableData;
             // If null, there was no change of ZOrder, we have nothing to do
             if (rd._firstChangedPrim === null) {
@@ -42288,7 +42384,8 @@ var BABYLON;
                 }
                 this.effectsReady = true;
             }
-            var engine = instanceInfo.owner.owner.engine;
+            var canvas = instanceInfo.owner.owner;
+            var engine = canvas.engine;
             var depthFunction = 0;
             if (this.effectFill && this.effectBorder) {
                 depthFunction = engine.getDepthFunction();
@@ -42308,11 +42405,13 @@ var BABYLON;
                     if (!this.instancingFillAttributes) {
                         this.instancingFillAttributes = this.loadInstancingAttributes(BABYLON.Shape2D.SHAPE2D_FILLPARTID, effect);
                     }
+                    canvas._addDrawCallCount(1, context.renderMode);
                     engine.updateAndBindInstancesBuffer(pid._partBuffer, null, this.instancingFillAttributes);
                     engine.draw(true, 0, this.fillIndicesCount, pid._partData.usedElementCount);
                     engine.unbindInstanceAttributes();
                 }
                 else {
+                    canvas._addDrawCallCount(context.partDataEndIndex - context.partDataStartIndex, context.renderMode);
                     for (var i = context.partDataStartIndex; i < context.partDataEndIndex; i++) {
                         this.setupUniforms(effect, partIndex, pid._partData, i);
                         engine.draw(true, 0, this.fillIndicesCount);
@@ -42332,11 +42431,13 @@ var BABYLON;
                     if (!this.instancingBorderAttributes) {
                         this.instancingBorderAttributes = this.loadInstancingAttributes(BABYLON.Shape2D.SHAPE2D_BORDERPARTID, effect);
                     }
+                    canvas._addDrawCallCount(1, context.renderMode);
                     engine.updateAndBindInstancesBuffer(pid._partBuffer, null, this.instancingBorderAttributes);
                     engine.draw(true, 0, this.borderIndicesCount, pid._partData.usedElementCount);
                     engine.unbindInstanceAttributes();
                 }
                 else {
+                    canvas._addDrawCallCount(context.partDataEndIndex - context.partDataStartIndex, context.renderMode);
                     for (var i = context.partDataStartIndex; i < context.partDataEndIndex; i++) {
                         this.setupUniforms(effect, partIndex, pid._partData, i);
                         engine.draw(true, 0, this.borderIndicesCount);
@@ -42636,6 +42737,18 @@ var BABYLON;
                 initialContentArea.height = Math.max(0, primSize.height - (rr * 2));
             }
         };
+        Rectangle2D.prototype._getActualSizeFromContentToRef = function (primSize, newPrimSize) {
+            // Fall back to default implementation if there's no round Radius
+            if (this._notRounded) {
+                _super.prototype._getActualSizeFromContentToRef.call(this, primSize, newPrimSize);
+            }
+            else {
+                var rr = Math.round((this.roundRadius - (this.roundRadius / Math.sqrt(2))) * 1.3);
+                newPrimSize.copyFrom(primSize);
+                newPrimSize.width += rr * 2;
+                newPrimSize.height += rr * 2;
+            }
+        };
         Rectangle2D.prototype.createInstanceDataParts = function () {
             var res = new Array();
             if (this.border) {
@@ -42718,7 +42831,8 @@ var BABYLON;
                 }
                 this.effectsReady = true;
             }
-            var engine = instanceInfo.owner.owner.engine;
+            var canvas = instanceInfo.owner.owner;
+            var engine = canvas.engine;
             var depthFunction = 0;
             if (this.effectFill && this.effectBorder) {
                 depthFunction = engine.getDepthFunction();
@@ -42738,11 +42852,13 @@ var BABYLON;
                     if (!this.instancingFillAttributes) {
                         this.instancingFillAttributes = this.loadInstancingAttributes(BABYLON.Shape2D.SHAPE2D_FILLPARTID, effect);
                     }
+                    canvas._addDrawCallCount(1, context.renderMode);
                     engine.updateAndBindInstancesBuffer(pid._partBuffer, null, this.instancingFillAttributes);
                     engine.draw(true, 0, this.fillIndicesCount, pid._partData.usedElementCount);
                     engine.unbindInstanceAttributes();
                 }
                 else {
+                    canvas._addDrawCallCount(context.partDataEndIndex - context.partDataStartIndex, context.renderMode);
                     for (var i = context.partDataStartIndex; i < context.partDataEndIndex; i++) {
                         this.setupUniforms(effect, partIndex, pid._partData, i);
                         engine.draw(true, 0, this.fillIndicesCount);
@@ -42762,11 +42878,13 @@ var BABYLON;
                     if (!this.instancingBorderAttributes) {
                         this.instancingBorderAttributes = this.loadInstancingAttributes(BABYLON.Shape2D.SHAPE2D_BORDERPARTID, effect);
                     }
+                    canvas._addDrawCallCount(1, context.renderMode);
                     engine.updateAndBindInstancesBuffer(pid._partBuffer, null, this.instancingBorderAttributes);
                     engine.draw(true, 0, this.borderIndicesCount, pid._partData.usedElementCount);
                     engine.unbindInstanceAttributes();
                 }
                 else {
+                    canvas._addDrawCallCount(context.partDataEndIndex - context.partDataStartIndex, context.renderMode);
                     for (var i = context.partDataStartIndex; i < context.partDataEndIndex; i++) {
                         this.setupUniforms(effect, partIndex, pid._partData, i);
                         engine.draw(true, 0, this.borderIndicesCount);
@@ -43054,7 +43172,8 @@ var BABYLON;
                 this.effectsReady = true;
             }
             // Compute the offset locations of the attributes in the vertex shader that will be mapped to the instance buffer data
-            var engine = instanceInfo.owner.owner.engine;
+            var canvas = instanceInfo.owner.owner;
+            var engine = canvas.engine;
             var effect = context.useInstancing ? this.effectInstanced : this.effect;
             engine.enableEffect(effect);
             effect.setTexture("diffuseSampler", this.texture);
@@ -43068,11 +43187,13 @@ var BABYLON;
                 if (!this.instancingAttributes) {
                     this.instancingAttributes = this.loadInstancingAttributes(Sprite2D.SPRITE2D_MAINPARTID, effect);
                 }
+                canvas._addDrawCallCount(1, context.renderMode);
                 engine.updateAndBindInstancesBuffer(pid._partBuffer, null, this.instancingAttributes);
                 engine.draw(true, 0, 6, pid._partData.usedElementCount);
                 engine.unbindInstanceAttributes();
             }
             else {
+                canvas._addDrawCallCount(context.partDataEndIndex - context.partDataStartIndex, context.renderMode);
                 for (var i = context.partDataStartIndex; i < context.partDataEndIndex; i++) {
                     this.setupUniforms(effect, 0, pid._partData, i);
                     engine.draw(true, 0, 6);
@@ -43406,7 +43527,8 @@ var BABYLON;
                 }
                 this.effectsReady = true;
             }
-            var engine = instanceInfo.owner.owner.engine;
+            var canvas = instanceInfo.owner.owner;
+            var engine = canvas.engine;
             this.fontTexture.update();
             var effect = context.useInstancing ? this.effectInstanced : this.effect;
             engine.enableEffect(effect);
@@ -43419,11 +43541,13 @@ var BABYLON;
                 if (!this.instancingAttributes) {
                     this.instancingAttributes = this.loadInstancingAttributes(Text2D.TEXT2D_MAINPARTID, effect);
                 }
+                canvas._addDrawCallCount(1, context.renderMode);
                 engine.updateAndBindInstancesBuffer(pid._partBuffer, null, this.instancingAttributes);
                 engine.draw(true, 0, 6, pid._partData.usedElementCount);
                 engine.unbindInstanceAttributes();
             }
             else {
+                canvas._addDrawCallCount(context.partDataEndIndex - context.partDataStartIndex, context.renderMode);
                 for (var i = context.partDataStartIndex; i < context.partDataEndIndex; i++) {
                     this.setupUniforms(effect, 0, pid._partData, i);
                     engine.draw(true, 0, 6);
@@ -43847,7 +43971,8 @@ var BABYLON;
                 }
                 this.effectsReady = true;
             }
-            var engine = instanceInfo.owner.owner.engine;
+            var canvas = instanceInfo.owner.owner;
+            var engine = canvas.engine;
             var depthFunction = 0;
             if (this.effectFill && this.effectBorder) {
                 depthFunction = engine.getDepthFunction();
@@ -43867,11 +43992,13 @@ var BABYLON;
                     if (!this.instancingFillAttributes) {
                         this.instancingFillAttributes = this.loadInstancingAttributes(BABYLON.Shape2D.SHAPE2D_FILLPARTID, effect);
                     }
+                    canvas._addDrawCallCount(1, context.renderMode);
                     engine.updateAndBindInstancesBuffer(pid._partBuffer, null, this.instancingFillAttributes);
                     engine.draw(true, 0, this.fillIndicesCount, pid._partData.usedElementCount);
                     engine.unbindInstanceAttributes();
                 }
                 else {
+                    canvas._addDrawCallCount(context.partDataEndIndex - context.partDataStartIndex, context.renderMode);
                     for (var i = context.partDataStartIndex; i < context.partDataEndIndex; i++) {
                         this.setupUniforms(effect, partIndex, pid._partData, i);
                         engine.draw(true, 0, this.fillIndicesCount);
@@ -43891,11 +44018,13 @@ var BABYLON;
                     if (!this.instancingBorderAttributes) {
                         this.instancingBorderAttributes = this.loadInstancingAttributes(BABYLON.Shape2D.SHAPE2D_BORDERPARTID, effect);
                     }
+                    canvas._addDrawCallCount(1, context.renderMode);
                     engine.updateAndBindInstancesBuffer(pid._partBuffer, null, this.instancingBorderAttributes);
                     engine.draw(true, 0, this.borderIndicesCount, pid._partData.usedElementCount);
                     engine.unbindInstanceAttributes();
                 }
                 else {
+                    canvas._addDrawCallCount(context.partDataEndIndex - context.partDataStartIndex, context.renderMode);
                     for (var i = context.partDataStartIndex; i < context.partDataEndIndex; i++) {
                         this.setupUniforms(effect, partIndex, pid._partData, i);
                         engine.draw(true, 0, this.borderIndicesCount);
@@ -45040,6 +45169,18 @@ var BABYLON;
             };
             this._notifDebugMode = false;
             this._mapCounter = 0;
+            this._drawCallsOpaqueCounter = new BABYLON.PerfCounter();
+            this._drawCallsAlphaTestCounter = new BABYLON.PerfCounter();
+            this._drawCallsTransparentCounter = new BABYLON.PerfCounter();
+            this._groupRenderCounter = new BABYLON.PerfCounter();
+            this._updateTransparentDataCounter = new BABYLON.PerfCounter();
+            this._cachedGroupRenderCounter = new BABYLON.PerfCounter();
+            this._updateCachedStateCounter = new BABYLON.PerfCounter();
+            this._updateLayoutCounter = new BABYLON.PerfCounter();
+            this._updatePositioningCounter = new BABYLON.PerfCounter();
+            this._updateLocalTransformCounter = new BABYLON.PerfCounter();
+            this._updateGlobalTransformCounter = new BABYLON.PerfCounter();
+            this._profileInfoText = null;
             BABYLON.Prim2DBase._isCanvasInit = false;
             if (!settings) {
                 settings = {};
@@ -45113,6 +45254,83 @@ var BABYLON;
             //this._supprtInstancedArray = false; // TODO REMOVE!!!
             this._setupInteraction(enableInteraction);
         }
+        Object.defineProperty(Canvas2D.prototype, "drawCallsOpaqueCounter", {
+            get: function () {
+                return this._drawCallsOpaqueCounter;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Canvas2D.prototype, "drawCallsAlphaTestCounter", {
+            get: function () {
+                return this._drawCallsAlphaTestCounter;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Canvas2D.prototype, "drawCallsTransparentCounter", {
+            get: function () {
+                return this._drawCallsTransparentCounter;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Canvas2D.prototype, "groupRenderCounter", {
+            get: function () {
+                return this._groupRenderCounter;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Canvas2D.prototype, "updateTransparentDataCounter", {
+            get: function () {
+                return this._updateTransparentDataCounter;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Canvas2D.prototype, "cachedGroupRenderCounter", {
+            get: function () {
+                return this._cachedGroupRenderCounter;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Canvas2D.prototype, "updateCachedStateCounter", {
+            get: function () {
+                return this._updateCachedStateCounter;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Canvas2D.prototype, "updateLayoutCounter", {
+            get: function () {
+                return this._updateLayoutCounter;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Canvas2D.prototype, "updatePositioningCounter", {
+            get: function () {
+                return this._updatePositioningCounter;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Canvas2D.prototype, "updateLocalTransformCounter", {
+            get: function () {
+                return this._updateLocalTransformCounter;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Canvas2D.prototype, "updateGlobalTransformCounter", {
+            get: function () {
+                return this._updateGlobalTransformCounter;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Canvas2D.prototype._canvasPreInit = function (settings) {
             var cachingStrategy = (settings.cachingStrategy == null) ? Canvas2D.CACHESTRATEGY_DONTCACHE : settings.cachingStrategy;
             this._cachingStrategy = cachingStrategy;
@@ -45717,11 +45935,106 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Canvas2D.prototype.createCanvasProfileInfoCanvas = function () {
+            var canvas = new ScreenSpaceCanvas2D(this.scene, {
+                id: "ProfileInfoCanvas", cachingStrategy: Canvas2D.CACHESTRATEGY_DONTCACHE, children: [
+                    new BABYLON.Rectangle2D({
+                        id: "ProfileBorder", border: "#FFFFFFFF", borderThickness: 2, roundRadius: 5, marginAlignment: "h: left, v: top", margin: "10", padding: "10", children: [
+                            new BABYLON.Text2D("Stats", { id: "ProfileInfoText", marginAlignment: "h: left, v: top", fontName: "10pt Lucida Console" })
+                        ]
+                    })
+                ]
+            });
+            this._profileInfoText = canvas.findById("ProfileInfoText");
+            return canvas;
+        };
         Canvas2D.prototype.checkBackgroundAvailability = function () {
             if (this._cachingStrategy === Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS) {
                 throw Error("Can't use Canvas Background with the caching strategy TOPLEVELGROUPS");
             }
         };
+        Canvas2D.prototype._initPerfMetrics = function () {
+            this._drawCallsOpaqueCounter.fetchNewFrame();
+            this._drawCallsAlphaTestCounter.fetchNewFrame();
+            this._drawCallsTransparentCounter.fetchNewFrame();
+            this._groupRenderCounter.fetchNewFrame();
+            this._updateTransparentDataCounter.fetchNewFrame();
+            this._cachedGroupRenderCounter.fetchNewFrame();
+            this._updateCachedStateCounter.fetchNewFrame();
+            this._updateLayoutCounter.fetchNewFrame();
+            this._updatePositioningCounter.fetchNewFrame();
+            this._updateLocalTransformCounter.fetchNewFrame();
+            this._updateGlobalTransformCounter.fetchNewFrame();
+        };
+        Canvas2D.prototype._fetchPerfMetrics = function () {
+            this._drawCallsOpaqueCounter.addCount(0, true);
+            this._drawCallsAlphaTestCounter.addCount(0, true);
+            this._drawCallsTransparentCounter.addCount(0, true);
+            this._groupRenderCounter.addCount(0, true);
+            this._updateTransparentDataCounter.addCount(0, true);
+            this._cachedGroupRenderCounter.addCount(0, true);
+            this._updateCachedStateCounter.addCount(0, true);
+            this._updateLayoutCounter.addCount(0, true);
+            this._updatePositioningCounter.addCount(0, true);
+            this._updateLocalTransformCounter.addCount(0, true);
+            this._updateGlobalTransformCounter.addCount(0, true);
+        };
+        Canvas2D.prototype._updateProfileCanvas = function () {
+            if (this._profileInfoText == null) {
+                return;
+            }
+            var format = function (v) { return (Math.round(v * 100) / 100).toString(); };
+            var p = "Draw Calls:\n" +
+                (" - Opaque:      " + this.drawCallsOpaqueCounter.current + ", (avg:" + format(this.drawCallsOpaqueCounter.lastSecAverage) + ", t:" + format(this.drawCallsOpaqueCounter.total) + ")\n") +
+                (" - AlphaTest:   " + this.drawCallsAlphaTestCounter.current + ", (avg:" + format(this.drawCallsAlphaTestCounter.lastSecAverage) + ", t:" + format(this.drawCallsAlphaTestCounter.total) + ")\n") +
+                (" - Transparent: " + this.drawCallsTransparentCounter.current + ", (avg:" + format(this.drawCallsTransparentCounter.lastSecAverage) + ", t:" + format(this.drawCallsTransparentCounter.total) + ")\n") +
+                ("Group Render: " + this.groupRenderCounter.current + ", (avg:" + format(this.groupRenderCounter.lastSecAverage) + ", t:" + format(this.groupRenderCounter.total) + ")\n") +
+                ("Update Transparent Data: " + this.updateTransparentDataCounter.current + ", (avg:" + format(this.updateTransparentDataCounter.lastSecAverage) + ", t:" + format(this.updateTransparentDataCounter.total) + ")\n") +
+                ("Cached Group Render: " + this.cachedGroupRenderCounter.current + ", (avg:" + format(this.cachedGroupRenderCounter.lastSecAverage) + ", t:" + format(this.cachedGroupRenderCounter.total) + ")\n") +
+                ("Update Cached States: " + this.updateCachedStateCounter.current + ", (avg:" + format(this.updateCachedStateCounter.lastSecAverage) + ", t:" + format(this.updateCachedStateCounter.total) + ")\n") +
+                (" - Update Layout: " + this.updateLayoutCounter.current + ", (avg:" + format(this.updateLayoutCounter.lastSecAverage) + ", t:" + format(this.updateLayoutCounter.total) + ")\n") +
+                (" - Update Positioning: " + this.updatePositioningCounter.current + ", (avg:" + format(this.updatePositioningCounter.lastSecAverage) + ", t:" + format(this.updatePositioningCounter.total) + ")\n") +
+                (" - Update Local  Trans: " + this.updateLocalTransformCounter.current + ", (avg:" + format(this.updateLocalTransformCounter.lastSecAverage) + ", t:" + format(this.updateLocalTransformCounter.total) + ")\n") +
+                (" - Update Global Trans: " + this.updateGlobalTransformCounter.current + ", (avg:" + format(this.updateGlobalTransformCounter.lastSecAverage) + ", t:" + format(this.updateGlobalTransformCounter.total) + ")\n");
+            this._profileInfoText.text = p;
+        };
+        Canvas2D.prototype._addDrawCallCount = function (count, renderMode) {
+            switch (renderMode) {
+                case BABYLON.Render2DContext.RenderModeOpaque:
+                    this._drawCallsOpaqueCounter.addCount(count, false);
+                    return;
+                case BABYLON.Render2DContext.RenderModeAlphaTest:
+                    this._drawCallsAlphaTestCounter.addCount(count, false);
+                    return;
+                case BABYLON.Render2DContext.RenderModeTransparent:
+                    this._drawCallsTransparentCounter.addCount(count, false);
+                    return;
+            }
+        };
+        Canvas2D.prototype._addGroupRenderCount = function (count) {
+            this._groupRenderCounter.addCount(count, false);
+        };
+        Canvas2D.prototype._addUpdateTransparentDataCount = function (count) {
+            this._updateTransparentDataCounter.addCount(count, false);
+        };
+        Canvas2D.prototype.addCachedGroupRenderCounter = function (count) {
+            this._cachedGroupRenderCounter.addCount(count, false);
+        };
+        Canvas2D.prototype.addUpdateCachedStateCounter = function (count) {
+            this._updateCachedStateCounter.addCount(count, false);
+        };
+        Canvas2D.prototype.addUpdateLayoutCounter = function (count) {
+            this._updateLayoutCounter.addCount(count, false);
+        };
+        Canvas2D.prototype.addUpdatePositioningCounter = function (count) {
+            this._updatePositioningCounter.addCount(count, false);
+        };
+        Canvas2D.prototype.addupdateLocalTransformCounter = function (count) {
+            this._updateLocalTransformCounter.addCount(count, false);
+        };
+        Canvas2D.prototype.addUpdateGlobalTransformCounter = function (count) {
+            this._updateGlobalTransformCounter.addCount(count, false);
+        };
         Canvas2D.prototype.onPrimBecomesDirty = function () {
             this._addPrimToDirtyList(this);
         };
@@ -45822,6 +46135,7 @@ var BABYLON;
          * Method that renders the Canvas, you should not invoke
          */
         Canvas2D.prototype._render = function () {
+            this._initPerfMetrics();
             this._updateTrackedNodes();
             this._updateCanvasState(false);
             if (!this._isScreenSpace) {
@@ -45844,6 +46158,8 @@ var BABYLON;
             if (this._cachingStrategy === Canvas2D.CACHESTRATEGY_CANVAS && this._cachedCanvasGroup) {
                 this._cachedCanvasGroup._renderCachedCanvas();
             }
+            this._fetchPerfMetrics();
+            this._updateProfileCanvas();
         };
         /**
          * Internal method that allocate a cache for the given group.

文件差異過大導致無法顯示
+ 41 - 41
dist/preview release/babylon.noworker.js


+ 10 - 0
src/Canvas2d/babylon.bounding2d.js

@@ -107,6 +107,16 @@ var BABYLON;
             r.extent = this.extent.clone();
             return r;
         };
+        BoundingInfo2D.prototype.clear = function () {
+            this.center.copyFromFloats(0, 0);
+            this.radius = 0;
+            this.extent.copyFromFloats(0, 0);
+        };
+        BoundingInfo2D.prototype.copyFrom = function (src) {
+            this.center.copyFrom(src.center);
+            this.radius = src.radius;
+            this.extent.copyFrom(src.extent);
+        };
         /**
          * return the max extend of the bounding info
          */

+ 187 - 0
src/Canvas2d/babylon.canvas2d.js

@@ -74,6 +74,18 @@ var BABYLON;
             };
             this._notifDebugMode = false;
             this._mapCounter = 0;
+            this._drawCallsOpaqueCounter = new BABYLON.PerfCounter();
+            this._drawCallsAlphaTestCounter = new BABYLON.PerfCounter();
+            this._drawCallsTransparentCounter = new BABYLON.PerfCounter();
+            this._groupRenderCounter = new BABYLON.PerfCounter();
+            this._updateTransparentDataCounter = new BABYLON.PerfCounter();
+            this._cachedGroupRenderCounter = new BABYLON.PerfCounter();
+            this._updateCachedStateCounter = new BABYLON.PerfCounter();
+            this._updateLayoutCounter = new BABYLON.PerfCounter();
+            this._updatePositioningCounter = new BABYLON.PerfCounter();
+            this._updateLocalTransformCounter = new BABYLON.PerfCounter();
+            this._updateGlobalTransformCounter = new BABYLON.PerfCounter();
+            this._profileInfoText = null;
             BABYLON.Prim2DBase._isCanvasInit = false;
             if (!settings) {
                 settings = {};
@@ -147,6 +159,83 @@ var BABYLON;
             //this._supprtInstancedArray = false; // TODO REMOVE!!!
             this._setupInteraction(enableInteraction);
         }
+        Object.defineProperty(Canvas2D.prototype, "drawCallsOpaqueCounter", {
+            get: function () {
+                return this._drawCallsOpaqueCounter;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Canvas2D.prototype, "drawCallsAlphaTestCounter", {
+            get: function () {
+                return this._drawCallsAlphaTestCounter;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Canvas2D.prototype, "drawCallsTransparentCounter", {
+            get: function () {
+                return this._drawCallsTransparentCounter;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Canvas2D.prototype, "groupRenderCounter", {
+            get: function () {
+                return this._groupRenderCounter;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Canvas2D.prototype, "updateTransparentDataCounter", {
+            get: function () {
+                return this._updateTransparentDataCounter;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Canvas2D.prototype, "cachedGroupRenderCounter", {
+            get: function () {
+                return this._cachedGroupRenderCounter;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Canvas2D.prototype, "updateCachedStateCounter", {
+            get: function () {
+                return this._updateCachedStateCounter;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Canvas2D.prototype, "updateLayoutCounter", {
+            get: function () {
+                return this._updateLayoutCounter;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Canvas2D.prototype, "updatePositioningCounter", {
+            get: function () {
+                return this._updatePositioningCounter;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Canvas2D.prototype, "updateLocalTransformCounter", {
+            get: function () {
+                return this._updateLocalTransformCounter;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Canvas2D.prototype, "updateGlobalTransformCounter", {
+            get: function () {
+                return this._updateGlobalTransformCounter;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Canvas2D.prototype._canvasPreInit = function (settings) {
             var cachingStrategy = (settings.cachingStrategy == null) ? Canvas2D.CACHESTRATEGY_DONTCACHE : settings.cachingStrategy;
             this._cachingStrategy = cachingStrategy;
@@ -751,11 +840,106 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Canvas2D.prototype.createCanvasProfileInfoCanvas = function () {
+            var canvas = new ScreenSpaceCanvas2D(this.scene, {
+                id: "ProfileInfoCanvas", cachingStrategy: Canvas2D.CACHESTRATEGY_DONTCACHE, children: [
+                    new BABYLON.Rectangle2D({
+                        id: "ProfileBorder", border: "#FFFFFFFF", borderThickness: 2, roundRadius: 5, marginAlignment: "h: left, v: top", margin: "10", padding: "10", children: [
+                            new BABYLON.Text2D("Stats", { id: "ProfileInfoText", marginAlignment: "h: left, v: top", fontName: "10pt Lucida Console" })
+                        ]
+                    })
+                ]
+            });
+            this._profileInfoText = canvas.findById("ProfileInfoText");
+            return canvas;
+        };
         Canvas2D.prototype.checkBackgroundAvailability = function () {
             if (this._cachingStrategy === Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS) {
                 throw Error("Can't use Canvas Background with the caching strategy TOPLEVELGROUPS");
             }
         };
+        Canvas2D.prototype._initPerfMetrics = function () {
+            this._drawCallsOpaqueCounter.fetchNewFrame();
+            this._drawCallsAlphaTestCounter.fetchNewFrame();
+            this._drawCallsTransparentCounter.fetchNewFrame();
+            this._groupRenderCounter.fetchNewFrame();
+            this._updateTransparentDataCounter.fetchNewFrame();
+            this._cachedGroupRenderCounter.fetchNewFrame();
+            this._updateCachedStateCounter.fetchNewFrame();
+            this._updateLayoutCounter.fetchNewFrame();
+            this._updatePositioningCounter.fetchNewFrame();
+            this._updateLocalTransformCounter.fetchNewFrame();
+            this._updateGlobalTransformCounter.fetchNewFrame();
+        };
+        Canvas2D.prototype._fetchPerfMetrics = function () {
+            this._drawCallsOpaqueCounter.addCount(0, true);
+            this._drawCallsAlphaTestCounter.addCount(0, true);
+            this._drawCallsTransparentCounter.addCount(0, true);
+            this._groupRenderCounter.addCount(0, true);
+            this._updateTransparentDataCounter.addCount(0, true);
+            this._cachedGroupRenderCounter.addCount(0, true);
+            this._updateCachedStateCounter.addCount(0, true);
+            this._updateLayoutCounter.addCount(0, true);
+            this._updatePositioningCounter.addCount(0, true);
+            this._updateLocalTransformCounter.addCount(0, true);
+            this._updateGlobalTransformCounter.addCount(0, true);
+        };
+        Canvas2D.prototype._updateProfileCanvas = function () {
+            if (this._profileInfoText == null) {
+                return;
+            }
+            var format = function (v) { return (Math.round(v * 100) / 100).toString(); };
+            var p = "Draw Calls:\n" +
+                (" - Opaque:      " + this.drawCallsOpaqueCounter.current + ", (avg:" + format(this.drawCallsOpaqueCounter.lastSecAverage) + ", t:" + format(this.drawCallsOpaqueCounter.total) + ")\n") +
+                (" - AlphaTest:   " + this.drawCallsAlphaTestCounter.current + ", (avg:" + format(this.drawCallsAlphaTestCounter.lastSecAverage) + ", t:" + format(this.drawCallsAlphaTestCounter.total) + ")\n") +
+                (" - Transparent: " + this.drawCallsTransparentCounter.current + ", (avg:" + format(this.drawCallsTransparentCounter.lastSecAverage) + ", t:" + format(this.drawCallsTransparentCounter.total) + ")\n") +
+                ("Group Render: " + this.groupRenderCounter.current + ", (avg:" + format(this.groupRenderCounter.lastSecAverage) + ", t:" + format(this.groupRenderCounter.total) + ")\n") +
+                ("Update Transparent Data: " + this.updateTransparentDataCounter.current + ", (avg:" + format(this.updateTransparentDataCounter.lastSecAverage) + ", t:" + format(this.updateTransparentDataCounter.total) + ")\n") +
+                ("Cached Group Render: " + this.cachedGroupRenderCounter.current + ", (avg:" + format(this.cachedGroupRenderCounter.lastSecAverage) + ", t:" + format(this.cachedGroupRenderCounter.total) + ")\n") +
+                ("Update Cached States: " + this.updateCachedStateCounter.current + ", (avg:" + format(this.updateCachedStateCounter.lastSecAverage) + ", t:" + format(this.updateCachedStateCounter.total) + ")\n") +
+                (" - Update Layout: " + this.updateLayoutCounter.current + ", (avg:" + format(this.updateLayoutCounter.lastSecAverage) + ", t:" + format(this.updateLayoutCounter.total) + ")\n") +
+                (" - Update Positioning: " + this.updatePositioningCounter.current + ", (avg:" + format(this.updatePositioningCounter.lastSecAverage) + ", t:" + format(this.updatePositioningCounter.total) + ")\n") +
+                (" - Update Local  Trans: " + this.updateLocalTransformCounter.current + ", (avg:" + format(this.updateLocalTransformCounter.lastSecAverage) + ", t:" + format(this.updateLocalTransformCounter.total) + ")\n") +
+                (" - Update Global Trans: " + this.updateGlobalTransformCounter.current + ", (avg:" + format(this.updateGlobalTransformCounter.lastSecAverage) + ", t:" + format(this.updateGlobalTransformCounter.total) + ")\n");
+            this._profileInfoText.text = p;
+        };
+        Canvas2D.prototype._addDrawCallCount = function (count, renderMode) {
+            switch (renderMode) {
+                case BABYLON.Render2DContext.RenderModeOpaque:
+                    this._drawCallsOpaqueCounter.addCount(count, false);
+                    return;
+                case BABYLON.Render2DContext.RenderModeAlphaTest:
+                    this._drawCallsAlphaTestCounter.addCount(count, false);
+                    return;
+                case BABYLON.Render2DContext.RenderModeTransparent:
+                    this._drawCallsTransparentCounter.addCount(count, false);
+                    return;
+            }
+        };
+        Canvas2D.prototype._addGroupRenderCount = function (count) {
+            this._groupRenderCounter.addCount(count, false);
+        };
+        Canvas2D.prototype._addUpdateTransparentDataCount = function (count) {
+            this._updateTransparentDataCounter.addCount(count, false);
+        };
+        Canvas2D.prototype.addCachedGroupRenderCounter = function (count) {
+            this._cachedGroupRenderCounter.addCount(count, false);
+        };
+        Canvas2D.prototype.addUpdateCachedStateCounter = function (count) {
+            this._updateCachedStateCounter.addCount(count, false);
+        };
+        Canvas2D.prototype.addUpdateLayoutCounter = function (count) {
+            this._updateLayoutCounter.addCount(count, false);
+        };
+        Canvas2D.prototype.addUpdatePositioningCounter = function (count) {
+            this._updatePositioningCounter.addCount(count, false);
+        };
+        Canvas2D.prototype.addupdateLocalTransformCounter = function (count) {
+            this._updateLocalTransformCounter.addCount(count, false);
+        };
+        Canvas2D.prototype.addUpdateGlobalTransformCounter = function (count) {
+            this._updateGlobalTransformCounter.addCount(count, false);
+        };
         Canvas2D.prototype.onPrimBecomesDirty = function () {
             this._addPrimToDirtyList(this);
         };
@@ -856,6 +1040,7 @@ var BABYLON;
          * Method that renders the Canvas, you should not invoke
          */
         Canvas2D.prototype._render = function () {
+            this._initPerfMetrics();
             this._updateTrackedNodes();
             this._updateCanvasState(false);
             if (!this._isScreenSpace) {
@@ -878,6 +1063,8 @@ var BABYLON;
             if (this._cachingStrategy === Canvas2D.CACHESTRATEGY_CANVAS && this._cachedCanvasGroup) {
                 this._cachedCanvasGroup._renderCachedCanvas();
             }
+            this._fetchPerfMetrics();
+            this._updateProfileCanvas();
         };
         /**
          * Internal method that allocate a cache for the given group.

+ 6 - 1
src/Canvas2d/babylon.ellipse2d.js

@@ -38,7 +38,8 @@ var BABYLON;
                 }
                 this.effectsReady = true;
             }
-            var engine = instanceInfo.owner.owner.engine;
+            var canvas = instanceInfo.owner.owner;
+            var engine = canvas.engine;
             var depthFunction = 0;
             if (this.effectFill && this.effectBorder) {
                 depthFunction = engine.getDepthFunction();
@@ -58,11 +59,13 @@ var BABYLON;
                     if (!this.instancingFillAttributes) {
                         this.instancingFillAttributes = this.loadInstancingAttributes(BABYLON.Shape2D.SHAPE2D_FILLPARTID, effect);
                     }
+                    canvas._addDrawCallCount(1, context.renderMode);
                     engine.updateAndBindInstancesBuffer(pid._partBuffer, null, this.instancingFillAttributes);
                     engine.draw(true, 0, this.fillIndicesCount, pid._partData.usedElementCount);
                     engine.unbindInstanceAttributes();
                 }
                 else {
+                    canvas._addDrawCallCount(context.partDataEndIndex - context.partDataStartIndex, context.renderMode);
                     for (var i = context.partDataStartIndex; i < context.partDataEndIndex; i++) {
                         this.setupUniforms(effect, partIndex, pid._partData, i);
                         engine.draw(true, 0, this.fillIndicesCount);
@@ -82,11 +85,13 @@ var BABYLON;
                     if (!this.instancingBorderAttributes) {
                         this.instancingBorderAttributes = this.loadInstancingAttributes(BABYLON.Shape2D.SHAPE2D_BORDERPARTID, effect);
                     }
+                    canvas._addDrawCallCount(1, context.renderMode);
                     engine.updateAndBindInstancesBuffer(pid._partBuffer, null, this.instancingBorderAttributes);
                     engine.draw(true, 0, this.borderIndicesCount, pid._partData.usedElementCount);
                     engine.unbindInstanceAttributes();
                 }
                 else {
+                    canvas._addDrawCallCount(context.partDataEndIndex - context.partDataStartIndex, context.renderMode);
                     for (var i = context.partDataStartIndex; i < context.partDataEndIndex; i++) {
                         this.setupUniforms(effect, partIndex, pid._partData, i);
                         engine.draw(true, 0, this.borderIndicesCount);

+ 3 - 0
src/Canvas2d/babylon.group2d.js

@@ -229,6 +229,7 @@ var BABYLON;
             this._renderableData._primDirtyList.push(prim);
         };
         Group2D.prototype._renderCachedCanvas = function () {
+            this.owner._addGroupRenderCount(1);
             this.updateCachedStates(true);
             var context = new BABYLON.PrepareRender2DContext();
             this._prepareGroupRender(context);
@@ -359,6 +360,7 @@ var BABYLON;
             }
             // Render the primitives if needed: either if we don't cache the content or if the content is cached but has changed
             if (!this.isCachedGroup || this._cacheGroupDirty) {
+                this.owner._addGroupRenderCount(1);
                 if (this.isCachedGroup) {
                     this._bindCacheTarget();
                 }
@@ -442,6 +444,7 @@ var BABYLON;
             }
         };
         Group2D.prototype._updateTransparentData = function () {
+            this.owner._addUpdateTransparentDataCount(1);
             var rd = this._renderableData;
             // If null, there was no change of ZOrder, we have nothing to do
             if (rd._firstChangedPrim === null) {

+ 6 - 1
src/Canvas2d/babylon.lines2d.js

@@ -38,7 +38,8 @@ var BABYLON;
                 }
                 this.effectsReady = true;
             }
-            var engine = instanceInfo.owner.owner.engine;
+            var canvas = instanceInfo.owner.owner;
+            var engine = canvas.engine;
             var depthFunction = 0;
             if (this.effectFill && this.effectBorder) {
                 depthFunction = engine.getDepthFunction();
@@ -58,11 +59,13 @@ var BABYLON;
                     if (!this.instancingFillAttributes) {
                         this.instancingFillAttributes = this.loadInstancingAttributes(BABYLON.Shape2D.SHAPE2D_FILLPARTID, effect);
                     }
+                    canvas._addDrawCallCount(1, context.renderMode);
                     engine.updateAndBindInstancesBuffer(pid._partBuffer, null, this.instancingFillAttributes);
                     engine.draw(true, 0, this.fillIndicesCount, pid._partData.usedElementCount);
                     engine.unbindInstanceAttributes();
                 }
                 else {
+                    canvas._addDrawCallCount(context.partDataEndIndex - context.partDataStartIndex, context.renderMode);
                     for (var i = context.partDataStartIndex; i < context.partDataEndIndex; i++) {
                         this.setupUniforms(effect, partIndex, pid._partData, i);
                         engine.draw(true, 0, this.fillIndicesCount);
@@ -82,11 +85,13 @@ var BABYLON;
                     if (!this.instancingBorderAttributes) {
                         this.instancingBorderAttributes = this.loadInstancingAttributes(BABYLON.Shape2D.SHAPE2D_BORDERPARTID, effect);
                     }
+                    canvas._addDrawCallCount(1, context.renderMode);
                     engine.updateAndBindInstancesBuffer(pid._partBuffer, null, this.instancingBorderAttributes);
                     engine.draw(true, 0, this.borderIndicesCount, pid._partData.usedElementCount);
                     engine.unbindInstanceAttributes();
                 }
                 else {
+                    canvas._addDrawCallCount(context.partDataEndIndex - context.partDataStartIndex, context.renderMode);
                     for (var i = context.partDataStartIndex; i < context.partDataEndIndex; i++) {
                         this.setupUniforms(effect, partIndex, pid._partData, i);
                         engine.draw(true, 0, this.borderIndicesCount);

+ 112 - 40
src/Canvas2d/babylon.prim2dBase.js

@@ -439,12 +439,21 @@ var BABYLON;
         }
         /**
          * Set the thickness from a string value
-         * @param thickness format is "top: <value>, left:<value>, right:<value>, bottom:<value>" each are optional, auto will be set if it's omitted.
+         * @param thickness format is "top: <value>, left:<value>, right:<value>, bottom:<value>" or "<value>" (same for all edges) each are optional, auto will be set if it's omitted.
          * Values are: 'auto', 'inherit', 'XX%' for percentage, 'XXpx' or 'XX' for pixels.
          */
         PrimitiveThickness.prototype.fromString = function (thickness) {
             this._clear();
             var m = thickness.trim().split(",");
+            // Special case, one value to apply to all edges
+            if (m.length === 1 && thickness.indexOf(":") === -1) {
+                this._setStringValue(m[0], 0, false);
+                this._setStringValue(m[0], 1, false);
+                this._setStringValue(m[0], 2, false);
+                this._setStringValue(m[0], 3, false);
+                this._changedCallback();
+                return;
+            }
             var res = false;
             for (var _i = 0; _i < m.length; _i++) {
                 var cm = m[_i];
@@ -1106,6 +1115,16 @@ var BABYLON;
             result.width = this.leftPixels + sourceArea.width + this.rightPixels;
             result.height = this.bottomPixels + sourceArea.height + this.topPixels;
         };
+        PrimitiveThickness.prototype.enlarge = function (sourceArea, dstOffset, enlargedArea) {
+            this._computePixels(0, sourceArea, true);
+            this._computePixels(1, sourceArea, true);
+            this._computePixels(2, sourceArea, true);
+            this._computePixels(3, sourceArea, true);
+            dstOffset.x = this.leftPixels;
+            enlargedArea.width = sourceArea.width + (dstOffset.x + this.rightPixels);
+            dstOffset.y = this.bottomPixels;
+            enlargedArea.height = sourceArea.height + (dstOffset.y + this.topPixels);
+        };
         PrimitiveThickness.Auto = 0x1;
         PrimitiveThickness.Inherit = 0x2;
         PrimitiveThickness.Percentage = 0x4;
@@ -1187,8 +1206,10 @@ var BABYLON;
             this._layoutArea = BABYLON.Size.Zero();
             this._layoutAreaPos = BABYLON.Vector2.Zero();
             this._marginOffset = BABYLON.Vector2.Zero();
-            this._parentMargingOffset = BABYLON.Vector2.Zero();
+            this._paddingOffset = BABYLON.Vector2.Zero();
+            this._parentPaddingOffset = BABYLON.Vector2.Zero();
             this._parentContentArea = BABYLON.Size.Zero();
+            this._lastAutoSizeArea = BABYLON.Size.Zero();
             this._contentArea = new BABYLON.Size(null, null);
             this._pointerEventObservable = new BABYLON.Observable();
             this._siblingDepthOffset = this._hierarchyDepthOffset = 0;
@@ -1422,7 +1443,7 @@ var BABYLON;
              * Setting this property may have no effect is specific alignment are in effect.
              */
             get: function () {
-                return this._position;
+                return this._position || Prim2DBase._nullPosition;
             },
             set: function (value) {
                 if (!this._checkPositionChange()) {
@@ -1904,7 +1925,12 @@ var BABYLON;
              */
             get: function () {
                 if (this._isFlagSet(BABYLON.SmartPropertyPrim.flagBoundingInfoDirty)) {
-                    this._boundingInfo = this.levelBoundingInfo.clone();
+                    if (this.isSizedByContent) {
+                        this._boundingInfo.clear();
+                    }
+                    else {
+                        this._boundingInfo.copyFrom(this.levelBoundingInfo);
+                    }
                     var bi = this._boundingInfo;
                     var tps = new BABYLON.BoundingInfo2D();
                     for (var _i = 0, _a = this._children; _i < _a.length; _i++) {
@@ -1934,6 +1960,16 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(Prim2DBase.prototype, "isSizedByContent", {
+            /**
+             * Return true if this prim has an auto size which is set by the children's global bounding box
+             */
+            get: function () {
+                return (this._size == null) && (this._children.length > 0);
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(Prim2DBase.prototype, "isPositionAuto", {
             /**
              * Determine if the position is automatically computed or fixed because manually specified.
@@ -2185,22 +2221,14 @@ var BABYLON;
             this._layoutEngine = engine;
         };
         Prim2DBase.prototype._updateLocalTransform = function () {
-            var parentMarginOffsetChanged = false;
-            var parentMarginOffset = null;
-            if (this._parent) {
-                parentMarginOffset = this._parent._marginOffset;
-                parentMarginOffsetChanged = !parentMarginOffset.equals(this._parentMargingOffset);
-                this._parentMargingOffset.copyFrom(parentMarginOffset);
-            }
-            else {
-                parentMarginOffset = Prim2DBase._v0;
-            }
             var tflags = Prim2DBase.actualPositionProperty.flagId | Prim2DBase.rotationProperty.flagId | Prim2DBase.scaleProperty.flagId | Prim2DBase.originProperty.flagId;
-            if (parentMarginOffsetChanged || this.checkPropertiesDirty(tflags)) {
+            if (this.checkPropertiesDirty(tflags)) {
+                this.owner.addupdateLocalTransformCounter(1);
                 var rot = BABYLON.Quaternion.RotationAxis(new BABYLON.Vector3(0, 0, 1), this._rotation);
                 var local;
+                var pos = this.position;
                 if (this._origin.x === 0 && this._origin.y === 0) {
-                    local = BABYLON.Matrix.Compose(new BABYLON.Vector3(this._scale, this._scale, 1), rot, new BABYLON.Vector3(this.actualPosition.x + parentMarginOffset.x, this.actualPosition.y + parentMarginOffset.y, 0));
+                    local = BABYLON.Matrix.Compose(new BABYLON.Vector3(this._scale, this._scale, 1), rot, new BABYLON.Vector3(pos.x, pos.y, 0));
                     this._localTransform = local;
                 }
                 else {
@@ -2213,8 +2241,8 @@ var BABYLON;
                     // -Origin * rotation * scale
                     BABYLON.Matrix.ScalingToRef(this._scale, this._scale, 1, Prim2DBase._t0);
                     Prim2DBase._t2.multiplyToRef(Prim2DBase._t0, Prim2DBase._t1);
-                    // -Origin * rotation * scale * (Origin + Position + Parent Margin Offset)
-                    BABYLON.Matrix.TranslationToRef((as.width * this._origin.x) + this.actualPosition.x + parentMarginOffset.x, (as.height * this._origin.y) + this.actualPosition.y + parentMarginOffset.y, 0, Prim2DBase._t2);
+                    // -Origin * rotation * scale * (Origin + Position)
+                    BABYLON.Matrix.TranslationToRef((as.width * this._origin.x) + pos.x, (as.height * this._origin.y) + pos.y, 0, Prim2DBase._t2);
                     Prim2DBase._t1.multiplyToRef(Prim2DBase._t2, this._localTransform);
                 }
                 this.clearPropertiesDirty(tflags);
@@ -2226,6 +2254,7 @@ var BABYLON;
             if (this.isDisposed) {
                 return;
             }
+            this.owner.addCachedGroupRenderCounter(1);
             // Check if the parent is synced
             if (this._parent && ((this._parent._globalTransformProcessStep !== this.owner._globalTransformProcessStep) || this._parent._areSomeFlagsSet(BABYLON.SmartPropertyPrim.flagLayoutDirty | BABYLON.SmartPropertyPrim.flagPositioningDirty))) {
                 this._parent.updateCachedStates(false);
@@ -2233,7 +2262,7 @@ var BABYLON;
             // Update actualSize only if there' not positioning to recompute and the size changed
             // Otherwise positioning will take care of it.
             var sizeDirty = this.checkPropertiesDirty(Prim2DBase.sizeProperty.flagId);
-            if (!this._isFlagSet(BABYLON.SmartPropertyPrim.flagLayoutDirty) && sizeDirty) {
+            if (!this._isFlagSet(BABYLON.SmartPropertyPrim.flagLayoutDirty) && !this._isFlagSet(BABYLON.SmartPropertyPrim.flagPositioningDirty) && sizeDirty) {
                 var size = this.size;
                 if (size) {
                     if (this.size.width != null) {
@@ -2248,14 +2277,22 @@ var BABYLON;
             // Check for layout update
             var positioningDirty = this._isFlagSet(BABYLON.SmartPropertyPrim.flagPositioningDirty);
             if (this._isFlagSet(BABYLON.SmartPropertyPrim.flagLayoutDirty)) {
+                this.owner.addUpdateLayoutCounter(1);
                 this._layoutEngine.updateLayout(this);
                 this._clearFlags(BABYLON.SmartPropertyPrim.flagLayoutDirty);
             }
             var positioningComputed = positioningDirty && !this._isFlagSet(BABYLON.SmartPropertyPrim.flagPositioningDirty);
+            var autoContentChanged = false;
+            if (this.isSizeAuto) {
+                autoContentChanged = (!this._lastAutoSizeArea.equals(this.size));
+            }
             // Check for positioning update
-            if (!positioningComputed && (sizeDirty || this._isFlagSet(BABYLON.SmartPropertyPrim.flagPositioningDirty) || (this._parent && !this._parent.contentArea.equals(this._parentContentArea)))) {
+            if (!positioningComputed && (autoContentChanged || sizeDirty || this._isFlagSet(BABYLON.SmartPropertyPrim.flagPositioningDirty) || (this._parent && !this._parent.contentArea.equals(this._parentContentArea)))) {
                 this._updatePositioning();
                 this._clearFlags(BABYLON.SmartPropertyPrim.flagPositioningDirty);
+                if (sizeDirty) {
+                    this.clearPropertiesDirty(Prim2DBase.sizeProperty.flagId);
+                }
                 positioningComputed = true;
             }
             if (positioningComputed && this._parent) {
@@ -2263,18 +2300,30 @@ var BABYLON;
             }
             // Check if we must update this prim
             if (this === this.owner || this._globalTransformProcessStep !== this.owner._globalTransformProcessStep) {
+                this.owner.addUpdateGlobalTransformCounter(1);
                 var curVisibleState = this.isVisible;
                 this.isVisible = (!this._parent || this._parent.isVisible) && this.levelVisible;
                 // Detect a change of visibility
                 this._changeFlags(BABYLON.SmartPropertyPrim.flagVisibilityChanged, curVisibleState !== this.isVisible);
                 // Get/compute the localTransform
                 var localDirty = this._updateLocalTransform();
+                var parentPaddingChanged = false;
+                var parentPaddingOffset = Prim2DBase._v0;
+                if (this._parent) {
+                    parentPaddingOffset = this._parent._paddingOffset;
+                    parentPaddingChanged = !parentPaddingOffset.equals(this._parentPaddingOffset);
+                }
                 // Check if there are changes in the parent that will force us to update the global matrix
                 var parentDirty = (this._parent != null) ? (this._parent._globalTransformStep !== this._parentTransformStep) : false;
                 // Check if we have to update the globalTransform
-                if (!this._globalTransform || localDirty || parentDirty) {
+                if (!this._globalTransform || localDirty || parentDirty || parentPaddingChanged) {
                     var globalTransform = this._parent ? this._parent._globalTransform : null;
-                    this._globalTransform = this._parent ? this._localTransform.multiply(globalTransform) : this._localTransform;
+                    var localTransform;
+                    Prim2DBase._transMtx.copyFrom(this._localTransform);
+                    Prim2DBase._transMtx.m[12] += this._layoutAreaPos.x + this._marginOffset.x + parentPaddingOffset.x;
+                    Prim2DBase._transMtx.m[13] += this._layoutAreaPos.y + this._marginOffset.y + parentPaddingOffset.y;
+                    localTransform = Prim2DBase._transMtx;
+                    this._globalTransform = this._parent ? localTransform.multiply(globalTransform) : localTransform.clone();
                     this._invGlobalTransform = BABYLON.Matrix.Invert(this._globalTransform);
                     this._globalTransformStep = this.owner._globalTransformProcessStep + 1;
                     this._parentTransformStep = this._parent ? this._parent._globalTransformStep : 0;
@@ -2290,6 +2339,7 @@ var BABYLON;
             }
         };
         Prim2DBase.prototype._updatePositioning = function () {
+            this.owner.addUpdatePositioningCounter(1);
             // From this point we assume that the primitive layoutArea is computed and up to date.
             // We know have to :
             //  1. Determine the PaddingArea and the ActualPosition based on the margin/marginAlignment properties, which will also set the size property of the primitive
@@ -2306,31 +2356,44 @@ var BABYLON;
             // Apply margin
             if (this._hasMargin) {
                 this.margin.computeWithAlignment(this.layoutArea, this.size, this.marginAlignment, this._marginOffset, Prim2DBase._size);
-                this.actualPosition = this._marginOffset.add(this._layoutAreaPos);
-                if (this.size.width != null) {
-                    this.size.width = Prim2DBase._size.width;
-                }
-                if (this.size.height != null) {
-                    this.size.height = Prim2DBase._size.height;
-                }
-                this.actualSize.copyFrom(Prim2DBase._size.clone());
+                this.actualSize = Prim2DBase._size.clone();
             }
+            var isSizeAuto = this.isSizeAuto;
             if (this._hasPadding) {
-                this._getInitialContentAreaToRef(this.actualSize, Prim2DBase._icPos, Prim2DBase._icArea);
-                Prim2DBase._icArea.width = Math.max(0, Prim2DBase._icArea.width);
-                Prim2DBase._icArea.height = Math.max(0, Prim2DBase._icArea.height);
-                this.padding.compute(Prim2DBase._icArea, this._marginOffset, Prim2DBase._size);
-                this._marginOffset.x += Prim2DBase._icPos.x;
-                this._marginOffset.y += Prim2DBase._icPos.y;
-                this._contentArea.copyFrom(Prim2DBase._size);
+                // Two cases from here: the size of the Primitive is Auto, its content can't be shrink, so me resize the primitive itself
+                if (isSizeAuto) {
+                    var content = this.size.clone();
+                    this._getActualSizeFromContentToRef(content, Prim2DBase._icArea);
+                    this.padding.enlarge(Prim2DBase._icArea, this._paddingOffset, Prim2DBase._size);
+                    this._contentArea.copyFrom(content);
+                    this.actualSize = Prim2DBase._size.clone();
+                    // Changing the padding has resize the prim, which forces us to recompute margin again
+                    if (this._hasMargin) {
+                        this.margin.computeWithAlignment(this.layoutArea, Prim2DBase._size, this.marginAlignment, this._marginOffset, Prim2DBase._size);
+                    }
+                }
+                else {
+                    this._getInitialContentAreaToRef(this.actualSize, Prim2DBase._icPos, Prim2DBase._icArea);
+                    Prim2DBase._icArea.width = Math.max(0, Prim2DBase._icArea.width);
+                    Prim2DBase._icArea.height = Math.max(0, Prim2DBase._icArea.height);
+                    this.padding.compute(Prim2DBase._icArea, this._paddingOffset, Prim2DBase._size);
+                    this._paddingOffset.x += Prim2DBase._icPos.x;
+                    this._paddingOffset.y += Prim2DBase._icPos.y;
+                    this._contentArea.copyFrom(Prim2DBase._size);
+                }
             }
             else {
                 this._getInitialContentAreaToRef(this.actualSize, Prim2DBase._icPos, Prim2DBase._icArea);
                 Prim2DBase._icArea.width = Math.max(0, Prim2DBase._icArea.width);
                 Prim2DBase._icArea.height = Math.max(0, Prim2DBase._icArea.height);
-                this._marginOffset.copyFrom(Prim2DBase._icPos);
+                this._paddingOffset.copyFrom(Prim2DBase._icPos);
                 this._contentArea.copyFrom(Prim2DBase._icArea);
             }
+            var aPos = new BABYLON.Vector2(this._layoutAreaPos.x + this._marginOffset.x, this._layoutAreaPos.y + this._marginOffset.y);
+            this.actualPosition = aPos;
+            if (isSizeAuto) {
+                this._lastAutoSizeArea = this.size;
+            }
         };
         Object.defineProperty(Prim2DBase.prototype, "contentArea", {
             /**
@@ -2393,10 +2456,18 @@ var BABYLON;
          * @param initialContentArea the size of the initial content area to compute, a valid object is passed, you have to set its properties. PLEASE ROUND the values, we're talking about pixels and fraction of them is not a good thing!
          */
         Prim2DBase.prototype._getInitialContentAreaToRef = function (primSize, initialContentPosition, initialContentArea) {
-            initialContentArea.width = primSize.width;
-            initialContentArea.height = primSize.height;
+            initialContentArea.copyFrom(primSize);
             initialContentPosition.x = initialContentPosition.y = 0;
         };
+        /**
+         * This method is used to calculate the new size of the primitive based on the content which must stay the same
+         * Check the Rectangle2D implementation for a concrete application.
+         * @param primSize the current size of the primitive
+         * @param newPrimSize the new size of the primitive. PLEASE ROUND THE values, we're talking about pixels and fraction of them are not our friends!
+         */
+        Prim2DBase.prototype._getActualSizeFromContentToRef = function (primSize, newPrimSize) {
+            newPrimSize.copyFrom(primSize);
+        };
         Prim2DBase.PRIM2DBASE_PROPCOUNT = 15;
         Prim2DBase._nullPosition = BABYLON.Vector2.Zero();
         Prim2DBase.boundinbBoxReentrency = false;
@@ -2406,6 +2477,7 @@ var BABYLON;
         Prim2DBase._t1 = new BABYLON.Matrix();
         Prim2DBase._t2 = new BABYLON.Matrix();
         Prim2DBase._v0 = BABYLON.Vector2.Zero(); // Must stay with the value 0,0
+        Prim2DBase._transMtx = BABYLON.Matrix.Zero();
         Prim2DBase._icPos = BABYLON.Vector2.Zero();
         Prim2DBase._icArea = BABYLON.Size.Zero();
         Prim2DBase._size = BABYLON.Size.Zero();

+ 18 - 1
src/Canvas2d/babylon.rectangle2d.js

@@ -38,7 +38,8 @@ var BABYLON;
                 }
                 this.effectsReady = true;
             }
-            var engine = instanceInfo.owner.owner.engine;
+            var canvas = instanceInfo.owner.owner;
+            var engine = canvas.engine;
             var depthFunction = 0;
             if (this.effectFill && this.effectBorder) {
                 depthFunction = engine.getDepthFunction();
@@ -58,11 +59,13 @@ var BABYLON;
                     if (!this.instancingFillAttributes) {
                         this.instancingFillAttributes = this.loadInstancingAttributes(BABYLON.Shape2D.SHAPE2D_FILLPARTID, effect);
                     }
+                    canvas._addDrawCallCount(1, context.renderMode);
                     engine.updateAndBindInstancesBuffer(pid._partBuffer, null, this.instancingFillAttributes);
                     engine.draw(true, 0, this.fillIndicesCount, pid._partData.usedElementCount);
                     engine.unbindInstanceAttributes();
                 }
                 else {
+                    canvas._addDrawCallCount(context.partDataEndIndex - context.partDataStartIndex, context.renderMode);
                     for (var i = context.partDataStartIndex; i < context.partDataEndIndex; i++) {
                         this.setupUniforms(effect, partIndex, pid._partData, i);
                         engine.draw(true, 0, this.fillIndicesCount);
@@ -82,11 +85,13 @@ var BABYLON;
                     if (!this.instancingBorderAttributes) {
                         this.instancingBorderAttributes = this.loadInstancingAttributes(BABYLON.Shape2D.SHAPE2D_BORDERPARTID, effect);
                     }
+                    canvas._addDrawCallCount(1, context.renderMode);
                     engine.updateAndBindInstancesBuffer(pid._partBuffer, null, this.instancingBorderAttributes);
                     engine.draw(true, 0, this.borderIndicesCount, pid._partData.usedElementCount);
                     engine.unbindInstanceAttributes();
                 }
                 else {
+                    canvas._addDrawCallCount(context.partDataEndIndex - context.partDataStartIndex, context.renderMode);
                     for (var i = context.partDataStartIndex; i < context.partDataEndIndex; i++) {
                         this.setupUniforms(effect, partIndex, pid._partData, i);
                         engine.draw(true, 0, this.borderIndicesCount);
@@ -386,6 +391,18 @@ var BABYLON;
                 initialContentArea.height = Math.max(0, primSize.height - (rr * 2));
             }
         };
+        Rectangle2D.prototype._getActualSizeFromContentToRef = function (primSize, newPrimSize) {
+            // Fall back to default implementation if there's no round Radius
+            if (this._notRounded) {
+                _super.prototype._getActualSizeFromContentToRef.call(this, primSize, newPrimSize);
+            }
+            else {
+                var rr = Math.round((this.roundRadius - (this.roundRadius / Math.sqrt(2))) * 1.3);
+                newPrimSize.copyFrom(primSize);
+                newPrimSize.width += rr * 2;
+                newPrimSize.height += rr * 2;
+            }
+        };
         Rectangle2D.prototype.createInstanceDataParts = function () {
             var res = new Array();
             if (this.border) {

+ 1 - 0
src/Canvas2d/babylon.smartPropertyPrim.js

@@ -301,6 +301,7 @@ var BABYLON;
                     curprim._setFlags(SmartPropertyPrim.flagBoundingInfoDirty);
                     if (curprim.isSizeAuto) {
                         curprim.onPrimitivePropertyDirty(BABYLON.Prim2DBase.sizeProperty.flagId);
+                        curprim._setFlags(SmartPropertyPrim.flagPositioningDirty);
                     }
                     if (curprim instanceof BABYLON.Group2D) {
                         if (curprim.isRenderableGroup) {

+ 4 - 1
src/Canvas2d/babylon.sprite2d.js

@@ -32,7 +32,8 @@ var BABYLON;
                 this.effectsReady = true;
             }
             // Compute the offset locations of the attributes in the vertex shader that will be mapped to the instance buffer data
-            var engine = instanceInfo.owner.owner.engine;
+            var canvas = instanceInfo.owner.owner;
+            var engine = canvas.engine;
             var effect = context.useInstancing ? this.effectInstanced : this.effect;
             engine.enableEffect(effect);
             effect.setTexture("diffuseSampler", this.texture);
@@ -46,11 +47,13 @@ var BABYLON;
                 if (!this.instancingAttributes) {
                     this.instancingAttributes = this.loadInstancingAttributes(Sprite2D.SPRITE2D_MAINPARTID, effect);
                 }
+                canvas._addDrawCallCount(1, context.renderMode);
                 engine.updateAndBindInstancesBuffer(pid._partBuffer, null, this.instancingAttributes);
                 engine.draw(true, 0, 6, pid._partData.usedElementCount);
                 engine.unbindInstanceAttributes();
             }
             else {
+                canvas._addDrawCallCount(context.partDataEndIndex - context.partDataStartIndex, context.renderMode);
                 for (var i = context.partDataStartIndex; i < context.partDataEndIndex; i++) {
                     this.setupUniforms(effect, 0, pid._partData, i);
                     engine.draw(true, 0, 6);

+ 4 - 1
src/Canvas2d/babylon.text2d.js

@@ -31,7 +31,8 @@ var BABYLON;
                 }
                 this.effectsReady = true;
             }
-            var engine = instanceInfo.owner.owner.engine;
+            var canvas = instanceInfo.owner.owner;
+            var engine = canvas.engine;
             this.fontTexture.update();
             var effect = context.useInstancing ? this.effectInstanced : this.effect;
             engine.enableEffect(effect);
@@ -44,11 +45,13 @@ var BABYLON;
                 if (!this.instancingAttributes) {
                     this.instancingAttributes = this.loadInstancingAttributes(Text2D.TEXT2D_MAINPARTID, effect);
                 }
+                canvas._addDrawCallCount(1, context.renderMode);
                 engine.updateAndBindInstancesBuffer(pid._partBuffer, null, this.instancingAttributes);
                 engine.draw(true, 0, 6, pid._partData.usedElementCount);
                 engine.unbindInstanceAttributes();
             }
             else {
+                canvas._addDrawCallCount(context.partDataEndIndex - context.partDataStartIndex, context.renderMode);
                 for (var i = context.partDataStartIndex; i < context.partDataEndIndex; i++) {
                     this.setupUniforms(effect, 0, pid._partData, i);
                     engine.draw(true, 0, 6);

+ 12 - 2
src/Tools/babylon.tools.js

@@ -985,6 +985,13 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(PerfCounter.prototype, "total", {
+            get: function () {
+                return this._totalAccumulated;
+            },
+            enumerable: true,
+            configurable: true
+        });
         /**
          * Call this method to start monitoring a new frame.
          * This scenario is typically used when you accumulate monitoring time many times for a single frame, you call this method at the start of the frame, then beginMonitoring to start recording and endMonitoring(false) to accumulated the recorded time to the PerfCounter or addCount() to accumulate a monitored count.
@@ -992,6 +999,7 @@ var BABYLON;
         PerfCounter.prototype.fetchNewFrame = function () {
             this._totalValueCount++;
             this._current = 0;
+            this._lastSecValueCount++;
         };
         /**
          * Call this method to monitor a count of something (e.g. mesh drawn in viewport count)
@@ -1027,14 +1035,16 @@ var BABYLON;
         };
         PerfCounter.prototype._fetchResult = function () {
             this._totalAccumulated += this._current;
+            this._lastSecAccumulated += this._current;
             // Min/Max update
             this._min = Math.min(this._min, this._current);
             this._max = Math.max(this._max, this._current);
             this._average = this._totalAccumulated / this._totalValueCount;
             // Reset last sec?
-            if ((this._startMonitoringTime - this._lastSecTime) > 1000) {
+            var now = Tools.Now;
+            if ((now - this._lastSecTime) > 1000) {
                 this._lastSecAverage = this._lastSecAccumulated / this._lastSecValueCount;
-                this._lastSecTime = this._startMonitoringTime;
+                this._lastSecTime = now;
                 this._lastSecAccumulated = 0;
                 this._lastSecValueCount = 0;
             }