This is an automated email from the ASF dual-hosted git repository. harbs pushed a commit to branch feature/layout-optimization in repository https://gitbox.apache.org/repos/asf/royale-asjs.git
commit 6461d5b1b40570c18277899c159ee2101f7d9f8f Author: Harbs <[email protected]> AuthorDate: Sat Mar 24 23:52:15 2018 +0300 Major performance increase I’m seeing about a 5 *times* performance improvement with this (~150 ms layout now takes ~30 ms) --- .../royale/org/apache/royale/core/LayoutBase.as | 51 +++++++------ .../royale/org/apache/royale/core/LayoutManager.as | 84 ++++++++++++++++++++++ .../main/royale/org/apache/royale/core/UIBase.as | 35 +++++++++ .../royale/org/apache/royale/core/UIButtonBase.as | 31 ++++++++ .../projects/Core/src/main/royale/CoreClasses.as | 1 + .../royale/org/apache/royale/core/ILayoutChild.as | 24 +++++++ .../org/apache/royale/utils/measureComponent.as | 27 +++++++ 7 files changed, 232 insertions(+), 21 deletions(-) diff --git a/frameworks/projects/Basic/src/main/royale/org/apache/royale/core/LayoutBase.as b/frameworks/projects/Basic/src/main/royale/org/apache/royale/core/LayoutBase.as index 97ac19a..7a4f95f 100644 --- a/frameworks/projects/Basic/src/main/royale/org/apache/royale/core/LayoutBase.as +++ b/frameworks/projects/Basic/src/main/royale/org/apache/royale/core/LayoutBase.as @@ -30,10 +30,12 @@ package org.apache.royale.core import org.apache.royale.core.IUIBase; import org.apache.royale.core.layout.EdgeData; import org.apache.royale.core.layout.MarginData; + import org.apache.royale.core.LayoutManager; import org.apache.royale.core.ValuesManager; import org.apache.royale.events.IEventDispatcher; import org.apache.royale.events.Event; import org.apache.royale.utils.CSSUtils; + import org.apache.royale.utils.measureComponent; /** * This class is the base class for most, if not all, layouts. @@ -302,6 +304,8 @@ package org.apache.royale.core * @playerversion Flash 10.2 * @playerversion AIR 2.6 * @productversion Royale 0.8 + * @royaleignorecoercion org.apache.royale.core.ILayoutParent + * @royaleignorecoercion org.apache.royale.core.UIBase */ public function performLayout():void { @@ -309,31 +313,36 @@ package org.apache.royale.core if (isLayoutRunning) return; isLayoutRunning = true; - - var oldWidth:Number = host.width; - var oldHeight:Number = host.height; + LayoutManager.addMeasurement(host); + // measureComponent(host); + // var oldWidth:Number = host.measuredWidth; + // var oldHeight:Number = host.measuredHeight; var viewBead:ILayoutHost = (host as ILayoutParent).getLayoutHost(); viewBead.beforeLayout(); - - if (layout()) { - viewBead.afterLayout(); - } - - isLayoutRunning = false; - - IEventDispatcher(host).dispatchEvent(new Event("layoutComplete")); - - // check sizes to see if layout changed the size or not - // and send an event to re-layout parent of host - if (host.width != oldWidth || - host.height != oldHeight) - { - isLayoutRunning = true; - host.dispatchEvent(new Event("sizeChanged")); - isLayoutRunning = false; - } + LayoutManager.addLayout( + function ():void + { + if (layout()) { + viewBead.afterLayout(); + } + + isLayoutRunning = false; + + IEventDispatcher(host).dispatchEvent(new Event("layoutComplete")); + + // check sizes to see if layout changed the size or not + // and send an event to re-layout parent of host + if (host.width != host.measuredWidth || + host.height != host.measuredHeight) + { + isLayoutRunning = true; + host.dispatchEvent(new Event("sizeChanged")); + isLayoutRunning = false; + } + } + ); } diff --git a/frameworks/projects/Basic/src/main/royale/org/apache/royale/core/LayoutManager.as b/frameworks/projects/Basic/src/main/royale/org/apache/royale/core/LayoutManager.as new file mode 100644 index 0000000..ce6b8ad --- /dev/null +++ b/frameworks/projects/Basic/src/main/royale/org/apache/royale/core/LayoutManager.as @@ -0,0 +1,84 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Licensed to the Apache Software Foundation (ASF) under one or more +// contributor license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright ownership. +// The ASF licenses this file to You under the Apache License, Version 2.0 +// (the "Licens"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////////// +package org.apache.royale.core +{ + COMPILE::SWF + { + import flash.utils.setTimeout; + } + import org.apache.royale.utils.measureComponent; + + public class LayoutManager + { + static private var layoutPending:Boolean; + static private var measurementPending:Boolean; + static private var measurements:Array = []; + static public function addMeasurement(component:ILayoutChild):void + { + measurements.push(component); + COMPILE::SWF + { + if(!measurementPending) + { + measurementPending = true; + setTimeout(executeMeasurements, 0); + } + } + + COMPILE::JS + { + if(!measurementPending) + { + measurementPending = true; + requestAnimationFrame(executeMeasurements); + } + } + } + static private var pendingLayouts:Array = []; + static public function addLayout(callback:Function):void + { + pendingLayouts.push(callback); + if(!layoutPending && !measurementPending) + { + layoutPending = true; + setTimeout(executeLayouts, 0); + } + } + static private function executeMeasurements():void + { + + measurementPending = false; + while(measurements.length) + { + var component:ILayoutChild = measurements.shift(); + measureComponent(component); + } + executeLayouts(); + } + static private function executeLayouts():void + { + while(pendingLayouts.length) + { + var callback:Function = pendingLayouts.shift(); + callback(); + } + layoutPending = false; + } + } +} \ No newline at end of file diff --git a/frameworks/projects/Basic/src/main/royale/org/apache/royale/core/UIBase.as b/frameworks/projects/Basic/src/main/royale/org/apache/royale/core/UIBase.as index 77ed10b..2e2c777 100644 --- a/frameworks/projects/Basic/src/main/royale/org/apache/royale/core/UIBase.as +++ b/frameworks/projects/Basic/src/main/royale/org/apache/royale/core/UIBase.as @@ -395,6 +395,8 @@ package org.apache.royale.core COMPILE::JS public function get width():Number { + if(!isNaN(_explicitWidth)) + return _explicitWidth; var pixels:Number; var strpixels:String = element.style.width as String; if(strpixels == null) @@ -487,6 +489,8 @@ package org.apache.royale.core COMPILE::JS public function get height():Number { + if(!isNaN(_explicitHeight)) + return _explicitHeight; var pixels:Number; var strpixels:String = element.style.height as String; if(strpixels == null) @@ -589,6 +593,37 @@ package org.apache.royale.core dispatchEvent(new Event("widthChanged")); } } + + /** + * @private + * Used by layout to prevent causing unnecessary reflows when measuring. + */ + private var _measuredWidth:Number; + + public function get measuredWidth():Number + { + return _measuredWidth; + } + + public function set measuredWidth(value:Number):void + { + _measuredWidth = value; + } + /** + * @private + * Used by layout to prevent causing unnecessary reflows when measuring. + */ + private var _measuredHeight:Number; + + public function get measuredHeight():Number + { + return _measuredHeight; + } + + public function set measuredHeight(value:Number):void + { + _measuredHeight = value; + } /** * @copy org.apache.royale.core.ILayoutChild#setWidthAndHeight diff --git a/frameworks/projects/Basic/src/main/royale/org/apache/royale/core/UIButtonBase.as b/frameworks/projects/Basic/src/main/royale/org/apache/royale/core/UIButtonBase.as index 75c68ef..51492cf 100644 --- a/frameworks/projects/Basic/src/main/royale/org/apache/royale/core/UIButtonBase.as +++ b/frameworks/projects/Basic/src/main/royale/org/apache/royale/core/UIButtonBase.as @@ -394,6 +394,37 @@ package org.apache.royale.core } /** + * @private + * Used by layout to prevent causing unnecessary reflows when measuring. + */ + private var _measuredWidth:Number; + + public function get measuredWidth():Number + { + return _measuredWidth; + } + + public function set measuredWidth(value:Number):void + { + _measuredWidth = value; + } + /** + * @private + * Used by layout to prevent causing unnecessary reflows when measuring. + */ + private var _measuredHeight:Number; + + public function get measuredHeight():Number + { + return _measuredHeight; + } + + public function set measuredHeight(value:Number):void + { + _measuredHeight = value; + } + + /** * @copy org.apache.royale.core.IUIBase#setHeight * * @langversion 3.0 diff --git a/frameworks/projects/Core/src/main/royale/CoreClasses.as b/frameworks/projects/Core/src/main/royale/CoreClasses.as index 2ec8859..22b0c3e 100644 --- a/frameworks/projects/Core/src/main/royale/CoreClasses.as +++ b/frameworks/projects/Core/src/main/royale/CoreClasses.as @@ -235,6 +235,7 @@ internal class CoreClasses import org.apache.royale.debugging.notNull; notNull; import org.apache.royale.debugging.throwError; throwError; + import org.apache.royale.utils.measureComponent; measureComponent; import org.apache.royale.utils.loadBeadFromValuesManager; loadBeadFromValuesManager; import org.apache.royale.utils.array.rangeCheck; rangeCheck; diff --git a/frameworks/projects/Core/src/main/royale/org/apache/royale/core/ILayoutChild.as b/frameworks/projects/Core/src/main/royale/org/apache/royale/core/ILayoutChild.as index 439190b..8d39cc4 100755 --- a/frameworks/projects/Core/src/main/royale/org/apache/royale/core/ILayoutChild.as +++ b/frameworks/projects/Core/src/main/royale/org/apache/royale/core/ILayoutChild.as @@ -81,6 +81,30 @@ package org.apache.royale.core */ function get explicitHeight():Number; function set explicitHeight(value:Number):void; + + /** + * The measured width of this component + * This value can be cached by layouts to prevent reflow caused by measuring + * + * @langversion 3.0 + * @playerversion Flash 10.2 + * @playerversion AIR 2.6 + * @productversion Royale 0.9.3 + */ + function get measuredWidth():Number; + function set measuredWidth(value:Number):void; + + /** + * The measured height of this component + * This value can be cached by layouts to prevent reflow caused by measuring + * + * @langversion 3.0 + * @playerversion Flash 10.2 + * @playerversion AIR 2.6 + * @productversion Royale 0.9.3 + */ + function get measuredHeight():Number; + function set measuredHeight(value:Number):void; /** * Sets the height of the component without diff --git a/frameworks/projects/Core/src/main/royale/org/apache/royale/utils/measureComponent.as b/frameworks/projects/Core/src/main/royale/org/apache/royale/utils/measureComponent.as new file mode 100644 index 0000000..3132eec --- /dev/null +++ b/frameworks/projects/Core/src/main/royale/org/apache/royale/utils/measureComponent.as @@ -0,0 +1,27 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Licensed to the Apache Software Foundation (ASF) under one or more +// contributor license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright ownership. +// The ASF licenses this file to You under the Apache License, Version 2.0 +// (the "Licens"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////////// +package org.apache.royale.utils +{ + import org.apache.royale.core.ILayoutChild + public function measureComponent(component:ILayoutChild):void + { + component.measuredWidth = component.width; + component.measuredHeight = component.height; + } +} \ No newline at end of file -- To stop receiving notification emails like this one, please contact [email protected].
