소스 검색

single redraw perf improvement

Trevor Baron 6 년 전
부모
커밋
7a5dedbfeb
3개의 변경된 파일77개의 추가작업 그리고 18개의 파일을 삭제
  1. 24 4
      gui/src/2D/advancedDynamicTexture.ts
  2. 23 7
      gui/src/2D/controls/container.ts
  3. 30 7
      gui/src/2D/controls/control.ts

+ 24 - 4
gui/src/2D/advancedDynamicTexture.ts

@@ -355,6 +355,18 @@ export class AdvancedDynamicTexture extends DynamicTexture {
         }
     }
 
+    private _invalidatedRectangle: Nullable<Measure> = null;
+    public invalidateRect(minX: number, minY: number, maxX: number, maxY: number) {
+        if (!this._invalidatedRectangle) {
+            this._invalidatedRectangle = new Measure(minX, minY, maxX - minX, maxY - minY);
+        }else {
+            this._invalidatedRectangle.left = Math.min(this._invalidatedRectangle.left, minX);
+            this._invalidatedRectangle.top = Math.min(this._invalidatedRectangle.top, minY);
+            this._invalidatedRectangle.width = Math.max(this._invalidatedRectangle.width, maxX - this._invalidatedRectangle.left);
+            this._invalidatedRectangle.height = Math.max(this._invalidatedRectangle.height, maxY - this._invalidatedRectangle.top);
+        }
+    }
+
     /**
      * Marks the texture as dirty forcing a complete update
      */
@@ -554,18 +566,25 @@ export class AdvancedDynamicTexture extends DynamicTexture {
         this.update(true, this.premulAlpha);
     }
 
