Kaynağa Gözat

Canvas2D: bug fixing time

 - renderingPhase now render only once, useful optim...
 - Canvas2D.minPartCountToUseInstancedArray setting added to change the minimal part count to create an Instanced Array
 - Different Caching Strategies now are more reliable, work with designSize
 - RectPackingMap bug fixed in case of alloc in a fragmented state
nockawa 8 yıl önce
ebeveyn
işleme
cae114ac15

+ 12 - 6
canvas2D/src/Engine/babylon.canvas2d.ts

@@ -63,7 +63,6 @@
          */
         public static RENDEROBSERVABLE_POST = 2;
 
-
         private static _INSTANCES : Array<Canvas2D> = [];
 
         constructor(scene: Scene, settings?: {
@@ -196,7 +195,7 @@
                         throw Error("You have to specify a valid camera and renderingGroup");
                     }
                     this._renderingGroupObserver = this._scene.onRenderingGroupObservable.add((e, s) => {
-                        if (this._scene.activeCamera === settings.renderingPhase.camera) {
+                        if ((this._scene.activeCamera === settings.renderingPhase.camera) && (e.renderStage===RenderingGroupInfo.STAGE_POSTTRANSPARENT)) {
                             this._engine.clear(null, false, true, true);
                             this._render();
                         }
@@ -1118,6 +1117,11 @@
             return canvas;
         }
 
+        /**
+         * Instanced Array will be create if there's at least this number of parts/prim that can fit into it
+         */
+        public minPartCountToUseInstancedArray = 5;
+
         private checkBackgroundAvailability() {
             if (this._cachingStrategy === Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS) {
                 throw Error("Can't use Canvas Background with the caching strategy TOPLEVELGROUPS");
@@ -1469,8 +1473,6 @@
             }
         }
 
-        private static _unS = new Vector2(1, 1);
-
         /**
          * Internal method that allocate a cache for the given group.
          * Caching is made using a collection of MapTexture where many groups have their bitmap cache stored inside.
@@ -1494,6 +1496,7 @@
             // Determine size
             let size = group.actualSize;
             size = new Size(Math.ceil(size.width * scale.x), Math.ceil(size.height * scale.y));
+            let originalSize = size.clone();
             if (minSize) {
                 size.width = Math.max(minSize.width, size.width);
                 size.height = Math.max(minSize.height, size.height);
@@ -1542,15 +1545,18 @@
 
                 let sprite: Sprite2D;
                 if (this._cachingStrategy === Canvas2D.CACHESTRATEGY_CANVAS) {
+                    if (this._cachedCanvasGroup) {
+                        this._cachedCanvasGroup.dispose();
+                    }
                     this._cachedCanvasGroup = Group2D._createCachedCanvasGroup(this);
-                    sprite = new Sprite2D(map, { parent: this._cachedCanvasGroup, id: "__cachedCanvasSprite__", spriteSize: node.contentSize, spriteLocation: node.pos });
+                    sprite = new Sprite2D(map, { parent: this._cachedCanvasGroup, id: "__cachedCanvasSprite__", spriteSize: originalSize, spriteLocation: node.pos });
                     sprite.zOrder = 1;
                     sprite.origin = Vector2.Zero();
                 }
 
                 // Create a Sprite that will be used to render this cache, the "__cachedSpriteOfGroup__" starting id is a hack to bypass exception throwing in case of the Canvas doesn't normally allows direct primitives
                 else {
-                    sprite = new Sprite2D(map, { parent: parent, id: `__cachedSpriteOfGroup__${group.id}`, x: group.actualPosition.x, y: group.actualPosition.y, spriteSize: node.contentSize, spriteLocation: node.pos, dontInheritParentScale: true });
+                    sprite = new Sprite2D(map, { parent: parent, id: `__cachedSpriteOfGroup__${group.id}`, x: group.actualPosition.x * scale.x, y: group.actualPosition.y * scale.y, spriteSize: originalSize, spriteLocation: node.pos, dontInheritParentScale: true });
                     sprite.origin = group.origin.clone();
                     sprite.addExternalData("__cachedGroup__", group);
                     sprite.pointerEventObservable.add((e, s) => {

+ 2 - 2
canvas2D/src/Engine/babylon.canvas2dLayoutEngine.ts

@@ -62,12 +62,12 @@
         private _doUpdate(prim: Prim2DBase) {
             // Canvas ?
             if (prim instanceof Canvas2D) {
-                prim.layoutArea = prim.actualSize;
+                prim.layoutArea = prim.actualSize.multiplyByFloats(prim.scaleX, prim.scaleY);
             }
 
             // Direct child of Canvas ?
             else if (prim.parent instanceof Canvas2D) {
-                prim.layoutArea = prim.owner.actualSize;
+                prim.layoutArea = prim.owner.actualSize.multiplyByFloats(prim.owner.scaleX, prim.owner.scaleY);
             }
 
             // Indirect child of Canvas

+ 65 - 27
canvas2D/src/Engine/babylon.group2d.ts

@@ -655,9 +655,9 @@
                 let engine = this.owner.engine;
                 let count = ts.endDataIndex - ts.startDataIndex;
 
-                // Use Instanced Array if it's supported and if there's at least 5 prims to draw.
-                // We don't want to create an Instanced Buffer for less that 5 prims
-                if (useInstanced && count >= 5) {
+                // Use Instanced Array if it's supported and if there's at least minPartCountToUseInstancedArray prims to draw.
+                // We don't want to create an Instanced Buffer for less that minPartCountToUseInstancedArray prims
+                if (useInstanced && count >= this.owner.minPartCountToUseInstancedArray) {
 
                     if (!ts.partBuffers) {
                         let buffers = new Array<WebGLBuffer>();
@@ -829,8 +829,13 @@
                 scale = this.actualScale;
             }
 
-            Group2D._s.width  = Math.ceil(this.actualSize.width * scale.x * rs);
-            Group2D._s.height = Math.ceil(this.actualSize.height * scale.y * rs);
+            if (isCanvas && this.owner.cachingStrategy===Canvas2D.CACHESTRATEGY_CANVAS) {
+                Group2D._s.width = this.owner.engine.getRenderWidth();
+                Group2D._s.height = this.owner.engine.getRenderHeight();
+            } else {
+                Group2D._s.width = Math.ceil(this.actualSize.width * scale.x * rs);
+                Group2D._s.height = Math.ceil(this.actualSize.height * scale.y * rs);
+            }
 
             let sizeChanged = !Group2D._s.equals(rd._cacheSize);
 
@@ -855,6 +860,9 @@
                 var res = this.owner._allocateGroupCache(this, this.parent && this.parent.renderGroup, curWidth ? new Size(curWidth, curHeight) : null, rd._useMipMap, rd._anisotropicLevel);
                 rd._cacheNode = res.node;
                 rd._cacheTexture = res.texture;
+                if (rd._cacheRenderSprite) {
+                    rd._cacheRenderSprite.dispose();
+                }
                 rd._cacheRenderSprite = res.sprite;
                 sizeChanged = true;
             }
@@ -879,6 +887,16 @@
             }
         }
 
+        protected _spreadActualScaleDirty() {
+            if (this._renderableData && this._renderableData._cacheRenderSprite) {
+                this.handleGroupChanged(Prim2DBase.actualScaleProperty);
+            }
+
+            super._spreadActualScaleDirty();
+        }
+
+        protected static _unS = new Vector2(1, 1);
+
         protected handleGroupChanged(prop: Prim2DPropInfo) {
             // This method is only for cachedGroup
             let rd = this._renderableData;
@@ -893,20 +911,36 @@
 
             // For now we only support these property changes
             // TODO: add more! :)
-            if (prop.id === Prim2DBase.actualPositionProperty.id) {
-                cachedSprite.actualPosition = this.actualPosition.clone();
-                if (cachedSprite.position != null) {
-                    cachedSprite.position = cachedSprite.actualPosition.clone();
-                }
-            } else if (prop.id === Prim2DBase.rotationProperty.id) {
-                cachedSprite.rotation = this.rotation;
-            } else if (prop.id === Prim2DBase.scaleProperty.id) {
-                cachedSprite.scale = this.scale;
-            } else if (prop.id === Prim2DBase.originProperty.id) {
-                cachedSprite.origin = this.origin.clone();
-            } else if (prop.id === Group2D.actualSizeProperty.id) {
-                cachedSprite.size = this.actualSize.clone();
-                //console.log(`[${this._globalTransformProcessStep}] Sync Sprite ${this.id}, width: ${this.actualSize.width}, height: ${this.actualSize.height}`);
+            switch (prop.id) {
+                case Prim2DBase.actualScaleProperty.id:
+                case Prim2DBase.actualPositionProperty.id:
+                    let noResizeScale = rd._noResizeOnScale;
+                    let isCanvas = parent == null;
+                    let scale: Vector2;
+                    if (noResizeScale) {
+                        scale = isCanvas ? Group2D._unS : this.parent.actualScale;
+                    } else {
+                        scale = this.actualScale;
+                    }
+
+                    cachedSprite.actualPosition = this.actualPosition.multiply(scale);
+                    if (cachedSprite.position != null) {
+                        cachedSprite.position = cachedSprite.actualPosition.clone();
+                    }
+                    break;
+
+                case Prim2DBase.rotationProperty.id:
+                    cachedSprite.rotation = this.rotation;
+                    break;
+                case Prim2DBase.scaleProperty.id:
+                    cachedSprite.scale = this.scale;
+                    break;
+                case Prim2DBase.originProperty.id:
+                    cachedSprite.origin = this.origin.clone();
+                    break;
+                case Group2D.actualSizeProperty.id:
+                    cachedSprite.size = this.actualSize.clone();
+                    break;
             }
         }
 
@@ -949,19 +983,23 @@
 
             // All Group cached mode, all groups are renderable/cached, including the Canvas, groups with the behavior DONTCACHE are renderable/not cached, groups with CACHEINPARENT are logical ones
             else if (canvasStrat === Canvas2D.CACHESTRATEGY_ALLGROUPS) {
-                var gcb = this.cacheBehavior & Group2D.GROUPCACHEBEHAVIOR_OPTIONMASK;
-                if ((gcb === Group2D.GROUPCACHEBEHAVIOR_DONTCACHEOVERRIDE) || (gcb === Group2D.GROUPCACHEBEHAVIOR_CACHEINPARENTGROUP)) {
-                    this._isRenderableGroup = gcb === Group2D.GROUPCACHEBEHAVIOR_DONTCACHEOVERRIDE;
+                if (isCanvas) {
+                    this._isRenderableGroup = true;
                     this._isCachedGroup = false;
-                }
+                } else {
+                    var gcb = this.cacheBehavior & Group2D.GROUPCACHEBEHAVIOR_OPTIONMASK;
+                    if ((gcb === Group2D.GROUPCACHEBEHAVIOR_DONTCACHEOVERRIDE) || (gcb === Group2D.GROUPCACHEBEHAVIOR_CACHEINPARENTGROUP)) {
+                        this._isRenderableGroup = gcb === Group2D.GROUPCACHEBEHAVIOR_DONTCACHEOVERRIDE;
+                        this._isCachedGroup = false;
+                    }
 
-                if (gcb === Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY) {
-                    this._isRenderableGroup = true;
-                    this._isCachedGroup = true;
+                    if (gcb === Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY) {
+                        this._isRenderableGroup = true;
+                        this._isCachedGroup = true;
+                    }
                 }
             }
 
-
             if (this._isRenderableGroup) {
                 // Yes, we do need that check, trust me, unfortunately we can call _detectGroupStates many time on the same object...
                 if (!this._renderableData) {

+ 8 - 2
canvas2D/src/Engine/babylon.prim2dBase.ts

@@ -1353,7 +1353,7 @@
      * Base class for a Primitive of the Canvas2D feature
      */
     export class Prim2DBase extends SmartPropertyPrim {
-        static PRIM2DBASE_PROPCOUNT: number = 24;
+        static PRIM2DBASE_PROPCOUNT: number = 25;
 
         public  static _bigInt = Math.pow(2, 30);
 
@@ -1783,6 +1783,11 @@
          */
         public static scaleYProperty: Prim2DPropInfo;
 
+        /**
+         * Metadata of the actualScale property
+         */
+        public static actualScaleProperty: Prim2DPropInfo;
+
         @instanceLevelProperty(1, pi => Prim2DBase.actualPositionProperty = pi, false, false, true)
         /**
          * Return the position where the primitive is rendered in the Canvas, this position may be different than the one returned by the position property due to layout/alignment/margin/padding computing.
@@ -2320,7 +2325,7 @@
             return this._scale.y;
         }
 
-        private _spreadActualScaleDirty() {
+        protected _spreadActualScaleDirty() {
             for (let child of this._children) {
                 child._setFlags(SmartPropertyPrim.flagActualScaleDirty);
                 child._spreadActualScaleDirty();
@@ -2330,6 +2335,7 @@
         /**
          * Returns the actual scale of this Primitive, the value is computed from the scale property of this primitive, multiplied by the actualScale of its parent one (if any). The Vector2 object returned contains the scale for both X and Y axis
          */
+        @instanceLevelProperty(SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 24, pi => Prim2DBase.actualScaleProperty = pi, false, true)
         public get actualScale(): Vector2 {
             if (this._isFlagSet(SmartPropertyPrim.flagActualScaleDirty)) {
                 let cur = this._isFlagSet(SmartPropertyPrim.flagDontInheritParentScale) ? null : this.parent;

+ 1 - 1
canvas2D/src/Engine/babylon.smartPropertyPrim.ts

@@ -1127,7 +1127,7 @@
             }
 
             // If the property belong to a group, check if it's a cached one, and dirty its render sprite accordingly
-            if (this instanceof Group2D) {
+            if (this instanceof Group2D && (<Group2D><any>this)._renderableData) {
                 (<SmartPropertyPrim>this).handleGroupChanged(propInfo);
             }
 

+ 7 - 11
canvas2D/src/Engine/babylon.sprite2d.ts

@@ -381,9 +381,9 @@
             this.texture = texture;
             this.texture.wrapU = Texture.CLAMP_ADDRESSMODE;
             this.texture.wrapV = Texture.CLAMP_ADDRESSMODE;
-            this.size = settings.spriteSize;
-            this.spriteLocation = settings.spriteLocation || new Vector2(0, 0);
-            this.spriteScaleFactor = settings.spriteScaleFactor || new Vector2(1, 1);
+            this.size = (settings.spriteSize!=null) ? settings.spriteSize.clone() : null;
+            this.spriteLocation = (settings.spriteLocation!=null) ? settings.spriteLocation.clone() : new Vector2(0, 0);
+            this.spriteScaleFactor = (settings.spriteScaleFactor!=null) ? settings.spriteScaleFactor : new Vector2(1, 1);
             this.spriteFrame = 0;
             this.invertY = (settings.invertY == null) ? false : settings.invertY;
             this.alignToPixel = (settings.alignToPixel == null) ? true : settings.alignToPixel;
@@ -391,11 +391,13 @@
 
             if (settings.spriteSize == null || !texture.isReady()) {
                 if (texture.isReady()) {
-                    this.size = <Size>texture.getBaseSize();
+                    let s = texture.getBaseSize();
+                    this.size = new Size(s.width, s.height);
                 } else {
                     texture.onLoadObservable.add(() => {
                         if (settings.spriteSize == null) {
-                            this.size = <Size>texture.getBaseSize();
+                            let s = texture.getBaseSize();
+                            this.size = new Size(s.width, s.height);
                         }
                         this._positioningDirty();
                         this._instanceDirtyFlags |= Prim2DBase.originProperty.flagId | Sprite2D.textureProperty.flagId;  // To make sure the sprite is issued again for render
@@ -404,12 +406,6 @@
             }
         }
 
-        static _createCachedCanvasSprite(owner: Canvas2D, texture: MapTexture, size: Size, pos: Vector2): Sprite2D {
-
-            let sprite = new Sprite2D(texture, { parent: owner, id: "__cachedCanvasSprite__", position: Vector2.Zero(), origin: Vector2.Zero(), spriteSize: size, spriteLocation: pos, alignToPixel: true });
-            return sprite;
-        }
-
         protected createModelRenderCache(modelKey: string): ModelRenderCache {
             let renderCache = new Sprite2DRenderCache(this.owner.engine, modelKey);
             return renderCache;

+ 17 - 6
src/Tools/babylon.rectPackingMap.ts

@@ -6,10 +6,15 @@
   */
     export class PackedRect {
         constructor(root: PackedRect, parent: PackedRect, pos: Vector2, size: Size) {
-            this._pos = pos;
-            this._size = size;
-            this._root = root;
-            this._parent = parent;
+            this._pos         = pos;
+            this._size        = size;
+            this._root        = root;
+            this._parent      = parent;
+            this._contentSize = null;
+            this._bottomNode  = null;
+            this._leftNode    = null;
+            this._initialSize = null;
+            this._rightNode   = null;
         }
 
         /**
@@ -104,8 +109,13 @@
             }
 
             // The node is free, but was previously allocated (_initialSize is set), rely on initialSize to make the test as it's the space we have
-            else if (this._initialSize && (size.width <= this._initialSize.width) && (size.height <= this._initialSize.height)) {
-                resNode = this;
+            else if (this._initialSize) {
+                if ((size.width <= this._initialSize.width) && (size.height <= this._initialSize.height))
+                {
+                    resNode = this;
+                } else {
+                    return null;
+                }
             }
 
             // The node is free and empty, rely on its size for the test
@@ -118,6 +128,7 @@
         private splitNode(contentSize: Size): PackedRect {
             // If there's no contentSize but an initialSize it means this node were previously allocated, but freed, we need to create a _leftNode as subNode and use to allocate the space we need (and this node will have a right/bottom subNode for the space left as this._initialSize may be greater than contentSize)
             if (!this._contentSize && this._initialSize) {
+                this._contentSize = contentSize.clone();
                 this._leftNode = new PackedRect(this._root, this, new Vector2(this._pos.x, this._pos.y), new Size(this._initialSize.width, this._initialSize.height));
                 return this._leftNode.splitNode(contentSize);
             } else {