<commit_before>﻿module BABYLON {
    @className("Group2D")
    export class Group2D extends Prim2DBase {
        static GROUP2D_PROPCOUNT: number = Prim2DBase.PRIM2DBASE_PROPCOUNT + 5;

        public static sizeProperty: Prim2DPropInfo;
        public static actualSizeProperty: Prim2DPropInfo;

      /**
         * Default behavior, the group will use the caching strategy defined at the Canvas Level
         */
        public static GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY = 0;

        /**
         * When used, this group's content won't be cached, no matter which strategy used.
         * If the group is part of a WorldSpace Canvas, its content will be drawn in the Canvas cache bitmap.
         */
        public static GROUPCACHEBEHAVIOR_DONTCACHEOVERRIDE = 1;

        /**
         * When used, the group's content will be cached in the nearest cached parent group/canvas
         */
        public static GROUPCACHEBEHAVIOR_CACHEINPARENTGROUP = 2;

        constructor() {
            super();
            this._primDirtyList = new Array<Prim2DBase>();
            this._childrenRenderableGroups = new Array<Group2D>();
            this.groupRenderInfo = new StringDictionary<GroupInstanceInfo>();
        }

        static CreateGroup2D(parent: Prim2DBase, id: string, position: Vector2, size?: Size, cacheBehabior: number = Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY): Group2D {
            Prim2DBase.CheckParent(parent);
            var g = new Group2D();
            g.setupGroup2D(parent.owner, parent, id, position, size, cacheBehabior);

            return g;
        }

        applyCachedTexture(vertexData: VertexData, material: StandardMaterial) {
            this._bindCacheTarget();

            var uv = vertexData.uvs;
            let nodeuv = this._cacheNode.UVs;
            for (let i = 0; i < 4; i++) {
                uv[i * 2 + 0] = nodeuv[i].x;
                uv[i * 2 + 1] = nodeuv[i].y;
            }

            material.diffuseTexture = this._cacheTexture;
            material.emissiveColor = new Color3(1, 1, 1);
            this._cacheTexture.hasAlpha = true;
            this._unbindCacheTarget();
        }

        public dispose(): boolean {
            if (!super.dispose()) {
                return false;
            }

            if (this._cacheRenderSprite) {
                this._cacheRenderSprite.dispose();
                this._cacheRenderSprite = null;
            }

            if (this._cacheTexture && this._cacheNode) {
                this._cacheTexture.freeRect(this._cacheNode);
                this._cacheTexture = null;
                this._cacheNode = null;
            }

            if(this._primDirtyList) {
                this._primDirtyList.splice(0);
                this._primDirtyList = null;
            }

            if (this.groupRenderInfo) {
                this.groupRenderInfo.forEach((k, v) => {
                    v.dispose();
                });
                this.groupRenderInfo = null;
            }

            return true;
        }

        /**
         * Create an instance of the Group Primitive.
         * A group act as a container for many sub primitives, if features:
         * - Maintain a size, not setting one will determine it based on its content.
         * - Play an essential role in the rendering pipeline. A group and its content can be cached into a bitmap to enhance rendering performance (at the cost of memory storage in GPU)
         * @param owner
         * @param id
         * @param position
         * @param size
         * @param dontcache
         */
        protected setupGroup2D(owner: Canvas2D,
            parent: Prim2DBase,
            id: string,
            position: Vector2,
            size?: Size,
            cacheBehavior: number = Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY) {
            this._cacheBehavior = cacheBehavior;
            this.setupPrim2DBase(owner, parent, id, position);
            this.size = size;
            this._viewportPosition = Vector2.Zero();
        }

        public get isRenderableGroup(): boolean {
            return this._isRenderableGroup;
        }

        public get isCachedGroup(): boolean {
            return this._isCachedGroup;
        }

        @instanceLevelProperty(Prim2DBase.PRIM2DBASE_PROPCOUNT + 1, pi => Group2D.sizeProperty = pi, false, true)
        public get size(): Size {
            return this._size;
        }

        public set size(val: Size) {
            this._size = val;
        }

        public get viewportSize(): ISize {
            return this._viewportSize;
        }

        @instanceLevelProperty(Prim2DBase.PRIM2DBASE_PROPCOUNT + 2, pi => Group2D.actualSizeProperty = pi)
        public get actualSize(): Size {
            // Return the size if set by the user
            if (this._size) {
                return this._size;
            }

            // Otherwise the size is computed based on the boundingInfo
            let m = this.boundingInfo.max();
            return new Size(m.x, m.y);
        }

        public get cacheBehavior(): number {
            return this._cacheBehavior;
        }

        public _addPrimToDirtyList(prim: Prim2DBase) {
            this._primDirtyList.push(prim);
        }

        protected updateLevelBoundingInfo() {
            let size: Size;

            // If the size is set by the user, the boundingInfo is computed from this value
            if (this.size) {
                size = this.size;
            }
            // Otherwise the group's level bouding info is "collapsed"
            else {
                size = new Size(0, 0);
            }

            BoundingInfo2D.CreateFromSizeToRef(size, this._levelBoundingInfo);
        }

        // Method called only on renderable groups to prepare the rendering
        protected _prepareGroupRender(context: Render2DContext) {
            let sortedDirtyList: Prim2DBase[] = null;

            // Update the Global Transformation and visibility status of the changed primitives
            if ((this._primDirtyList.length > 0) || context.forceRefreshPrimitive) {
                sortedDirtyList = this._primDirtyList.sort((a, b) => a.hierarchyDepth - b.hierarchyDepth);
                this.updateGlobalTransVisOf(sortedDirtyList, true);
            }

            // Setup the size of the rendering viewport
            // In non cache mode, we're rendering directly to the rendering canvas, in this case we have to detect if the canvas size changed since the previous iteration, if it's the case all primitives must be preprared again because their transformation must be recompute
            if (!this._isCachedGroup) {
                // Compute the WebGL viewport's location/size
                let t = this._globalTransform.getTranslation();
                let s = this.actualSize.clone();
                let rs = this.owner._renderingSize;
                s.height = Math.min(s.height, rs.height - t.y);
                s.width = Math.min(s.width, rs.width - t.x);
                let x = t.x;
                let y = (rs.height - s.height) - t.y;

                // The viewport where we're rendering must be the size of the canvas if this one fit in the rendering screen or clipped to the screen dimensions if needed
                this._viewportPosition.x = x;
                this._viewportPosition.y = y;
                let vw = s.width;
                let vh = s.height;

                if (!this._viewportSize) {
                    this._viewportSize = new Size(vw, vh);
                } else {
                    if (this._viewportSize.width !== vw || this._viewportSize.height !== vh) {
                        context.forceRefreshPrimitive = true;
                    }
                    this._viewportSize.width = vw;
                    this._viewportSize.height = vh;
                }
            } else {
                this._viewportSize = this.actualSize;
            }

            if ((this._primDirtyList.length > 0) || context.forceRefreshPrimitive) {
                // If the group is cached, set the dirty flag to true because of the incoming changes
                this._cacheGroupDirty = this._isCachedGroup;

                // If it's a force refresh, prepare all the children
                if (context.forceRefreshPrimitive) {
                    for (let p of this._children) {
                        p._prepareRender(context);
                    }
                } else {
                    // Each primitive that changed at least once was added into the primDirtyList, we have to sort this level using
                    //  the hierarchyDepth in order to prepare primitives from top to bottom
                    if (!sortedDirtyList) {
                        sortedDirtyList = this._primDirtyList.sort((a, b) => a.hierarchyDepth - b.hierarchyDepth);
                    }

                    sortedDirtyList.forEach(p => {

                        // We need to check if prepare is needed because even if the primitive is in the dirtyList, its parent primitive may also have been modified, then prepared, then recurse on its children primitives (this one for instance) if the changes where impacting them.
                        // For instance: a Rect's position change, the position of its children primitives will also change so a prepare will be call on them. If a child was in the dirtyList we will avoid a second prepare by making this check.
                        if (!p.isDisposed && p.needPrepare()) {
                            p._prepareRender(context);
                        }
                    });

                    // Everything is updated, clear the dirty list
                    this._primDirtyList.splice(0);
                }
            }

            // A renderable group has a list of direct children that are also renderable groups, we recurse on them to also prepare them
            this._childrenRenderableGroups.forEach(g => {
                g._prepareGroupRender(context);
            });
        }

        protected _groupRender(context: Render2DContext) {
            let engine = this.owner.engine;
            let failedCount = 0;

            // First recurse to children render group to render them (in their cache or on screen)
            for (let childGroup of this._childrenRenderableGroups) {
                childGroup._groupRender(context);
            }

            // 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) {
                if (this.isCachedGroup) {
                    this._bindCacheTarget();
                } else {
                    var curVP = engine.setDirectViewport(this._viewportPosition.x, this._viewportPosition.y, this._viewportSize.width, this._viewportSize.height);
                }

                // For each different model of primitive to render
                let totalRenderCount = 0;
                this.groupRenderInfo.forEach((k, v) => {

                    // This part will pack the dynfloatarray and update the instanced array WebGLBufffer
                    // Skip it if instanced arrays are not supported
                    if (this.owner.supportInstancedArray) {
                        for (let i = 0; i < v._instancesPartsData.length; i++) {
                            // If the instances of the model was changed, pack the data
                            let array = v._instancesPartsData[i];
                            let instanceData = array.pack();
                            totalRenderCount += array.usedElementCount;

                            // Compute the size the instance buffer should have
                            let neededSize = array.usedElementCount * array.stride * 4;

                            // Check if we have to (re)create the instancesBuffer because there's none or the size is too small
                            if (!v._instancesPartsBuffer[i] || (v._instancesPartsBufferSize[i] <= neededSize)) {
                                if (v._instancesPartsBuffer[i]) {
                                    engine.deleteInstancesBuffer(v._instancesPartsBuffer[i]);
                                }
                                v._instancesPartsBuffer[i] = engine.createInstancesBuffer(neededSize);
                                v._instancesPartsBufferSize[i] = neededSize;
                                v._dirtyInstancesData = true;

                                // Update the WebGL buffer to match the new content of the instances data
                                engine._gl.bufferSubData(engine._gl.ARRAY_BUFFER, 0, instanceData);
                            } else if (v._dirtyInstancesData) {
                                // Update the WebGL buffer to match the new content of the instances data
                                engine._gl.bindBuffer(engine._gl.ARRAY_BUFFER, v._instancesPartsBuffer[i]);
                                engine._gl.bufferSubData(engine._gl.ARRAY_BUFFER, 0, instanceData);

                                v._dirtyInstancesData = false;
                            }
                        }
                    }

                    // Submit render only if we have something to render (everything may be hiden and the floatarray empty)
                    if (totalRenderCount > 0) {
                        // render all the instances of this model, if the render method returns true then our instances are no longer dirty
                        let renderFailed = !v._modelCache.render(v, context);

                        // Update dirty flag/related
                        v._dirtyInstancesData = renderFailed;
                        failedCount += renderFailed ? 1 : 0;
                    }
                });

                // The group's content is no longer dirty
                this._cacheGroupDirty = failedCount !== 0;

                if (this.isCachedGroup) {
                    this._unbindCacheTarget();
                } else {
                    if (curVP) {
                        engine.setViewport(curVP);
                    }
                }
            }<commit_msg>Bug fix on non instanced pipeline<commit_after>﻿module BABYLON {
    @className("Group2D")
    export class Group2D extends Prim2DBase {
        static GROUP2D_PROPCOUNT: number = Prim2DBase.PRIM2DBASE_PROPCOUNT + 5;

        public static sizeProperty: Prim2DPropInfo;
        public static actualSizeProperty: Prim2DPropInfo;

      /**
         * Default behavior, the group will use the caching strategy defined at the Canvas Level
         */
        public static GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY = 0;

        /**
         * When used, this group's content won't be cached, no matter which strategy used.
         * If the group is part of a WorldSpace Canvas, its content will be drawn in the Canvas cache bitmap.
         */
        public static GROUPCACHEBEHAVIOR_DONTCACHEOVERRIDE = 1;

        /**
         * When used, the group's content will be cached in the nearest cached parent group/canvas
         */
        public static GROUPCACHEBEHAVIOR_CACHEINPARENTGROUP = 2;

        constructor() {
            super();
            this._primDirtyList = new Array<Prim2DBase>();
            this._childrenRenderableGroups = new Array<Group2D>();
            this.groupRenderInfo = new StringDictionary<GroupInstanceInfo>();
        }

        static CreateGroup2D(parent: Prim2DBase, id: string, position: Vector2, size?: Size, cacheBehabior: number = Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY): Group2D {
            Prim2DBase.CheckParent(parent);
            var g = new Group2D();
            g.setupGroup2D(parent.owner, parent, id, position, size, cacheBehabior);

            return g;
        }

        applyCachedTexture(vertexData: VertexData, material: StandardMaterial) {
            this._bindCacheTarget();

            var uv = vertexData.uvs;
            let nodeuv = this._cacheNode.UVs;
            for (let i = 0; i < 4; i++) {
                uv[i * 2 + 0] = nodeuv[i].x;
                uv[i * 2 + 1] = nodeuv[i].y;
            }

            material.diffuseTexture = this._cacheTexture;
            material.emissiveColor = new Color3(1, 1, 1);
            this._cacheTexture.hasAlpha = true;
            this._unbindCacheTarget();
        }

        public dispose(): boolean {
            if (!super.dispose()) {
                return false;
            }

            if (this._cacheRenderSprite) {
                this._cacheRenderSprite.dispose();
                this._cacheRenderSprite = null;
            }

            if (this._cacheTexture && this._cacheNode) {
                this._cacheTexture.freeRect(this._cacheNode);
                this._cacheTexture = null;
                this._cacheNode = null;
            }

            if(this._primDirtyList) {
                this._primDirtyList.splice(0);
                this._primDirtyList = null;
            }

            if (this.groupRenderInfo) {
                this.groupRenderInfo.forEach((k, v) => {
                    v.dispose();
                });
                this.groupRenderInfo = null;
            }

            return true;
        }

        /**
         * Create an instance of the Group Primitive.
         * A group act as a container for many sub primitives, if features:
         * - Maintain a size, not setting one will determine it based on its content.
         * - Play an essential role in the rendering pipeline. A group and its content can be cached into a bitmap to enhance rendering performance (at the cost of memory storage in GPU)
         * @param owner
         * @param id
         * @param position
         * @param size
         * @param dontcache
         */
        protected setupGroup2D(owner: Canvas2D,
            parent: Prim2DBase,
            id: string,
            position: Vector2,
            size?: Size,
            cacheBehavior: number = Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY) {
            this._cacheBehavior = cacheBehavior;
            this.setupPrim2DBase(owner, parent, id, position);
            this.size = size;
            this._viewportPosition = Vector2.Zero();
        }

        public get isRenderableGroup(): boolean {
            return this._isRenderableGroup;
        }

        public get isCachedGroup(): boolean {
            return this._isCachedGroup;
        }

        @instanceLevelProperty(Prim2DBase.PRIM2DBASE_PROPCOUNT + 1, pi => Group2D.sizeProperty = pi, false, true)
        public get size(): Size {
            return this._size;
        }

        public set size(val: Size) {
            this._size = val;
        }

        public get viewportSize(): ISize {
            return this._viewportSize;
        }

        @instanceLevelProperty(Prim2DBase.PRIM2DBASE_PROPCOUNT + 2, pi => Group2D.actualSizeProperty = pi)
        public get actualSize(): Size {
            // Return the size if set by the user
            if (this._size) {
                return this._size;
            }

            // Otherwise the size is computed based on the boundingInfo
            let m = this.boundingInfo.max();
            return new Size(m.x, m.y);
        }

        public get cacheBehavior(): number {
            return this._cacheBehavior;
        }

        public _addPrimToDirtyList(prim: Prim2DBase) {
            this._primDirtyList.push(prim);
        }

        protected updateLevelBoundingInfo() {
            let size: Size;

            // If the size is set by the user, the boundingInfo is computed from this value
            if (this.size) {
                size = this.size;
            }
            // Otherwise the group's level bouding info is "collapsed"
            else {
                size = new Size(0, 0);
            }

            BoundingInfo2D.CreateFromSizeToRef(size, this._levelBoundingInfo);
        }

        // Method called only on renderable groups to prepare the rendering
        protected _prepareGroupRender(context: Render2DContext) {
            let sortedDirtyList: Prim2DBase[] = null;

            // Update the Global Transformation and visibility status of the changed primitives
            if ((this._primDirtyList.length > 0) || context.forceRefreshPrimitive) {
                sortedDirtyList = this._primDirtyList.sort((a, b) => a.hierarchyDepth - b.hierarchyDepth);
                this.updateGlobalTransVisOf(sortedDirtyList, true);
            }

            // Setup the size of the rendering viewport
            // In non cache mode, we're rendering directly to the rendering canvas, in this case we have to detect if the canvas size changed since the previous iteration, if it's the case all primitives must be preprared again because their transformation must be recompute
            if (!this._isCachedGroup) {
                // Compute the WebGL viewport's location/size
                let t = this._globalTransform.getTranslation();
                let s = this.actualSize.clone();
                let rs = this.owner._renderingSize;
                s.height = Math.min(s.height, rs.height - t.y);
                s.width = Math.min(s.width, rs.width - t.x);
                let x = t.x;
                let y = (rs.height - s.height) - t.y;

                // The viewport where we're rendering must be the size of the canvas if this one fit in the rendering screen or clipped to the screen dimensions if needed
                this._viewportPosition.x = x;
                this._viewportPosition.y = y;
                let vw = s.width;
                let vh = s.height;

                if (!this._viewportSize) {
                    this._viewportSize = new Size(vw, vh);
                } else {
                    if (this._viewportSize.width !== vw || this._viewportSize.height !== vh) {
                        context.forceRefreshPrimitive = true;
                    }
                    this._viewportSize.width = vw;
                    this._viewportSize.height = vh;
                }
            } else {
                this._viewportSize = this.actualSize;
            }

            if ((this._primDirtyList.length > 0) || context.forceRefreshPrimitive) {
                // If the group is cached, set the dirty flag to true because of the incoming changes
                this._cacheGroupDirty = this._isCachedGroup;

                // If it's a force refresh, prepare all the children
                if (context.forceRefreshPrimitive) {
                    for (let p of this._children) {
                        p._prepareRender(context);
                    }
                } else {
                    // Each primitive that changed at least once was added into the primDirtyList, we have to sort this level using
                    //  the hierarchyDepth in order to prepare primitives from top to bottom
                    if (!sortedDirtyList) {
                        sortedDirtyList = this._primDirtyList.sort((a, b) => a.hierarchyDepth - b.hierarchyDepth);
                    }

                    sortedDirtyList.forEach(p => {

                        // We need to check if prepare is needed because even if the primitive is in the dirtyList, its parent primitive may also have been modified, then prepared, then recurse on its children primitives (this one for instance) if the changes where impacting them.
                        // For instance: a Rect's position change, the position of its children primitives will also change so a prepare will be call on them. If a child was in the dirtyList we will avoid a second prepare by making this check.
                        if (!p.isDisposed && p.needPrepare()) {
                            p._prepareRender(context);
                        }
                    });

                    // Everything is updated, clear the dirty list
                    this._primDirtyList.splice(0);
                }
            }

            // A renderable group has a list of direct children that are also renderable groups, we recurse on them to also prepare them
            this._childrenRenderableGroups.forEach(g => {
                g._prepareGroupRender(context);
            });
        }

        protected _groupRender(context: Render2DContext) {
            let engine = this.owner.engine;
            let failedCount = 0;

            // First recurse to children render group to render them (in their cache or on screen)
            for (let childGroup of this._childrenRenderableGroups) {
                childGroup._groupRender(context);
            }

            // 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) {
                if (this.isCachedGroup) {
                    this._bindCacheTarget();
                } else {
                    var curVP = engine.setDirectViewport(this._viewportPosition.x, this._viewportPosition.y, this._viewportSize.width, this._viewportSize.height);
                }

                // For each different model of primitive to render
                let totalRenderCount = 0;
                this.groupRenderInfo.forEach((k, v) => {

                    // This part will pack the dynfloatarray and update the instanced array WebGLBufffer
                    // Skip it if instanced arrays are not supported
                    if (this.owner.supportInstancedArray) {
                        for (let i = 0; i < v._instancesPartsData.length; i++) {
                            // If the instances of the model was changed, pack the data
                            let array = v._instancesPartsData[i];
                            let instanceData = array.pack();
                            totalRenderCount += array.usedElementCount;

                            // Compute the size the instance buffer should have
                            let neededSize = array.usedElementCount * array.stride * 4;

                            // Check if we have to (re)create the instancesBuffer because there's none or the size is too small
                            if (!v._instancesPartsBuffer[i] || (v._instancesPartsBufferSize[i] <= neededSize)) {
                                if (v._instancesPartsBuffer[i]) {
                                    engine.deleteInstancesBuffer(v._instancesPartsBuffer[i]);
                                }
                                v._instancesPartsBuffer[i] = engine.createInstancesBuffer(neededSize);
                                v._instancesPartsBufferSize[i] = neededSize;
                                v._dirtyInstancesData = true;

                                // Update the WebGL buffer to match the new content of the instances data
                                engine._gl.bufferSubData(engine._gl.ARRAY_BUFFER, 0, instanceData);
                            } else if (v._dirtyInstancesData) {
                                // Update the WebGL buffer to match the new content of the instances data
                                engine._gl.bindBuffer(engine._gl.ARRAY_BUFFER, v._instancesPartsBuffer[i]);
                                engine._gl.bufferSubData(engine._gl.ARRAY_BUFFER, 0, instanceData);

                                v._dirtyInstancesData = false;
                            }
                        }
                    }

                    // Submit render only if we have something to render (everything may be hiden and the floatarray empty)
                    if (!this.owner.supportInstancedArray || totalRenderCount > 0) {
                        // render all the instances of this model, if the render method returns true then our instances are no longer dirty
                        let renderFailed = !v._modelCache.render(v, context);

                        // Update dirty flag/related
                        v._dirtyInstancesData = renderFailed;
                        failedCount += renderFailed ? 1 : 0;
                    }
                });

                // The group's content is no longer dirty
                this._cacheGroupDirty = failedCount !== 0;

                if (this.isCachedGroup) {
                    this._unbindCacheTarget();
                } else {
                    if (curVP) {
                        engine.setViewport(curVP);
                    }
                }
            }
{'fraction_non_alphanumeric': 0.07407796574550804, 'fraction_numerical': 0.004763405835172148, 'mean_word_length': 1.52226148409894, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 31, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2207615', 'n_tokens_mistral': 7235, 'n_tokens_neox': 6798, 'n_words': 2152}