+    private _clearMeasure = new Measure(0, 0, 0, 0);
     private _render(): void {
         var textureSize = this.getSize();
         var renderWidth = textureSize.width;
         var renderHeight = textureSize.height;
 
+        if (this._invalidatedRectangle) {
+            this._clearMeasure.copyFrom(this._invalidatedRectangle);
+        }else {
+            this._clearMeasure.copyFromFloats(0, 0, renderWidth, renderHeight);
+        }
+
         // Clear
         var context = this.getContext();
-        context.clearRect(0, 0, renderWidth, renderHeight);
+        context.clearRect(this._clearMeasure.left, this._clearMeasure.top, this._clearMeasure.width, this._clearMeasure.height);
         if (this._background) {
             context.save();
             context.fillStyle = this._background;
-            context.fillRect(0, 0, renderWidth, renderHeight);
+            context.fillRect(this._clearMeasure.left, this._clearMeasure.top, this._clearMeasure.width, this._clearMeasure.height);
             context.restore();
         }
 
@@ -573,10 +592,11 @@ export class AdvancedDynamicTexture extends DynamicTexture {
         context.font = "18px Arial";
         context.strokeStyle = "white";
         var measure = new Measure(0, 0, renderWidth, renderHeight);
-        this._rootContainer._layout(measure, context);
+        this._rootContainer._layout(measure, context, this._invalidatedRectangle);
         this._isDirty = false; // Restoring the dirty state that could have been set by controls during layout processing
 
-        this._rootContainer._render(context);
+        this._rootContainer._render(context, this._invalidatedRectangle);
+        this._invalidatedRectangle = null;
     }
 
     /** @hidden */

+ 23 - 7
gui/src/2D/controls/container.ts

@@ -243,7 +243,7 @@ export class Container extends Control {
     }
 
     /** @hidden */
-    protected _localDraw(context: CanvasRenderingContext2D): void {
+    protected _localDraw(context: CanvasRenderingContext2D, invalidatedRectangle?: Measure): void {
         if (this._background) {
             context.save();
             if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
@@ -254,7 +254,11 @@ export class Container extends Control {
             }
 
             context.fillStyle = this._background;
-            context.fillRect(this._currentMeasure.left, this._currentMeasure.top, this._currentMeasure.width, this._currentMeasure.height);
+            if (invalidatedRectangle) {
+                context.fillRect(invalidatedRectangle.left, invalidatedRectangle.top, invalidatedRectangle.width, invalidatedRectangle.height);
+            }else {
+                context.fillRect(this._currentMeasure.left, this._currentMeasure.top, this._currentMeasure.width, this._currentMeasure.height);
+            }
             context.restore();
         }
     }
@@ -274,7 +278,7 @@ export class Container extends Control {
     }
 
     /** @hidden */
-    public _layout(parentMeasure: Measure, context: CanvasRenderingContext2D): boolean {
+    public _layout(parentMeasure: Measure, context: CanvasRenderingContext2D, invalidatedRectangle?: Nullable<Measure>): boolean {
         if (!this.isVisible || this.notRenderable) {
             return false;
         }
@@ -295,9 +299,15 @@ export class Container extends Control {
 
             if (!this._isClipped) {
                 for (var child of this._children) {
+                    // Only redraw parts of the screen that are invalidated
+                    if (invalidatedRectangle) {
+                        if (!child.intersectsRect(invalidatedRectangle)) {
+                            continue;
+                        }
+                    }
                     child._tempParentMeasure.copyFrom(this._measureForChildren);
 
-                    if (child._layout(this._measureForChildren, context)) {
+                    if (child._layout(this._measureForChildren, context, invalidatedRectangle)) {
 
                         if (this.adaptWidthToChildren && child._width.isPixel) {
                             computedWidth = Math.max(computedWidth, child._currentMeasure.width);
@@ -343,16 +353,22 @@ export class Container extends Control {
     }
 
     /** @hidden */
-    public _draw(context: CanvasRenderingContext2D): void {
+    public _draw(context: CanvasRenderingContext2D, invalidatedRectangle?: Measure): void {
 
-        this._localDraw(context);
+        this._localDraw(context, invalidatedRectangle);
 
         if (this.clipChildren) {
             this._clipForChildren(context);
         }
 
         for (var child of this._children) {
-            child._render(context);
+            // Only redraw parts of the screen that are invalidated
+            if (invalidatedRectangle) {
+                if (!child.intersectsRect(invalidatedRectangle)) {
+                    continue;
+                }
+            }
+            child._render(context, invalidatedRectangle);
         }
     }
 

+ 30 - 7
gui/src/2D/controls/control.ts

@@ -1071,6 +1071,28 @@ export class Control {
     }
 
     /** @hidden */
+    public intersectsRect(rect: Measure) {
+        var hit = ! (this._currentMeasure.left > rect.left + rect.width ||
+             this._currentMeasure.left + this._currentMeasure.width < rect.left ||
+             this._currentMeasure.top > rect.top + rect.height ||
+             this._currentMeasure.top + this._currentMeasure.height < rect.top
+        );
+        return hit;
+    }
+
+    /** @hidden */
+    protected invalidateRect() {
+        if (this.host) {
+            this.host.invalidateRect(
+                this._currentMeasure.left,
+                this._currentMeasure.top,
+                this._currentMeasure.left + this._currentMeasure.width,
+                this._currentMeasure.top + this._currentMeasure.height
+            );
+        }
+    }
+
+    /** @hidden */
     public _markAsDirty(force = false): void {
         if (!this._isVisible && !force) {
             return;
@@ -1078,10 +1100,11 @@ export class Control {
 
         this._isDirty = true;
 
-        if (!this._host) {
-            return; // Not yet connected
+        // Redraw only the this rectangle
+        if (this._host) {
+            this._host.markAsDirty();
+            this.invalidateRect();
         }
-        this._host.markAsDirty();
     }
 
     /** @hidden */
@@ -1180,7 +1203,7 @@ export class Control {
     }
 
     /** @hidden */
-    public _layout(parentMeasure: Measure, context: CanvasRenderingContext2D): boolean {
+    public _layout(parentMeasure: Measure, context: CanvasRenderingContext2D, invalidatedRectangle?: Nullable<Measure>): boolean {
         if (!this.isVisible || this.notRenderable) {
             return false;
         }
@@ -1397,7 +1420,7 @@ export class Control {
     }
 
     /** @hidden */
-    public _render(context: CanvasRenderingContext2D): boolean {
+    public _render(context: CanvasRenderingContext2D, invalidatedRectangle?: Nullable<Measure>): boolean {
         if (!this.isVisible || this.notRenderable || this._isClipped) {
             this._isDirty = false;
             return false;
@@ -1421,7 +1444,7 @@ export class Control {
         if (this.useBitmapCache && !this._wasDirty && this._cacheData) {
             context.putImageData(this._cacheData, this._currentMeasure.left, this._currentMeasure.top);
         } else {
-            this._draw(context);
+            this._draw(context, invalidatedRectangle);
         }
 
         if (this.useBitmapCache && this._wasDirty) {
@@ -1440,7 +1463,7 @@ export class Control {
     }
 
     /** @hidden */
-    public _draw(context: CanvasRenderingContext2D): void {
+    public _draw(context: CanvasRenderingContext2D, invalidatedRectangle?: Nullable<Measure>): void {
         // Do nothing
     }