From cfb72c7efd8722fddf06a3999ea179566572ff2e Mon Sep 17 00:00:00 2001 From: luo2430 Date: Wed, 17 Jun 2026 20:57:51 +0800 Subject: [PATCH 01/37] refactor(rhi-webgl): extract isOffscreenCanvas helper --- packages/rhi-webgl/src/WebCanvas.ts | 10 ++++-- packages/rhi-webgl/src/WebGLGraphicDevice.ts | 6 ++-- tests/src/rhi-webgl/WebCanvas.test.ts | 38 ++++++++++++++++++++ 3 files changed, 48 insertions(+), 6 deletions(-) create mode 100644 tests/src/rhi-webgl/WebCanvas.test.ts diff --git a/packages/rhi-webgl/src/WebCanvas.ts b/packages/rhi-webgl/src/WebCanvas.ts index 41df0f6749..e9cfb1610f 100644 --- a/packages/rhi-webgl/src/WebCanvas.ts +++ b/packages/rhi-webgl/src/WebCanvas.ts @@ -9,6 +9,10 @@ type OffscreenCanvas = any; export class WebCanvas extends Canvas { _webCanvas: HTMLCanvasElement | OffscreenCanvas; + isOffscreenCanvas(): boolean { + return typeof OffscreenCanvas !== "undefined" && this._webCanvas instanceof OffscreenCanvas; + } + private _scale: Vector2 = new Vector2(); /** @@ -17,7 +21,7 @@ export class WebCanvas extends Canvas { */ get scale(): Vector2 { const webCanvas = this._webCanvas; - if (typeof OffscreenCanvas === "undefined" || !(webCanvas instanceof OffscreenCanvas)) { + if (!this.isOffscreenCanvas()) { this._scale.set( (webCanvas.clientWidth * devicePixelRatio) / webCanvas.width, (webCanvas.clientHeight * devicePixelRatio) / webCanvas.height @@ -28,7 +32,7 @@ export class WebCanvas extends Canvas { set scale(value: Vector2) { const webCanvas = this._webCanvas; - if (typeof OffscreenCanvas === "undefined" || !(webCanvas instanceof OffscreenCanvas)) { + if (!this.isOffscreenCanvas()) { webCanvas.style.transformOrigin = `left top`; webCanvas.style.transform = `scale(${value.x}, ${value.y})`; } @@ -40,7 +44,7 @@ export class WebCanvas extends Canvas { */ resizeByClientSize(pixelRatio: number = window.devicePixelRatio): void { const webCanvas = this._webCanvas; - if (typeof OffscreenCanvas === "undefined" || !(webCanvas instanceof OffscreenCanvas)) { + if (!this.isOffscreenCanvas()) { const exportWidth = webCanvas.clientWidth * pixelRatio; const exportHeight = webCanvas.clientHeight * pixelRatio; this.width = exportWidth; diff --git a/packages/rhi-webgl/src/WebGLGraphicDevice.ts b/packages/rhi-webgl/src/WebGLGraphicDevice.ts index 7394b0409a..1b1037106e 100644 --- a/packages/rhi-webgl/src/WebGLGraphicDevice.ts +++ b/packages/rhi-webgl/src/WebGLGraphicDevice.ts @@ -213,10 +213,10 @@ export class WebGLGraphicDevice implements IHardwareRenderer { this._webCanvas = webCanvas; const webGLOptions = this._webGLOptions; - let gl: (WebGLRenderingContext & WebGLExtension) | WebGL2RenderingContext; + let gl: (WebGLRenderingContext & WebGLExtension) | WebGL2RenderingContext | undefined; if (webGLMode == WebGLMode.Auto || webGLMode == WebGLMode.WebGL2) { gl = webCanvas.getContext("webgl2", webGLOptions); - if (!gl && (typeof OffscreenCanvas === "undefined" || !(webCanvas instanceof OffscreenCanvas))) { + if (!gl && !(canvas as WebCanvas).isOffscreenCanvas()) { gl = webCanvas.getContext("experimental-webgl2", webGLOptions); } this._isWebGL2 = true; @@ -230,7 +230,7 @@ export class WebGLGraphicDevice implements IHardwareRenderer { if (!gl) { if (webGLMode == WebGLMode.Auto || webGLMode == WebGLMode.WebGL1) { gl = webCanvas.getContext("webgl", webGLOptions); - if (!gl && (typeof OffscreenCanvas === "undefined" || !(webCanvas instanceof OffscreenCanvas))) { + if (!gl && !(canvas as WebCanvas).isOffscreenCanvas()) { gl = webCanvas.getContext("experimental-webgl", webGLOptions); } this._isWebGL2 = false; diff --git a/tests/src/rhi-webgl/WebCanvas.test.ts b/tests/src/rhi-webgl/WebCanvas.test.ts new file mode 100644 index 0000000000..d9b67e9732 --- /dev/null +++ b/tests/src/rhi-webgl/WebCanvas.test.ts @@ -0,0 +1,38 @@ +import { WebCanvas } from "@galacean/engine"; +import { describe, expect, it } from "vitest"; + +describe("WebCanvas", () => { + describe("isOffscreenCanvas", () => { + it("should return false when _webCanvas is an HTMLCanvasElement", () => { + const canvas = document.createElement("canvas"); + const webCanvas = new WebCanvas(canvas); + expect(webCanvas.isOffscreenCanvas()).toBe(false); + }); + + it("should return true when _webCanvas is an OffscreenCanvas", () => { + const offscreen = new OffscreenCanvas(300, 150); + const webCanvas = new WebCanvas(offscreen); + expect(webCanvas.isOffscreenCanvas()).toBe(true); + }); + + it("should return false when _webCanvas is replaced with an HTMLCanvasElement after construction", () => { + const canvas = document.createElement("canvas"); + const webCanvas = new WebCanvas(canvas); + + // Replace the internal canvas with an HTMLCanvasElement + const anotherCanvas = document.createElement("canvas"); + webCanvas._webCanvas = anotherCanvas; + expect(webCanvas.isOffscreenCanvas()).toBe(false); + }); + + it("should return true when _webCanvas is replaced with an OffscreenCanvas after construction", () => { + const canvas = document.createElement("canvas"); + const webCanvas = new WebCanvas(canvas); + + // Replace the internal canvas with an OffscreenCanvas + const offscreen = new OffscreenCanvas(300, 150); + webCanvas._webCanvas = offscreen; + expect(webCanvas.isOffscreenCanvas()).toBe(true); + }); + }); +}); From c7d793aeec0e03182a7d8b5607ae66387ad75ddf Mon Sep 17 00:00:00 2001 From: luo2430 Date: Thu, 18 Jun 2026 08:15:41 +0800 Subject: [PATCH 02/37] feat(rhi-webgl): add WebGLEngine auto-resize support via ResizeObserver --- packages/rhi-webgl/src/WebGLEngine.ts | 50 ++++++++++++++++++++++++- tests/src/rhi-webgl/WebGLEngine.test.ts | 42 ++++++++++++++++++++- 2 files changed, 90 insertions(+), 2 deletions(-) diff --git a/packages/rhi-webgl/src/WebGLEngine.ts b/packages/rhi-webgl/src/WebGLEngine.ts index 2d21b71801..e6b547d5a5 100644 --- a/packages/rhi-webgl/src/WebGLEngine.ts +++ b/packages/rhi-webgl/src/WebGLEngine.ts @@ -1,4 +1,4 @@ -import { Engine, EngineConfiguration, Scene } from "@galacean/engine-core"; +import { Engine, EngineConfiguration, EngineEventType, Scene } from "@galacean/engine-core"; import { WebGLGraphicDevice, WebGLGraphicDeviceOptions } from "./"; import { WebCanvas } from "./WebCanvas"; @@ -31,6 +31,54 @@ export class WebGLEngine extends Engine { // @ts-ignore return this._canvas as WebCanvas; } + + private _resizeObserver?: ResizeObserver; + + private static _cleanupResizeObserver(engine: WebGLEngine): void { + if (engine._resizeObserver) { + engine._resizeObserver.disconnect(); + engine._resizeObserver = undefined; + } + } + + /** + * Enable automatic canvas resizing via ResizeObserver. + * @param pixelRatio - Optional custom pixel ratio; falls back to device pixel ratio if omitted + */ + enableAutoResize(pixelRatio?: number): void { + const webCanvas = this.canvas._webCanvas; + + if (!this.canvas.isOffscreenCanvas()) { + if (!this._resizeObserver) { + this.once(EngineEventType.Shutdown, WebGLEngine._cleanupResizeObserver); + } + + // Disconnect previous observer to avoid duplicate observation + this._resizeObserver?.disconnect(); + + // Always create a new ResizeObserver to capture the latest pixelRatio parameter, + // avoiding stale closure values when enableAutoResize is called again. + this._resizeObserver = new ResizeObserver(() => { + this.canvas.resizeByClientSize(pixelRatio); + }); + + // Start observing the canvas element for size changes. + // Note: ResizeObserver callbacks are delivered asynchronously in a microtask + // before the next paint (NOT synchronously during observe()). The canvas will + // still be resized early enough in the frame lifecycle to be correct for rendering. + this._resizeObserver.observe(webCanvas); + } + } + + /** + * Disable automatic canvas resizing and clean up the observer. + */ + disableAutoResize(): void { + if (this._resizeObserver) { + this.off(EngineEventType.Shutdown, WebGLEngine._cleanupResizeObserver); + WebGLEngine._cleanupResizeObserver(this); + } + } } /** diff --git a/tests/src/rhi-webgl/WebGLEngine.test.ts b/tests/src/rhi-webgl/WebGLEngine.test.ts index 91cd371285..31a0455435 100644 --- a/tests/src/rhi-webgl/WebGLEngine.test.ts +++ b/tests/src/rhi-webgl/WebGLEngine.test.ts @@ -1,4 +1,4 @@ -import { Camera, Entity, Script } from "@galacean/engine-core"; +import { Camera, EngineEventType, Entity, Script } from "@galacean/engine-core"; import { Vector3 } from "@galacean/engine-math"; import { WebGLEngine } from "@galacean/engine"; import { vi, describe, expect, it } from "vitest"; @@ -102,4 +102,44 @@ describe("webgl engine test", () => { engine.destroy(); } }); + + it("engine auto resize", async () => { + const canvas = document.createElement("canvas"); + const engine = await WebGLEngine.create({ canvas }); + engine.run(); + + const rawListenerCount = engine.listenerCount(EngineEventType.Shutdown); + + engine.enableAutoResize(); + expect(engine.listenerCount(EngineEventType.Shutdown)).toBe(rawListenerCount + 1); + expect((engine as any)._resizeObserver).toBeDefined(); + const firstObserver = (engine as any)._resizeObserver; + const firstObserverDisconnectSpy = vi.spyOn(firstObserver, "disconnect"); + expect(firstObserverDisconnectSpy).toHaveBeenCalledTimes(0); + + engine.enableAutoResize(); + expect(engine.listenerCount(EngineEventType.Shutdown)).toBe(rawListenerCount + 1); + expect((engine as any)._resizeObserver).toBeDefined(); + expect((engine as any)._resizeObserver).not.toBe(firstObserver); + expect(firstObserverDisconnectSpy).toHaveBeenCalledTimes(1); + + engine.disableAutoResize(); + expect((engine as any)._resizeObserver).toBeUndefined(); + expect(engine.listenerCount(EngineEventType.Shutdown)).toBe(rawListenerCount); + + const resizeSpy = vi.spyOn(engine.canvas, "resizeByClientSize"); + engine.enableAutoResize(); + expect((engine as any)._resizeObserver).toBeDefined(); + expect(engine.listenerCount(EngineEventType.Shutdown)).toBe(rawListenerCount + 1); + + canvas.style.width = "800px"; + canvas.style.height = "600px"; + // Wait for the ResizeObserver to fire the resize callback + await new Promise((resolve) => setTimeout(resolve, 1000)); + expect(resizeSpy).toHaveBeenCalledTimes(1); + + engine.destroy(); + expect((engine as any)._resizeObserver).toBeUndefined(); + expect(engine.listenerCount(EngineEventType.Shutdown)).toBe(rawListenerCount); + }); }); From b794d5400e5cd994744f5c32dd22800b54344ccc Mon Sep 17 00:00:00 2001 From: luo2430 Date: Thu, 18 Jun 2026 08:15:56 +0800 Subject: [PATCH 03/37] chore(vitest): disable screenshotFailures to avoid taking blank screenshots --- .../TextRenderer-bounds-1.png | Bin 3012 -> 0 bytes .../Sprite.test.ts/Sprite-get-set-size-1.png | Bin 3012 -> 0 bytes .../SpriteMask.test.ts/SpriteMask--render-1.png | Bin 3012 -> 0 bytes .../SpriteMask-get-set-size-1.png | Bin 3012 -> 0 bytes .../SpriteRenderer--render-1.png | Bin 3012 -> 0 bytes .../SpriteRenderer-get-set-size-1.png | Bin 3012 -> 0 bytes .../AudioSource.test.ts/AudioSource-load-1.png | Bin 3012 -> 0 bytes tests/vitest.config.ts | 1 + 8 files changed, 1 insertion(+) delete mode 100644 tests/src/core/2d/text/__screenshots__/TextRenderer.test.ts/TextRenderer-bounds-1.png delete mode 100644 tests/src/core/__screenshots__/Sprite.test.ts/Sprite-get-set-size-1.png delete mode 100644 tests/src/core/__screenshots__/SpriteMask.test.ts/SpriteMask--render-1.png delete mode 100644 tests/src/core/__screenshots__/SpriteMask.test.ts/SpriteMask-get-set-size-1.png delete mode 100644 tests/src/core/__screenshots__/SpriteRenderer.test.ts/SpriteRenderer--render-1.png delete mode 100644 tests/src/core/__screenshots__/SpriteRenderer.test.ts/SpriteRenderer-get-set-size-1.png delete mode 100644 tests/src/core/audio/__screenshots__/AudioSource.test.ts/AudioSource-load-1.png diff --git a/tests/src/core/2d/text/__screenshots__/TextRenderer.test.ts/TextRenderer-bounds-1.png b/tests/src/core/2d/text/__screenshots__/TextRenderer.test.ts/TextRenderer-bounds-1.png deleted file mode 100644 index f88c55c1cad7d3de8229e8a42a99ad5e0bc9e037..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3012 zcmeAS@N?(olHy`uVBq!ia0y~yV4TOmz}&#W1QcnFZJGt77>k44ofy`glX=O&z+LC* z;uumf=k1k)oD2#A2MkL8&pofovQS0%%T-%r?gdZ(@G(@Jzty0S&D^wxmt&Ke!iiDN zXc&y9gVDS&S{{s+iKF$xXmvQ+Bp7WLjW!L*YaZ)u@!|YCPt|%AuvyOF>FVdQ&MBb@ E0IgVhRsaA1 diff --git a/tests/src/core/__screenshots__/Sprite.test.ts/Sprite-get-set-size-1.png b/tests/src/core/__screenshots__/Sprite.test.ts/Sprite-get-set-size-1.png deleted file mode 100644 index f88c55c1cad7d3de8229e8a42a99ad5e0bc9e037..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3012 zcmeAS@N?(olHy`uVBq!ia0y~yV4TOmz}&#W1QcnFZJGt77>k44ofy`glX=O&z+LC* z;uumf=k1k)oD2#A2MkL8&pofovQS0%%T-%r?gdZ(@G(@Jzty0S&D^wxmt&Ke!iiDN zXc&y9gVDS&S{{s+iKF$xXmvQ+Bp7WLjW!L*YaZ)u@!|YCPt|%AuvyOF>FVdQ&MBb@ E0IgVhRsaA1 diff --git a/tests/src/core/__screenshots__/SpriteMask.test.ts/SpriteMask--render-1.png b/tests/src/core/__screenshots__/SpriteMask.test.ts/SpriteMask--render-1.png deleted file mode 100644 index f88c55c1cad7d3de8229e8a42a99ad5e0bc9e037..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3012 zcmeAS@N?(olHy`uVBq!ia0y~yV4TOmz}&#W1QcnFZJGt77>k44ofy`glX=O&z+LC* z;uumf=k1k)oD2#A2MkL8&pofovQS0%%T-%r?gdZ(@G(@Jzty0S&D^wxmt&Ke!iiDN zXc&y9gVDS&S{{s+iKF$xXmvQ+Bp7WLjW!L*YaZ)u@!|YCPt|%AuvyOF>FVdQ&MBb@ E0IgVhRsaA1 diff --git a/tests/src/core/__screenshots__/SpriteMask.test.ts/SpriteMask-get-set-size-1.png b/tests/src/core/__screenshots__/SpriteMask.test.ts/SpriteMask-get-set-size-1.png deleted file mode 100644 index f88c55c1cad7d3de8229e8a42a99ad5e0bc9e037..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3012 zcmeAS@N?(olHy`uVBq!ia0y~yV4TOmz}&#W1QcnFZJGt77>k44ofy`glX=O&z+LC* z;uumf=k1k)oD2#A2MkL8&pofovQS0%%T-%r?gdZ(@G(@Jzty0S&D^wxmt&Ke!iiDN zXc&y9gVDS&S{{s+iKF$xXmvQ+Bp7WLjW!L*YaZ)u@!|YCPt|%AuvyOF>FVdQ&MBb@ E0IgVhRsaA1 diff --git a/tests/src/core/__screenshots__/SpriteRenderer.test.ts/SpriteRenderer--render-1.png b/tests/src/core/__screenshots__/SpriteRenderer.test.ts/SpriteRenderer--render-1.png deleted file mode 100644 index f88c55c1cad7d3de8229e8a42a99ad5e0bc9e037..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3012 zcmeAS@N?(olHy`uVBq!ia0y~yV4TOmz}&#W1QcnFZJGt77>k44ofy`glX=O&z+LC* z;uumf=k1k)oD2#A2MkL8&pofovQS0%%T-%r?gdZ(@G(@Jzty0S&D^wxmt&Ke!iiDN zXc&y9gVDS&S{{s+iKF$xXmvQ+Bp7WLjW!L*YaZ)u@!|YCPt|%AuvyOF>FVdQ&MBb@ E0IgVhRsaA1 diff --git a/tests/src/core/__screenshots__/SpriteRenderer.test.ts/SpriteRenderer-get-set-size-1.png b/tests/src/core/__screenshots__/SpriteRenderer.test.ts/SpriteRenderer-get-set-size-1.png deleted file mode 100644 index f88c55c1cad7d3de8229e8a42a99ad5e0bc9e037..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3012 zcmeAS@N?(olHy`uVBq!ia0y~yV4TOmz}&#W1QcnFZJGt77>k44ofy`glX=O&z+LC* z;uumf=k1k)oD2#A2MkL8&pofovQS0%%T-%r?gdZ(@G(@Jzty0S&D^wxmt&Ke!iiDN zXc&y9gVDS&S{{s+iKF$xXmvQ+Bp7WLjW!L*YaZ)u@!|YCPt|%AuvyOF>FVdQ&MBb@ E0IgVhRsaA1 diff --git a/tests/src/core/audio/__screenshots__/AudioSource.test.ts/AudioSource-load-1.png b/tests/src/core/audio/__screenshots__/AudioSource.test.ts/AudioSource-load-1.png deleted file mode 100644 index f88c55c1cad7d3de8229e8a42a99ad5e0bc9e037..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3012 zcmeAS@N?(olHy`uVBq!ia0y~yV4TOmz}&#W1QcnFZJGt77>k44ofy`glX=O&z+LC* z;uumf=k1k)oD2#A2MkL8&pofovQS0%%T-%r?gdZ(@G(@Jzty0S&D^wxmt&Ke!iiDN zXc&y9gVDS&S{{s+iKF$xXmvQ+Bp7WLjW!L*YaZ)u@!|YCPt|%AuvyOF>FVdQ&MBb@ E0IgVhRsaA1 diff --git a/tests/vitest.config.ts b/tests/vitest.config.ts index e6d2526da8..d6c396b06e 100644 --- a/tests/vitest.config.ts +++ b/tests/vitest.config.ts @@ -18,6 +18,7 @@ export default defineProject({ browser: { provider: "playwright", enabled: true, + screenshotFailures: false, instances: [ { browser: "chromium", From 9fc8dce847a424efd56cc83eb9795731616d1f8b Mon Sep 17 00:00:00 2001 From: luo2430 Date: Thu, 18 Jun 2026 12:18:05 +0800 Subject: [PATCH 04/37] docs(rhi-webgl): update isOffscreenCanvas and ResizeObserver.observe docs --- packages/rhi-webgl/src/WebCanvas.ts | 4 ++++ packages/rhi-webgl/src/WebGLEngine.ts | 7 ++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/rhi-webgl/src/WebCanvas.ts b/packages/rhi-webgl/src/WebCanvas.ts index e9cfb1610f..540f1986ee 100644 --- a/packages/rhi-webgl/src/WebCanvas.ts +++ b/packages/rhi-webgl/src/WebCanvas.ts @@ -9,6 +9,10 @@ type OffscreenCanvas = any; export class WebCanvas extends Canvas { _webCanvas: HTMLCanvasElement | OffscreenCanvas; + /** + * Whether the underlying canvas is an OffscreenCanvas. + * @returns Whether it is an OffscreenCanvas + */ isOffscreenCanvas(): boolean { return typeof OffscreenCanvas !== "undefined" && this._webCanvas instanceof OffscreenCanvas; } diff --git a/packages/rhi-webgl/src/WebGLEngine.ts b/packages/rhi-webgl/src/WebGLEngine.ts index e6b547d5a5..dbae81a58d 100644 --- a/packages/rhi-webgl/src/WebGLEngine.ts +++ b/packages/rhi-webgl/src/WebGLEngine.ts @@ -63,9 +63,10 @@ export class WebGLEngine extends Engine { }); // Start observing the canvas element for size changes. - // Note: ResizeObserver callbacks are delivered asynchronously in a microtask - // before the next paint (NOT synchronously during observe()). The canvas will - // still be resized early enough in the frame lifecycle to be correct for rendering. + // Note: ResizeObserver callbacks fire after the current frame's rAF, + // so the canvas resize only takes effect in the following frame. + // The current frame may render with stale dimensions (e.g. a flash of + // incorrect sizing / blank area), and will recover on the next frame. this._resizeObserver.observe(webCanvas); } } From a15b539274c8d0124eb5761ef0f113867d1fddc0 Mon Sep 17 00:00:00 2001 From: luo2430 Date: Mon, 22 Jun 2026 19:21:31 +0800 Subject: [PATCH 05/37] fix(rhi-webgl): defer auto-resize to prevent white flash --- examples/src/device-restore.ts | 4 +-- packages/rhi-webgl/src/WebGLEngine.ts | 42 ++++++++++++++++++------- tests/src/rhi-webgl/WebGLEngine.test.ts | 4 +++ 3 files changed, 36 insertions(+), 14 deletions(-) diff --git a/examples/src/device-restore.ts b/examples/src/device-restore.ts index 8c8bf06450..102ce1f7b9 100644 --- a/examples/src/device-restore.ts +++ b/examples/src/device-restore.ts @@ -6,7 +6,7 @@ import { Animator, Camera, Color, GLTFResource, Vector3, WebGLEngine } from "@galacean/engine"; WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.resizeByClientSize(); + engine.enableAutoResize(); const rootEntity = engine.sceneManager.activeScene.createRootEntity(); rootEntity.scene.background.solidColor = new Color(0.39, 0.31, 0.55, 1.0); @@ -27,7 +27,7 @@ WebGLEngine.create({ canvas: "canvas" }).then((engine) => { rootEntity.addChild(defaultSceneRoot); const animator = defaultSceneRoot.getComponent(Animator); - animator.play(animations![0].name); + animator?.play(animations![0].name); engine.on("devicelost", () => { domText.textContent = "Device is lost!"; diff --git a/packages/rhi-webgl/src/WebGLEngine.ts b/packages/rhi-webgl/src/WebGLEngine.ts index dbae81a58d..9e23a09c51 100644 --- a/packages/rhi-webgl/src/WebGLEngine.ts +++ b/packages/rhi-webgl/src/WebGLEngine.ts @@ -33,40 +33,44 @@ export class WebGLEngine extends Engine { } private _resizeObserver?: ResizeObserver; + private _pendingResize: boolean = false; + private _pendingResizePixelRatio?: number; - private static _cleanupResizeObserver(engine: WebGLEngine): void { + private static _cleanupAutoResize(engine: WebGLEngine): void { if (engine._resizeObserver) { engine._resizeObserver.disconnect(); engine._resizeObserver = undefined; } + engine._pendingResize = false; + engine._pendingResizePixelRatio = undefined; } /** * Enable automatic canvas resizing via ResizeObserver. - * @param pixelRatio - Optional custom pixel ratio; falls back to device pixel ratio if omitted + * @param pixelRatio - Optional custom pixel ratio; lazily reads window.devicePixelRatio on resize when omitted */ enableAutoResize(pixelRatio?: number): void { const webCanvas = this.canvas._webCanvas; if (!this.canvas.isOffscreenCanvas()) { if (!this._resizeObserver) { - this.once(EngineEventType.Shutdown, WebGLEngine._cleanupResizeObserver); + this.once(EngineEventType.Shutdown, WebGLEngine._cleanupAutoResize); } // Disconnect previous observer to avoid duplicate observation this._resizeObserver?.disconnect(); - // Always create a new ResizeObserver to capture the latest pixelRatio parameter, - // avoiding stale closure values when enableAutoResize is called again. + // Re-create the observer each call so the closure always captures the + // latest pixelRatio. The actual resize is deferred to update(), keeping + // it in the same frame as rendering to prevent a blank flash. + // When pixelRatio is undefined, resizeByClientSize falls back to + // window.devicePixelRatio on each invocation, naturally tracking DPR changes. this._resizeObserver = new ResizeObserver(() => { - this.canvas.resizeByClientSize(pixelRatio); + this._pendingResize = true; + this._pendingResizePixelRatio = pixelRatio; }); // Start observing the canvas element for size changes. - // Note: ResizeObserver callbacks fire after the current frame's rAF, - // so the canvas resize only takes effect in the following frame. - // The current frame may render with stale dimensions (e.g. a flash of - // incorrect sizing / blank area), and will recover on the next frame. this._resizeObserver.observe(webCanvas); } } @@ -76,10 +80,24 @@ export class WebGLEngine extends Engine { */ disableAutoResize(): void { if (this._resizeObserver) { - this.off(EngineEventType.Shutdown, WebGLEngine._cleanupResizeObserver); - WebGLEngine._cleanupResizeObserver(this); + this.off(EngineEventType.Shutdown, WebGLEngine._cleanupAutoResize); + WebGLEngine._cleanupAutoResize(this); } } + + /** + * @override + * Apply any pending canvas resize before the rendering pipeline runs, + * ensuring that resize and render occur within the same frame to avoid white flickering. + */ + override update(): void { + if (this._pendingResize) { + this.canvas.resizeByClientSize(this._pendingResizePixelRatio); + this._pendingResize = false; + this._pendingResizePixelRatio = undefined; + } + super.update(); + } } /** diff --git a/tests/src/rhi-webgl/WebGLEngine.test.ts b/tests/src/rhi-webgl/WebGLEngine.test.ts index 31a0455435..34b32cc817 100644 --- a/tests/src/rhi-webgl/WebGLEngine.test.ts +++ b/tests/src/rhi-webgl/WebGLEngine.test.ts @@ -125,6 +125,7 @@ describe("webgl engine test", () => { engine.disableAutoResize(); expect((engine as any)._resizeObserver).toBeUndefined(); + expect((engine as any)._pendingResizePixelRatio).toBeUndefined(); expect(engine.listenerCount(EngineEventType.Shutdown)).toBe(rawListenerCount); const resizeSpy = vi.spyOn(engine.canvas, "resizeByClientSize"); @@ -137,9 +138,12 @@ describe("webgl engine test", () => { // Wait for the ResizeObserver to fire the resize callback await new Promise((resolve) => setTimeout(resolve, 1000)); expect(resizeSpy).toHaveBeenCalledTimes(1); + // _pendingResizePixelRatio should be consumed by update() and cleared + expect((engine as any)._pendingResizePixelRatio).toBeUndefined(); engine.destroy(); expect((engine as any)._resizeObserver).toBeUndefined(); + expect((engine as any)._pendingResizePixelRatio).toBeUndefined(); expect(engine.listenerCount(EngineEventType.Shutdown)).toBe(rawListenerCount); }); }); From 7c5c1c1ce46e08c3e568bb868c19bbed094a170a Mon Sep 17 00:00:00 2001 From: "chenmo.gl" Date: Wed, 1 Jul 2026 17:37:02 +0800 Subject: [PATCH 06/37] refactor(rhi-webgl): move field declarations before methods Fields were interleaved with methods, breaking the field-then-method ordering convention used by the base Canvas/Engine classes. Group the declarations at the top: WebCanvas._scale before isOffscreenCanvas, and WebGLEngine's _resizeObserver/_pendingResize/_pendingResizePixelRatio before the canvas getter. --- packages/rhi-webgl/src/WebCanvas.ts | 16 ++++++++-------- packages/rhi-webgl/src/WebGLEngine.ts | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/rhi-webgl/src/WebCanvas.ts b/packages/rhi-webgl/src/WebCanvas.ts index 540f1986ee..e98ca0ac2e 100644 --- a/packages/rhi-webgl/src/WebCanvas.ts +++ b/packages/rhi-webgl/src/WebCanvas.ts @@ -9,14 +9,6 @@ type OffscreenCanvas = any; export class WebCanvas extends Canvas { _webCanvas: HTMLCanvasElement | OffscreenCanvas; - /** - * Whether the underlying canvas is an OffscreenCanvas. - * @returns Whether it is an OffscreenCanvas - */ - isOffscreenCanvas(): boolean { - return typeof OffscreenCanvas !== "undefined" && this._webCanvas instanceof OffscreenCanvas; - } - private _scale: Vector2 = new Vector2(); /** @@ -42,6 +34,14 @@ export class WebCanvas extends Canvas { } } + /** + * Whether the underlying canvas is an OffscreenCanvas. + * @returns Whether it is an OffscreenCanvas + */ + isOffscreenCanvas(): boolean { + return typeof OffscreenCanvas !== "undefined" && this._webCanvas instanceof OffscreenCanvas; + } + /** * Resize the rendering size according to the clientWidth and clientHeight of the canvas. * @param pixelRatio - Pixel ratio diff --git a/packages/rhi-webgl/src/WebGLEngine.ts b/packages/rhi-webgl/src/WebGLEngine.ts index 9e23a09c51..cb409e86d1 100644 --- a/packages/rhi-webgl/src/WebGLEngine.ts +++ b/packages/rhi-webgl/src/WebGLEngine.ts @@ -24,6 +24,10 @@ export class WebGLEngine extends Engine { }); } + private _resizeObserver?: ResizeObserver; + private _pendingResize: boolean = false; + private _pendingResizePixelRatio?: number; + /** * Web canvas. */ @@ -32,10 +36,6 @@ export class WebGLEngine extends Engine { return this._canvas as WebCanvas; } - private _resizeObserver?: ResizeObserver; - private _pendingResize: boolean = false; - private _pendingResizePixelRatio?: number; - private static _cleanupAutoResize(engine: WebGLEngine): void { if (engine._resizeObserver) { engine._resizeObserver.disconnect(); From f7ad066438096f61b30a3aee6f6ef08a9906dea9 Mon Sep 17 00:00:00 2001 From: "chenmo.gl" Date: Wed, 1 Jul 2026 23:15:27 +0800 Subject: [PATCH 07/37] refactor(rhi-webgl): rework canvas resolution API, follow display size by default Auto-resize was a canvas concern but lived on WebGLEngine (the white-flash fix needed the engine's frame timing). Move it to WebCanvas; the engine keeps one collaboration point (pump the pending resize each frame) plus observer cleanup on shutdown. The atomic size entry and setResolution live on the base Canvas. The render buffer now follows the canvas display size by default, so most apps need no resize code at all. The deferred resize is skipped while the canvas has no layout size, so an unmounted canvas never gets a 0x0 buffer. Public API (canvas-owned): - canvas.setAutoResolution(scale?) follow the display size; scale is a perf multiplier on the auto-applied device pixel ratio - canvas.setResolution(w, h) lock an absolute buffer, exit follow mode - canvas.width / height read-only (resolution is atomic) - WebGLEngine.create({ autoResize }) opt out of the default follow resizeByClientSize is removed (examples rely on the default follow). enableAutoResize/disableAutoResize kept as deprecated forwarders. BREAKING CHANGE: canvas.width/height are read-only; resizeByClientSize removed; auto-resize is on by default (pass autoResize:false to opt out). --- e2e/case/.initPostProcessEnv.ts | 2 +- e2e/case/animator-additive.ts | 2 +- e2e/case/animator-blendShape-quantization.ts | 2 +- e2e/case/animator-blendShape.ts | 2 +- e2e/case/animator-crossfade.ts | 2 +- e2e/case/animator-customAnimationClip.ts | 2 +- e2e/case/animator-customBlendShape.ts | 2 +- e2e/case/animator-event.ts | 2 +- e2e/case/animator-multiSubMeshBlendShape.ts | 6 +- e2e/case/animator-play-backwards.ts | 2 +- e2e/case/animator-play-beforeActive.ts | 2 +- e2e/case/animator-play.ts | 2 +- e2e/case/animator-reuse.ts | 2 +- e2e/case/animator-stateMachine.ts | 6 +- e2e/case/animator-stateMachineScript.ts | 2 +- e2e/case/camera-fxaa.ts | 2 +- e2e/case/camera-opaque-texture.ts | 2 +- e2e/case/camera-ssao.ts | 2 +- e2e/case/canvas-transparency.ts | 2 +- e2e/case/gltf-blendshape.ts | 6 +- e2e/case/gltf-meshopt.ts | 2 +- e2e/case/gpu-instancing-auto-batch.ts | 2 +- e2e/case/gpu-instancing-custom-data.ts | 2 +- e2e/case/litePhysics-collision-group.ts | 2 +- e2e/case/material-LUT.ts | 2 +- e2e/case/material-blendMode.ts | 2 +- e2e/case/material-blinn-phong.ts | 2 +- e2e/case/material-pbr-clearcoat.ts | 2 +- e2e/case/material-pbr-specular.ts | 2 +- e2e/case/material-pbr.ts | 2 +- e2e/case/material-shader.ts | 2 +- e2e/case/material-shaderReplacement.ts | 2 +- e2e/case/material-unlit.ts | 2 +- e2e/case/material-white-furnace.ts | 2 +- e2e/case/multi-camera-no-clear.ts | 2 +- e2e/case/multi-scene-clear.ts | 2 +- e2e/case/multi-scene-no-clear.ts | 2 +- e2e/case/particleRenderer-burst-cycles.ts | 2 +- e2e/case/particleRenderer-customShader.ts | 2 +- e2e/case/particleRenderer-dream.ts | 2 +- e2e/case/particleRenderer-emissive.ts | 2 +- ...rticleRenderer-emit-billboard-stretched.ts | 7 +- ...sh-cone-scale-3d-rotation-life-seperate.ts | 2 +- ...nderer-emit-mesh-cone-scale-3d-rotation.ts | 4 +- ...-mesh-cone-scale-rotation-life-seperate.ts | 7 +- ...erer-emit-mesh-cone-scale-rotation-life.ts | 7 +- ...rer-emit-mesh-cone-scale-rotation-world.ts | 15 ++-- ...eRenderer-emit-mesh-cone-scale-rotation.ts | 12 +-- e2e/case/particleRenderer-emit-mesh-cone.ts | 2 +- ...rticleRenderer-emit-mesh-no-shape-world.ts | 2 +- .../particleRenderer-emit-mesh-no-shape.ts | 2 +- ...eRenderer-emit-mesh-rotation-life-curve.ts | 2 +- e2e/case/particleRenderer-fire.ts | 2 +- e2e/case/particleRenderer-force.ts | 2 +- .../particleRenderer-horizontal-billboard.ts | 2 +- e2e/case/particleRenderer-limitVelocity.ts | 2 +- e2e/case/particleRenderer-noise.ts | 2 +- e2e/case/particleRenderer-rateOverDistance.ts | 2 +- e2e/case/particleRenderer-shape-mesh.ts | 2 +- e2e/case/particleRenderer-shape-transform.ts | 2 +- .../particleRenderer-textureSheetAnimation.ts | 2 +- e2e/case/physx-collision-group.ts | 2 +- e2e/case/physx-collision.ts | 2 +- e2e/case/physx-customUrl.ts | 2 +- e2e/case/physx-deferred-contact.ts | 2 +- e2e/case/physx-mesh-collider-data.ts | 2 +- e2e/case/physx-mesh-collider.ts | 2 +- e2e/case/primitive-capsule.ts | 2 +- e2e/case/primitive-cone.ts | 2 +- e2e/case/primitive-cuboid.ts | 2 +- e2e/case/primitive-cylinder.ts | 2 +- e2e/case/primitive-plane.ts | 2 +- e2e/case/primitive-sphere.ts | 2 +- e2e/case/primitive-torus.ts | 2 +- e2e/case/shader-mrt.ts | 2 +- e2e/case/shader-renderState.ts | 2 +- e2e/case/shadow-basic.ts | 2 +- e2e/case/shadow-transparent.ts | 2 +- e2e/case/spriteMask-customStencil.ts | 2 +- e2e/case/text-character-spacing.ts | 11 ++- e2e/case/text-typed.ts | 19 ++-- e2e/case/texture-R8G8.ts | 2 +- e2e/case/texture-hdr-ktx2.ts | 2 +- e2e/case/texture-hdr.ts | 2 +- e2e/case/texture-sRGB-KTX2.ts | 2 +- e2e/case/trailRenderer-basic.ts | 2 +- e2e/case/ui-batch-order.ts | 2 +- examples/src/CSS-DOM.ts | 2 - examples/src/buffer-mesh-independent.ts | 1 - examples/src/buffer-mesh-instance.ts | 2 - examples/src/buffer-mesh-interleaved.ts | 2 - examples/src/device-restore.ts | 2 - examples/src/gltf-loader.ts | 1 - examples/src/gpu-instancing-auto-batch.ts | 1 - examples/src/gpu-instancing-custom-data.ts | 1 - examples/src/paricle-emit-mesh.ts | 10 +-- examples/src/project-loader.ts | 2 - examples/src/screenshot.ts | 1 - examples/src/shader-01-basic-shader.ts | 2 - examples/src/shader-02-render-state.ts | 2 - examples/src/shader-03-ui-script.ts | 2 - examples/src/shader-04-multi-pass.ts | 2 - examples/src/shader-05-advance.ts | 2 - examples/src/ui-batch-massive.ts | 2 - packages/core/src/Canvas.ts | 59 +++++++++---- packages/rhi-webgl/src/WebCanvas.ts | 64 ++++++++++---- packages/rhi-webgl/src/WebGLEngine.ts | 75 +++++----------- tests/src/core/Camera.test.ts | 5 +- tests/src/core/input/InputManager.test.ts | 3 +- tests/src/rhi-webgl/WebGLEngine.test.ts | 86 +++++++++++-------- tests/src/ui/Image.test.ts | 3 +- tests/src/ui/Text.test.ts | 6 +- tests/src/ui/UICanvas.test.ts | 3 +- tests/src/ui/UIEvent.test.ts | 3 +- tests/src/ui/UIGroup.test.ts | 3 +- tests/src/ui/UIInteractive.test.ts | 3 +- tests/src/ui/UITransform.test.ts | 35 ++++---- 117 files changed, 322 insertions(+), 315 deletions(-) diff --git a/e2e/case/.initPostProcessEnv.ts b/e2e/case/.initPostProcessEnv.ts index b4c8bd9ab3..b83c92015a 100644 --- a/e2e/case/.initPostProcessEnv.ts +++ b/e2e/case/.initPostProcessEnv.ts @@ -18,7 +18,7 @@ export async function initPostProcessEnv( shaderCompiler?: ShaderCompiler ) { WebGLEngine.create({ canvas: "canvas", shaderCompiler }).then((engine) => { - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/animator-additive.ts b/e2e/case/animator-additive.ts index 462f8f4f51..e085de94e2 100644 --- a/e2e/case/animator-additive.ts +++ b/e2e/case/animator-additive.ts @@ -20,7 +20,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.resizeByClientSize(2); + engine.canvas.setResolution(2400, 1600); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/animator-blendShape-quantization.ts b/e2e/case/animator-blendShape-quantization.ts index afeed92e4f..5351a962d7 100644 --- a/e2e/case/animator-blendShape-quantization.ts +++ b/e2e/case/animator-blendShape-quantization.ts @@ -19,7 +19,7 @@ WebGLEngine.create({ canvas: "canvas", glTF: { meshOpt: { workerCount: 0 } } }).then(async (engine) => { - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const rootNode = scene.createRootEntity(); scene.ambientLight.diffuseSolidColor.set(1, 1, 1, 1); diff --git a/e2e/case/animator-blendShape.ts b/e2e/case/animator-blendShape.ts index 4baa64029b..2ca8dbee4b 100644 --- a/e2e/case/animator-blendShape.ts +++ b/e2e/case/animator-blendShape.ts @@ -16,7 +16,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.resizeByClientSize(2); + engine.canvas.setResolution(2400, 1600); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/animator-crossfade.ts b/e2e/case/animator-crossfade.ts index 6f96fcdd63..c518e7143c 100644 --- a/e2e/case/animator-crossfade.ts +++ b/e2e/case/animator-crossfade.ts @@ -7,7 +7,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.resizeByClientSize(2); + engine.canvas.setResolution(2400, 1600); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/animator-customAnimationClip.ts b/e2e/case/animator-customAnimationClip.ts index 03c5e787e3..777429b4ed 100644 --- a/e2e/case/animator-customAnimationClip.ts +++ b/e2e/case/animator-customAnimationClip.ts @@ -26,7 +26,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.resizeByClientSize(2); + engine.canvas.setResolution(2400, 1600); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/animator-customBlendShape.ts b/e2e/case/animator-customBlendShape.ts index 673a56b487..59728e30a3 100644 --- a/e2e/case/animator-customBlendShape.ts +++ b/e2e/case/animator-customBlendShape.ts @@ -23,7 +23,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.resizeByClientSize(2); + engine.canvas.setResolution(2400, 1600); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/animator-event.ts b/e2e/case/animator-event.ts index eb83a74734..d6f3510e3d 100644 --- a/e2e/case/animator-event.ts +++ b/e2e/case/animator-event.ts @@ -18,7 +18,7 @@ import { import { initScreenshot, updateForE2E } from "./.mockForE2E"; WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.resizeByClientSize(2); + engine.canvas.setResolution(2400, 1600); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); // camera diff --git a/e2e/case/animator-multiSubMeshBlendShape.ts b/e2e/case/animator-multiSubMeshBlendShape.ts index 5c1365a758..7cbdc084b1 100644 --- a/e2e/case/animator-multiSubMeshBlendShape.ts +++ b/e2e/case/animator-multiSubMeshBlendShape.ts @@ -23,8 +23,10 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.width = window.innerWidth * SystemInfo.devicePixelRatio; - engine.canvas.height = window.innerHeight * SystemInfo.devicePixelRatio; + engine.canvas.setResolution( + window.innerWidth * SystemInfo.devicePixelRatio, + window.innerHeight * SystemInfo.devicePixelRatio + ); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/animator-play-backwards.ts b/e2e/case/animator-play-backwards.ts index 936a370048..7cfee113b4 100644 --- a/e2e/case/animator-play-backwards.ts +++ b/e2e/case/animator-play-backwards.ts @@ -7,7 +7,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.resizeByClientSize(2); + engine.canvas.setResolution(2400, 1600); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/animator-play-beforeActive.ts b/e2e/case/animator-play-beforeActive.ts index 639397d802..71115a4b47 100644 --- a/e2e/case/animator-play-beforeActive.ts +++ b/e2e/case/animator-play-beforeActive.ts @@ -7,7 +7,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.resizeByClientSize(2); + engine.canvas.setResolution(2400, 1600); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/animator-play.ts b/e2e/case/animator-play.ts index c958fca3a8..477cf442cd 100644 --- a/e2e/case/animator-play.ts +++ b/e2e/case/animator-play.ts @@ -7,7 +7,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.resizeByClientSize(2); + engine.canvas.setResolution(2400, 1600); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/animator-reuse.ts b/e2e/case/animator-reuse.ts index 619718b5fe..f89d760602 100644 --- a/e2e/case/animator-reuse.ts +++ b/e2e/case/animator-reuse.ts @@ -17,7 +17,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.resizeByClientSize(2); + engine.canvas.setResolution(2400, 1600); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/animator-stateMachine.ts b/e2e/case/animator-stateMachine.ts index 4f449d8487..32754e7e69 100644 --- a/e2e/case/animator-stateMachine.ts +++ b/e2e/case/animator-stateMachine.ts @@ -18,8 +18,10 @@ import { import { initScreenshot, updateForE2E } from "./.mockForE2E"; WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.width = window.innerWidth * SystemInfo.devicePixelRatio; - engine.canvas.height = window.innerHeight * SystemInfo.devicePixelRatio; + engine.canvas.setResolution( + window.innerWidth * SystemInfo.devicePixelRatio, + window.innerHeight * SystemInfo.devicePixelRatio + ); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/animator-stateMachineScript.ts b/e2e/case/animator-stateMachineScript.ts index 5f6d8d9f26..60134c5c61 100644 --- a/e2e/case/animator-stateMachineScript.ts +++ b/e2e/case/animator-stateMachineScript.ts @@ -20,7 +20,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.resizeByClientSize(2); + engine.canvas.setResolution(2400, 1600); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/camera-fxaa.ts b/e2e/case/camera-fxaa.ts index 67368129f1..4290c6b79c 100644 --- a/e2e/case/camera-fxaa.ts +++ b/e2e/case/camera-fxaa.ts @@ -17,7 +17,7 @@ initPostProcessEnv((camera: Camera) => { const postProcess = globalPostProcessEntity.addComponent(PostProcess); const bloomEffect = postProcess.addEffect(BloomEffect); - bloomEffect.threshold.value = 0.7874122893956174 + bloomEffect.threshold.value = 0.7874122893956174; bloomEffect.intensity.value = 1; const tonemappingEffect = postProcess.addEffect(TonemappingEffect); diff --git a/e2e/case/camera-opaque-texture.ts b/e2e/case/camera-opaque-texture.ts index 7e39623e24..b44426ac5a 100644 --- a/e2e/case/camera-opaque-texture.ts +++ b/e2e/case/camera-opaque-texture.ts @@ -111,7 +111,7 @@ WebGLEngine.create({ canvas: "canvas", shaderCompiler: new ShaderCompiler() }).t } } `); - engine.canvas.resizeByClientSize(2); + engine.canvas.setResolution(2400, 1600); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/camera-ssao.ts b/e2e/case/camera-ssao.ts index f07e1bd6f7..48266cd6d9 100644 --- a/e2e/case/camera-ssao.ts +++ b/e2e/case/camera-ssao.ts @@ -30,7 +30,7 @@ WebGLEngine.create({ shaderCompiler: new ShaderCompiler(), graphicDeviceOptions: { webGLMode: WebGLMode.WebGL1 } }).then((engine) => { - engine.canvas.resizeByClientSize(2); + engine.canvas.setResolution(2400, 1600); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/canvas-transparency.ts b/e2e/case/canvas-transparency.ts index 9836304642..405bfcd2e5 100644 --- a/e2e/case/canvas-transparency.ts +++ b/e2e/case/canvas-transparency.ts @@ -28,7 +28,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas", graphicDeviceOptions: { alpha: false } }).then((engine) => { - engine.canvas.resizeByClientSize(2); + engine.canvas.setResolution(2400, 1600); engine.resourceManager .load([ diff --git a/e2e/case/gltf-blendshape.ts b/e2e/case/gltf-blendshape.ts index 5d4c1bde89..8776b0e38e 100644 --- a/e2e/case/gltf-blendshape.ts +++ b/e2e/case/gltf-blendshape.ts @@ -19,8 +19,10 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.width = window.innerWidth * SystemInfo.devicePixelRatio; - engine.canvas.height = window.innerHeight * SystemInfo.devicePixelRatio; + engine.canvas.setResolution( + window.innerWidth * SystemInfo.devicePixelRatio, + window.innerHeight * SystemInfo.devicePixelRatio + ); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/gltf-meshopt.ts b/e2e/case/gltf-meshopt.ts index 538002ef63..5dd440843e 100644 --- a/e2e/case/gltf-meshopt.ts +++ b/e2e/case/gltf-meshopt.ts @@ -7,7 +7,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.resizeByClientSize(2); + engine.canvas.setResolution(2400, 1600); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/gpu-instancing-auto-batch.ts b/e2e/case/gpu-instancing-auto-batch.ts index e9f80e35ab..1149fcf27a 100644 --- a/e2e/case/gpu-instancing-auto-batch.ts +++ b/e2e/case/gpu-instancing-auto-batch.ts @@ -17,7 +17,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then(async (engine) => { - engine.canvas.resizeByClientSize(2); + engine.canvas.setResolution(2400, 1600); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity("Root"); diff --git a/e2e/case/gpu-instancing-custom-data.ts b/e2e/case/gpu-instancing-custom-data.ts index 1c6f1755dd..4ea1c371ef 100644 --- a/e2e/case/gpu-instancing-custom-data.ts +++ b/e2e/case/gpu-instancing-custom-data.ts @@ -61,7 +61,7 @@ const customInstanceShaderSource = `Shader "CustomInstanceShader" { WebGLEngine.create({ canvas: "canvas", shaderCompiler }).then((engine) => { Shader.create(customInstanceShaderSource); - engine.canvas.resizeByClientSize(2); + engine.canvas.setResolution(2400, 1600); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity("Root"); diff --git a/e2e/case/litePhysics-collision-group.ts b/e2e/case/litePhysics-collision-group.ts index baa610bd47..52f9bdebcc 100644 --- a/e2e/case/litePhysics-collision-group.ts +++ b/e2e/case/litePhysics-collision-group.ts @@ -66,7 +66,7 @@ function createPhysicsSphere( } WebGLEngine.create({ canvas: "canvas", physics: new LitePhysics() }).then((engine) => { - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity("root"); diff --git a/e2e/case/material-LUT.ts b/e2e/case/material-LUT.ts index def9ea9c21..f9357789bf 100644 --- a/e2e/case/material-LUT.ts +++ b/e2e/case/material-LUT.ts @@ -43,7 +43,7 @@ const shaderSource = `Shader "LUT-test" { // Create engine WebGLEngine.create({ canvas: "canvas", shaderCompiler }) .then((engine) => { - engine.canvas.resizeByClientSize(2); + engine.canvas.setResolution(2400, 1600); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/material-blendMode.ts b/e2e/case/material-blendMode.ts index 265c7b76ab..5a476249ff 100644 --- a/e2e/case/material-blendMode.ts +++ b/e2e/case/material-blendMode.ts @@ -7,7 +7,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; // Create engine WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/material-blinn-phong.ts b/e2e/case/material-blinn-phong.ts index 15d93b04cf..8ac4900971 100644 --- a/e2e/case/material-blinn-phong.ts +++ b/e2e/case/material-blinn-phong.ts @@ -18,7 +18,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; // Create engine WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/material-pbr-clearcoat.ts b/e2e/case/material-pbr-clearcoat.ts index d4b8eefac4..cf5697e54e 100644 --- a/e2e/case/material-pbr-clearcoat.ts +++ b/e2e/case/material-pbr-clearcoat.ts @@ -18,7 +18,7 @@ Logger.enable(); // Create engine WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const { background } = scene; diff --git a/e2e/case/material-pbr-specular.ts b/e2e/case/material-pbr-specular.ts index db33c4c99d..529f2631ae 100644 --- a/e2e/case/material-pbr-specular.ts +++ b/e2e/case/material-pbr-specular.ts @@ -21,7 +21,7 @@ Logger.enable(); // Create engine WebGLEngine.create({ canvas: "canvas", shaderCompiler: new ShaderCompiler() }).then((engine) => { - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/material-pbr.ts b/e2e/case/material-pbr.ts index 243c09b10e..fbaefe460c 100644 --- a/e2e/case/material-pbr.ts +++ b/e2e/case/material-pbr.ts @@ -16,7 +16,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; // Create engine WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/material-shader.ts b/e2e/case/material-shader.ts index 7024f323f3..c52ace58e7 100644 --- a/e2e/case/material-shader.ts +++ b/e2e/case/material-shader.ts @@ -67,7 +67,7 @@ function createPlaneMesh(engine: WebGLEngine) { Logger.enable(); WebGLEngine.create({ canvas: "canvas", shaderCompiler }).then((engine) => { - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const shaderMap = { normal: Shader.create(normalShaderSource) diff --git a/e2e/case/material-shaderReplacement.ts b/e2e/case/material-shaderReplacement.ts index 0ad9b0c548..133ba26c0b 100644 --- a/e2e/case/material-shaderReplacement.ts +++ b/e2e/case/material-shaderReplacement.ts @@ -24,7 +24,7 @@ async function main() { // Create engine const engine = await WebGLEngine.create({ canvas: "canvas", shaderCompiler: new ShaderCompiler() }); - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); initShader(); diff --git a/e2e/case/material-unlit.ts b/e2e/case/material-unlit.ts index f1555ae096..251b347fbe 100644 --- a/e2e/case/material-unlit.ts +++ b/e2e/case/material-unlit.ts @@ -8,7 +8,7 @@ Logger.enable(); // Create engine WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.resizeByClientSize(2); + engine.canvas.setResolution(2400, 1600); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/material-white-furnace.ts b/e2e/case/material-white-furnace.ts index 706602cc08..af7a4ad971 100644 --- a/e2e/case/material-white-furnace.ts +++ b/e2e/case/material-white-furnace.ts @@ -20,7 +20,7 @@ import { import { initScreenshot, updateForE2E } from "./.mockForE2E"; WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/multi-camera-no-clear.ts b/e2e/case/multi-camera-no-clear.ts index eef42c9539..603cf90255 100644 --- a/e2e/case/multi-camera-no-clear.ts +++ b/e2e/case/multi-camera-no-clear.ts @@ -29,7 +29,7 @@ WebGLEngine.create({ antialias: false } }).then((engine) => { - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); initFirstScene(engine); engine.run(); diff --git a/e2e/case/multi-scene-clear.ts b/e2e/case/multi-scene-clear.ts index ca0b15b632..3b29f4de96 100644 --- a/e2e/case/multi-scene-clear.ts +++ b/e2e/case/multi-scene-clear.ts @@ -20,7 +20,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const firstCamera = initFirstScene(engine); const secondCamera = initSecondScene(engine); diff --git a/e2e/case/multi-scene-no-clear.ts b/e2e/case/multi-scene-no-clear.ts index dd08bd09de..200738a485 100644 --- a/e2e/case/multi-scene-no-clear.ts +++ b/e2e/case/multi-scene-no-clear.ts @@ -27,7 +27,7 @@ WebGLEngine.create({ webGLMode: WebGLMode.WebGL2 } }).then((engine) => { - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const firstCamera = initFirstScene(engine); const secondCamera = initSecondScene(engine); diff --git a/e2e/case/particleRenderer-burst-cycles.ts b/e2e/case/particleRenderer-burst-cycles.ts index 597e38c7dd..7fb52c4ac9 100644 --- a/e2e/case/particleRenderer-burst-cycles.ts +++ b/e2e/case/particleRenderer-burst-cycles.ts @@ -25,7 +25,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/particleRenderer-customShader.ts b/e2e/case/particleRenderer-customShader.ts index bb7451debd..80a8f1788f 100644 --- a/e2e/case/particleRenderer-customShader.ts +++ b/e2e/case/particleRenderer-customShader.ts @@ -85,7 +85,7 @@ const customParticleShaderSource = `Shader "Test/ParticleCustom" { Logger.enable(); WebGLEngine.create({ canvas: "canvas", shaderCompiler }).then((engine) => { - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/particleRenderer-dream.ts b/e2e/case/particleRenderer-dream.ts index ebc37614e8..737856ea7c 100644 --- a/e2e/case/particleRenderer-dream.ts +++ b/e2e/case/particleRenderer-dream.ts @@ -27,7 +27,7 @@ WebGLEngine.create({ canvas: "canvas" }).then((engine) => { Logger.enable(); - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/particleRenderer-emissive.ts b/e2e/case/particleRenderer-emissive.ts index b1a20a1ad9..8f7f187c11 100644 --- a/e2e/case/particleRenderer-emissive.ts +++ b/e2e/case/particleRenderer-emissive.ts @@ -29,7 +29,7 @@ WebGLEngine.create({ graphicDeviceOptions: { webGLMode: WebGLMode.Auto } }).then((engine) => { Logger.enable(); - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/particleRenderer-emit-billboard-stretched.ts b/e2e/case/particleRenderer-emit-billboard-stretched.ts index c717bac56f..6a48fd5918 100644 --- a/e2e/case/particleRenderer-emit-billboard-stretched.ts +++ b/e2e/case/particleRenderer-emit-billboard-stretched.ts @@ -29,7 +29,7 @@ WebGLEngine.create({ canvas: "canvas" }).then((engine) => { Logger.enable(); - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const rootEntity = engine.sceneManager.activeScene.createRootEntity("Root"); @@ -89,7 +89,8 @@ function createFireParticle(rootEntity: Entity, engine: Engine, texture: Texture const generator = particleRenderer.generator; generator.useAutoRandomSeed = false; - const { main, emission, rotationOverLifetime, textureSheetAnimation, sizeOverLifetime, colorOverLifetime } = generator; + const { main, emission, rotationOverLifetime, textureSheetAnimation, sizeOverLifetime, colorOverLifetime } = + generator; // Main module const { startLifetime, startSpeed, startSize, startRotationZ } = main; @@ -99,8 +100,6 @@ function createFireParticle(rootEntity: Entity, engine: Engine, texture: Texture main.simulationSpace = ParticleSimulationSpace.Local; // main.startRotation3D = true; main.startRotationZ.constant = 0; - - const coneShape = new ConeShape(); coneShape.angle = 0.01; diff --git a/e2e/case/particleRenderer-emit-mesh-cone-scale-3d-rotation-life-seperate.ts b/e2e/case/particleRenderer-emit-mesh-cone-scale-3d-rotation-life-seperate.ts index d8b3638f92..f130ad56ff 100644 --- a/e2e/case/particleRenderer-emit-mesh-cone-scale-3d-rotation-life-seperate.ts +++ b/e2e/case/particleRenderer-emit-mesh-cone-scale-3d-rotation-life-seperate.ts @@ -29,7 +29,7 @@ WebGLEngine.create({ canvas: "canvas" }).then((engine) => { Logger.enable(); - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const rootEntity = engine.sceneManager.activeScene.createRootEntity("Root"); diff --git a/e2e/case/particleRenderer-emit-mesh-cone-scale-3d-rotation.ts b/e2e/case/particleRenderer-emit-mesh-cone-scale-3d-rotation.ts index 3446cdbf1a..bd33ad9655 100644 --- a/e2e/case/particleRenderer-emit-mesh-cone-scale-3d-rotation.ts +++ b/e2e/case/particleRenderer-emit-mesh-cone-scale-3d-rotation.ts @@ -29,7 +29,7 @@ WebGLEngine.create({ canvas: "canvas" }).then((engine) => { Logger.enable(); - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const rootEntity = engine.sceneManager.activeScene.createRootEntity("Root"); @@ -123,4 +123,4 @@ function createFireParticle(rootEntity: Entity, engine: Engine, texture: Texture emission.shape = coneShape; return particleEntity; -} \ No newline at end of file +} diff --git a/e2e/case/particleRenderer-emit-mesh-cone-scale-rotation-life-seperate.ts b/e2e/case/particleRenderer-emit-mesh-cone-scale-rotation-life-seperate.ts index 3e65929bb8..228d3df273 100644 --- a/e2e/case/particleRenderer-emit-mesh-cone-scale-rotation-life-seperate.ts +++ b/e2e/case/particleRenderer-emit-mesh-cone-scale-rotation-life-seperate.ts @@ -29,7 +29,7 @@ WebGLEngine.create({ canvas: "canvas" }).then((engine) => { Logger.enable(); - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const rootEntity = engine.sceneManager.activeScene.createRootEntity("Root"); @@ -92,7 +92,8 @@ function createFireParticle(rootEntity: Entity, engine: Engine, texture: Texture const generator = particleRenderer.generator; generator.useAutoRandomSeed = false; - const { main, emission, rotationOverLifetime, textureSheetAnimation, sizeOverLifetime, colorOverLifetime } = generator; + const { main, emission, rotationOverLifetime, textureSheetAnimation, sizeOverLifetime, colorOverLifetime } = + generator; // Main module const { startLifetime, startSpeed, startSize, startRotationZ } = main; @@ -127,4 +128,4 @@ function createFireParticle(rootEntity: Entity, engine: Engine, texture: Texture emission.shape = coneShape; return particleEntity; -} \ No newline at end of file +} diff --git a/e2e/case/particleRenderer-emit-mesh-cone-scale-rotation-life.ts b/e2e/case/particleRenderer-emit-mesh-cone-scale-rotation-life.ts index 8e7a3b355c..3f6bafb491 100644 --- a/e2e/case/particleRenderer-emit-mesh-cone-scale-rotation-life.ts +++ b/e2e/case/particleRenderer-emit-mesh-cone-scale-rotation-life.ts @@ -29,7 +29,7 @@ WebGLEngine.create({ canvas: "canvas" }).then((engine) => { Logger.enable(); - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const rootEntity = engine.sceneManager.activeScene.createRootEntity("Root"); @@ -92,7 +92,8 @@ function createFireParticle(rootEntity: Entity, engine: Engine, texture: Texture const generator = particleRenderer.generator; generator.useAutoRandomSeed = false; - const { main, emission, rotationOverLifetime, textureSheetAnimation, sizeOverLifetime, colorOverLifetime } = generator; + const { main, emission, rotationOverLifetime, textureSheetAnimation, sizeOverLifetime, colorOverLifetime } = + generator; // Main module const { startLifetime, startSpeed, startSize, startRotationZ } = main; @@ -126,4 +127,4 @@ function createFireParticle(rootEntity: Entity, engine: Engine, texture: Texture emission.shape = coneShape; return particleEntity; -} \ No newline at end of file +} diff --git a/e2e/case/particleRenderer-emit-mesh-cone-scale-rotation-world.ts b/e2e/case/particleRenderer-emit-mesh-cone-scale-rotation-world.ts index 090b9179f1..a04c6aea39 100644 --- a/e2e/case/particleRenderer-emit-mesh-cone-scale-rotation-world.ts +++ b/e2e/case/particleRenderer-emit-mesh-cone-scale-rotation-world.ts @@ -29,7 +29,7 @@ WebGLEngine.create({ canvas: "canvas" }).then((engine) => { Logger.enable(); - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const rootEntity = engine.sceneManager.activeScene.createRootEntity("Root"); @@ -67,7 +67,7 @@ WebGLEngine.create({ }); function createFireParticle(rootEntity: Entity, engine: Engine, texture: Texture2D, glTFModel: GLTFResource): Entity { - const meshRenderer = rootEntity.createChild("Mesh").addComponent(MeshRenderer); + const meshRenderer = rootEntity.createChild("Mesh").addComponent(MeshRenderer); meshRenderer.mesh = PrimitiveMesh.createCylinder(engine); const unlitMaterial = new UnlitMaterial(engine); unlitMaterial.baseTexture = glTFModel.textures[0]; @@ -75,8 +75,7 @@ function createFireParticle(rootEntity: Entity, engine: Engine, texture: Texture const particleEntity = new Entity(engine, "EmitMeshParticle"); particleEntity.transform.rotation.set(30, -45, 30); - particleEntity.transform.scale.set(20,1,20); - + particleEntity.transform.scale.set(20, 1, 20); const particleRenderer = particleEntity.addComponent(ParticleRenderer); @@ -84,7 +83,7 @@ function createFireParticle(rootEntity: Entity, engine: Engine, texture: Texture material.baseColor = new Color(1.0, 1.0, 1.0, 1.0); // material.blendMode = BlendMode.Additive; material.baseTexture = glTFModel.textures[0]; - material.baseTexture.anisoLevel=12; + material.baseTexture.anisoLevel = 12; particleRenderer.setMaterial(material); particleRenderer.renderMode = ParticleRenderMode.Mesh; @@ -97,11 +96,11 @@ function createFireParticle(rootEntity: Entity, engine: Engine, texture: Texture // Main module const { startLifetime, startSpeed, startSize, startRotationZ } = main; - startSpeed.constant =1.0; + startSpeed.constant = 1.0; startSize.constant = 0.2; main.simulationSpace = ParticleSimulationSpace.World; - main.startRotationZ.constant=-90; + main.startRotationZ.constant = -90; // startLifetime.constantMin = 0.2; // startLifetime.constantMax = 0.8; // startLifetime.mode = ParticleCurveMode.TwoConstants; @@ -115,7 +114,7 @@ function createFireParticle(rootEntity: Entity, engine: Engine, texture: Texture // startSize.mode = ParticleCurveMode.TwoConstants; const coneShape = new ConeShape(); - coneShape.angle=0.01; + coneShape.angle = 0.01; emission.shape = coneShape; return particleEntity; diff --git a/e2e/case/particleRenderer-emit-mesh-cone-scale-rotation.ts b/e2e/case/particleRenderer-emit-mesh-cone-scale-rotation.ts index 6656fd5bed..8840dbb501 100644 --- a/e2e/case/particleRenderer-emit-mesh-cone-scale-rotation.ts +++ b/e2e/case/particleRenderer-emit-mesh-cone-scale-rotation.ts @@ -29,7 +29,7 @@ WebGLEngine.create({ canvas: "canvas" }).then((engine) => { Logger.enable(); - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const rootEntity = engine.sceneManager.activeScene.createRootEntity("Root"); @@ -75,7 +75,7 @@ function createFireParticle(rootEntity: Entity, engine: Engine, texture: Texture const particleEntity = new Entity(engine, "EmitMeshParticle"); particleEntity.transform.rotation.set(220, -20, 0); - particleEntity.transform.scale.set(10,1,1); + particleEntity.transform.scale.set(10, 1, 1); // var r=0; // setInterval(() => { @@ -89,7 +89,7 @@ function createFireParticle(rootEntity: Entity, engine: Engine, texture: Texture material.baseColor = new Color(1.0, 1.0, 1.0, 1.0); // material.blendMode = BlendMode.Additive; material.baseTexture = glTFModel.textures[0]; - material.baseTexture.anisoLevel=12; + material.baseTexture.anisoLevel = 12; particleRenderer.setMaterial(material); particleRenderer.renderMode = ParticleRenderMode.Mesh; @@ -102,11 +102,11 @@ function createFireParticle(rootEntity: Entity, engine: Engine, texture: Texture // Main module const { startLifetime, startSpeed, startSize, startRotationZ } = main; - startSpeed.constant =1.0; + startSpeed.constant = 1.0; startSize.constant = 0.2; main.simulationSpace = ParticleSimulationSpace.Local; - main.startRotationZ.constant=-45; + main.startRotationZ.constant = -45; // startLifetime.constantMin = 0.2; // startLifetime.constantMax = 0.8; // startLifetime.mode = ParticleCurveMode.TwoConstants; @@ -120,7 +120,7 @@ function createFireParticle(rootEntity: Entity, engine: Engine, texture: Texture // startSize.mode = ParticleCurveMode.TwoConstants; const coneShape = new ConeShape(); - coneShape.angle=0.01; + coneShape.angle = 0.01; emission.shape = coneShape; return particleEntity; diff --git a/e2e/case/particleRenderer-emit-mesh-cone.ts b/e2e/case/particleRenderer-emit-mesh-cone.ts index 6954fff204..5badd4c0d0 100644 --- a/e2e/case/particleRenderer-emit-mesh-cone.ts +++ b/e2e/case/particleRenderer-emit-mesh-cone.ts @@ -41,7 +41,7 @@ WebGLEngine.create({ canvas: "canvas" }).then((engine) => { Logger.enable(); - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const rootEntity = engine.sceneManager.activeScene.createRootEntity("Root"); diff --git a/e2e/case/particleRenderer-emit-mesh-no-shape-world.ts b/e2e/case/particleRenderer-emit-mesh-no-shape-world.ts index 28c8e2edd0..4e98220d0b 100644 --- a/e2e/case/particleRenderer-emit-mesh-no-shape-world.ts +++ b/e2e/case/particleRenderer-emit-mesh-no-shape-world.ts @@ -28,7 +28,7 @@ WebGLEngine.create({ canvas: "canvas" }).then((engine) => { Logger.enable(); - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const rootEntity = engine.sceneManager.activeScene.createRootEntity("Root"); diff --git a/e2e/case/particleRenderer-emit-mesh-no-shape.ts b/e2e/case/particleRenderer-emit-mesh-no-shape.ts index e80c228f94..460f587f96 100644 --- a/e2e/case/particleRenderer-emit-mesh-no-shape.ts +++ b/e2e/case/particleRenderer-emit-mesh-no-shape.ts @@ -28,7 +28,7 @@ WebGLEngine.create({ canvas: "canvas" }).then((engine) => { Logger.enable(); - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const rootEntity = engine.sceneManager.activeScene.createRootEntity("Root"); diff --git a/e2e/case/particleRenderer-emit-mesh-rotation-life-curve.ts b/e2e/case/particleRenderer-emit-mesh-rotation-life-curve.ts index c26405a3ff..03090dd034 100644 --- a/e2e/case/particleRenderer-emit-mesh-rotation-life-curve.ts +++ b/e2e/case/particleRenderer-emit-mesh-rotation-life-curve.ts @@ -32,7 +32,7 @@ type Mode = "constant" | "curve"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; scene.background.mode = BackgroundMode.SolidColor; diff --git a/e2e/case/particleRenderer-fire.ts b/e2e/case/particleRenderer-fire.ts index d1b0c56899..87c8b00eeb 100644 --- a/e2e/case/particleRenderer-fire.ts +++ b/e2e/case/particleRenderer-fire.ts @@ -36,7 +36,7 @@ WebGLEngine.create({ canvas: "canvas" }).then((engine) => { Logger.enable(); - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/particleRenderer-force.ts b/e2e/case/particleRenderer-force.ts index 553007a2ce..2a9d97095f 100644 --- a/e2e/case/particleRenderer-force.ts +++ b/e2e/case/particleRenderer-force.ts @@ -31,7 +31,7 @@ WebGLEngine.create({ canvas: "canvas" }).then((engine) => { Logger.enable(); - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const leftScene = engine.sceneManager.activeScene; const leftRootEntity = leftScene.createRootEntity(); diff --git a/e2e/case/particleRenderer-horizontal-billboard.ts b/e2e/case/particleRenderer-horizontal-billboard.ts index f05a3cffea..7b4bc4c396 100644 --- a/e2e/case/particleRenderer-horizontal-billboard.ts +++ b/e2e/case/particleRenderer-horizontal-billboard.ts @@ -20,7 +20,7 @@ WebGLEngine.create({ canvas: "canvas" }).then((engine) => { Logger.enable(); - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const rootEntity = engine.sceneManager.activeScene.createRootEntity("Root"); diff --git a/e2e/case/particleRenderer-limitVelocity.ts b/e2e/case/particleRenderer-limitVelocity.ts index 87b5d53044..dd37e2abe3 100644 --- a/e2e/case/particleRenderer-limitVelocity.ts +++ b/e2e/case/particleRenderer-limitVelocity.ts @@ -28,7 +28,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/particleRenderer-noise.ts b/e2e/case/particleRenderer-noise.ts index 2f4c9220c6..685ce96df6 100644 --- a/e2e/case/particleRenderer-noise.ts +++ b/e2e/case/particleRenderer-noise.ts @@ -24,7 +24,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/particleRenderer-rateOverDistance.ts b/e2e/case/particleRenderer-rateOverDistance.ts index 8d36aa1a2e..fdb941c730 100644 --- a/e2e/case/particleRenderer-rateOverDistance.ts +++ b/e2e/case/particleRenderer-rateOverDistance.ts @@ -23,7 +23,7 @@ import { import { initScreenshot, updateForE2E } from "./.mockForE2E"; WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/particleRenderer-shape-mesh.ts b/e2e/case/particleRenderer-shape-mesh.ts index f44fe1deb8..4b82ae5a1d 100644 --- a/e2e/case/particleRenderer-shape-mesh.ts +++ b/e2e/case/particleRenderer-shape-mesh.ts @@ -29,7 +29,7 @@ WebGLEngine.create({ graphicDeviceOptions: { webGLMode: WebGLMode.Auto } }).then((engine) => { Logger.enable(); - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/particleRenderer-shape-transform.ts b/e2e/case/particleRenderer-shape-transform.ts index 9fa0c2417e..73c2b20df2 100644 --- a/e2e/case/particleRenderer-shape-transform.ts +++ b/e2e/case/particleRenderer-shape-transform.ts @@ -22,7 +22,7 @@ WebGLEngine.create({ canvas: "canvas" }).then((engine) => { Logger.enable(); - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const rootEntity = engine.sceneManager.activeScene.createRootEntity("Root"); diff --git a/e2e/case/particleRenderer-textureSheetAnimation.ts b/e2e/case/particleRenderer-textureSheetAnimation.ts index 23504f6c53..606d94f5cf 100644 --- a/e2e/case/particleRenderer-textureSheetAnimation.ts +++ b/e2e/case/particleRenderer-textureSheetAnimation.ts @@ -24,7 +24,7 @@ WebGLEngine.create({ canvas: "canvas" }).then((engine) => { Logger.enable(); - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const ambientLight = scene.ambientLight; diff --git a/e2e/case/physx-collision-group.ts b/e2e/case/physx-collision-group.ts index 96ed4c7441..bbcdc7314e 100644 --- a/e2e/case/physx-collision-group.ts +++ b/e2e/case/physx-collision-group.ts @@ -72,7 +72,7 @@ function createPhysicsSphere( } WebGLEngine.create({ canvas: "canvas", physics: new PhysXPhysics() }).then((engine) => { - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity("root"); diff --git a/e2e/case/physx-collision.ts b/e2e/case/physx-collision.ts index 4700cd0fcb..68cc19f58e 100644 --- a/e2e/case/physx-collision.ts +++ b/e2e/case/physx-collision.ts @@ -57,7 +57,7 @@ function addBox(rootEntity: Entity, cubeSize: number, x: number, y: number, z: n } WebGLEngine.create({ canvas: "canvas", physics: new PhysXPhysics() }).then((engine) => { - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity("root"); diff --git a/e2e/case/physx-customUrl.ts b/e2e/case/physx-customUrl.ts index cf31ecda23..a416c608cc 100644 --- a/e2e/case/physx-customUrl.ts +++ b/e2e/case/physx-customUrl.ts @@ -62,7 +62,7 @@ WebGLEngine.create({ wasmModeUrl: "../physx.release.js" }) }).then((engine) => { - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity("root"); diff --git a/e2e/case/physx-deferred-contact.ts b/e2e/case/physx-deferred-contact.ts index 2c661017ca..4f5d27ea59 100644 --- a/e2e/case/physx-deferred-contact.ts +++ b/e2e/case/physx-deferred-contact.ts @@ -18,7 +18,7 @@ import { PhysXPhysics } from "@galacean/engine-physics-physx"; import { initScreenshot, updateForE2E } from "./.mockForE2E"; WebGLEngine.create({ canvas: "canvas", physics: new PhysXPhysics() }).then((engine) => { - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity("root"); diff --git a/e2e/case/physx-mesh-collider-data.ts b/e2e/case/physx-mesh-collider-data.ts index ef5cd99b58..6e0a2e252d 100644 --- a/e2e/case/physx-mesh-collider-data.ts +++ b/e2e/case/physx-mesh-collider-data.ts @@ -219,7 +219,7 @@ class CollisionScript extends Script { } WebGLEngine.create({ canvas: "canvas", physics: new PhysXPhysics() }).then((engine) => { - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity("root"); diff --git a/e2e/case/physx-mesh-collider.ts b/e2e/case/physx-mesh-collider.ts index c5f8d6cd66..f99b2e2079 100644 --- a/e2e/case/physx-mesh-collider.ts +++ b/e2e/case/physx-mesh-collider.ts @@ -57,7 +57,7 @@ function createFallingBox( const physics = new PhysXPhysics(PhysXRuntimeMode.WebAssemblySIMD); WebGLEngine.create({ canvas: "canvas", physics }).then((engine) => { - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity("root"); diff --git a/e2e/case/primitive-capsule.ts b/e2e/case/primitive-capsule.ts index 5e561d622e..ce897be332 100644 --- a/e2e/case/primitive-capsule.ts +++ b/e2e/case/primitive-capsule.ts @@ -20,7 +20,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.resizeByClientSize(2); + engine.canvas.setResolution(2400, 1600); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/primitive-cone.ts b/e2e/case/primitive-cone.ts index 3e4be8256c..6aac691615 100644 --- a/e2e/case/primitive-cone.ts +++ b/e2e/case/primitive-cone.ts @@ -20,7 +20,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.resizeByClientSize(2); + engine.canvas.setResolution(2400, 1600); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/primitive-cuboid.ts b/e2e/case/primitive-cuboid.ts index 8c4e21f926..d25c152700 100644 --- a/e2e/case/primitive-cuboid.ts +++ b/e2e/case/primitive-cuboid.ts @@ -20,7 +20,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.resizeByClientSize(2); + engine.canvas.setResolution(2400, 1600); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/primitive-cylinder.ts b/e2e/case/primitive-cylinder.ts index c0ab21691a..7ded075f44 100644 --- a/e2e/case/primitive-cylinder.ts +++ b/e2e/case/primitive-cylinder.ts @@ -20,7 +20,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.resizeByClientSize(2); + engine.canvas.setResolution(2400, 1600); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/primitive-plane.ts b/e2e/case/primitive-plane.ts index 8d50906492..d7c0aeac7e 100644 --- a/e2e/case/primitive-plane.ts +++ b/e2e/case/primitive-plane.ts @@ -20,7 +20,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.resizeByClientSize(2); + engine.canvas.setResolution(2400, 1600); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/primitive-sphere.ts b/e2e/case/primitive-sphere.ts index 162a962391..1bc3fb7783 100644 --- a/e2e/case/primitive-sphere.ts +++ b/e2e/case/primitive-sphere.ts @@ -20,7 +20,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.resizeByClientSize(2); + engine.canvas.setResolution(2400, 1600); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/primitive-torus.ts b/e2e/case/primitive-torus.ts index 90aa3a76ba..d6ddeaedde 100644 --- a/e2e/case/primitive-torus.ts +++ b/e2e/case/primitive-torus.ts @@ -20,7 +20,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.resizeByClientSize(2); + engine.canvas.setResolution(2400, 1600); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/shader-mrt.ts b/e2e/case/shader-mrt.ts index 45a732e6ed..2e05503b8b 100644 --- a/e2e/case/shader-mrt.ts +++ b/e2e/case/shader-mrt.ts @@ -83,7 +83,7 @@ const shaderSource = `Shader "/custom.shader" { Logger.enable(); WebGLEngine.create({ canvas: "canvas", shaderCompiler }).then((engine) => { - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const shader = Shader.create(shaderSource); const scene = engine.sceneManager.activeScene; diff --git a/e2e/case/shader-renderState.ts b/e2e/case/shader-renderState.ts index f55e95d3de..c696301265 100644 --- a/e2e/case/shader-renderState.ts +++ b/e2e/case/shader-renderState.ts @@ -85,7 +85,7 @@ const shaderSource = `Shader "Test RenderState" { Logger.enable(); WebGLEngine.create({ canvas: "canvas", shaderCompiler }).then((engine) => { - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const shader = Shader.create(shaderSource); const scene = engine.sceneManager.activeScene; diff --git a/e2e/case/shadow-basic.ts b/e2e/case/shadow-basic.ts index d801ba02ae..380517d237 100644 --- a/e2e/case/shadow-basic.ts +++ b/e2e/case/shadow-basic.ts @@ -19,7 +19,7 @@ import { import { initScreenshot, updateForE2E } from "./.mockForE2E"; WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); scene.shadowResolution = ShadowResolution.Medium; diff --git a/e2e/case/shadow-transparent.ts b/e2e/case/shadow-transparent.ts index 4b7c5b3c98..33d0a11f87 100644 --- a/e2e/case/shadow-transparent.ts +++ b/e2e/case/shadow-transparent.ts @@ -26,7 +26,7 @@ WebGLEngine.create({ webGLMode: WebGLMode.WebGL2 } }).then((engine) => { - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); scene.shadowResolution = ShadowResolution.Medium; diff --git a/e2e/case/spriteMask-customStencil.ts b/e2e/case/spriteMask-customStencil.ts index 2056ae66f2..eaeb317fee 100644 --- a/e2e/case/spriteMask-customStencil.ts +++ b/e2e/case/spriteMask-customStencil.ts @@ -109,7 +109,7 @@ const customStencilShaderSource = `Shader "CustomStencilSprite" { // Create engine WebGLEngine.create({ canvas: "canvas", shaderCompiler: new ShaderCompiler() }).then((engine) => { - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); // Register the custom stencil shader once. const customStencilShader = Shader.create(customStencilShaderSource); diff --git a/e2e/case/text-character-spacing.ts b/e2e/case/text-character-spacing.ts index 5db306f671..8516a86b58 100644 --- a/e2e/case/text-character-spacing.ts +++ b/e2e/case/text-character-spacing.ts @@ -3,11 +3,18 @@ * @category Text */ -import { Camera, TextHorizontalAlignment, TextRenderer, TextVerticalAlignment, Vector3, WebGLEngine } from "@galacean/engine"; +import { + Camera, + TextHorizontalAlignment, + TextRenderer, + TextVerticalAlignment, + Vector3, + WebGLEngine +} from "@galacean/engine"; import { initScreenshot, updateForE2E } from "./.mockForE2E"; WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/text-typed.ts b/e2e/case/text-typed.ts index c900dfec60..d3286f716e 100644 --- a/e2e/case/text-typed.ts +++ b/e2e/case/text-typed.ts @@ -19,7 +19,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); @@ -78,10 +78,15 @@ WebGLEngine.create({ canvas: "canvas" }).then((engine) => { } } - engine.resourceManager.load({ url: "https://mdn.alipayobjects.com/oasis_be/afts/file/A*1Bo5To15x0oAAAAAC_AAAAgAekp5AQ/钟齐志莽行书.json", type: AssetType.Font }).then((font) => { - const typedText = entity.addComponent(TypedText); - typedText.play(textRenderer, "我这一生,走过许多地方的桥儿"); - typedText.font = font; - updateForE2E(engine, 100, 100); - }); + engine.resourceManager + .load({ + url: "https://mdn.alipayobjects.com/oasis_be/afts/file/A*1Bo5To15x0oAAAAAC_AAAAgAekp5AQ/钟齐志莽行书.json", + type: AssetType.Font + }) + .then((font) => { + const typedText = entity.addComponent(TypedText); + typedText.play(textRenderer, "我这一生,走过许多地方的桥儿"); + typedText.font = font; + updateForE2E(engine, 100, 100); + }); }); diff --git a/e2e/case/texture-R8G8.ts b/e2e/case/texture-R8G8.ts index 1e33c73126..fcf496f6c0 100644 --- a/e2e/case/texture-R8G8.ts +++ b/e2e/case/texture-R8G8.ts @@ -16,7 +16,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.resizeByClientSize(2); + engine.canvas.setResolution(2400, 1600); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/texture-hdr-ktx2.ts b/e2e/case/texture-hdr-ktx2.ts index 5fb24d1a12..d2773f2ed8 100644 --- a/e2e/case/texture-hdr-ktx2.ts +++ b/e2e/case/texture-hdr-ktx2.ts @@ -55,7 +55,7 @@ const shaderSource = `Shader "test-hdr" { }`; WebGLEngine.create({ canvas: "canvas", shaderCompiler, ktx2Loader: { workerCount: 0 } }).then((engine) => { - engine.canvas.resizeByClientSize(2); + engine.canvas.setResolution(2400, 1600); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/texture-hdr.ts b/e2e/case/texture-hdr.ts index 7d78e5b71d..6cb26564f3 100644 --- a/e2e/case/texture-hdr.ts +++ b/e2e/case/texture-hdr.ts @@ -18,7 +18,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.resizeByClientSize(2); + engine.canvas.setResolution(2400, 1600); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/texture-sRGB-KTX2.ts b/e2e/case/texture-sRGB-KTX2.ts index 93b5c94ad6..fea0311f29 100644 --- a/e2e/case/texture-sRGB-KTX2.ts +++ b/e2e/case/texture-sRGB-KTX2.ts @@ -15,7 +15,7 @@ import { import { initScreenshot, updateForE2E } from "./.mockForE2E"; WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.resizeByClientSize(2); + engine.canvas.setResolution(2400, 1600); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/trailRenderer-basic.ts b/e2e/case/trailRenderer-basic.ts index 01955223bf..38a6eb7839 100644 --- a/e2e/case/trailRenderer-basic.ts +++ b/e2e/case/trailRenderer-basic.ts @@ -168,7 +168,7 @@ WebGLEngine.create({ canvas: "canvas" }).then((engine) => { Logger.enable(); - engine.canvas.resizeByClientSize(); + engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/ui-batch-order.ts b/e2e/case/ui-batch-order.ts index a0d234004f..4942211b4d 100644 --- a/e2e/case/ui-batch-order.ts +++ b/e2e/case/ui-batch-order.ts @@ -16,7 +16,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; registerGUI(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.resizeByClientSize(2); + engine.canvas.setResolution(2400, 1600); const scene = engine.sceneManager.activeScene; const root = scene.createRootEntity("root"); diff --git a/examples/src/CSS-DOM.ts b/examples/src/CSS-DOM.ts index c06dd346c6..192b998265 100644 --- a/examples/src/CSS-DOM.ts +++ b/examples/src/CSS-DOM.ts @@ -25,8 +25,6 @@ async function main() { graphicDeviceOptions: { webGLMode: WebGLMode.Auto } }); - engine.canvas.resizeByClientSize(); - // Create root entity const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/examples/src/buffer-mesh-independent.ts b/examples/src/buffer-mesh-independent.ts index e1a35d893c..dc67adc56d 100644 --- a/examples/src/buffer-mesh-independent.ts +++ b/examples/src/buffer-mesh-independent.ts @@ -65,7 +65,6 @@ main(); async function main() { // Create engine and get root entity. const engine = await WebGLEngine.create({ canvas: "canvas" }); - engine.canvas.resizeByClientSize(); const rootEntity = engine.sceneManager.activeScene.createRootEntity("Root"); diff --git a/examples/src/buffer-mesh-instance.ts b/examples/src/buffer-mesh-instance.ts index ac64da53be..1783099d5a 100644 --- a/examples/src/buffer-mesh-instance.ts +++ b/examples/src/buffer-mesh-instance.ts @@ -25,8 +25,6 @@ import { ShaderCompiler } from "@galacean/engine-shader-compiler"; // Create engine WebGLEngine.create({ canvas: "canvas", shaderCompiler: new ShaderCompiler() }).then((engine) => { - engine.canvas.resizeByClientSize(); - // Get scene and root entity const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity("Root"); diff --git a/examples/src/buffer-mesh-interleaved.ts b/examples/src/buffer-mesh-interleaved.ts index 15061d6904..9685498bee 100644 --- a/examples/src/buffer-mesh-interleaved.ts +++ b/examples/src/buffer-mesh-interleaved.ts @@ -23,8 +23,6 @@ import { // Create engine and get root entity WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.resizeByClientSize(); - const rootEntity = engine.sceneManager.activeScene.createRootEntity("Root"); // Create light. diff --git a/examples/src/device-restore.ts b/examples/src/device-restore.ts index 102ce1f7b9..9a278160c9 100644 --- a/examples/src/device-restore.ts +++ b/examples/src/device-restore.ts @@ -6,8 +6,6 @@ import { Animator, Camera, Color, GLTFResource, Vector3, WebGLEngine } from "@galacean/engine"; WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.enableAutoResize(); - const rootEntity = engine.sceneManager.activeScene.createRootEntity(); rootEntity.scene.background.solidColor = new Color(0.39, 0.31, 0.55, 1.0); diff --git a/examples/src/gltf-loader.ts b/examples/src/gltf-loader.ts index ae54a6d198..40f4fac362 100644 --- a/examples/src/gltf-loader.ts +++ b/examples/src/gltf-loader.ts @@ -186,7 +186,6 @@ class Oasis { } initScene() { - this.engine.canvas.resizeByClientSize(); this.controler.minDistance = 0; // debug sync diff --git a/examples/src/gpu-instancing-auto-batch.ts b/examples/src/gpu-instancing-auto-batch.ts index 3bf57fc429..ce27044161 100644 --- a/examples/src/gpu-instancing-auto-batch.ts +++ b/examples/src/gpu-instancing-auto-batch.ts @@ -126,7 +126,6 @@ WebGLEngine.create({ shaderCompiler }).then(async (engine) => { Shader.create(customInstanceShaderSource); - engine.canvas.resizeByClientSize(); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity("Root"); diff --git a/examples/src/gpu-instancing-custom-data.ts b/examples/src/gpu-instancing-custom-data.ts index eb7c9e8917..52b0ea89a3 100644 --- a/examples/src/gpu-instancing-custom-data.ts +++ b/examples/src/gpu-instancing-custom-data.ts @@ -111,7 +111,6 @@ WebGLEngine.create({ shaderCompiler }).then((engine) => { Shader.create(customInstanceShaderSource); - engine.canvas.resizeByClientSize(); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity("Root"); diff --git a/examples/src/paricle-emit-mesh.ts b/examples/src/paricle-emit-mesh.ts index 730e316257..1986ca4912 100644 --- a/examples/src/paricle-emit-mesh.ts +++ b/examples/src/paricle-emit-mesh.ts @@ -56,7 +56,8 @@ function createFireParticle(rootEntity: Entity, engine: Engine, texture: Texture const generator = particleRenderer.generator; generator.useAutoRandomSeed = false; - const { main, emission, rotationOverLifetime, textureSheetAnimation, sizeOverLifetime, colorOverLifetime } = generator; + const { main, emission, rotationOverLifetime, textureSheetAnimation, sizeOverLifetime, colorOverLifetime } = + generator; // Main module const { startLifetime, startSpeed, startSize, startRotationZ } = main; @@ -65,13 +66,12 @@ function createFireParticle(rootEntity: Entity, engine: Engine, texture: Texture main.simulationSpace = ParticleSimulationSpace.Local; main.startRotation3D = true; - main.startRotationX.constant = -40; + main.startRotationX.constant = -40; main.startRotationZ.constant = 0; - + // main.startRotationY.constant = 40; // main.startRotationZ.constant = -70; - rotationOverLifetime.enabled = true; rotationOverLifetime.separateAxes = true; rotationOverLifetime.rotationX.constant = 100; @@ -94,7 +94,6 @@ function createFireParticle(rootEntity: Entity, engine: Engine, texture: Texture // coneShape.angle = 0.01; // emission.shape = coneShape; - const coneShape = new ConeShape(); coneShape.angle = 0.01; emission.shape = coneShape; @@ -105,7 +104,6 @@ function createFireParticle(rootEntity: Entity, engine: Engine, texture: Texture async function main() { // Create engine and get root entity const engine = await WebGLEngine.create({ canvas: "canvas" }); - engine.canvas.resizeByClientSize(); const rootEntity = engine.sceneManager.activeScene.createRootEntity("Root"); diff --git a/examples/src/project-loader.ts b/examples/src/project-loader.ts index 49c49400ca..127697bbd4 100644 --- a/examples/src/project-loader.ts +++ b/examples/src/project-loader.ts @@ -13,8 +13,6 @@ WebGLEngine.create({ canvas: document.getElementById("canvas") as HTMLCanvasElement, shaderCompiler }).then((engine) => { - engine.canvas.resizeByClientSize(); - engine.resourceManager .load({ url: "https://mdn.alipayobjects.com/oasis_be/afts/file/A*aE8_QotPNL4AAAAAQNAAAAgAekp5AQ/project.json", diff --git a/examples/src/screenshot.ts b/examples/src/screenshot.ts index 795241fd16..f60c81f0db 100644 --- a/examples/src/screenshot.ts +++ b/examples/src/screenshot.ts @@ -21,7 +21,6 @@ import { const gui = new dat.GUI(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.resizeByClientSize(); const scene = engine.sceneManager.activeScene; scene.background.solidColor.set(0, 0, 0, 0); const rootEntity = scene.createRootEntity(); diff --git a/examples/src/shader-01-basic-shader.ts b/examples/src/shader-01-basic-shader.ts index 34c001ca85..f92c1e9cea 100644 --- a/examples/src/shader-01-basic-shader.ts +++ b/examples/src/shader-01-basic-shader.ts @@ -113,8 +113,6 @@ function createTexture(engine: Engine): Promise { // 主程序 WebGLEngine.create({ canvas: "canvas", shaderCompiler: new ShaderCompiler() }).then(async (engine) => { - engine.canvas.resizeByClientSize(); - // 创建场景和相机 const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/examples/src/shader-02-render-state.ts b/examples/src/shader-02-render-state.ts index 464a1d38db..4a7538c064 100644 --- a/examples/src/shader-02-render-state.ts +++ b/examples/src/shader-02-render-state.ts @@ -121,8 +121,6 @@ function createPlane(engine: Engine, name: string, position: Vector3): Entity { // 主程序 Logger.enable(); WebGLEngine.create({ canvas: "canvas", shaderCompiler: new ShaderCompiler() }).then((engine) => { - engine.canvas.resizeByClientSize(); - // 创建场景和相机 const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/examples/src/shader-03-ui-script.ts b/examples/src/shader-03-ui-script.ts index d220609ce9..321a3e4b75 100644 --- a/examples/src/shader-03-ui-script.ts +++ b/examples/src/shader-03-ui-script.ts @@ -160,8 +160,6 @@ function createCheckerTexture(engine: Engine): Texture2D { // 主程序 Logger.enable(); WebGLEngine.create({ canvas: "canvas", shaderCompiler: new ShaderCompiler() }).then((engine) => { - engine.canvas.resizeByClientSize(); - // 创建场景和相机 const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/examples/src/shader-04-multi-pass.ts b/examples/src/shader-04-multi-pass.ts index b3f279ae02..427982f394 100644 --- a/examples/src/shader-04-multi-pass.ts +++ b/examples/src/shader-04-multi-pass.ts @@ -142,8 +142,6 @@ function createSphere(engine: Engine, name: string, position: Vector3): Entity { // 主程序 Logger.enable(); WebGLEngine.create({ canvas: "canvas", shaderCompiler: new ShaderCompiler() }).then((engine) => { - engine.canvas.resizeByClientSize(); - // 创建场景和相机 const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/examples/src/shader-05-advance.ts b/examples/src/shader-05-advance.ts index ea79a6664d..c69836b8e9 100644 --- a/examples/src/shader-05-advance.ts +++ b/examples/src/shader-05-advance.ts @@ -81,8 +81,6 @@ Shader "Tutorial/05-SkinnedUnlit" { Logger.enable(); WebGLEngine.create({ canvas: "canvas", shaderCompiler: new ShaderCompiler() }).then(async (engine) => { - engine.canvas.resizeByClientSize(); - // 创建场景和相机 const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/examples/src/ui-batch-massive.ts b/examples/src/ui-batch-massive.ts index 62d3a5c2b2..8649effc87 100644 --- a/examples/src/ui-batch-massive.ts +++ b/examples/src/ui-batch-massive.ts @@ -21,8 +21,6 @@ import { registerGUI(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.resizeByClientSize(); - const scene = engine.sceneManager.activeScene; const root = scene.createRootEntity("root"); diff --git a/packages/core/src/Canvas.ts b/packages/core/src/Canvas.ts index 6fc64dc2cf..88c44bdb5d 100644 --- a/packages/core/src/Canvas.ts +++ b/packages/core/src/Canvas.ts @@ -7,40 +7,63 @@ export abstract class Canvas { /* @internal */ _sizeUpdateFlagManager: UpdateFlagManager = new UpdateFlagManager(); - private _width: number; - private _height: number; + private _width: number = 0; + private _height: number = 0; /** - * The width of the canvas. + * The render buffer width of the canvas, in pixels. + * @remarks Read-only. Use `setResolution` or `setAutoResolution` to change the render resolution. */ get width(): number { return this._width; } - set width(value: number) { - if (this._width !== value) { - this._width = value; - this._onWidthChanged(value); - this._sizeUpdateFlagManager.dispatch(); - } - } - /** - *The height of the canvas. + * The render buffer height of the canvas, in pixels. + * @remarks Read-only. Use `setResolution` or `setAutoResolution` to change the render resolution. */ get height(): number { return this._height; } - set height(value: number) { - if (this._height !== value) { - this._height = value; - this._onHeightChange(value); + /** + * Lock the render buffer to an explicit resolution and stop following the display size. + * @remarks Resolution is set atomically; width and height always change together in a single frame. + * @param width - Render buffer width in pixels + * @param height - Render buffer height in pixels + */ + setResolution(width: number, height: number): void { + if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) { + throw new Error(`Canvas.setResolution: invalid size ${width}x${height}`); + } + + this._exitAutoResize(); + this._setSize(width, height); + } + + /** + * Make the render buffer automatically follow the canvas display size. + * @param scale - Performance multiplier on the device pixel ratio (1 = clear, 0.7 = save GPU, 2 = supersample), defaults to `1` + */ + abstract setAutoResolution(scale?: number): void; + + // Sole atomic entry for render-buffer size: width and height change together in one dispatch. + protected _setSize(width: number, height: number): void { + if (this._width !== width || this._height !== height) { + this._width = width; + this._height = height; + this._onSizeChanged(width, height); this._sizeUpdateFlagManager.dispatch(); } } - protected abstract _onWidthChanged(value: number): void; + // Applied by the engine each frame, before rendering, so resize lands in the render frame. + abstract _pumpPendingResize(): void; + + abstract _destroy(): void; + + // Tear down the auto-resize subscription; no-op for platforms without an auto mode. + protected _exitAutoResize(): void {} - protected abstract _onHeightChange(value: number): void; + protected abstract _onSizeChanged(width: number, height: number): void; } diff --git a/packages/rhi-webgl/src/WebCanvas.ts b/packages/rhi-webgl/src/WebCanvas.ts index e98ca0ac2e..36621e6036 100644 --- a/packages/rhi-webgl/src/WebCanvas.ts +++ b/packages/rhi-webgl/src/WebCanvas.ts @@ -11,6 +11,10 @@ export class WebCanvas extends Canvas { private _scale: Vector2 = new Vector2(); + private _resizeObserver?: ResizeObserver; + private _autoResolutionScale: number = 1; + private _pendingResize: boolean = false; + /** * The scale of canvas, the value is visible width/height divide the render width/height. * @remarks Need to re-assign after modification to ensure that the modification takes effect. @@ -42,18 +46,19 @@ export class WebCanvas extends Canvas { return typeof OffscreenCanvas !== "undefined" && this._webCanvas instanceof OffscreenCanvas; } - /** - * Resize the rendering size according to the clientWidth and clientHeight of the canvas. - * @param pixelRatio - Pixel ratio - */ - resizeByClientSize(pixelRatio: number = window.devicePixelRatio): void { - const webCanvas = this._webCanvas; - if (!this.isOffscreenCanvas()) { - const exportWidth = webCanvas.clientWidth * pixelRatio; - const exportHeight = webCanvas.clientHeight * pixelRatio; - this.width = exportWidth; - this.height = exportHeight; + override setAutoResolution(scale: number = 1): void { + // TODO: OffscreenCanvas has no display size; warn once. + if (this.isOffscreenCanvas()) return; + + this._autoResolutionScale = scale; + + if (!this._resizeObserver) { + // Flag only; the resize is applied in `_pumpPendingResize` so it lands in the render frame. + this._resizeObserver = new ResizeObserver(() => (this._pendingResize = true)); + this._resizeObserver.observe(this._webCanvas); } + + this._pendingResize = true; } /** @@ -62,11 +67,8 @@ export class WebCanvas extends Canvas { */ constructor(webCanvas: HTMLCanvasElement | OffscreenCanvas) { super(); - const width = webCanvas.width; - const height = webCanvas.height; this._webCanvas = webCanvas; - this.width = width; - this.height = height; + this._setSize(webCanvas.width, webCanvas.height); } /** @@ -79,11 +81,35 @@ export class WebCanvas extends Canvas { this.scale = this._scale; } - protected override _onWidthChanged(value: number): void { - this._webCanvas.width = value; + override _pumpPendingResize(): void { + if (!this._pendingResize) return; + + const webCanvas = this._webCanvas; + // Skip while the canvas has no layout size yet (e.g. not mounted); keep the flag so a later + // observe with a real size applies the resize, never setting a 0x0 buffer. + if (webCanvas.clientWidth === 0 || webCanvas.clientHeight === 0) return; + + this._pendingResize = false; + const pixelRatio = window.devicePixelRatio * this._autoResolutionScale; + // Round so the cached size matches the integer buffer; TODO: use `devicePixelContentBoxSize`. + this._setSize(Math.round(webCanvas.clientWidth * pixelRatio), Math.round(webCanvas.clientHeight * pixelRatio)); + } + + override _destroy(): void { + this._exitAutoResize(); } - protected override _onHeightChange(value: number): void { - this._webCanvas.height = value; + protected override _exitAutoResize(): void { + if (this._resizeObserver) { + this._resizeObserver.disconnect(); + this._resizeObserver = undefined; + } + this._pendingResize = false; + } + + protected override _onSizeChanged(width: number, height: number): void { + const webCanvas = this._webCanvas; + webCanvas.width = width; + webCanvas.height = height; } } diff --git a/packages/rhi-webgl/src/WebGLEngine.ts b/packages/rhi-webgl/src/WebGLEngine.ts index cb409e86d1..992455bb88 100644 --- a/packages/rhi-webgl/src/WebGLEngine.ts +++ b/packages/rhi-webgl/src/WebGLEngine.ts @@ -16,6 +16,13 @@ export class WebGLEngine extends Engine { const webCanvas = new WebCanvas(typeof canvas === "string" ? document.getElementById(canvas) : canvas); const webGLGraphicDevice = new WebGLGraphicDevice(configuration.graphicDeviceOptions); const engine = new WebGLEngine(webCanvas, webGLGraphicDevice, configuration); + // Release the canvas resize observer when the engine shuts down. + engine.once(EngineEventType.Shutdown, WebGLEngine._releaseCanvas); + // Follow the display size by default; the first resize is deferred to update() and skipped while + // the canvas has no layout size, so an unmounted canvas never gets a 0x0 buffer. + if (configuration.autoResize ?? true) { + webCanvas.setAutoResolution(); + } // @ts-ignore const promise = engine._initialize(configuration) as Promise; return promise.then(() => { @@ -24,9 +31,9 @@ export class WebGLEngine extends Engine { }); } - private _resizeObserver?: ResizeObserver; - private _pendingResize: boolean = false; - private _pendingResizePixelRatio?: number; + private static _releaseCanvas(engine: WebGLEngine): void { + engine.canvas._destroy(); + } /** * Web canvas. @@ -36,66 +43,28 @@ export class WebGLEngine extends Engine { return this._canvas as WebCanvas; } - private static _cleanupAutoResize(engine: WebGLEngine): void { - if (engine._resizeObserver) { - engine._resizeObserver.disconnect(); - engine._resizeObserver = undefined; - } - engine._pendingResize = false; - engine._pendingResizePixelRatio = undefined; - } - /** - * Enable automatic canvas resizing via ResizeObserver. - * @param pixelRatio - Optional custom pixel ratio; lazily reads window.devicePixelRatio on resize when omitted + * Enable automatic canvas resizing. + * @deprecated Use `engine.canvas.setAutoResolution()` instead. Auto-resolution is a canvas capability. + * @param pixelRatio - Deprecated; the device pixel ratio is applied automatically */ enableAutoResize(pixelRatio?: number): void { - const webCanvas = this.canvas._webCanvas; - - if (!this.canvas.isOffscreenCanvas()) { - if (!this._resizeObserver) { - this.once(EngineEventType.Shutdown, WebGLEngine._cleanupAutoResize); - } - - // Disconnect previous observer to avoid duplicate observation - this._resizeObserver?.disconnect(); - - // Re-create the observer each call so the closure always captures the - // latest pixelRatio. The actual resize is deferred to update(), keeping - // it in the same frame as rendering to prevent a blank flash. - // When pixelRatio is undefined, resizeByClientSize falls back to - // window.devicePixelRatio on each invocation, naturally tracking DPR changes. - this._resizeObserver = new ResizeObserver(() => { - this._pendingResize = true; - this._pendingResizePixelRatio = pixelRatio; - }); - - // Start observing the canvas element for size changes. - this._resizeObserver.observe(webCanvas); - } + // The old pixelRatio replaced dpr; the new model applies dpr automatically, so drop the argument. + this.canvas.setAutoResolution(); } /** - * Disable automatic canvas resizing and clean up the observer. + * Disable automatic canvas resizing. + * @deprecated Use `engine.canvas.setResolution(width, height)` to lock a fixed resolution instead. */ disableAutoResize(): void { - if (this._resizeObserver) { - this.off(EngineEventType.Shutdown, WebGLEngine._cleanupAutoResize); - WebGLEngine._cleanupAutoResize(this); - } + const canvas = this.canvas; + canvas.setResolution(canvas.width, canvas.height); } - /** - * @override - * Apply any pending canvas resize before the rendering pipeline runs, - * ensuring that resize and render occur within the same frame to avoid white flickering. - */ override update(): void { - if (this._pendingResize) { - this.canvas.resizeByClientSize(this._pendingResizePixelRatio); - this._pendingResize = false; - this._pendingResizePixelRatio = undefined; - } + // Apply pending resize before rendering so both land in the same frame (no white flash). + this.canvas._pumpPendingResize(); super.update(); } } @@ -108,4 +77,6 @@ export interface WebGLEngineConfiguration extends EngineConfiguration { canvas: HTMLCanvasElement | OffscreenCanvas | string; /** Graphic device options. */ graphicDeviceOptions?: WebGLGraphicDeviceOptions; + /** Whether the render buffer automatically follows the canvas display size. Defaults to `true`. */ + autoResize?: boolean; } diff --git a/tests/src/core/Camera.test.ts b/tests/src/core/Camera.test.ts index 0892400f07..2aa6fefa21 100644 --- a/tests/src/core/Camera.test.ts +++ b/tests/src/core/Camera.test.ts @@ -171,13 +171,12 @@ describe("camera test", function () { expect(viewportPoint.z).to.be.closeTo(expectedviewportPoint.z, 0.1, "Result z should match expected value"); const webCanvas = engine.canvas; - webCanvas.width = 100; - webCanvas.height = 1000; + webCanvas.setResolution(100, 1000); camera.viewportToWorldPoint(viewportPoint, worldPoint); expect(worldPoint.x).to.be.closeTo(-1.73, 0.1, "Result x should match expected value"); expect(worldPoint.y).to.be.closeTo(17.32, 0.1, "Result y should match expected value"); expect(worldPoint.z).to.be.closeTo(-30, 0.1, "Result z should match expected value"); - webCanvas.width = webCanvas.height = 256; + webCanvas.setResolution(256, 256); }); it("viewport to world point, while isOrthographic = true", () => { diff --git a/tests/src/core/input/InputManager.test.ts b/tests/src/core/input/InputManager.test.ts index f883f8fd42..9deab49b2b 100644 --- a/tests/src/core/input/InputManager.test.ts +++ b/tests/src/core/input/InputManager.test.ts @@ -23,7 +23,8 @@ body.appendChild(canvasDOM); describe("InputManager", async () => { const engine = await WebGLEngine.create({ canvas: canvasDOM, physics: new LitePhysics() }); const { inputManager, canvas } = engine; - canvas.resizeByClientSize(2); + // Canvas is 5x5 CSS px; lock the render buffer to the equivalent size at pixelRatio 2. + canvas.setResolution(10, 10); const scene = engine.sceneManager.scenes[0]; const root = scene.createRootEntity("root"); diff --git a/tests/src/rhi-webgl/WebGLEngine.test.ts b/tests/src/rhi-webgl/WebGLEngine.test.ts index 34b32cc817..83b13a0b8b 100644 --- a/tests/src/rhi-webgl/WebGLEngine.test.ts +++ b/tests/src/rhi-webgl/WebGLEngine.test.ts @@ -1,4 +1,4 @@ -import { Camera, EngineEventType, Entity, Script } from "@galacean/engine-core"; +import { Camera, Entity, Script } from "@galacean/engine-core"; import { Vector3 } from "@galacean/engine-math"; import { WebGLEngine } from "@galacean/engine"; import { vi, describe, expect, it } from "vitest"; @@ -103,47 +103,57 @@ describe("webgl engine test", () => { } }); - it("engine auto resize", async () => { + it("canvas auto resize", async () => { + // autoResize defaults to true: create enters auto mode and attaches an observer. const canvas = document.createElement("canvas"); const engine = await WebGLEngine.create({ canvas }); engine.run(); + const webCanvas = engine.canvas; + const observer = (webCanvas as any)._resizeObserver; + expect(observer).toBeDefined(); + + // Re-entering auto reuses the same observer and updates the scale. + webCanvas.setAutoResolution(0.5); + expect((webCanvas as any)._resizeObserver).toBe(observer); + expect((webCanvas as any)._autoResolutionScale).toBe(0.5); + + // A pending resize is applied on the next frame pump. jsdom has no layout, so stub clientWidth/Height. + Object.defineProperty(canvas, "clientWidth", { value: 800, configurable: true }); + Object.defineProperty(canvas, "clientHeight", { value: 600, configurable: true }); + (webCanvas as any)._pendingResize = true; + webCanvas._pumpPendingResize(); + expect((webCanvas as any)._pendingResize).toBe(false); + expect(webCanvas.width).toBe(Math.round(800 * window.devicePixelRatio * 0.5)); + expect(webCanvas.height).toBe(Math.round(600 * window.devicePixelRatio * 0.5)); + + // A pending resize is skipped while the canvas has no layout size (never a 0x0 buffer). + Object.defineProperty(canvas, "clientWidth", { value: 0, configurable: true }); + (webCanvas as any)._pendingResize = true; + webCanvas._pumpPendingResize(); + expect((webCanvas as any)._pendingResize).toBe(true); // kept for a later valid observe + + // setResolution locks a fixed size and exits auto mode (observer released). + webCanvas.setResolution(320, 240); + expect((webCanvas as any)._resizeObserver).toBeUndefined(); + expect(webCanvas.width).toBe(320); + expect(webCanvas.height).toBe(240); + + // setResolution rejects invalid sizes. + expect(() => webCanvas.setResolution(0, 100)).to.throw(); + expect(() => webCanvas.setResolution(-1, 100)).to.throw(); + + // destroy releases the observer. + webCanvas.setAutoResolution(); + expect((webCanvas as any)._resizeObserver).toBeDefined(); + engine.destroy(); + expect((webCanvas as any)._resizeObserver).toBeUndefined(); + }); - const rawListenerCount = engine.listenerCount(EngineEventType.Shutdown); - - engine.enableAutoResize(); - expect(engine.listenerCount(EngineEventType.Shutdown)).toBe(rawListenerCount + 1); - expect((engine as any)._resizeObserver).toBeDefined(); - const firstObserver = (engine as any)._resizeObserver; - const firstObserverDisconnectSpy = vi.spyOn(firstObserver, "disconnect"); - expect(firstObserverDisconnectSpy).toHaveBeenCalledTimes(0); - - engine.enableAutoResize(); - expect(engine.listenerCount(EngineEventType.Shutdown)).toBe(rawListenerCount + 1); - expect((engine as any)._resizeObserver).toBeDefined(); - expect((engine as any)._resizeObserver).not.toBe(firstObserver); - expect(firstObserverDisconnectSpy).toHaveBeenCalledTimes(1); - - engine.disableAutoResize(); - expect((engine as any)._resizeObserver).toBeUndefined(); - expect((engine as any)._pendingResizePixelRatio).toBeUndefined(); - expect(engine.listenerCount(EngineEventType.Shutdown)).toBe(rawListenerCount); - - const resizeSpy = vi.spyOn(engine.canvas, "resizeByClientSize"); - engine.enableAutoResize(); - expect((engine as any)._resizeObserver).toBeDefined(); - expect(engine.listenerCount(EngineEventType.Shutdown)).toBe(rawListenerCount + 1); - - canvas.style.width = "800px"; - canvas.style.height = "600px"; - // Wait for the ResizeObserver to fire the resize callback - await new Promise((resolve) => setTimeout(resolve, 1000)); - expect(resizeSpy).toHaveBeenCalledTimes(1); - // _pendingResizePixelRatio should be consumed by update() and cleared - expect((engine as any)._pendingResizePixelRatio).toBeUndefined(); - + it("autoResize false disables the default follow", async () => { + const canvas = document.createElement("canvas"); + const engine = await WebGLEngine.create({ canvas, autoResize: false }); + // No observer attached when auto-resize is opted out. + expect((engine.canvas as any)._resizeObserver).toBeUndefined(); engine.destroy(); - expect((engine as any)._resizeObserver).toBeUndefined(); - expect((engine as any)._pendingResizePixelRatio).toBeUndefined(); - expect(engine.listenerCount(EngineEventType.Shutdown)).toBe(rawListenerCount); }); }); diff --git a/tests/src/ui/Image.test.ts b/tests/src/ui/Image.test.ts index ca13eef6a0..eaa6bd026a 100644 --- a/tests/src/ui/Image.test.ts +++ b/tests/src/ui/Image.test.ts @@ -7,8 +7,7 @@ describe("Image", async () => { const canvas = document.createElement("canvas"); const engine = await WebGLEngine.create({ canvas: canvas }); const webCanvas = engine.canvas; - webCanvas.width = 750; - webCanvas.height = 1334; + webCanvas.setResolution(750, 1334); const scene = engine.sceneManager.scenes[0]; const root = scene.createRootEntity("root"); diff --git a/tests/src/ui/Text.test.ts b/tests/src/ui/Text.test.ts index c00c3c2d6a..99c3299472 100644 --- a/tests/src/ui/Text.test.ts +++ b/tests/src/ui/Text.test.ts @@ -21,8 +21,7 @@ describe("Text", async () => { const canvas = document.createElement("canvas"); const engine = await WebGLEngine.create({ canvas: canvas }); const webCanvas = engine.canvas; - webCanvas.width = 750; - webCanvas.height = 1334; + webCanvas.setResolution(750, 1334); const scene = engine.sceneManager.scenes[0]; const root = scene.createRootEntity("root"); @@ -154,8 +153,7 @@ describe("Text - referenceResolutionPerUnit dirty propagation", async () => { const canvas = document.createElement("canvas"); const engine = await WebGLEngine.create({ canvas }); const webCanvas = engine.canvas; - webCanvas.width = 750; - webCanvas.height = 1334; + webCanvas.setResolution(750, 1334); const scene = engine.sceneManager.scenes[0]; const root = scene.createRootEntity("regression-root"); diff --git a/tests/src/ui/UICanvas.test.ts b/tests/src/ui/UICanvas.test.ts index aa8f70e866..5e534763c8 100644 --- a/tests/src/ui/UICanvas.test.ts +++ b/tests/src/ui/UICanvas.test.ts @@ -8,8 +8,7 @@ describe("UICanvas", async () => { const canvas = document.createElement("canvas"); const engine = await WebGLEngine.create({ canvas: canvas }); const webCanvas = engine.canvas; - webCanvas.width = 750; - webCanvas.height = 1334; + webCanvas.setResolution(750, 1334); const scene = engine.sceneManager.scenes[0]; const root = scene.createRootEntity("root"); diff --git a/tests/src/ui/UIEvent.test.ts b/tests/src/ui/UIEvent.test.ts index 982173e0a2..cbc87ba359 100644 --- a/tests/src/ui/UIEvent.test.ts +++ b/tests/src/ui/UIEvent.test.ts @@ -12,8 +12,7 @@ describe("UIEvent", async () => { const engine = await WebGLEngine.create({ canvas: canvasDOM }); const webCanvas = engine.canvas; - webCanvas.width = 300; - webCanvas.height = 300; + webCanvas.setResolution(300, 300); const scene = engine.sceneManager.scenes[0]; const root = scene.createRootEntity("root"); const inputManager = engine.inputManager; diff --git a/tests/src/ui/UIGroup.test.ts b/tests/src/ui/UIGroup.test.ts index 18d11056ce..93d30b420f 100644 --- a/tests/src/ui/UIGroup.test.ts +++ b/tests/src/ui/UIGroup.test.ts @@ -8,8 +8,7 @@ describe("UIGroup", async () => { const canvas = document.createElement("canvas"); const engine = await WebGLEngine.create({ canvas: canvas }); const webCanvas = engine.canvas; - webCanvas.width = 750; - webCanvas.height = 1334; + webCanvas.setResolution(750, 1334); const scene = engine.sceneManager.scenes[0]; const root = scene.createRootEntity("root"); diff --git a/tests/src/ui/UIInteractive.test.ts b/tests/src/ui/UIInteractive.test.ts index 3889666fd8..113e03f465 100644 --- a/tests/src/ui/UIInteractive.test.ts +++ b/tests/src/ui/UIInteractive.test.ts @@ -31,8 +31,7 @@ describe("Button", async () => { const canvas = document.createElement("canvas"); const engine = await WebGLEngine.create({ canvas: canvas }); const webCanvas = engine.canvas; - webCanvas.width = 750; - webCanvas.height = 1334; + webCanvas.setResolution(750, 1334); const scene = engine.sceneManager.scenes[0]; const root = scene.createRootEntity("root"); diff --git a/tests/src/ui/UITransform.test.ts b/tests/src/ui/UITransform.test.ts index f835b82055..534f0f3927 100644 --- a/tests/src/ui/UITransform.test.ts +++ b/tests/src/ui/UITransform.test.ts @@ -1,18 +1,12 @@ import { WebGLEngine } from "@galacean/engine"; -import { - HorizontalAlignmentMode, - UICanvas, - UITransform, - VerticalAlignmentMode, -} from "@galacean/engine-ui"; +import { HorizontalAlignmentMode, UICanvas, UITransform, VerticalAlignmentMode } from "@galacean/engine-ui"; import { describe, expect, it } from "vitest"; describe("UITransform", async () => { const canvas = document.createElement("canvas"); const engine = await WebGLEngine.create({ canvas }); const webCanvas = engine.canvas; - webCanvas.width = 750; - webCanvas.height = 1334; + webCanvas.setResolution(750, 1334); const scene = engine.sceneManager.scenes[0]; const root = scene.createRootEntity("root"); @@ -321,9 +315,11 @@ describe("UITransform", async () => { const child = parent.createChild("stretch-child-manual"); const t = child.transform as UITransform; t.horizontalAlignment = HorizontalAlignmentMode.LeftAndRight; - t.alignLeft = 10; t.alignRight = 20; + t.alignLeft = 10; + t.alignRight = 20; t.verticalAlignment = VerticalAlignmentMode.TopAndBottom; - t.alignTop = 5; t.alignBottom = 15; + t.alignTop = 5; + t.alignBottom = 15; // Layout-driven size expect(t.size.x).to.eq(170); @@ -336,7 +332,6 @@ describe("UITransform", async () => { expect(t.size.y).to.eq(130); }); }); - }); it("widget changes reflect in worldPosition when parent moves", () => { @@ -375,9 +370,9 @@ describe("UITransform", async () => { const originX = testMatrix.elements[12]; const originY = testMatrix.elements[13]; const originZ = testMatrix.elements[14]; - const modifyX = testMatrix.elements[12] = originX + 50; - const modifyY = testMatrix.elements[13] = originY + 50; - const modifyZ = testMatrix.elements[14] = originZ + 50; + const modifyX = (testMatrix.elements[12] = originX + 50); + const modifyY = (testMatrix.elements[13] = originY + 50); + const modifyZ = (testMatrix.elements[14] = originZ + 50); childTransform.localMatrix = testMatrix; const actualMatrix = childTransform.localMatrix; expect(actualMatrix.elements[12]).to.not.eq(modifyX); @@ -449,7 +444,7 @@ describe("UITransform", async () => { originalTransform.alignLeft = 10; originalTransform.alignRight = 20; originalTransform.alignCenter = 5; - + originalTransform.verticalAlignment = VerticalAlignmentMode.TopAndBottom; originalTransform.alignTop = 15; originalTransform.alignBottom = 25; @@ -462,7 +457,7 @@ describe("UITransform", async () => { expect(clonedTransform.alignLeft).to.eq(10); expect(clonedTransform.alignRight).to.eq(20); expect(clonedTransform.alignCenter).to.eq(5); - + expect(clonedTransform.verticalAlignment).to.eq(VerticalAlignmentMode.TopAndBottom); expect(clonedTransform.alignTop).to.eq(15); expect(clonedTransform.alignBottom).to.eq(25); @@ -497,13 +492,13 @@ describe("UITransform", async () => { parent.addComponent(UICanvas); const parentTransform = parent.transform as UITransform; parentTransform.size.set(300, 200); - + const original = parent.createChild("original-behavior"); const originalTransform = original.transform as UITransform; - + originalTransform.horizontalAlignment = HorizontalAlignmentMode.Left; originalTransform.alignLeft = 20; - + const cloned = original.clone(); parent.addChild(cloned); const clonedTransform = cloned.transform as UITransform; @@ -518,4 +513,4 @@ describe("UITransform", async () => { enum UITransformModifyFlags { Size = 0x200, Pivot = 0x400 -} \ No newline at end of file +} From ada33cdda46014d28cbe60c23662c0ce7e6f4e48 Mon Sep 17 00:00:00 2001 From: "chenmo.gl" Date: Thu, 2 Jul 2026 01:24:38 +0800 Subject: [PATCH 08/37] test(rhi-webgl): cover deprecated enableAutoResize/disableAutoResize forwarders --- tests/src/rhi-webgl/WebGLEngine.test.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/src/rhi-webgl/WebGLEngine.test.ts b/tests/src/rhi-webgl/WebGLEngine.test.ts index 83b13a0b8b..2f525fbaa9 100644 --- a/tests/src/rhi-webgl/WebGLEngine.test.ts +++ b/tests/src/rhi-webgl/WebGLEngine.test.ts @@ -156,4 +156,22 @@ describe("webgl engine test", () => { expect((engine.canvas as any)._resizeObserver).toBeUndefined(); engine.destroy(); }); + + it("deprecated enableAutoResize / disableAutoResize forward to the canvas", async () => { + const canvas = document.createElement("canvas"); + const engine = await WebGLEngine.create({ canvas, autoResize: false }); + const webCanvas = engine.canvas; + + // enableAutoResize forwards to setAutoResolution (enters auto mode, attaches observer). + engine.enableAutoResize(); + expect((webCanvas as any)._resizeObserver).toBeDefined(); + + // disableAutoResize forwards to setResolution(current size) (exits auto, releases observer). + engine.disableAutoResize(); + expect((webCanvas as any)._resizeObserver).toBeUndefined(); + expect(webCanvas.width).toBeGreaterThan(0); + expect(webCanvas.height).toBeGreaterThan(0); + + engine.destroy(); + }); }); From a4c593bc5f2378a3514baa6068e1d74c09fb6385 Mon Sep 17 00:00:00 2001 From: "chenmo.gl" Date: Thu, 2 Jul 2026 11:36:52 +0800 Subject: [PATCH 09/37] test(e2e): rely on default auto-resolution instead of explicit setResolution The no-arg resizeByClientSize cases previously produced clientSize x dpr, which is exactly what the default auto-resolution computes - so drop the explicit call and let the 85 real-browser cases exercise the new default path (observer + first-frame pump). The pixelRatio-2 cases map to setAutoResolution(2), which yields the same 2400x1600 under the fixed playwright dpr of 1. Screenshot baselines are unchanged. --- e2e/case/.initPostProcessEnv.ts | 2 -- e2e/case/animator-additive.ts | 2 +- e2e/case/animator-blendShape-quantization.ts | 1 - e2e/case/animator-blendShape.ts | 2 +- e2e/case/animator-crossfade.ts | 2 +- e2e/case/animator-customAnimationClip.ts | 2 +- e2e/case/animator-customBlendShape.ts | 2 +- e2e/case/animator-event.ts | 2 +- e2e/case/animator-multiSubMeshBlendShape.ts | 5 ----- e2e/case/animator-play-backwards.ts | 2 +- e2e/case/animator-play-beforeActive.ts | 2 +- e2e/case/animator-play.ts | 2 +- e2e/case/animator-reuse.ts | 2 +- e2e/case/animator-stateMachine.ts | 5 ----- e2e/case/animator-stateMachineScript.ts | 2 +- e2e/case/camera-opaque-texture.ts | 2 +- e2e/case/camera-ssao.ts | 2 +- e2e/case/canvas-transparency.ts | 2 +- e2e/case/gltf-blendshape.ts | 5 ----- e2e/case/gltf-meshopt.ts | 2 +- e2e/case/gpu-instancing-auto-batch.ts | 2 +- e2e/case/gpu-instancing-custom-data.ts | 2 +- e2e/case/material-LUT.ts | 2 +- e2e/case/material-blendMode.ts | 2 -- e2e/case/material-blinn-phong.ts | 2 -- e2e/case/material-pbr-clearcoat.ts | 2 -- e2e/case/material-pbr-specular.ts | 2 -- e2e/case/material-pbr.ts | 2 -- e2e/case/material-shader.ts | 2 -- e2e/case/material-shaderReplacement.ts | 1 - e2e/case/material-unlit.ts | 2 +- e2e/case/material-white-furnace.ts | 2 -- e2e/case/multi-camera-no-clear.ts | 2 -- e2e/case/multi-scene-clear.ts | 2 -- e2e/case/multi-scene-no-clear.ts | 2 -- e2e/case/particleRenderer-burst-cycles.ts | 2 -- e2e/case/particleRenderer-customShader.ts | 2 -- e2e/case/particleRenderer-dream.ts | 1 - e2e/case/particleRenderer-emissive.ts | 1 - e2e/case/particleRenderer-emit-billboard-stretched.ts | 1 - ...enderer-emit-mesh-cone-scale-3d-rotation-life-seperate.ts | 1 - .../particleRenderer-emit-mesh-cone-scale-3d-rotation.ts | 1 - ...leRenderer-emit-mesh-cone-scale-rotation-life-seperate.ts | 1 - .../particleRenderer-emit-mesh-cone-scale-rotation-life.ts | 1 - .../particleRenderer-emit-mesh-cone-scale-rotation-world.ts | 1 - e2e/case/particleRenderer-emit-mesh-cone-scale-rotation.ts | 1 - e2e/case/particleRenderer-emit-mesh-cone.ts | 1 - e2e/case/particleRenderer-emit-mesh-no-shape-world.ts | 1 - e2e/case/particleRenderer-emit-mesh-no-shape.ts | 1 - e2e/case/particleRenderer-emit-mesh-rotation-life-curve.ts | 2 -- e2e/case/particleRenderer-fire.ts | 1 - e2e/case/particleRenderer-force.ts | 1 - e2e/case/particleRenderer-horizontal-billboard.ts | 1 - e2e/case/particleRenderer-limitVelocity.ts | 2 -- e2e/case/particleRenderer-noise.ts | 2 -- e2e/case/particleRenderer-rateOverDistance.ts | 2 -- e2e/case/particleRenderer-shape-mesh.ts | 1 - e2e/case/particleRenderer-shape-transform.ts | 1 - e2e/case/particleRenderer-sub-emitter.ts | 2 -- e2e/case/particleRenderer-textureSheetAnimation.ts | 1 - e2e/case/physx-collision-group.ts | 1 - e2e/case/physx-collision.ts | 1 - e2e/case/physx-customUrl.ts | 1 - e2e/case/physx-deferred-contact.ts | 1 - e2e/case/physx-mesh-collider-data.ts | 1 - e2e/case/physx-mesh-collider.ts | 1 - e2e/case/primitive-capsule.ts | 2 +- e2e/case/primitive-cone.ts | 2 +- e2e/case/primitive-cuboid.ts | 2 +- e2e/case/primitive-cylinder.ts | 2 +- e2e/case/primitive-plane.ts | 2 +- e2e/case/primitive-sphere.ts | 2 +- e2e/case/primitive-torus.ts | 2 +- e2e/case/shader-mrt.ts | 2 -- e2e/case/shader-renderState.ts | 2 -- e2e/case/shadow-basic.ts | 1 - e2e/case/shadow-transparent.ts | 1 - e2e/case/spriteMask-customStencil.ts | 2 -- e2e/case/text-character-spacing.ts | 1 - e2e/case/text-typed.ts | 1 - e2e/case/texture-R8G8.ts | 2 +- e2e/case/texture-hdr-ktx2.ts | 2 +- e2e/case/texture-hdr.ts | 2 +- e2e/case/texture-sRGB-KTX2.ts | 2 +- e2e/case/trailRenderer-basic.ts | 1 - e2e/case/ui-batch-order.ts | 2 +- 86 files changed, 31 insertions(+), 119 deletions(-) diff --git a/e2e/case/.initPostProcessEnv.ts b/e2e/case/.initPostProcessEnv.ts index b83c92015a..bbcba54183 100644 --- a/e2e/case/.initPostProcessEnv.ts +++ b/e2e/case/.initPostProcessEnv.ts @@ -18,8 +18,6 @@ export async function initPostProcessEnv( shaderCompiler?: ShaderCompiler ) { WebGLEngine.create({ canvas: "canvas", shaderCompiler }).then((engine) => { - engine.canvas.setResolution(1200, 800); - const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/animator-additive.ts b/e2e/case/animator-additive.ts index e085de94e2..078952bcb0 100644 --- a/e2e/case/animator-additive.ts +++ b/e2e/case/animator-additive.ts @@ -20,7 +20,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.setResolution(2400, 1600); + engine.canvas.setAutoResolution(2); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/animator-blendShape-quantization.ts b/e2e/case/animator-blendShape-quantization.ts index 5351a962d7..653d91c6e3 100644 --- a/e2e/case/animator-blendShape-quantization.ts +++ b/e2e/case/animator-blendShape-quantization.ts @@ -19,7 +19,6 @@ WebGLEngine.create({ canvas: "canvas", glTF: { meshOpt: { workerCount: 0 } } }).then(async (engine) => { - engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const rootNode = scene.createRootEntity(); scene.ambientLight.diffuseSolidColor.set(1, 1, 1, 1); diff --git a/e2e/case/animator-blendShape.ts b/e2e/case/animator-blendShape.ts index 2ca8dbee4b..890228a29f 100644 --- a/e2e/case/animator-blendShape.ts +++ b/e2e/case/animator-blendShape.ts @@ -16,7 +16,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.setResolution(2400, 1600); + engine.canvas.setAutoResolution(2); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/animator-crossfade.ts b/e2e/case/animator-crossfade.ts index c518e7143c..31762553c9 100644 --- a/e2e/case/animator-crossfade.ts +++ b/e2e/case/animator-crossfade.ts @@ -7,7 +7,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.setResolution(2400, 1600); + engine.canvas.setAutoResolution(2); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/animator-customAnimationClip.ts b/e2e/case/animator-customAnimationClip.ts index 777429b4ed..ef2fd20be9 100644 --- a/e2e/case/animator-customAnimationClip.ts +++ b/e2e/case/animator-customAnimationClip.ts @@ -26,7 +26,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.setResolution(2400, 1600); + engine.canvas.setAutoResolution(2); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/animator-customBlendShape.ts b/e2e/case/animator-customBlendShape.ts index 59728e30a3..c0f6e05bde 100644 --- a/e2e/case/animator-customBlendShape.ts +++ b/e2e/case/animator-customBlendShape.ts @@ -23,7 +23,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.setResolution(2400, 1600); + engine.canvas.setAutoResolution(2); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/animator-event.ts b/e2e/case/animator-event.ts index d6f3510e3d..52f7a29e9b 100644 --- a/e2e/case/animator-event.ts +++ b/e2e/case/animator-event.ts @@ -18,7 +18,7 @@ import { import { initScreenshot, updateForE2E } from "./.mockForE2E"; WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.setResolution(2400, 1600); + engine.canvas.setAutoResolution(2); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); // camera diff --git a/e2e/case/animator-multiSubMeshBlendShape.ts b/e2e/case/animator-multiSubMeshBlendShape.ts index 7cbdc084b1..cf4919cfad 100644 --- a/e2e/case/animator-multiSubMeshBlendShape.ts +++ b/e2e/case/animator-multiSubMeshBlendShape.ts @@ -8,7 +8,6 @@ import { DirectLight, Logger, SkinnedMeshRenderer, - SystemInfo, Vector3, WebGLEngine, GLTFResource, @@ -23,10 +22,6 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.setResolution( - window.innerWidth * SystemInfo.devicePixelRatio, - window.innerHeight * SystemInfo.devicePixelRatio - ); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/animator-play-backwards.ts b/e2e/case/animator-play-backwards.ts index 7cfee113b4..b55cc36e3f 100644 --- a/e2e/case/animator-play-backwards.ts +++ b/e2e/case/animator-play-backwards.ts @@ -7,7 +7,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.setResolution(2400, 1600); + engine.canvas.setAutoResolution(2); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/animator-play-beforeActive.ts b/e2e/case/animator-play-beforeActive.ts index 71115a4b47..a14b2eb8ac 100644 --- a/e2e/case/animator-play-beforeActive.ts +++ b/e2e/case/animator-play-beforeActive.ts @@ -7,7 +7,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.setResolution(2400, 1600); + engine.canvas.setAutoResolution(2); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/animator-play.ts b/e2e/case/animator-play.ts index 477cf442cd..82e7966ac3 100644 --- a/e2e/case/animator-play.ts +++ b/e2e/case/animator-play.ts @@ -7,7 +7,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.setResolution(2400, 1600); + engine.canvas.setAutoResolution(2); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/animator-reuse.ts b/e2e/case/animator-reuse.ts index f89d760602..f49d248739 100644 --- a/e2e/case/animator-reuse.ts +++ b/e2e/case/animator-reuse.ts @@ -17,7 +17,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.setResolution(2400, 1600); + engine.canvas.setAutoResolution(2); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/animator-stateMachine.ts b/e2e/case/animator-stateMachine.ts index 32754e7e69..fe450c1729 100644 --- a/e2e/case/animator-stateMachine.ts +++ b/e2e/case/animator-stateMachine.ts @@ -10,7 +10,6 @@ import { Color, DirectLight, GLTFResource, - SystemInfo, TextRenderer, Vector3, WebGLEngine @@ -18,10 +17,6 @@ import { import { initScreenshot, updateForE2E } from "./.mockForE2E"; WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.setResolution( - window.innerWidth * SystemInfo.devicePixelRatio, - window.innerHeight * SystemInfo.devicePixelRatio - ); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/animator-stateMachineScript.ts b/e2e/case/animator-stateMachineScript.ts index 60134c5c61..b1cec13332 100644 --- a/e2e/case/animator-stateMachineScript.ts +++ b/e2e/case/animator-stateMachineScript.ts @@ -20,7 +20,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.setResolution(2400, 1600); + engine.canvas.setAutoResolution(2); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/camera-opaque-texture.ts b/e2e/case/camera-opaque-texture.ts index b44426ac5a..646ae31809 100644 --- a/e2e/case/camera-opaque-texture.ts +++ b/e2e/case/camera-opaque-texture.ts @@ -111,7 +111,7 @@ WebGLEngine.create({ canvas: "canvas", shaderCompiler: new ShaderCompiler() }).t } } `); - engine.canvas.setResolution(2400, 1600); + engine.canvas.setAutoResolution(2); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/camera-ssao.ts b/e2e/case/camera-ssao.ts index 48266cd6d9..d38998ef58 100644 --- a/e2e/case/camera-ssao.ts +++ b/e2e/case/camera-ssao.ts @@ -30,7 +30,7 @@ WebGLEngine.create({ shaderCompiler: new ShaderCompiler(), graphicDeviceOptions: { webGLMode: WebGLMode.WebGL1 } }).then((engine) => { - engine.canvas.setResolution(2400, 1600); + engine.canvas.setAutoResolution(2); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/canvas-transparency.ts b/e2e/case/canvas-transparency.ts index 405bfcd2e5..f140cb0334 100644 --- a/e2e/case/canvas-transparency.ts +++ b/e2e/case/canvas-transparency.ts @@ -28,7 +28,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas", graphicDeviceOptions: { alpha: false } }).then((engine) => { - engine.canvas.setResolution(2400, 1600); + engine.canvas.setAutoResolution(2); engine.resourceManager .load([ diff --git a/e2e/case/gltf-blendshape.ts b/e2e/case/gltf-blendshape.ts index 8776b0e38e..6dde5095f1 100644 --- a/e2e/case/gltf-blendshape.ts +++ b/e2e/case/gltf-blendshape.ts @@ -10,7 +10,6 @@ import { DirectLight, Logger, SkinnedMeshRenderer, - SystemInfo, Vector3, WebGLEngine, GLTFResource @@ -19,10 +18,6 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.setResolution( - window.innerWidth * SystemInfo.devicePixelRatio, - window.innerHeight * SystemInfo.devicePixelRatio - ); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/gltf-meshopt.ts b/e2e/case/gltf-meshopt.ts index 5dd440843e..e47fde0591 100644 --- a/e2e/case/gltf-meshopt.ts +++ b/e2e/case/gltf-meshopt.ts @@ -7,7 +7,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.setResolution(2400, 1600); + engine.canvas.setAutoResolution(2); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/gpu-instancing-auto-batch.ts b/e2e/case/gpu-instancing-auto-batch.ts index 1149fcf27a..b45eeb6527 100644 --- a/e2e/case/gpu-instancing-auto-batch.ts +++ b/e2e/case/gpu-instancing-auto-batch.ts @@ -17,7 +17,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then(async (engine) => { - engine.canvas.setResolution(2400, 1600); + engine.canvas.setAutoResolution(2); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity("Root"); diff --git a/e2e/case/gpu-instancing-custom-data.ts b/e2e/case/gpu-instancing-custom-data.ts index 4ea1c371ef..290f478ca9 100644 --- a/e2e/case/gpu-instancing-custom-data.ts +++ b/e2e/case/gpu-instancing-custom-data.ts @@ -61,7 +61,7 @@ const customInstanceShaderSource = `Shader "CustomInstanceShader" { WebGLEngine.create({ canvas: "canvas", shaderCompiler }).then((engine) => { Shader.create(customInstanceShaderSource); - engine.canvas.setResolution(2400, 1600); + engine.canvas.setAutoResolution(2); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity("Root"); diff --git a/e2e/case/material-LUT.ts b/e2e/case/material-LUT.ts index f9357789bf..149c0fd5c1 100644 --- a/e2e/case/material-LUT.ts +++ b/e2e/case/material-LUT.ts @@ -43,7 +43,7 @@ const shaderSource = `Shader "LUT-test" { // Create engine WebGLEngine.create({ canvas: "canvas", shaderCompiler }) .then((engine) => { - engine.canvas.setResolution(2400, 1600); + engine.canvas.setAutoResolution(2); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/material-blendMode.ts b/e2e/case/material-blendMode.ts index 5a476249ff..c9ee076d7c 100644 --- a/e2e/case/material-blendMode.ts +++ b/e2e/case/material-blendMode.ts @@ -7,8 +7,6 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; // Create engine WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.setResolution(1200, 800); - const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/material-blinn-phong.ts b/e2e/case/material-blinn-phong.ts index 8ac4900971..4e510d59f7 100644 --- a/e2e/case/material-blinn-phong.ts +++ b/e2e/case/material-blinn-phong.ts @@ -18,8 +18,6 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; // Create engine WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.setResolution(1200, 800); - const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/material-pbr-clearcoat.ts b/e2e/case/material-pbr-clearcoat.ts index cf5697e54e..5e66e9f209 100644 --- a/e2e/case/material-pbr-clearcoat.ts +++ b/e2e/case/material-pbr-clearcoat.ts @@ -18,8 +18,6 @@ Logger.enable(); // Create engine WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.setResolution(1200, 800); - const scene = engine.sceneManager.activeScene; const { background } = scene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/material-pbr-specular.ts b/e2e/case/material-pbr-specular.ts index 529f2631ae..a159b82d1a 100644 --- a/e2e/case/material-pbr-specular.ts +++ b/e2e/case/material-pbr-specular.ts @@ -21,8 +21,6 @@ Logger.enable(); // Create engine WebGLEngine.create({ canvas: "canvas", shaderCompiler: new ShaderCompiler() }).then((engine) => { - engine.canvas.setResolution(1200, 800); - const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/material-pbr.ts b/e2e/case/material-pbr.ts index fbaefe460c..7cf589ba89 100644 --- a/e2e/case/material-pbr.ts +++ b/e2e/case/material-pbr.ts @@ -16,8 +16,6 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; // Create engine WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.setResolution(1200, 800); - const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/material-shader.ts b/e2e/case/material-shader.ts index c52ace58e7..c77418bda1 100644 --- a/e2e/case/material-shader.ts +++ b/e2e/case/material-shader.ts @@ -67,8 +67,6 @@ function createPlaneMesh(engine: WebGLEngine) { Logger.enable(); WebGLEngine.create({ canvas: "canvas", shaderCompiler }).then((engine) => { - engine.canvas.setResolution(1200, 800); - const shaderMap = { normal: Shader.create(normalShaderSource) }; diff --git a/e2e/case/material-shaderReplacement.ts b/e2e/case/material-shaderReplacement.ts index 133ba26c0b..0169446f3f 100644 --- a/e2e/case/material-shaderReplacement.ts +++ b/e2e/case/material-shaderReplacement.ts @@ -24,7 +24,6 @@ async function main() { // Create engine const engine = await WebGLEngine.create({ canvas: "canvas", shaderCompiler: new ShaderCompiler() }); - engine.canvas.setResolution(1200, 800); initShader(); diff --git a/e2e/case/material-unlit.ts b/e2e/case/material-unlit.ts index 251b347fbe..6fccb3c086 100644 --- a/e2e/case/material-unlit.ts +++ b/e2e/case/material-unlit.ts @@ -8,7 +8,7 @@ Logger.enable(); // Create engine WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.setResolution(2400, 1600); + engine.canvas.setAutoResolution(2); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/material-white-furnace.ts b/e2e/case/material-white-furnace.ts index af7a4ad971..00baa80276 100644 --- a/e2e/case/material-white-furnace.ts +++ b/e2e/case/material-white-furnace.ts @@ -20,8 +20,6 @@ import { import { initScreenshot, updateForE2E } from "./.mockForE2E"; WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.setResolution(1200, 800); - const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/multi-camera-no-clear.ts b/e2e/case/multi-camera-no-clear.ts index 603cf90255..6215088fc2 100644 --- a/e2e/case/multi-camera-no-clear.ts +++ b/e2e/case/multi-camera-no-clear.ts @@ -29,8 +29,6 @@ WebGLEngine.create({ antialias: false } }).then((engine) => { - engine.canvas.setResolution(1200, 800); - initFirstScene(engine); engine.run(); }); diff --git a/e2e/case/multi-scene-clear.ts b/e2e/case/multi-scene-clear.ts index 3b29f4de96..78cc90f01b 100644 --- a/e2e/case/multi-scene-clear.ts +++ b/e2e/case/multi-scene-clear.ts @@ -20,8 +20,6 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.setResolution(1200, 800); - const firstCamera = initFirstScene(engine); const secondCamera = initSecondScene(engine); diff --git a/e2e/case/multi-scene-no-clear.ts b/e2e/case/multi-scene-no-clear.ts index 200738a485..976e67e8ae 100644 --- a/e2e/case/multi-scene-no-clear.ts +++ b/e2e/case/multi-scene-no-clear.ts @@ -27,8 +27,6 @@ WebGLEngine.create({ webGLMode: WebGLMode.WebGL2 } }).then((engine) => { - engine.canvas.setResolution(1200, 800); - const firstCamera = initFirstScene(engine); const secondCamera = initSecondScene(engine); diff --git a/e2e/case/particleRenderer-burst-cycles.ts b/e2e/case/particleRenderer-burst-cycles.ts index 7fb52c4ac9..7abe9e055b 100644 --- a/e2e/case/particleRenderer-burst-cycles.ts +++ b/e2e/case/particleRenderer-burst-cycles.ts @@ -25,8 +25,6 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.setResolution(1200, 800); - const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); scene.background.solidColor = new Color(0, 0, 0, 1); diff --git a/e2e/case/particleRenderer-customShader.ts b/e2e/case/particleRenderer-customShader.ts index 80a8f1788f..8fdfcbce5b 100644 --- a/e2e/case/particleRenderer-customShader.ts +++ b/e2e/case/particleRenderer-customShader.ts @@ -85,8 +85,6 @@ const customParticleShaderSource = `Shader "Test/ParticleCustom" { Logger.enable(); WebGLEngine.create({ canvas: "canvas", shaderCompiler }).then((engine) => { - engine.canvas.setResolution(1200, 800); - const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); scene.background.solidColor = new Color(0.05, 0.05, 0.05, 1); diff --git a/e2e/case/particleRenderer-dream.ts b/e2e/case/particleRenderer-dream.ts index 737856ea7c..714a91e12f 100644 --- a/e2e/case/particleRenderer-dream.ts +++ b/e2e/case/particleRenderer-dream.ts @@ -27,7 +27,6 @@ WebGLEngine.create({ canvas: "canvas" }).then((engine) => { Logger.enable(); - engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/particleRenderer-emissive.ts b/e2e/case/particleRenderer-emissive.ts index 8f7f187c11..2de9f66fbe 100644 --- a/e2e/case/particleRenderer-emissive.ts +++ b/e2e/case/particleRenderer-emissive.ts @@ -29,7 +29,6 @@ WebGLEngine.create({ graphicDeviceOptions: { webGLMode: WebGLMode.Auto } }).then((engine) => { Logger.enable(); - engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/particleRenderer-emit-billboard-stretched.ts b/e2e/case/particleRenderer-emit-billboard-stretched.ts index 6a48fd5918..5aead90270 100644 --- a/e2e/case/particleRenderer-emit-billboard-stretched.ts +++ b/e2e/case/particleRenderer-emit-billboard-stretched.ts @@ -29,7 +29,6 @@ WebGLEngine.create({ canvas: "canvas" }).then((engine) => { Logger.enable(); - engine.canvas.setResolution(1200, 800); const rootEntity = engine.sceneManager.activeScene.createRootEntity("Root"); diff --git a/e2e/case/particleRenderer-emit-mesh-cone-scale-3d-rotation-life-seperate.ts b/e2e/case/particleRenderer-emit-mesh-cone-scale-3d-rotation-life-seperate.ts index f130ad56ff..c468303d06 100644 --- a/e2e/case/particleRenderer-emit-mesh-cone-scale-3d-rotation-life-seperate.ts +++ b/e2e/case/particleRenderer-emit-mesh-cone-scale-3d-rotation-life-seperate.ts @@ -29,7 +29,6 @@ WebGLEngine.create({ canvas: "canvas" }).then((engine) => { Logger.enable(); - engine.canvas.setResolution(1200, 800); const rootEntity = engine.sceneManager.activeScene.createRootEntity("Root"); diff --git a/e2e/case/particleRenderer-emit-mesh-cone-scale-3d-rotation.ts b/e2e/case/particleRenderer-emit-mesh-cone-scale-3d-rotation.ts index bd33ad9655..b6b93f8858 100644 --- a/e2e/case/particleRenderer-emit-mesh-cone-scale-3d-rotation.ts +++ b/e2e/case/particleRenderer-emit-mesh-cone-scale-3d-rotation.ts @@ -29,7 +29,6 @@ WebGLEngine.create({ canvas: "canvas" }).then((engine) => { Logger.enable(); - engine.canvas.setResolution(1200, 800); const rootEntity = engine.sceneManager.activeScene.createRootEntity("Root"); diff --git a/e2e/case/particleRenderer-emit-mesh-cone-scale-rotation-life-seperate.ts b/e2e/case/particleRenderer-emit-mesh-cone-scale-rotation-life-seperate.ts index 228d3df273..947c5e9323 100644 --- a/e2e/case/particleRenderer-emit-mesh-cone-scale-rotation-life-seperate.ts +++ b/e2e/case/particleRenderer-emit-mesh-cone-scale-rotation-life-seperate.ts @@ -29,7 +29,6 @@ WebGLEngine.create({ canvas: "canvas" }).then((engine) => { Logger.enable(); - engine.canvas.setResolution(1200, 800); const rootEntity = engine.sceneManager.activeScene.createRootEntity("Root"); diff --git a/e2e/case/particleRenderer-emit-mesh-cone-scale-rotation-life.ts b/e2e/case/particleRenderer-emit-mesh-cone-scale-rotation-life.ts index 3f6bafb491..1f166514be 100644 --- a/e2e/case/particleRenderer-emit-mesh-cone-scale-rotation-life.ts +++ b/e2e/case/particleRenderer-emit-mesh-cone-scale-rotation-life.ts @@ -29,7 +29,6 @@ WebGLEngine.create({ canvas: "canvas" }).then((engine) => { Logger.enable(); - engine.canvas.setResolution(1200, 800); const rootEntity = engine.sceneManager.activeScene.createRootEntity("Root"); diff --git a/e2e/case/particleRenderer-emit-mesh-cone-scale-rotation-world.ts b/e2e/case/particleRenderer-emit-mesh-cone-scale-rotation-world.ts index a04c6aea39..dcda6ea142 100644 --- a/e2e/case/particleRenderer-emit-mesh-cone-scale-rotation-world.ts +++ b/e2e/case/particleRenderer-emit-mesh-cone-scale-rotation-world.ts @@ -29,7 +29,6 @@ WebGLEngine.create({ canvas: "canvas" }).then((engine) => { Logger.enable(); - engine.canvas.setResolution(1200, 800); const rootEntity = engine.sceneManager.activeScene.createRootEntity("Root"); diff --git a/e2e/case/particleRenderer-emit-mesh-cone-scale-rotation.ts b/e2e/case/particleRenderer-emit-mesh-cone-scale-rotation.ts index 8840dbb501..912dd62913 100644 --- a/e2e/case/particleRenderer-emit-mesh-cone-scale-rotation.ts +++ b/e2e/case/particleRenderer-emit-mesh-cone-scale-rotation.ts @@ -29,7 +29,6 @@ WebGLEngine.create({ canvas: "canvas" }).then((engine) => { Logger.enable(); - engine.canvas.setResolution(1200, 800); const rootEntity = engine.sceneManager.activeScene.createRootEntity("Root"); diff --git a/e2e/case/particleRenderer-emit-mesh-cone.ts b/e2e/case/particleRenderer-emit-mesh-cone.ts index 5badd4c0d0..265d10699e 100644 --- a/e2e/case/particleRenderer-emit-mesh-cone.ts +++ b/e2e/case/particleRenderer-emit-mesh-cone.ts @@ -41,7 +41,6 @@ WebGLEngine.create({ canvas: "canvas" }).then((engine) => { Logger.enable(); - engine.canvas.setResolution(1200, 800); const rootEntity = engine.sceneManager.activeScene.createRootEntity("Root"); diff --git a/e2e/case/particleRenderer-emit-mesh-no-shape-world.ts b/e2e/case/particleRenderer-emit-mesh-no-shape-world.ts index 4e98220d0b..77ca228968 100644 --- a/e2e/case/particleRenderer-emit-mesh-no-shape-world.ts +++ b/e2e/case/particleRenderer-emit-mesh-no-shape-world.ts @@ -28,7 +28,6 @@ WebGLEngine.create({ canvas: "canvas" }).then((engine) => { Logger.enable(); - engine.canvas.setResolution(1200, 800); const rootEntity = engine.sceneManager.activeScene.createRootEntity("Root"); diff --git a/e2e/case/particleRenderer-emit-mesh-no-shape.ts b/e2e/case/particleRenderer-emit-mesh-no-shape.ts index 460f587f96..c47e03d989 100644 --- a/e2e/case/particleRenderer-emit-mesh-no-shape.ts +++ b/e2e/case/particleRenderer-emit-mesh-no-shape.ts @@ -28,7 +28,6 @@ WebGLEngine.create({ canvas: "canvas" }).then((engine) => { Logger.enable(); - engine.canvas.setResolution(1200, 800); const rootEntity = engine.sceneManager.activeScene.createRootEntity("Root"); diff --git a/e2e/case/particleRenderer-emit-mesh-rotation-life-curve.ts b/e2e/case/particleRenderer-emit-mesh-rotation-life-curve.ts index 03090dd034..d946107da7 100644 --- a/e2e/case/particleRenderer-emit-mesh-rotation-life-curve.ts +++ b/e2e/case/particleRenderer-emit-mesh-rotation-life-curve.ts @@ -32,8 +32,6 @@ type Mode = "constant" | "curve"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.setResolution(1200, 800); - const scene = engine.sceneManager.activeScene; scene.background.mode = BackgroundMode.SolidColor; scene.background.solidColor.set(0.15, 0.15, 0.18, 1); diff --git a/e2e/case/particleRenderer-fire.ts b/e2e/case/particleRenderer-fire.ts index 87c8b00eeb..6df2df3400 100644 --- a/e2e/case/particleRenderer-fire.ts +++ b/e2e/case/particleRenderer-fire.ts @@ -36,7 +36,6 @@ WebGLEngine.create({ canvas: "canvas" }).then((engine) => { Logger.enable(); - engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/particleRenderer-force.ts b/e2e/case/particleRenderer-force.ts index 2a9d97095f..3456f2c34b 100644 --- a/e2e/case/particleRenderer-force.ts +++ b/e2e/case/particleRenderer-force.ts @@ -31,7 +31,6 @@ WebGLEngine.create({ canvas: "canvas" }).then((engine) => { Logger.enable(); - engine.canvas.setResolution(1200, 800); const leftScene = engine.sceneManager.activeScene; const leftRootEntity = leftScene.createRootEntity(); diff --git a/e2e/case/particleRenderer-horizontal-billboard.ts b/e2e/case/particleRenderer-horizontal-billboard.ts index 7b4bc4c396..58a10c187c 100644 --- a/e2e/case/particleRenderer-horizontal-billboard.ts +++ b/e2e/case/particleRenderer-horizontal-billboard.ts @@ -20,7 +20,6 @@ WebGLEngine.create({ canvas: "canvas" }).then((engine) => { Logger.enable(); - engine.canvas.setResolution(1200, 800); const rootEntity = engine.sceneManager.activeScene.createRootEntity("Root"); diff --git a/e2e/case/particleRenderer-limitVelocity.ts b/e2e/case/particleRenderer-limitVelocity.ts index dd37e2abe3..a8f01712cf 100644 --- a/e2e/case/particleRenderer-limitVelocity.ts +++ b/e2e/case/particleRenderer-limitVelocity.ts @@ -28,8 +28,6 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.setResolution(1200, 800); - const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); scene.background.solidColor = new Color(0, 0, 0, 1); diff --git a/e2e/case/particleRenderer-noise.ts b/e2e/case/particleRenderer-noise.ts index 685ce96df6..2cbe0ed218 100644 --- a/e2e/case/particleRenderer-noise.ts +++ b/e2e/case/particleRenderer-noise.ts @@ -24,8 +24,6 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.setResolution(1200, 800); - const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); scene.background.solidColor = new Color(0, 0, 0, 1); diff --git a/e2e/case/particleRenderer-rateOverDistance.ts b/e2e/case/particleRenderer-rateOverDistance.ts index fdb941c730..533a8ea5e8 100644 --- a/e2e/case/particleRenderer-rateOverDistance.ts +++ b/e2e/case/particleRenderer-rateOverDistance.ts @@ -23,8 +23,6 @@ import { import { initScreenshot, updateForE2E } from "./.mockForE2E"; WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.setResolution(1200, 800); - const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); scene.background.solidColor = new Color(0, 0, 0, 1); diff --git a/e2e/case/particleRenderer-shape-mesh.ts b/e2e/case/particleRenderer-shape-mesh.ts index 4b82ae5a1d..5fd658231e 100644 --- a/e2e/case/particleRenderer-shape-mesh.ts +++ b/e2e/case/particleRenderer-shape-mesh.ts @@ -29,7 +29,6 @@ WebGLEngine.create({ graphicDeviceOptions: { webGLMode: WebGLMode.Auto } }).then((engine) => { Logger.enable(); - engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/particleRenderer-shape-transform.ts b/e2e/case/particleRenderer-shape-transform.ts index 73c2b20df2..a940712cf0 100644 --- a/e2e/case/particleRenderer-shape-transform.ts +++ b/e2e/case/particleRenderer-shape-transform.ts @@ -22,7 +22,6 @@ WebGLEngine.create({ canvas: "canvas" }).then((engine) => { Logger.enable(); - engine.canvas.setResolution(1200, 800); const rootEntity = engine.sceneManager.activeScene.createRootEntity("Root"); diff --git a/e2e/case/particleRenderer-sub-emitter.ts b/e2e/case/particleRenderer-sub-emitter.ts index cbb8fb5687..de59db1303 100644 --- a/e2e/case/particleRenderer-sub-emitter.ts +++ b/e2e/case/particleRenderer-sub-emitter.ts @@ -34,8 +34,6 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.setResolution(1200, 800); - const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); scene.background.solidColor = new Color(0, 0, 0, 1); diff --git a/e2e/case/particleRenderer-textureSheetAnimation.ts b/e2e/case/particleRenderer-textureSheetAnimation.ts index 606d94f5cf..475cda208f 100644 --- a/e2e/case/particleRenderer-textureSheetAnimation.ts +++ b/e2e/case/particleRenderer-textureSheetAnimation.ts @@ -24,7 +24,6 @@ WebGLEngine.create({ canvas: "canvas" }).then((engine) => { Logger.enable(); - engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const ambientLight = scene.ambientLight; diff --git a/e2e/case/physx-collision-group.ts b/e2e/case/physx-collision-group.ts index bbcdc7314e..94263f6d80 100644 --- a/e2e/case/physx-collision-group.ts +++ b/e2e/case/physx-collision-group.ts @@ -72,7 +72,6 @@ function createPhysicsSphere( } WebGLEngine.create({ canvas: "canvas", physics: new PhysXPhysics() }).then((engine) => { - engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity("root"); diff --git a/e2e/case/physx-collision.ts b/e2e/case/physx-collision.ts index 68cc19f58e..a7e2989974 100644 --- a/e2e/case/physx-collision.ts +++ b/e2e/case/physx-collision.ts @@ -57,7 +57,6 @@ function addBox(rootEntity: Entity, cubeSize: number, x: number, y: number, z: n } WebGLEngine.create({ canvas: "canvas", physics: new PhysXPhysics() }).then((engine) => { - engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity("root"); diff --git a/e2e/case/physx-customUrl.ts b/e2e/case/physx-customUrl.ts index a416c608cc..ddc1d1a752 100644 --- a/e2e/case/physx-customUrl.ts +++ b/e2e/case/physx-customUrl.ts @@ -62,7 +62,6 @@ WebGLEngine.create({ wasmModeUrl: "../physx.release.js" }) }).then((engine) => { - engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity("root"); diff --git a/e2e/case/physx-deferred-contact.ts b/e2e/case/physx-deferred-contact.ts index 4f5d27ea59..a3e903a3d5 100644 --- a/e2e/case/physx-deferred-contact.ts +++ b/e2e/case/physx-deferred-contact.ts @@ -18,7 +18,6 @@ import { PhysXPhysics } from "@galacean/engine-physics-physx"; import { initScreenshot, updateForE2E } from "./.mockForE2E"; WebGLEngine.create({ canvas: "canvas", physics: new PhysXPhysics() }).then((engine) => { - engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity("root"); diff --git a/e2e/case/physx-mesh-collider-data.ts b/e2e/case/physx-mesh-collider-data.ts index 6e0a2e252d..2e143e41c6 100644 --- a/e2e/case/physx-mesh-collider-data.ts +++ b/e2e/case/physx-mesh-collider-data.ts @@ -219,7 +219,6 @@ class CollisionScript extends Script { } WebGLEngine.create({ canvas: "canvas", physics: new PhysXPhysics() }).then((engine) => { - engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity("root"); diff --git a/e2e/case/physx-mesh-collider.ts b/e2e/case/physx-mesh-collider.ts index f99b2e2079..f3d5f63701 100644 --- a/e2e/case/physx-mesh-collider.ts +++ b/e2e/case/physx-mesh-collider.ts @@ -57,7 +57,6 @@ function createFallingBox( const physics = new PhysXPhysics(PhysXRuntimeMode.WebAssemblySIMD); WebGLEngine.create({ canvas: "canvas", physics }).then((engine) => { - engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity("root"); diff --git a/e2e/case/primitive-capsule.ts b/e2e/case/primitive-capsule.ts index ce897be332..ae9da8711a 100644 --- a/e2e/case/primitive-capsule.ts +++ b/e2e/case/primitive-capsule.ts @@ -20,7 +20,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.setResolution(2400, 1600); + engine.canvas.setAutoResolution(2); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/primitive-cone.ts b/e2e/case/primitive-cone.ts index 6aac691615..8eb2680ae6 100644 --- a/e2e/case/primitive-cone.ts +++ b/e2e/case/primitive-cone.ts @@ -20,7 +20,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.setResolution(2400, 1600); + engine.canvas.setAutoResolution(2); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/primitive-cuboid.ts b/e2e/case/primitive-cuboid.ts index d25c152700..dd9e655672 100644 --- a/e2e/case/primitive-cuboid.ts +++ b/e2e/case/primitive-cuboid.ts @@ -20,7 +20,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.setResolution(2400, 1600); + engine.canvas.setAutoResolution(2); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/primitive-cylinder.ts b/e2e/case/primitive-cylinder.ts index 7ded075f44..77cf1c0043 100644 --- a/e2e/case/primitive-cylinder.ts +++ b/e2e/case/primitive-cylinder.ts @@ -20,7 +20,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.setResolution(2400, 1600); + engine.canvas.setAutoResolution(2); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/primitive-plane.ts b/e2e/case/primitive-plane.ts index d7c0aeac7e..f56cf5f085 100644 --- a/e2e/case/primitive-plane.ts +++ b/e2e/case/primitive-plane.ts @@ -20,7 +20,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.setResolution(2400, 1600); + engine.canvas.setAutoResolution(2); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/primitive-sphere.ts b/e2e/case/primitive-sphere.ts index 1bc3fb7783..709da5c504 100644 --- a/e2e/case/primitive-sphere.ts +++ b/e2e/case/primitive-sphere.ts @@ -20,7 +20,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.setResolution(2400, 1600); + engine.canvas.setAutoResolution(2); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/primitive-torus.ts b/e2e/case/primitive-torus.ts index d6ddeaedde..b658731611 100644 --- a/e2e/case/primitive-torus.ts +++ b/e2e/case/primitive-torus.ts @@ -20,7 +20,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.setResolution(2400, 1600); + engine.canvas.setAutoResolution(2); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/shader-mrt.ts b/e2e/case/shader-mrt.ts index 2e05503b8b..39513e1c12 100644 --- a/e2e/case/shader-mrt.ts +++ b/e2e/case/shader-mrt.ts @@ -83,8 +83,6 @@ const shaderSource = `Shader "/custom.shader" { Logger.enable(); WebGLEngine.create({ canvas: "canvas", shaderCompiler }).then((engine) => { - engine.canvas.setResolution(1200, 800); - const shader = Shader.create(shaderSource); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/shader-renderState.ts b/e2e/case/shader-renderState.ts index c696301265..c968c12d4e 100644 --- a/e2e/case/shader-renderState.ts +++ b/e2e/case/shader-renderState.ts @@ -85,8 +85,6 @@ const shaderSource = `Shader "Test RenderState" { Logger.enable(); WebGLEngine.create({ canvas: "canvas", shaderCompiler }).then((engine) => { - engine.canvas.setResolution(1200, 800); - const shader = Shader.create(shaderSource); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/shadow-basic.ts b/e2e/case/shadow-basic.ts index 380517d237..cea64a22bc 100644 --- a/e2e/case/shadow-basic.ts +++ b/e2e/case/shadow-basic.ts @@ -19,7 +19,6 @@ import { import { initScreenshot, updateForE2E } from "./.mockForE2E"; WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); scene.shadowResolution = ShadowResolution.Medium; diff --git a/e2e/case/shadow-transparent.ts b/e2e/case/shadow-transparent.ts index 33d0a11f87..8b03ecb580 100644 --- a/e2e/case/shadow-transparent.ts +++ b/e2e/case/shadow-transparent.ts @@ -26,7 +26,6 @@ WebGLEngine.create({ webGLMode: WebGLMode.WebGL2 } }).then((engine) => { - engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); scene.shadowResolution = ShadowResolution.Medium; diff --git a/e2e/case/spriteMask-customStencil.ts b/e2e/case/spriteMask-customStencil.ts index eaeb317fee..2feb29411a 100644 --- a/e2e/case/spriteMask-customStencil.ts +++ b/e2e/case/spriteMask-customStencil.ts @@ -109,8 +109,6 @@ const customStencilShaderSource = `Shader "CustomStencilSprite" { // Create engine WebGLEngine.create({ canvas: "canvas", shaderCompiler: new ShaderCompiler() }).then((engine) => { - engine.canvas.setResolution(1200, 800); - // Register the custom stencil shader once. const customStencilShader = Shader.create(customStencilShaderSource); diff --git a/e2e/case/text-character-spacing.ts b/e2e/case/text-character-spacing.ts index 8516a86b58..87fdb3058b 100644 --- a/e2e/case/text-character-spacing.ts +++ b/e2e/case/text-character-spacing.ts @@ -14,7 +14,6 @@ import { import { initScreenshot, updateForE2E } from "./.mockForE2E"; WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/text-typed.ts b/e2e/case/text-typed.ts index d3286f716e..bbc58a50c7 100644 --- a/e2e/case/text-typed.ts +++ b/e2e/case/text-typed.ts @@ -19,7 +19,6 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/texture-R8G8.ts b/e2e/case/texture-R8G8.ts index fcf496f6c0..0bcce16c57 100644 --- a/e2e/case/texture-R8G8.ts +++ b/e2e/case/texture-R8G8.ts @@ -16,7 +16,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.setResolution(2400, 1600); + engine.canvas.setAutoResolution(2); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/texture-hdr-ktx2.ts b/e2e/case/texture-hdr-ktx2.ts index d2773f2ed8..9442c6ca6f 100644 --- a/e2e/case/texture-hdr-ktx2.ts +++ b/e2e/case/texture-hdr-ktx2.ts @@ -55,7 +55,7 @@ const shaderSource = `Shader "test-hdr" { }`; WebGLEngine.create({ canvas: "canvas", shaderCompiler, ktx2Loader: { workerCount: 0 } }).then((engine) => { - engine.canvas.setResolution(2400, 1600); + engine.canvas.setAutoResolution(2); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/texture-hdr.ts b/e2e/case/texture-hdr.ts index 6cb26564f3..2c9531a82b 100644 --- a/e2e/case/texture-hdr.ts +++ b/e2e/case/texture-hdr.ts @@ -18,7 +18,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; Logger.enable(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.setResolution(2400, 1600); + engine.canvas.setAutoResolution(2); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/texture-sRGB-KTX2.ts b/e2e/case/texture-sRGB-KTX2.ts index fea0311f29..99803b82aa 100644 --- a/e2e/case/texture-sRGB-KTX2.ts +++ b/e2e/case/texture-sRGB-KTX2.ts @@ -15,7 +15,7 @@ import { import { initScreenshot, updateForE2E } from "./.mockForE2E"; WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.setResolution(2400, 1600); + engine.canvas.setAutoResolution(2); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/trailRenderer-basic.ts b/e2e/case/trailRenderer-basic.ts index 38a6eb7839..687869e330 100644 --- a/e2e/case/trailRenderer-basic.ts +++ b/e2e/case/trailRenderer-basic.ts @@ -168,7 +168,6 @@ WebGLEngine.create({ canvas: "canvas" }).then((engine) => { Logger.enable(); - engine.canvas.setResolution(1200, 800); const scene = engine.sceneManager.activeScene; const rootEntity = scene.createRootEntity(); diff --git a/e2e/case/ui-batch-order.ts b/e2e/case/ui-batch-order.ts index 4942211b4d..44cca1b04c 100644 --- a/e2e/case/ui-batch-order.ts +++ b/e2e/case/ui-batch-order.ts @@ -16,7 +16,7 @@ import { initScreenshot, updateForE2E } from "./.mockForE2E"; registerGUI(); WebGLEngine.create({ canvas: "canvas" }).then((engine) => { - engine.canvas.setResolution(2400, 1600); + engine.canvas.setAutoResolution(2); const scene = engine.sceneManager.activeScene; const root = scene.createRootEntity("root"); From 7a74b119f4d2fa582dc540683643301e373660f2 Mon Sep 17 00:00:00 2001 From: "chenmo.gl" Date: Thu, 2 Jul 2026 11:47:08 +0800 Subject: [PATCH 10/37] docs(core): drop redundant Read-only note from width/height remarks The getter-only declaration already enforces read-only at the type level; keep only the pointer to setResolution/setAutoResolution. --- packages/core/src/Canvas.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/src/Canvas.ts b/packages/core/src/Canvas.ts index 88c44bdb5d..d0dd9f6680 100644 --- a/packages/core/src/Canvas.ts +++ b/packages/core/src/Canvas.ts @@ -12,7 +12,7 @@ export abstract class Canvas { /** * The render buffer width of the canvas, in pixels. - * @remarks Read-only. Use `setResolution` or `setAutoResolution` to change the render resolution. + * @remarks Use `setResolution` or `setAutoResolution` to change the render resolution. */ get width(): number { return this._width; @@ -20,7 +20,7 @@ export abstract class Canvas { /** * The render buffer height of the canvas, in pixels. - * @remarks Read-only. Use `setResolution` or `setAutoResolution` to change the render resolution. + * @remarks Use `setResolution` or `setAutoResolution` to change the render resolution. */ get height(): number { return this._height; From 4a23dd9910b48aaf4c70101bcffca4e3d99b362a Mon Sep 17 00:00:00 2001 From: "chenmo.gl" Date: Thu, 2 Jul 2026 11:48:12 +0800 Subject: [PATCH 11/37] docs(core): drop migration pointer from width/height docs Migration guidance belongs in the changelog (already covered by the BREAKING CHANGE note), not in the doc of the current API. --- packages/core/src/Canvas.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/core/src/Canvas.ts b/packages/core/src/Canvas.ts index d0dd9f6680..84e4c7dd27 100644 --- a/packages/core/src/Canvas.ts +++ b/packages/core/src/Canvas.ts @@ -12,7 +12,6 @@ export abstract class Canvas { /** * The render buffer width of the canvas, in pixels. - * @remarks Use `setResolution` or `setAutoResolution` to change the render resolution. */ get width(): number { return this._width; @@ -20,7 +19,6 @@ export abstract class Canvas { /** * The render buffer height of the canvas, in pixels. - * @remarks Use `setResolution` or `setAutoResolution` to change the render resolution. */ get height(): number { return this._height; @@ -28,7 +26,6 @@ export abstract class Canvas { /** * Lock the render buffer to an explicit resolution and stop following the display size. - * @remarks Resolution is set atomically; width and height always change together in a single frame. * @param width - Render buffer width in pixels * @param height - Render buffer height in pixels */ From 83dd8c7346c4a87452aa4dfa9b6e9ce69a3226d0 Mon Sep 17 00:00:00 2001 From: "chenmo.gl" Date: Thu, 2 Jul 2026 15:54:14 +0800 Subject: [PATCH 12/37] docs: consolidate resize comments; validate auto-resolution scale Give each rationale a single authoritative home (resize timing on the _pumpPendingResize declaration, 0x0 deferral on the WebCanvas guard) and drop the duplicated or restating comments. Fix two inaccurate ones: the retry mechanism is the per-frame pump, not a later observe, and the pump is called by the platform engine, not core Engine. setAutoResolution now rejects non-finite or <= 0 scales; scale 0 previously slipped past the zero-layout guard and wrote a 0x0 buffer. --- packages/core/src/Canvas.ts | 8 ++++---- packages/rhi-webgl/src/WebCanvas.ts | 8 ++++++-- packages/rhi-webgl/src/WebGLEngine.ts | 7 +------ tests/src/rhi-webgl/WebGLEngine.test.ts | 4 +++- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/packages/core/src/Canvas.ts b/packages/core/src/Canvas.ts index 84e4c7dd27..3d930e79e6 100644 --- a/packages/core/src/Canvas.ts +++ b/packages/core/src/Canvas.ts @@ -1,7 +1,7 @@ import { UpdateFlagManager } from "./UpdateFlagManager"; /** - * Canvas. + * Platform-neutral render surface; subclasses bind it to a concrete backing (HTMLCanvasElement, OffscreenCanvas, ...). */ export abstract class Canvas { /* @internal */ @@ -40,11 +40,11 @@ export abstract class Canvas { /** * Make the render buffer automatically follow the canvas display size. - * @param scale - Performance multiplier on the device pixel ratio (1 = clear, 0.7 = save GPU, 2 = supersample), defaults to `1` + * @param scale - Multiplier applied to the device pixel ratio (1 = native sharpness, 0.7 = save GPU, 2 = supersample), defaults to `1` */ abstract setAutoResolution(scale?: number): void; - // Sole atomic entry for render-buffer size: width and height change together in one dispatch. + // Sole mutation path for render-buffer size — one dispatch, so listeners never observe a half-updated size. protected _setSize(width: number, height: number): void { if (this._width !== width || this._height !== height) { this._width = width; @@ -54,7 +54,7 @@ export abstract class Canvas { } } - // Applied by the engine each frame, before rendering, so resize lands in the render frame. + // Called by the platform engine each frame, before rendering, so resize and render land in the same frame (no white flash). abstract _pumpPendingResize(): void; abstract _destroy(): void; diff --git a/packages/rhi-webgl/src/WebCanvas.ts b/packages/rhi-webgl/src/WebCanvas.ts index 36621e6036..71974e2bc3 100644 --- a/packages/rhi-webgl/src/WebCanvas.ts +++ b/packages/rhi-webgl/src/WebCanvas.ts @@ -47,13 +47,17 @@ export class WebCanvas extends Canvas { } override setAutoResolution(scale: number = 1): void { + if (!Number.isFinite(scale) || scale <= 0) { + throw new Error(`WebCanvas.setAutoResolution: invalid scale ${scale}`); + } + // TODO: OffscreenCanvas has no display size; warn once. if (this.isOffscreenCanvas()) return; this._autoResolutionScale = scale; if (!this._resizeObserver) { - // Flag only; the resize is applied in `_pumpPendingResize` so it lands in the render frame. + // Flag only; applied in `_pumpPendingResize`. this._resizeObserver = new ResizeObserver(() => (this._pendingResize = true)); this._resizeObserver.observe(this._webCanvas); } @@ -86,7 +90,7 @@ export class WebCanvas extends Canvas { const webCanvas = this._webCanvas; // Skip while the canvas has no layout size yet (e.g. not mounted); keep the flag so a later - // observe with a real size applies the resize, never setting a 0x0 buffer. + // frame's pump retries once layout exists — an unmounted canvas never becomes a 0x0 buffer. if (webCanvas.clientWidth === 0 || webCanvas.clientHeight === 0) return; this._pendingResize = false; diff --git a/packages/rhi-webgl/src/WebGLEngine.ts b/packages/rhi-webgl/src/WebGLEngine.ts index 992455bb88..25004f1f6e 100644 --- a/packages/rhi-webgl/src/WebGLEngine.ts +++ b/packages/rhi-webgl/src/WebGLEngine.ts @@ -16,10 +16,7 @@ export class WebGLEngine extends Engine { const webCanvas = new WebCanvas(typeof canvas === "string" ? document.getElementById(canvas) : canvas); const webGLGraphicDevice = new WebGLGraphicDevice(configuration.graphicDeviceOptions); const engine = new WebGLEngine(webCanvas, webGLGraphicDevice, configuration); - // Release the canvas resize observer when the engine shuts down. engine.once(EngineEventType.Shutdown, WebGLEngine._releaseCanvas); - // Follow the display size by default; the first resize is deferred to update() and skipped while - // the canvas has no layout size, so an unmounted canvas never gets a 0x0 buffer. if (configuration.autoResize ?? true) { webCanvas.setAutoResolution(); } @@ -45,11 +42,10 @@ export class WebGLEngine extends Engine { /** * Enable automatic canvas resizing. - * @deprecated Use `engine.canvas.setAutoResolution()` instead. Auto-resolution is a canvas capability. + * @deprecated Use `engine.canvas.setAutoResolution()` instead. * @param pixelRatio - Deprecated; the device pixel ratio is applied automatically */ enableAutoResize(pixelRatio?: number): void { - // The old pixelRatio replaced dpr; the new model applies dpr automatically, so drop the argument. this.canvas.setAutoResolution(); } @@ -63,7 +59,6 @@ export class WebGLEngine extends Engine { } override update(): void { - // Apply pending resize before rendering so both land in the same frame (no white flash). this.canvas._pumpPendingResize(); super.update(); } diff --git a/tests/src/rhi-webgl/WebGLEngine.test.ts b/tests/src/rhi-webgl/WebGLEngine.test.ts index 2f525fbaa9..499227bba8 100644 --- a/tests/src/rhi-webgl/WebGLEngine.test.ts +++ b/tests/src/rhi-webgl/WebGLEngine.test.ts @@ -138,9 +138,11 @@ describe("webgl engine test", () => { expect(webCanvas.width).toBe(320); expect(webCanvas.height).toBe(240); - // setResolution rejects invalid sizes. + // setResolution rejects invalid sizes; setAutoResolution rejects invalid scales. expect(() => webCanvas.setResolution(0, 100)).to.throw(); expect(() => webCanvas.setResolution(-1, 100)).to.throw(); + expect(() => webCanvas.setAutoResolution(0)).to.throw(); + expect(() => webCanvas.setAutoResolution(-1)).to.throw(); // destroy releases the observer. webCanvas.setAutoResolution(); From b2fa6fee123a379ec2381e390d4eca9bae4543ec Mon Sep 17 00:00:00 2001 From: "chenmo.gl" Date: Fri, 3 Jul 2026 00:28:12 +0800 Subject: [PATCH 13/37] docs: fix comment defects introduced by the comment audit itself MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Revert the Canvas class doc to its original form — the rewritten one leaked web types into the platform-neutral core package, restated the abstract keyword, and duplicated the WebCanvas doc. Fix the two comments still describing the retry mechanism as a later observe (it is the per-frame pump), and drop the dead pixelRatio concept from the InputManager test note. --- packages/core/src/Canvas.ts | 2 +- tests/src/core/input/InputManager.test.ts | 2 +- tests/src/rhi-webgl/WebGLEngine.test.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/core/src/Canvas.ts b/packages/core/src/Canvas.ts index 3d930e79e6..1fb8ecf398 100644 --- a/packages/core/src/Canvas.ts +++ b/packages/core/src/Canvas.ts @@ -1,7 +1,7 @@ import { UpdateFlagManager } from "./UpdateFlagManager"; /** - * Platform-neutral render surface; subclasses bind it to a concrete backing (HTMLCanvasElement, OffscreenCanvas, ...). + * Canvas. */ export abstract class Canvas { /* @internal */ diff --git a/tests/src/core/input/InputManager.test.ts b/tests/src/core/input/InputManager.test.ts index f594168480..c552d39465 100644 --- a/tests/src/core/input/InputManager.test.ts +++ b/tests/src/core/input/InputManager.test.ts @@ -23,7 +23,7 @@ body.appendChild(canvasDOM); describe("InputManager", async () => { const engine = await WebGLEngine.create({ canvas: canvasDOM, physics: new PhysXPhysics() }); const { inputManager, canvas } = engine; - // Canvas is 5x5 CSS px; lock the render buffer to the equivalent size at pixelRatio 2. + // Twice the 5x5 CSS size, so pointer coordinates map at a 2x ratio. canvas.setResolution(10, 10); const scene = engine.sceneManager.scenes[0]; const root = scene.createRootEntity("root"); diff --git a/tests/src/rhi-webgl/WebGLEngine.test.ts b/tests/src/rhi-webgl/WebGLEngine.test.ts index 499227bba8..f43008cc29 100644 --- a/tests/src/rhi-webgl/WebGLEngine.test.ts +++ b/tests/src/rhi-webgl/WebGLEngine.test.ts @@ -130,7 +130,7 @@ describe("webgl engine test", () => { Object.defineProperty(canvas, "clientWidth", { value: 0, configurable: true }); (webCanvas as any)._pendingResize = true; webCanvas._pumpPendingResize(); - expect((webCanvas as any)._pendingResize).toBe(true); // kept for a later valid observe + expect((webCanvas as any)._pendingResize).toBe(true); // kept so a later frame's pump retries // setResolution locks a fixed size and exits auto mode (observer released). webCanvas.setResolution(320, 240); From 6a6b15bdcaab5a0e517b811e40bbc24015047177 Mon Sep 17 00:00:00 2001 From: "chenmo.gl" Date: Fri, 3 Jul 2026 00:31:49 +0800 Subject: [PATCH 14/37] docs(core): describe Canvas from the user's perspective MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A user reaching this class via engine.canvas needs to know what it is and what it is for — the engine's render target and the home of the render-buffer resolution — not a placeholder or an architecture note. --- packages/core/src/Canvas.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/Canvas.ts b/packages/core/src/Canvas.ts index 1fb8ecf398..24fce98ebd 100644 --- a/packages/core/src/Canvas.ts +++ b/packages/core/src/Canvas.ts @@ -1,7 +1,7 @@ import { UpdateFlagManager } from "./UpdateFlagManager"; /** - * Canvas. + * The canvas the engine renders to; owns the render-buffer resolution. */ export abstract class Canvas { /* @internal */ From f7b5be9b25989508d82c713db54053bb5e862395 Mon Sep 17 00:00:00 2001 From: "chenmo.gl" Date: Fri, 3 Jul 2026 00:50:23 +0800 Subject: [PATCH 15/37] docs: rewrite comments from the reader's perspective Every public doc now answers what the hovering user needs (the engine.canvas gateway points to the resolution methods, disableAutoResize states that the buffer freezes at its current size, pixelRatio says it is ignored and how to get a custom multiplier, throwing contracts are documented). Internal notes carry the caller contracts subclass authors need, and the permanent rationale is split out of deletable TODOs. --- packages/core/src/Canvas.ts | 4 ++++ packages/rhi-webgl/src/WebCanvas.ts | 6 ++++-- packages/rhi-webgl/src/WebGLEngine.ts | 14 +++++++++----- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/packages/core/src/Canvas.ts b/packages/core/src/Canvas.ts index 24fce98ebd..e4c5b5cca3 100644 --- a/packages/core/src/Canvas.ts +++ b/packages/core/src/Canvas.ts @@ -28,6 +28,7 @@ export abstract class Canvas { * Lock the render buffer to an explicit resolution and stop following the display size. * @param width - Render buffer width in pixels * @param height - Render buffer height in pixels + * @throws Error if width or height is not a positive finite number */ setResolution(width: number, height: number): void { if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) { @@ -41,6 +42,7 @@ export abstract class Canvas { /** * Make the render buffer automatically follow the canvas display size. * @param scale - Multiplier applied to the device pixel ratio (1 = native sharpness, 0.7 = save GPU, 2 = supersample), defaults to `1` + * @throws Error if scale is not a positive finite number */ abstract setAutoResolution(scale?: number): void; @@ -57,10 +59,12 @@ export abstract class Canvas { // Called by the platform engine each frame, before rendering, so resize and render land in the same frame (no white flash). abstract _pumpPendingResize(): void; + // Called by the platform engine on shutdown; release any subscriptions (e.g. auto-resize). abstract _destroy(): void; // Tear down the auto-resize subscription; no-op for platforms without an auto mode. protected _exitAutoResize(): void {} + // Invoked by _setSize after the cached size changes; apply the new size to the native surface. protected abstract _onSizeChanged(width: number, height: number): void; } diff --git a/packages/rhi-webgl/src/WebCanvas.ts b/packages/rhi-webgl/src/WebCanvas.ts index 71974e2bc3..48c4f04b10 100644 --- a/packages/rhi-webgl/src/WebCanvas.ts +++ b/packages/rhi-webgl/src/WebCanvas.ts @@ -51,7 +51,7 @@ export class WebCanvas extends Canvas { throw new Error(`WebCanvas.setAutoResolution: invalid scale ${scale}`); } - // TODO: OffscreenCanvas has no display size; warn once. + // OffscreenCanvas has no display size to follow — auto mode is a silent no-op. TODO: warn once. if (this.isOffscreenCanvas()) return; this._autoResolutionScale = scale; @@ -95,7 +95,9 @@ export class WebCanvas extends Canvas { this._pendingResize = false; const pixelRatio = window.devicePixelRatio * this._autoResolutionScale; - // Round so the cached size matches the integer buffer; TODO: use `devicePixelContentBoxSize`. + // Round so the cached size matches the integer buffer. TODO: `devicePixelContentBoxSize` would give + // exact device pixels (no dpr rounding error), but it arrives on observer entries — requires moving + // size capture into the callback. this._setSize(Math.round(webCanvas.clientWidth * pixelRatio), Math.round(webCanvas.clientHeight * pixelRatio)); } diff --git a/packages/rhi-webgl/src/WebGLEngine.ts b/packages/rhi-webgl/src/WebGLEngine.ts index 25004f1f6e..65638fe3a5 100644 --- a/packages/rhi-webgl/src/WebGLEngine.ts +++ b/packages/rhi-webgl/src/WebGLEngine.ts @@ -3,7 +3,7 @@ import { WebGLGraphicDevice, WebGLGraphicDeviceOptions } from "./"; import { WebCanvas } from "./WebCanvas"; /** - * WebGL platform engine,support includes WebGL1.0 and WebGL2.0. + * WebGL platform engine, supports WebGL 1.0 and WebGL 2.0. */ export class WebGLEngine extends Engine { /** @@ -28,12 +28,14 @@ export class WebGLEngine extends Engine { }); } + // Core Engine never destroys the canvas; the platform engine owns teardown. private static _releaseCanvas(engine: WebGLEngine): void { engine.canvas._destroy(); } /** - * Web canvas. + * The canvas the engine renders to; call `setResolution()` or `setAutoResolution()` on it to control + * the render-buffer resolution. */ override get canvas(): WebCanvas { // @ts-ignore @@ -43,14 +45,15 @@ export class WebGLEngine extends Engine { /** * Enable automatic canvas resizing. * @deprecated Use `engine.canvas.setAutoResolution()` instead. - * @param pixelRatio - Deprecated; the device pixel ratio is applied automatically + * @param pixelRatio - Ignored; the device pixel ratio is applied automatically. For a custom multiplier + * on top of it, use `engine.canvas.setAutoResolution(scale)` */ enableAutoResize(pixelRatio?: number): void { this.canvas.setAutoResolution(); } /** - * Disable automatic canvas resizing. + * Lock the render buffer at its current size; it stops following the display size. * @deprecated Use `engine.canvas.setResolution(width, height)` to lock a fixed resolution instead. */ disableAutoResize(): void { @@ -59,13 +62,14 @@ export class WebGLEngine extends Engine { } override update(): void { + // Must run before super.update() — see Canvas._pumpPendingResize for why. this.canvas._pumpPendingResize(); super.update(); } } /** - * WebGL engine configuration. + * Options for `WebGLEngine.create()`. */ export interface WebGLEngineConfiguration extends EngineConfiguration { /** Canvas element or canvas id. */ From 90d5ce0a17c5e2af45d02accdd48b8caeadfe281 Mon Sep 17 00:00:00 2001 From: "chenmo.gl" Date: Sat, 4 Jul 2026 15:56:11 +0800 Subject: [PATCH 16/37] docs(core): align @throws format with the repo's existing convention Match the block-tag style already used in Shader.ts (@throws on its own line, description below) rather than the inline form. --- packages/core/src/Canvas.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/core/src/Canvas.ts b/packages/core/src/Canvas.ts index e4c5b5cca3..76daebfa7d 100644 --- a/packages/core/src/Canvas.ts +++ b/packages/core/src/Canvas.ts @@ -28,7 +28,9 @@ export abstract class Canvas { * Lock the render buffer to an explicit resolution and stop following the display size. * @param width - Render buffer width in pixels * @param height - Render buffer height in pixels - * @throws Error if width or height is not a positive finite number + * + * @throws + * Throw an error if width or height is not a positive finite number. */ setResolution(width: number, height: number): void { if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) { @@ -42,7 +44,9 @@ export abstract class Canvas { /** * Make the render buffer automatically follow the canvas display size. * @param scale - Multiplier applied to the device pixel ratio (1 = native sharpness, 0.7 = save GPU, 2 = supersample), defaults to `1` - * @throws Error if scale is not a positive finite number + * + * @throws + * Throw an error if scale is not a positive finite number. */ abstract setAutoResolution(scale?: number): void; From 4350dd8da40c61a0b4e5a81ee4e0d442e55d2dc4 Mon Sep 17 00:00:00 2001 From: "chenmo.gl" Date: Sat, 4 Jul 2026 16:08:04 +0800 Subject: [PATCH 17/37] docs(core): drop self-evident comments on internal size methods _setSize / _pumpPendingResize / _destroy / _onSizeChanged are short and named clearly enough that the added contract comments only restated the signature. Keep only the note on the empty _exitAutoResize body, which otherwise reads like a missing implementation. --- packages/core/src/Canvas.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/core/src/Canvas.ts b/packages/core/src/Canvas.ts index 76daebfa7d..d361e96e34 100644 --- a/packages/core/src/Canvas.ts +++ b/packages/core/src/Canvas.ts @@ -50,7 +50,6 @@ export abstract class Canvas { */ abstract setAutoResolution(scale?: number): void; - // Sole mutation path for render-buffer size — one dispatch, so listeners never observe a half-updated size. protected _setSize(width: number, height: number): void { if (this._width !== width || this._height !== height) { this._width = width; @@ -60,15 +59,12 @@ export abstract class Canvas { } } - // Called by the platform engine each frame, before rendering, so resize and render land in the same frame (no white flash). abstract _pumpPendingResize(): void; - // Called by the platform engine on shutdown; release any subscriptions (e.g. auto-resize). abstract _destroy(): void; - // Tear down the auto-resize subscription; no-op for platforms without an auto mode. + // Default no-op; overridden by platforms that have an auto-resize mode to tear down. protected _exitAutoResize(): void {} - // Invoked by _setSize after the cached size changes; apply the new size to the native surface. protected abstract _onSizeChanged(width: number, height: number): void; } From 2fb80a87c3b86908c1e206040466e055fbf2cd3f Mon Sep 17 00:00:00 2001 From: "chenmo.gl" Date: Sat, 4 Jul 2026 16:09:22 +0800 Subject: [PATCH 18/37] docs(core): drop the _exitAutoResize note An empty protected body is a clear default-no-op override point on its own. --- packages/core/src/Canvas.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/core/src/Canvas.ts b/packages/core/src/Canvas.ts index d361e96e34..b4bbcd4622 100644 --- a/packages/core/src/Canvas.ts +++ b/packages/core/src/Canvas.ts @@ -63,7 +63,6 @@ export abstract class Canvas { abstract _destroy(): void; - // Default no-op; overridden by platforms that have an auto-resize mode to tear down. protected _exitAutoResize(): void {} protected abstract _onSizeChanged(width: number, height: number): void; From 7a5813fe4528a272a61428a4798c676713db2c03 Mon Sep 17 00:00:00 2001 From: "chenmo.gl" Date: Sat, 4 Jul 2026 16:34:51 +0800 Subject: [PATCH 19/37] docs(core): frame width/height as canvas resolution components MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit They are the width and height of the canvas resolution, the same value setResolution writes — not a separate size concept. --- packages/core/src/Canvas.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/src/Canvas.ts b/packages/core/src/Canvas.ts index b4bbcd4622..81a4487c17 100644 --- a/packages/core/src/Canvas.ts +++ b/packages/core/src/Canvas.ts @@ -11,14 +11,14 @@ export abstract class Canvas { private _height: number = 0; /** - * The render buffer width of the canvas, in pixels. + * The width component of the canvas resolution, in pixels. */ get width(): number { return this._width; } /** - * The render buffer height of the canvas, in pixels. + * The height component of the canvas resolution, in pixels. */ get height(): number { return this._height; From ddbe7a611a5cc668cfefefb4ef3022825d0792d8 Mon Sep 17 00:00:00 2001 From: "chenmo.gl" Date: Sat, 4 Jul 2026 17:05:03 +0800 Subject: [PATCH 20/37] refactor(rhi-webgl): make isOffscreenCanvas internal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It has no external or cross-package consumers — the only cross-package need (InputManager) inlines its own instanceof check — and it exposes an implementation detail (the underlying canvas type) rather than a user contract. Rename to _isOffscreenCanvas + @internal; it stays a package -internal dedup helper for WebCanvas and WebGLGraphicDevice. --- packages/rhi-webgl/src/WebCanvas.ts | 11 +++++------ packages/rhi-webgl/src/WebGLGraphicDevice.ts | 4 ++-- tests/src/rhi-webgl/WebCanvas.test.ts | 10 +++++----- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/packages/rhi-webgl/src/WebCanvas.ts b/packages/rhi-webgl/src/WebCanvas.ts index 48c4f04b10..3b8cb98546 100644 --- a/packages/rhi-webgl/src/WebCanvas.ts +++ b/packages/rhi-webgl/src/WebCanvas.ts @@ -21,7 +21,7 @@ export class WebCanvas extends Canvas { */ get scale(): Vector2 { const webCanvas = this._webCanvas; - if (!this.isOffscreenCanvas()) { + if (!this._isOffscreenCanvas()) { this._scale.set( (webCanvas.clientWidth * devicePixelRatio) / webCanvas.width, (webCanvas.clientHeight * devicePixelRatio) / webCanvas.height @@ -32,17 +32,16 @@ export class WebCanvas extends Canvas { set scale(value: Vector2) { const webCanvas = this._webCanvas; - if (!this.isOffscreenCanvas()) { + if (!this._isOffscreenCanvas()) { webCanvas.style.transformOrigin = `left top`; webCanvas.style.transform = `scale(${value.x}, ${value.y})`; } } /** - * Whether the underlying canvas is an OffscreenCanvas. - * @returns Whether it is an OffscreenCanvas + * @internal */ - isOffscreenCanvas(): boolean { + _isOffscreenCanvas(): boolean { return typeof OffscreenCanvas !== "undefined" && this._webCanvas instanceof OffscreenCanvas; } @@ -52,7 +51,7 @@ export class WebCanvas extends Canvas { } // OffscreenCanvas has no display size to follow — auto mode is a silent no-op. TODO: warn once. - if (this.isOffscreenCanvas()) return; + if (this._isOffscreenCanvas()) return; this._autoResolutionScale = scale; diff --git a/packages/rhi-webgl/src/WebGLGraphicDevice.ts b/packages/rhi-webgl/src/WebGLGraphicDevice.ts index 1b1037106e..9e710c2a33 100644 --- a/packages/rhi-webgl/src/WebGLGraphicDevice.ts +++ b/packages/rhi-webgl/src/WebGLGraphicDevice.ts @@ -216,7 +216,7 @@ export class WebGLGraphicDevice implements IHardwareRenderer { let gl: (WebGLRenderingContext & WebGLExtension) | WebGL2RenderingContext | undefined; if (webGLMode == WebGLMode.Auto || webGLMode == WebGLMode.WebGL2) { gl = webCanvas.getContext("webgl2", webGLOptions); - if (!gl && !(canvas as WebCanvas).isOffscreenCanvas()) { + if (!gl && !(canvas as WebCanvas)._isOffscreenCanvas()) { gl = webCanvas.getContext("experimental-webgl2", webGLOptions); } this._isWebGL2 = true; @@ -230,7 +230,7 @@ export class WebGLGraphicDevice implements IHardwareRenderer { if (!gl) { if (webGLMode == WebGLMode.Auto || webGLMode == WebGLMode.WebGL1) { gl = webCanvas.getContext("webgl", webGLOptions); - if (!gl && !(canvas as WebCanvas).isOffscreenCanvas()) { + if (!gl && !(canvas as WebCanvas)._isOffscreenCanvas()) { gl = webCanvas.getContext("experimental-webgl", webGLOptions); } this._isWebGL2 = false; diff --git a/tests/src/rhi-webgl/WebCanvas.test.ts b/tests/src/rhi-webgl/WebCanvas.test.ts index d9b67e9732..05fc5d5533 100644 --- a/tests/src/rhi-webgl/WebCanvas.test.ts +++ b/tests/src/rhi-webgl/WebCanvas.test.ts @@ -2,17 +2,17 @@ import { WebCanvas } from "@galacean/engine"; import { describe, expect, it } from "vitest"; describe("WebCanvas", () => { - describe("isOffscreenCanvas", () => { + describe("_isOffscreenCanvas", () => { it("should return false when _webCanvas is an HTMLCanvasElement", () => { const canvas = document.createElement("canvas"); const webCanvas = new WebCanvas(canvas); - expect(webCanvas.isOffscreenCanvas()).toBe(false); + expect((webCanvas as any)._isOffscreenCanvas()).toBe(false); }); it("should return true when _webCanvas is an OffscreenCanvas", () => { const offscreen = new OffscreenCanvas(300, 150); const webCanvas = new WebCanvas(offscreen); - expect(webCanvas.isOffscreenCanvas()).toBe(true); + expect((webCanvas as any)._isOffscreenCanvas()).toBe(true); }); it("should return false when _webCanvas is replaced with an HTMLCanvasElement after construction", () => { @@ -22,7 +22,7 @@ describe("WebCanvas", () => { // Replace the internal canvas with an HTMLCanvasElement const anotherCanvas = document.createElement("canvas"); webCanvas._webCanvas = anotherCanvas; - expect(webCanvas.isOffscreenCanvas()).toBe(false); + expect((webCanvas as any)._isOffscreenCanvas()).toBe(false); }); it("should return true when _webCanvas is replaced with an OffscreenCanvas after construction", () => { @@ -32,7 +32,7 @@ describe("WebCanvas", () => { // Replace the internal canvas with an OffscreenCanvas const offscreen = new OffscreenCanvas(300, 150); webCanvas._webCanvas = offscreen; - expect(webCanvas.isOffscreenCanvas()).toBe(true); + expect((webCanvas as any)._isOffscreenCanvas()).toBe(true); }); }); }); From edadc3f2263f71fc22ccdad9d13bab5e01cea8bb Mon Sep 17 00:00:00 2001 From: "chenmo.gl" Date: Sat, 4 Jul 2026 17:08:06 +0800 Subject: [PATCH 21/37] docs: drop unfounded warn-once TODO and trailing periods on line comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OffscreenCanvas not following the display size is the correct, only sensible behavior, not a gap to warn about — so the no-op needs no TODO. Also drop trailing periods from single-line // comments for consistency. --- packages/rhi-webgl/src/WebCanvas.ts | 8 ++++---- packages/rhi-webgl/src/WebGLEngine.ts | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/rhi-webgl/src/WebCanvas.ts b/packages/rhi-webgl/src/WebCanvas.ts index 3b8cb98546..d5f8e97b23 100644 --- a/packages/rhi-webgl/src/WebCanvas.ts +++ b/packages/rhi-webgl/src/WebCanvas.ts @@ -50,13 +50,13 @@ export class WebCanvas extends Canvas { throw new Error(`WebCanvas.setAutoResolution: invalid scale ${scale}`); } - // OffscreenCanvas has no display size to follow — auto mode is a silent no-op. TODO: warn once. + // OffscreenCanvas has no display size to follow if (this._isOffscreenCanvas()) return; this._autoResolutionScale = scale; if (!this._resizeObserver) { - // Flag only; applied in `_pumpPendingResize`. + // Flag only; applied in `_pumpPendingResize` this._resizeObserver = new ResizeObserver(() => (this._pendingResize = true)); this._resizeObserver.observe(this._webCanvas); } @@ -89,14 +89,14 @@ export class WebCanvas extends Canvas { const webCanvas = this._webCanvas; // Skip while the canvas has no layout size yet (e.g. not mounted); keep the flag so a later - // frame's pump retries once layout exists — an unmounted canvas never becomes a 0x0 buffer. + // frame's pump retries once layout exists — an unmounted canvas never becomes a 0x0 buffer if (webCanvas.clientWidth === 0 || webCanvas.clientHeight === 0) return; this._pendingResize = false; const pixelRatio = window.devicePixelRatio * this._autoResolutionScale; // Round so the cached size matches the integer buffer. TODO: `devicePixelContentBoxSize` would give // exact device pixels (no dpr rounding error), but it arrives on observer entries — requires moving - // size capture into the callback. + // size capture into the callback this._setSize(Math.round(webCanvas.clientWidth * pixelRatio), Math.round(webCanvas.clientHeight * pixelRatio)); } diff --git a/packages/rhi-webgl/src/WebGLEngine.ts b/packages/rhi-webgl/src/WebGLEngine.ts index 65638fe3a5..5fc49da7ce 100644 --- a/packages/rhi-webgl/src/WebGLEngine.ts +++ b/packages/rhi-webgl/src/WebGLEngine.ts @@ -28,7 +28,7 @@ export class WebGLEngine extends Engine { }); } - // Core Engine never destroys the canvas; the platform engine owns teardown. + // Core Engine never destroys the canvas; the platform engine owns teardown private static _releaseCanvas(engine: WebGLEngine): void { engine.canvas._destroy(); } @@ -62,7 +62,7 @@ export class WebGLEngine extends Engine { } override update(): void { - // Must run before super.update() — see Canvas._pumpPendingResize for why. + // Must run before super.update() — see Canvas._pumpPendingResize for why this.canvas._pumpPendingResize(); super.update(); } From 4c0db61c57cb5aadc858388009414757ea611db6 Mon Sep 17 00:00:00 2001 From: "chenmo.gl" Date: Sat, 4 Jul 2026 17:14:58 +0800 Subject: [PATCH 22/37] docs(rhi-webgl): drop self-evident comment on the resize observer callback The callback body already reads as flag-only. --- packages/rhi-webgl/src/WebCanvas.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/rhi-webgl/src/WebCanvas.ts b/packages/rhi-webgl/src/WebCanvas.ts index d5f8e97b23..28587988e5 100644 --- a/packages/rhi-webgl/src/WebCanvas.ts +++ b/packages/rhi-webgl/src/WebCanvas.ts @@ -56,7 +56,6 @@ export class WebCanvas extends Canvas { this._autoResolutionScale = scale; if (!this._resizeObserver) { - // Flag only; applied in `_pumpPendingResize` this._resizeObserver = new ResizeObserver(() => (this._pendingResize = true)); this._resizeObserver.observe(this._webCanvas); } From 8bfff967c64384e3dc88691cc0ac48e7f41e97b0 Mon Sep 17 00:00:00 2001 From: "chenmo.gl" Date: Sat, 4 Jul 2026 17:18:37 +0800 Subject: [PATCH 23/37] docs(rhi-webgl): tighten _pumpPendingResize comments Keep only the non-obvious why: the 0x0 guard exists because an unmounted canvas has no layout size, and the rounding is a fractional-dpr TODO. Drop the parts that restate the code (the retained flag, the Math.round). --- packages/rhi-webgl/src/WebCanvas.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/rhi-webgl/src/WebCanvas.ts b/packages/rhi-webgl/src/WebCanvas.ts index 28587988e5..b2fa2d9c82 100644 --- a/packages/rhi-webgl/src/WebCanvas.ts +++ b/packages/rhi-webgl/src/WebCanvas.ts @@ -87,15 +87,12 @@ export class WebCanvas extends Canvas { if (!this._pendingResize) return; const webCanvas = this._webCanvas; - // Skip while the canvas has no layout size yet (e.g. not mounted); keep the flag so a later - // frame's pump retries once layout exists — an unmounted canvas never becomes a 0x0 buffer + // An unmounted canvas has no layout size yet; retry on a later pump instead of setting a 0x0 buffer if (webCanvas.clientWidth === 0 || webCanvas.clientHeight === 0) return; this._pendingResize = false; const pixelRatio = window.devicePixelRatio * this._autoResolutionScale; - // Round so the cached size matches the integer buffer. TODO: `devicePixelContentBoxSize` would give - // exact device pixels (no dpr rounding error), but it arrives on observer entries — requires moving - // size capture into the callback + // TODO: use `devicePixelContentBoxSize` from the observer entry to avoid fractional-dpr rounding error this._setSize(Math.round(webCanvas.clientWidth * pixelRatio), Math.round(webCanvas.clientHeight * pixelRatio)); } From daed532ec67537131af008ef65f7cbbc0a26dc3d Mon Sep 17 00:00:00 2001 From: "chenmo.gl" Date: Sat, 4 Jul 2026 17:24:48 +0800 Subject: [PATCH 24/37] docs(rhi-webgl): drop 0x0 guard comment That an unmounted canvas reports clientWidth 0 is common web knowledge, and the guard reads clearly on its own. --- packages/rhi-webgl/src/WebCanvas.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/rhi-webgl/src/WebCanvas.ts b/packages/rhi-webgl/src/WebCanvas.ts index b2fa2d9c82..3d2ef5f644 100644 --- a/packages/rhi-webgl/src/WebCanvas.ts +++ b/packages/rhi-webgl/src/WebCanvas.ts @@ -87,7 +87,6 @@ export class WebCanvas extends Canvas { if (!this._pendingResize) return; const webCanvas = this._webCanvas; - // An unmounted canvas has no layout size yet; retry on a later pump instead of setting a 0x0 buffer if (webCanvas.clientWidth === 0 || webCanvas.clientHeight === 0) return; this._pendingResize = false; From f55779190514cde8214767e7474bed3f9868903f Mon Sep 17 00:00:00 2001 From: "chenmo.gl" Date: Sat, 4 Jul 2026 17:26:05 +0800 Subject: [PATCH 25/37] docs(rhi-webgl): note the compat constraint in the devicePixelContentBoxSize TODO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is not Baseline (no Safari), so it can't be swapped in unconditionally — the reason it isn't used yet, invisible from the code. --- packages/rhi-webgl/src/WebCanvas.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rhi-webgl/src/WebCanvas.ts b/packages/rhi-webgl/src/WebCanvas.ts index 3d2ef5f644..faf7c56954 100644 --- a/packages/rhi-webgl/src/WebCanvas.ts +++ b/packages/rhi-webgl/src/WebCanvas.ts @@ -91,7 +91,7 @@ export class WebCanvas extends Canvas { this._pendingResize = false; const pixelRatio = window.devicePixelRatio * this._autoResolutionScale; - // TODO: use `devicePixelContentBoxSize` from the observer entry to avoid fractional-dpr rounding error + // TODO: `devicePixelContentBoxSize` would avoid fractional-dpr rounding, but needs feature detection (no Safari) this._setSize(Math.round(webCanvas.clientWidth * pixelRatio), Math.round(webCanvas.clientHeight * pixelRatio)); } From e7a157e9bf32e4af7b7fa0682453f26be6336596 Mon Sep 17 00:00:00 2001 From: "chenmo.gl" Date: Sat, 4 Jul 2026 17:34:01 +0800 Subject: [PATCH 26/37] docs(rhi-webgl): reframe the devicePixelContentBoxSize TODO as an added path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is a separate exact-pixel path gated by feature detection, with this clientWidth-based rounding kept as the permanent fallback — not a replacement (the value can't be polyfilled where the browser lacks it). --- packages/rhi-webgl/src/WebCanvas.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/rhi-webgl/src/WebCanvas.ts b/packages/rhi-webgl/src/WebCanvas.ts index faf7c56954..e3e40ba211 100644 --- a/packages/rhi-webgl/src/WebCanvas.ts +++ b/packages/rhi-webgl/src/WebCanvas.ts @@ -91,7 +91,8 @@ export class WebCanvas extends Canvas { this._pendingResize = false; const pixelRatio = window.devicePixelRatio * this._autoResolutionScale; - // TODO: `devicePixelContentBoxSize` would avoid fractional-dpr rounding, but needs feature detection (no Safari) + // TODO: add a `devicePixelContentBoxSize` path for exact device pixels where supported (not Safari), + // keeping this clientWidth-based rounding as the fallback this._setSize(Math.round(webCanvas.clientWidth * pixelRatio), Math.round(webCanvas.clientHeight * pixelRatio)); } From a1395050ddaeab3be0f3a112e79d74d756eadaa9 Mon Sep 17 00:00:00 2001 From: "chenmo.gl" Date: Sat, 4 Jul 2026 17:38:47 +0800 Subject: [PATCH 27/37] feat(rhi-webgl): use devicePixelContentBoxSize for exact auto-resize sizing Capture the observer entry's devicePixelContentBoxSize (exact device pixels, so only the scale is applied) and fall back to clientWidth * dpr where it is unavailable (Safari). This removes the fractional-dpr rounding error on browsers that support it, without changing behavior elsewhere. --- packages/rhi-webgl/src/WebCanvas.ts | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/packages/rhi-webgl/src/WebCanvas.ts b/packages/rhi-webgl/src/WebCanvas.ts index e3e40ba211..4920bf9e55 100644 --- a/packages/rhi-webgl/src/WebCanvas.ts +++ b/packages/rhi-webgl/src/WebCanvas.ts @@ -14,6 +14,9 @@ export class WebCanvas extends Canvas { private _resizeObserver?: ResizeObserver; private _autoResolutionScale: number = 1; private _pendingResize: boolean = false; + // Exact device-pixel content box from the latest observer entry, when the browser supports it (not Safari) + private _pendingDevicePixelWidth: number = 0; + private _pendingDevicePixelHeight: number = 0; /** * The scale of canvas, the value is visible width/height divide the render width/height. @@ -56,7 +59,13 @@ export class WebCanvas extends Canvas { this._autoResolutionScale = scale; if (!this._resizeObserver) { - this._resizeObserver = new ResizeObserver(() => (this._pendingResize = true)); + this._resizeObserver = new ResizeObserver((entries) => { + // `devicePixelContentBoxSize` gives exact device pixels; it is only readable here, on the entry + const box = entries[entries.length - 1].devicePixelContentBoxSize?.[0]; + this._pendingDevicePixelWidth = box ? box.inlineSize : 0; + this._pendingDevicePixelHeight = box ? box.blockSize : 0; + this._pendingResize = true; + }); this._resizeObserver.observe(this._webCanvas); } @@ -90,10 +99,18 @@ export class WebCanvas extends Canvas { if (webCanvas.clientWidth === 0 || webCanvas.clientHeight === 0) return; this._pendingResize = false; - const pixelRatio = window.devicePixelRatio * this._autoResolutionScale; - // TODO: add a `devicePixelContentBoxSize` path for exact device pixels where supported (not Safari), - // keeping this clientWidth-based rounding as the fallback - this._setSize(Math.round(webCanvas.clientWidth * pixelRatio), Math.round(webCanvas.clientHeight * pixelRatio)); + const scale = this._autoResolutionScale; + if (this._pendingDevicePixelWidth > 0 && this._pendingDevicePixelHeight > 0) { + // Exact device pixels already include the device pixel ratio, so only apply the scale + this._setSize( + Math.round(this._pendingDevicePixelWidth * scale), + Math.round(this._pendingDevicePixelHeight * scale) + ); + } else { + // Fallback where `devicePixelContentBoxSize` is unavailable (Safari): clientWidth is CSS pixels + const pixelRatio = window.devicePixelRatio * scale; + this._setSize(Math.round(webCanvas.clientWidth * pixelRatio), Math.round(webCanvas.clientHeight * pixelRatio)); + } } override _destroy(): void { @@ -106,6 +123,8 @@ export class WebCanvas extends Canvas { this._resizeObserver = undefined; } this._pendingResize = false; + this._pendingDevicePixelWidth = 0; + this._pendingDevicePixelHeight = 0; } protected override _onSizeChanged(width: number, height: number): void { From d0eb8e8bf66f4fe1e5cb2fd833c5e90186028c8e Mon Sep 17 00:00:00 2001 From: "chenmo.gl" Date: Sat, 4 Jul 2026 17:42:28 +0800 Subject: [PATCH 28/37] refactor(rhi-webgl): move _isOffscreenCanvas below the public members Group the @internal method with the other internal methods instead of placing it between the public scale accessors and setAutoResolution. --- packages/rhi-webgl/src/WebCanvas.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/rhi-webgl/src/WebCanvas.ts b/packages/rhi-webgl/src/WebCanvas.ts index 4920bf9e55..0948dcb146 100644 --- a/packages/rhi-webgl/src/WebCanvas.ts +++ b/packages/rhi-webgl/src/WebCanvas.ts @@ -41,13 +41,6 @@ export class WebCanvas extends Canvas { } } - /** - * @internal - */ - _isOffscreenCanvas(): boolean { - return typeof OffscreenCanvas !== "undefined" && this._webCanvas instanceof OffscreenCanvas; - } - override setAutoResolution(scale: number = 1): void { if (!Number.isFinite(scale) || scale <= 0) { throw new Error(`WebCanvas.setAutoResolution: invalid scale ${scale}`); @@ -92,6 +85,13 @@ export class WebCanvas extends Canvas { this.scale = this._scale; } + /** + * @internal + */ + _isOffscreenCanvas(): boolean { + return typeof OffscreenCanvas !== "undefined" && this._webCanvas instanceof OffscreenCanvas; + } + override _pumpPendingResize(): void { if (!this._pendingResize) return; From 49c1869e33e4f5ee4c5ff262de0f0ec03bb49000 Mon Sep 17 00:00:00 2001 From: "chenmo.gl" Date: Sat, 4 Jul 2026 17:43:22 +0800 Subject: [PATCH 29/37] docs(rhi-webgl): drop the pending-device-pixel field comment The field names and the callback that assigns them are self-explanatory. --- packages/rhi-webgl/src/WebCanvas.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/rhi-webgl/src/WebCanvas.ts b/packages/rhi-webgl/src/WebCanvas.ts index 0948dcb146..8b9cb5c11b 100644 --- a/packages/rhi-webgl/src/WebCanvas.ts +++ b/packages/rhi-webgl/src/WebCanvas.ts @@ -14,7 +14,6 @@ export class WebCanvas extends Canvas { private _resizeObserver?: ResizeObserver; private _autoResolutionScale: number = 1; private _pendingResize: boolean = false; - // Exact device-pixel content box from the latest observer entry, when the browser supports it (not Safari) private _pendingDevicePixelWidth: number = 0; private _pendingDevicePixelHeight: number = 0; From ba05525f1efc76d8a716fd7f4b341c139f650af5 Mon Sep 17 00:00:00 2001 From: "chenmo.gl" Date: Sat, 4 Jul 2026 17:47:47 +0800 Subject: [PATCH 30/37] fix(rhi-webgl): let the first auto-resize use exact device pixels The eager synchronous _pendingResize=true ran before the observer's first entry arrived, so the first resize always took the clientWidth fallback even where devicePixelContentBoxSize is supported. Drive the first resize from observe()'s initial callback instead; only reapply synchronously when already observing (a scale change), reusing the last measured size. --- packages/rhi-webgl/src/WebCanvas.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/rhi-webgl/src/WebCanvas.ts b/packages/rhi-webgl/src/WebCanvas.ts index 8b9cb5c11b..55bffdd878 100644 --- a/packages/rhi-webgl/src/WebCanvas.ts +++ b/packages/rhi-webgl/src/WebCanvas.ts @@ -50,9 +50,12 @@ export class WebCanvas extends Canvas { this._autoResolutionScale = scale; - if (!this._resizeObserver) { + if (this._resizeObserver) { + // Already observing: reapply the new scale to the last measured size + this._pendingResize = true; + } else { + // observe() fires an initial entry, which sets the pending size (with exact device pixels when supported) this._resizeObserver = new ResizeObserver((entries) => { - // `devicePixelContentBoxSize` gives exact device pixels; it is only readable here, on the entry const box = entries[entries.length - 1].devicePixelContentBoxSize?.[0]; this._pendingDevicePixelWidth = box ? box.inlineSize : 0; this._pendingDevicePixelHeight = box ? box.blockSize : 0; @@ -60,8 +63,6 @@ export class WebCanvas extends Canvas { }); this._resizeObserver.observe(this._webCanvas); } - - this._pendingResize = true; } /** From 9df47a601042982f5f26fb88b6d58396ad577be0 Mon Sep 17 00:00:00 2001 From: "chenmo.gl" Date: Sat, 4 Jul 2026 18:19:08 +0800 Subject: [PATCH 31/37] refactor(rhi-webgl): drop engine-level enableAutoResize/disableAutoResize MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These were introduced earlier in this PR and never shipped, so keeping them as deprecated forwarders was contradictory (new + deprecated in the same change). The capability lives on the canvas now — use engine.canvas.setAutoResolution() / setResolution() directly. --- packages/rhi-webgl/src/WebGLEngine.ts | 19 ------------------- tests/src/rhi-webgl/WebGLEngine.test.ts | 18 ------------------ 2 files changed, 37 deletions(-) diff --git a/packages/rhi-webgl/src/WebGLEngine.ts b/packages/rhi-webgl/src/WebGLEngine.ts index 5fc49da7ce..61930b6402 100644 --- a/packages/rhi-webgl/src/WebGLEngine.ts +++ b/packages/rhi-webgl/src/WebGLEngine.ts @@ -42,25 +42,6 @@ export class WebGLEngine extends Engine { return this._canvas as WebCanvas; } - /** - * Enable automatic canvas resizing. - * @deprecated Use `engine.canvas.setAutoResolution()` instead. - * @param pixelRatio - Ignored; the device pixel ratio is applied automatically. For a custom multiplier - * on top of it, use `engine.canvas.setAutoResolution(scale)` - */ - enableAutoResize(pixelRatio?: number): void { - this.canvas.setAutoResolution(); - } - - /** - * Lock the render buffer at its current size; it stops following the display size. - * @deprecated Use `engine.canvas.setResolution(width, height)` to lock a fixed resolution instead. - */ - disableAutoResize(): void { - const canvas = this.canvas; - canvas.setResolution(canvas.width, canvas.height); - } - override update(): void { // Must run before super.update() — see Canvas._pumpPendingResize for why this.canvas._pumpPendingResize(); diff --git a/tests/src/rhi-webgl/WebGLEngine.test.ts b/tests/src/rhi-webgl/WebGLEngine.test.ts index f43008cc29..80d95d9dde 100644 --- a/tests/src/rhi-webgl/WebGLEngine.test.ts +++ b/tests/src/rhi-webgl/WebGLEngine.test.ts @@ -158,22 +158,4 @@ describe("webgl engine test", () => { expect((engine.canvas as any)._resizeObserver).toBeUndefined(); engine.destroy(); }); - - it("deprecated enableAutoResize / disableAutoResize forward to the canvas", async () => { - const canvas = document.createElement("canvas"); - const engine = await WebGLEngine.create({ canvas, autoResize: false }); - const webCanvas = engine.canvas; - - // enableAutoResize forwards to setAutoResolution (enters auto mode, attaches observer). - engine.enableAutoResize(); - expect((webCanvas as any)._resizeObserver).toBeDefined(); - - // disableAutoResize forwards to setResolution(current size) (exits auto, releases observer). - engine.disableAutoResize(); - expect((webCanvas as any)._resizeObserver).toBeUndefined(); - expect(webCanvas.width).toBeGreaterThan(0); - expect(webCanvas.height).toBeGreaterThan(0); - - engine.destroy(); - }); }); From 5aacc8551c9f478bd791e4e659e86da75bdedb59 Mon Sep 17 00:00:00 2001 From: "chenmo.gl" Date: Sat, 4 Jul 2026 18:23:30 +0800 Subject: [PATCH 32/37] refactor(rhi-webgl): move _releaseCanvas to the bottom, drop its comment --- packages/rhi-webgl/src/WebGLEngine.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/rhi-webgl/src/WebGLEngine.ts b/packages/rhi-webgl/src/WebGLEngine.ts index 61930b6402..5359ecb854 100644 --- a/packages/rhi-webgl/src/WebGLEngine.ts +++ b/packages/rhi-webgl/src/WebGLEngine.ts @@ -28,11 +28,6 @@ export class WebGLEngine extends Engine { }); } - // Core Engine never destroys the canvas; the platform engine owns teardown - private static _releaseCanvas(engine: WebGLEngine): void { - engine.canvas._destroy(); - } - /** * The canvas the engine renders to; call `setResolution()` or `setAutoResolution()` on it to control * the render-buffer resolution. @@ -47,6 +42,10 @@ export class WebGLEngine extends Engine { this.canvas._pumpPendingResize(); super.update(); } + + private static _releaseCanvas(engine: WebGLEngine): void { + engine.canvas._destroy(); + } } /** From 4889464b0a4bc7ab854f955c723281cfb1f9a4fe Mon Sep 17 00:00:00 2001 From: "chenmo.gl" Date: Sat, 4 Jul 2026 18:28:11 +0800 Subject: [PATCH 33/37] refactor: pump pending canvas resize in base Engine.update Move the per-frame resize pump into Engine.update so WebGLEngine no longer overrides update. _pumpPendingResize becomes a base Canvas no-op that WebCanvas overrides, like _exitAutoResize. --- packages/core/src/Canvas.ts | 2 +- packages/core/src/Engine.ts | 3 +++ packages/rhi-webgl/src/WebGLEngine.ts | 6 ------ 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/packages/core/src/Canvas.ts b/packages/core/src/Canvas.ts index 81a4487c17..df4b0c5a2a 100644 --- a/packages/core/src/Canvas.ts +++ b/packages/core/src/Canvas.ts @@ -59,7 +59,7 @@ export abstract class Canvas { } } - abstract _pumpPendingResize(): void; + _pumpPendingResize(): void {} abstract _destroy(): void; diff --git a/packages/core/src/Engine.ts b/packages/core/src/Engine.ts index e1643ac002..1394b944b4 100644 --- a/packages/core/src/Engine.ts +++ b/packages/core/src/Engine.ts @@ -322,6 +322,9 @@ export class Engine extends EventDispatcher { * Update the engine loop manually. If you call engine.run(), you generally don't need to call this function. */ update(): void { + // Apply any pending canvas resize before the frame so resize and render land together + this._canvas._pumpPendingResize(); + const time = this._time; time._update(); diff --git a/packages/rhi-webgl/src/WebGLEngine.ts b/packages/rhi-webgl/src/WebGLEngine.ts index 5359ecb854..a0f4007135 100644 --- a/packages/rhi-webgl/src/WebGLEngine.ts +++ b/packages/rhi-webgl/src/WebGLEngine.ts @@ -37,12 +37,6 @@ export class WebGLEngine extends Engine { return this._canvas as WebCanvas; } - override update(): void { - // Must run before super.update() — see Canvas._pumpPendingResize for why - this.canvas._pumpPendingResize(); - super.update(); - } - private static _releaseCanvas(engine: WebGLEngine): void { engine.canvas._destroy(); } From 24df15f5f7fdcb18122a5cd45bd6f2b5eecbd18d Mon Sep 17 00:00:00 2001 From: "chenmo.gl" Date: Sat, 4 Jul 2026 18:30:16 +0800 Subject: [PATCH 34/37] docs(core): drop the pump comment in Engine.update --- packages/core/src/Engine.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/core/src/Engine.ts b/packages/core/src/Engine.ts index 1394b944b4..07c8dc834e 100644 --- a/packages/core/src/Engine.ts +++ b/packages/core/src/Engine.ts @@ -322,7 +322,6 @@ export class Engine extends EventDispatcher { * Update the engine loop manually. If you call engine.run(), you generally don't need to call this function. */ update(): void { - // Apply any pending canvas resize before the frame so resize and render land together this._canvas._pumpPendingResize(); const time = this._time; From f1dc487134eb89e6e09a1fc7f219666b9becd694 Mon Sep 17 00:00:00 2001 From: "chenmo.gl" Date: Sat, 4 Jul 2026 18:32:43 +0800 Subject: [PATCH 35/37] docs(rhi-webgl): simplify the canvas getter doc Just state what it returns; the resolution API is documented on the WebCanvas/Canvas class it returns, not restated on the getter. --- packages/rhi-webgl/src/WebGLEngine.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/rhi-webgl/src/WebGLEngine.ts b/packages/rhi-webgl/src/WebGLEngine.ts index a0f4007135..5e846c1fcb 100644 --- a/packages/rhi-webgl/src/WebGLEngine.ts +++ b/packages/rhi-webgl/src/WebGLEngine.ts @@ -29,8 +29,7 @@ export class WebGLEngine extends Engine { } /** - * The canvas the engine renders to; call `setResolution()` or `setAutoResolution()` on it to control - * the render-buffer resolution. + * The web canvas the engine renders to. */ override get canvas(): WebCanvas { // @ts-ignore From 724dd3cee67eefcef131e871ad6bcad220dc2124 Mon Sep 17 00:00:00 2001 From: "chenmo.gl" Date: Sat, 4 Jul 2026 18:47:09 +0800 Subject: [PATCH 36/37] refactor(rhi-webgl): drop the autoResize create option Auto-resize stays on by default (create always calls setAutoResolution); apps wanting a fixed resolution call setResolution afterwards, which exits auto mode without a flicker. Removes an init-only opt-out that runtime setResolution already covers. --- packages/rhi-webgl/src/WebGLEngine.ts | 6 +----- tests/src/rhi-webgl/WebGLEngine.test.ts | 10 +--------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/packages/rhi-webgl/src/WebGLEngine.ts b/packages/rhi-webgl/src/WebGLEngine.ts index 5e846c1fcb..d114e98f9b 100644 --- a/packages/rhi-webgl/src/WebGLEngine.ts +++ b/packages/rhi-webgl/src/WebGLEngine.ts @@ -17,9 +17,7 @@ export class WebGLEngine extends Engine { const webGLGraphicDevice = new WebGLGraphicDevice(configuration.graphicDeviceOptions); const engine = new WebGLEngine(webCanvas, webGLGraphicDevice, configuration); engine.once(EngineEventType.Shutdown, WebGLEngine._releaseCanvas); - if (configuration.autoResize ?? true) { - webCanvas.setAutoResolution(); - } + webCanvas.setAutoResolution(); // @ts-ignore const promise = engine._initialize(configuration) as Promise; return promise.then(() => { @@ -49,6 +47,4 @@ export interface WebGLEngineConfiguration extends EngineConfiguration { canvas: HTMLCanvasElement | OffscreenCanvas | string; /** Graphic device options. */ graphicDeviceOptions?: WebGLGraphicDeviceOptions; - /** Whether the render buffer automatically follows the canvas display size. Defaults to `true`. */ - autoResize?: boolean; } diff --git a/tests/src/rhi-webgl/WebGLEngine.test.ts b/tests/src/rhi-webgl/WebGLEngine.test.ts index 80d95d9dde..b92af54fca 100644 --- a/tests/src/rhi-webgl/WebGLEngine.test.ts +++ b/tests/src/rhi-webgl/WebGLEngine.test.ts @@ -104,7 +104,7 @@ describe("webgl engine test", () => { }); it("canvas auto resize", async () => { - // autoResize defaults to true: create enters auto mode and attaches an observer. + // create enters auto mode by default and attaches an observer. const canvas = document.createElement("canvas"); const engine = await WebGLEngine.create({ canvas }); engine.run(); @@ -150,12 +150,4 @@ describe("webgl engine test", () => { engine.destroy(); expect((webCanvas as any)._resizeObserver).toBeUndefined(); }); - - it("autoResize false disables the default follow", async () => { - const canvas = document.createElement("canvas"); - const engine = await WebGLEngine.create({ canvas, autoResize: false }); - // No observer attached when auto-resize is opted out. - expect((engine.canvas as any)._resizeObserver).toBeUndefined(); - engine.destroy(); - }); }); From 90a8755382a4238e0c7f45f641a34f2b808f0220 Mon Sep 17 00:00:00 2001 From: luo2430 Date: Sat, 4 Jul 2026 19:44:47 +0800 Subject: [PATCH 37/37] chore(docs): migrate canvas resizing APIs and update guides - Updated canvas resizing logic in docs from resizeByClientSize to setAutoResolution and setResolution - Aligned English and Chinese documentation for physics and platform modules - Removed deprecated method calls from README and example snippets - Enhanced clarity on device pixel ratio (DPR) handling and resolution strategies --- README.md | 3 +- docs/en/core/canvas.mdx | 103 ++++++++++++++++-------------- docs/en/physics/query/raycast.mdx | 1 - docs/en/platform/platform.mdx | 2 +- docs/zh/core/canvas.mdx | 103 ++++++++++++++++-------------- docs/zh/physics/query/raycast.mdx | 1 - docs/zh/platform/platform.mdx | 2 +- packages/galacean/README.md | 3 +- 8 files changed, 116 insertions(+), 102 deletions(-) diff --git a/README.md b/README.md index 9cacb86b1c..0013443a68 100644 --- a/README.md +++ b/README.md @@ -40,9 +40,8 @@ Create a simple scene: ```typescript import { BlinnPhongMaterial, Camera, DirectLight, MeshRenderer, WebGLEngine, PrimitiveMesh } from "@galacean/engine"; -// Create engine by passing in the HTMLCanvasElement id and adjust canvas size +// Create engine by passing in the HTMLCanvasElement id (canvas auto-resizes by default) const engine = await WebGLEngine.create({ canvas: "canvas-id" }); -engine.canvas.resizeByClientSize(); // Get scene and create root entity const scene = engine.sceneManager.activeScene; diff --git a/docs/en/core/canvas.mdx b/docs/en/core/canvas.mdx index b8d9dcf669..84c043cbba 100644 --- a/docs/en/core/canvas.mdx +++ b/docs/en/core/canvas.mdx @@ -27,85 +27,94 @@ const engine = await WebGLEngine.create({ canvas: "canvas" }); console.log(engine.canvas); // => WebCanvas instance ``` -### Basic Adaptation +### Auto Resolution (Default) -Canvas size is generally controlled by the **device pixel ratio**. Taking [WebCanvas](/apis/rhi-webgl/#WebCanvas) as an example: +By default, the canvas render buffer automatically follows the display size, accounting for the device pixel ratio: -```mermaid -flowchart TD - A[HtmlCanvas.clientWidth] -->|pixelRatio| B[WebCanvas.width] - C[HtmlCanvas.clientHeight] -->|pixelRatio| D[WebCanvas.height] +```typescript +// Canvas auto-resizes out of the box +const engine = await WebGLEngine.create({ canvas: "canvas" }); ``` -Call `resizeByClientSize` to adapt the canvas: +When the display size of the canvas changes (e.g., when the browser window changes), the render buffer resizes automatically so the image stays sharp. In most cases, the default behavior meets adaptation needs. If you have more complex requirements, read the "Advanced Usage" section. + +### Auto Resolution with Custom Scale + +Use `setAutoResolution(scale)` to control how the device pixel ratio is applied. The `scale` parameter defaults to `1` (native sharpness): ```typescript -// Adjust canvas size using the device pixel ratio (window.devicePixelRatio) -engine.canvas.resizeByClientSize(); -// Adjust canvas size using a custom pixel ratio -engine.canvas.resizeByClientSize(1.5); +// Native sharpness (1x device pixel ratio) +engine.canvas.setAutoResolution(); + +// Save GPU load — render at 70% of native resolution +engine.canvas.setAutoResolution(0.7); + +// Supersample — render at 2x native resolution +engine.canvas.setAutoResolution(2); ``` -When the display size of the canvas changes (e.g., when the browser window changes), the image may appear stretched or compressed. You can restore it to normal by calling `resizeByClientSize`. In most cases, this line of code can meet the adaptation requirements. If you have more complex adaptation needs, please read the "Advanced Usage" section. +Calling `setAutoResolution` switches the canvas back to auto-resize mode, overriding a previous `setResolution` call. + +### Explicit Resolution Control + +If you need manual control over the render buffer size, use `setResolution`: + +```typescript +// Lock the render buffer to a fixed resolution, independent of display size +engine.canvas.setResolution(1920, 1080); +``` + +Once `setResolution` is called, the canvas stops following display size changes and stays at the specified resolution until `setAutoResolution` is called again. The `width` and `height` properties are read-only and reflect the current render buffer resolution. ## Advanced Usage Regarding adaptation, the key point is the **device pixel ratio**. Taking iPhone X as an example, the device pixel ratio `window.devicePixelRatio` is _3_, the window width `window.innerWidth` is _375_, and the physical screen pixel width is: 375 * 3 = *1125*. -Rendering pressure is proportional to the physical pixel width and height of the screen. The larger the physical pixel, the greater the rendering pressure and the more power it consumes. It is recommended to set the canvas width and height through APIs exposed by [WebCanvas](/apis/rhi-webgl/WebCanvas), rather than using native canvas APIs such as `canvas.width` or `canvas.style.width`. +Rendering pressure is proportional to the physical pixel width and height of the screen. The larger the physical pixel, the greater the rendering pressure and the more power it consumes. It is recommended to set the canvas resolution through APIs exposed by [WebCanvas](/apis/rhi-webgl/WebCanvas), rather than using native canvas APIs such as `canvas.width` or `canvas.style.width`. -> ⚠️ **Note**: Some front-end scaffolds may insert the following tag to modify the page's zoom ratio: +> **Note**: Some front-end scaffolds may insert the following tag to modify the page's zoom ratio: > > `` > > This line of code changes the value of `window.innerWidth` from 375 to 1125. -Apart from `resizeByClientSize`, which automatically adapts, the following two modes are recommended: +### How Auto-Resolution Works -### Energy-Saving Mode +```mermaid +flowchart TD + A[ResizeObserver] -->|monitors| B[Canvas Element] + B -->|devicePixelContentBoxSize| C[Device Pixels] + B -->|fallback: clientWidth × devicePixelRatio| C + C -->|× scale| D[Render Buffer Size] +``` -Considering that mobile devices, despite having high-definition screens (high device pixel ratio), may not have graphics card performance that meets the performance requirements for HD real-time rendering (the rendering area ratio of 3x screen to 2x screen is 9:4, and 3x screens can easily cause phones to overheat), the engine achieves adaptation by scaling the canvas in this mode. The code is as follows: +The engine uses a `ResizeObserver` to monitor the canvas element. On each resize, the observer captures the canvas's device-pixel dimensions, then the engine computes the render buffer size from one of two sources: -```typescript -const canvas = document.getElementById("canvas"); -const webcanvas = new WebCanvas(canvas); -const pixelRatio = window.devicePixelRatio; // If meta scale is set, please set to 1 -const scale = 3 / 2; // Calculate canvas size for 3x HD screen as if it were 2x - -/** - * Set energy-saving mode, defaults to full screen, but you can set any width and height - */ -webcanvas.width = (window.innerWidth * pixelRatio) / scale; -webcanvas.height = (window.innerHeight * pixelRatio) / scale; -webcanvas.setScale(scale, scale); // Scale canvas -``` +- **`devicePixelContentBoxSize` (Chrome/Edge/Firefox)**: Already represents exact device pixels, so only `scale` is applied +- **`clientWidth × devicePixelRatio` (Safari fallback)**: CSS pixels are multiplied by the device pixel ratio, then by `scale` -If the canvas width and height have been set via CSS (e.g., `width: 100vw; height: 100vh;`), you can scale the canvas by passing parameters to `resizeByClientSize`: +The resize is deferred to the next frame (`engine.update()`) to prevent tearing. -```typescript -const canvas = document.getElementById("canvas"); -const webcanvas = new WebCanvas(canvas); -const scale = 2 / 3; // Calculate canvas size for 3x HD screen as if it were 2x +### Energy-Saving Mode -webcanvas.resizeByClientSize(scale); // Scale canvas +Considering that mobile devices, despite having high-definition screens (high device pixel ratio), may not have graphics card performance that meets the performance requirements for HD real-time rendering (the rendering area ratio of 3x screen to 2x screen is 9:4, and 3x screens can easily cause phones to overheat), use a scale factor below `1` to reduce GPU load: + +```typescript +engine.canvas.setAutoResolution(2 / 3); ``` ### Fixed Width Mode -In certain situations, such as when a design draft has a fixed width of 750, developers might hard-code the canvas width to reduce adaptation costs. The code is as follows: +In certain situations, such as when a design draft has a fixed width of 750, developers might hard-code the canvas width to reduce adaptation costs: ```typescript import { WebCanvas } from "@galacean/engine"; -const canvas = document.getElementById("canvas"); -const webcanvas = new WebCanvas(canvas); -const fixedWidth = 750; // Fixed width of 750 +const canvas = document.getElementById("canvas") as HTMLCanvasElement; +const webCanvas = new WebCanvas(canvas); +const fixedWidth = 750; -/** - * Set fixed width mode - */ const scale = window.innerWidth / fixedWidth; -webcanvas.width = fixedWidth; -webcanvas.height = window.innerHeight / scale; -webcanvas.setScale(scale, scale); // Scale canvas -``` \ No newline at end of file +webCanvas.setResolution(fixedWidth, Math.round(window.innerHeight / scale)); +webCanvas.setScale(scale, scale); +``` diff --git a/docs/en/physics/query/raycast.mdx b/docs/en/physics/query/raycast.mdx index afde660da1..bac64d38b8 100644 --- a/docs/en/physics/query/raycast.mdx +++ b/docs/en/physics/query/raycast.mdx @@ -64,7 +64,6 @@ const engine = await WebGLEngine.create({ canvas: "canvas", physics: new PhysXPhysics() }); -engine.canvas.resizeByClientSize(); const scene = engine.scenes[0]; // Create a ray from origin pointing downward diff --git a/docs/en/platform/platform.mdx b/docs/en/platform/platform.mdx index 3ba21f63ad..11494e19a8 100644 --- a/docs/en/platform/platform.mdx +++ b/docs/en/platform/platform.mdx @@ -40,7 +40,7 @@ The platform-related export configuration will be described separately in the pl | WebGL Mode | Select the version to use with WebGL:
**Auto:** WebGL2.0 is preferred. If the operating environment does not support it, it will automatically switch to WebGL1.0
**WebGL1.0:** Using WebGL1.0
**WebGL2.0:** Using WebGL2.0 | | Alpha | Whether the canvas supports transparent background. If you want the content below the canvas to be visible, you can turn it on. | | Preserve Drawing Buffer | Controls whether the drawing buffer retains its contents after calling the gl.clear() method. | -| DPR Mode | [The pixel ratio of the device](/en/docs/core/canvas),Control the size of the canvas by calling engine.canvas.resizeByClientSize:
**Auto:** Automatic adaptation, that is, the parameter is window.devicePixelRatio
**Fixed:** Developers set their own parameters After selecting Fixed, developers can enter the parameters they need to set. | +| DPR Mode | [Device pixel ratio](/en/docs/core/canvas),Control the canvas resolution via `engine.canvas.setAutoResolution`:
**Auto:** Automatically follow the display size with devicePixelRatio
**Fixed:** Developer sets a custom scale parameter After selecting Fixed, developers can enter the scale value. | ## Supported export platforms Currently, Galacean supports exporting to the following platforms: diff --git a/docs/zh/core/canvas.mdx b/docs/zh/core/canvas.mdx index c749fdaf04..665f8c990e 100644 --- a/docs/zh/core/canvas.mdx +++ b/docs/zh/core/canvas.mdx @@ -27,85 +27,94 @@ const engine = await WebGLEngine.create({ canvas: "canvas" }); console.log(engine.canvas); // => WebCanvas 实例 ``` -### 基础适配 +### 自动分辨率(默认) -画布尺寸一般通过**设备像素比**控制,以 [WebCanvas](/apis/rhi-webgl/#WebCanvas) 为例: +默认情况下,画布会自动跟随显示尺寸调整渲染缓冲区,并适配设备像素比,无需手动干预: -```mermaid -flowchart TD - A[HtmlCanvas.clientWidth] -->|pixelRatio| B[WebCanvas.width] - C[HtmlCanvas.clientHeight] -->|pixelRatio| D[WebCanvas.height] +```typescript +// 画布自动适配尺寸,开箱即用 +const engine = await WebGLEngine.create({ canvas: "canvas" }); +``` + +当画布的显示尺寸发生变化时(比如浏览器窗口发生变化),画布会自动调整渲染缓冲区大小,画面始终保持清晰。一般情况下默认行为已经可以满足适配需求,如果你有更复杂的适配需求,请阅读"高级使用"部分。 + +### 自定义缩放比例 + +使用 `setAutoResolution(scale)` 控制设备像素比的缩放倍率。`scale` 参数默认为 `1`(原生清晰度): + +```typescript +// 原生清晰度(1x 设备像素比) +engine.canvas.setAutoResolution(); + +// 降低 GPU 负载 — 以原生 70% 分辨率渲染 +engine.canvas.setAutoResolution(0.7); + +// 超采样 — 以原生 2x 分辨率渲染 +engine.canvas.setAutoResolution(2); ``` -调用 `resizeByClientSize` 适配画布: +调用 `setAutoResolution` 会将画布切回自动适配模式,覆盖之前 `setResolution` 的设置。 + +### 手动控制分辨率 + +如果需要对渲染缓冲区大小进行精确控制,可使用 `setResolution`: ```typescript -// 使用设备像素比( window.devicePixelRatio )调整画布尺寸, -engine.canvas.resizeByClientSize(); -// 自定义像素比调整画布尺寸 -engine.canvas.resizeByClientSize(1.5); +// 将渲染缓冲区锁定为固定分辨率,不再跟随显示尺寸变化 +engine.canvas.setResolution(1920, 1080); ``` -当画布的显示尺寸发生变化时(比如浏览器窗口发生变化时),画面可能出现拉伸或压缩,可以通过调用 `resizeByClientSize` 来恢复正常。一般情况下这行代码已经可以满足适配的需求,如果你有更复杂的适配需求,请阅读“高级使用”部分。 +调用 `setResolution` 后,画布不再跟随显示尺寸变化,始终保持在指定的分辨率,直到再次调用 `setAutoResolution` 恢复自动适配。`width` 和 `height` 属性为只读,反映当前渲染缓冲区的实际分辨率。 ## 高级使用 -关于适配,核心要注意的点是**设备像素比**,以 iPhoneX 为例,设备像素比 `window.devicePixelRatio` 为 _3_,  窗口宽度 `window.innerWidth` 为 _375_,屏幕物理像素宽度则为:375 * 3 = *1125\*。 +关于适配,核心要注意的点是**设备像素比**,以 iPhoneX 为例,设备像素比 `window.devicePixelRatio` 为 _3_,窗口宽度 `window.innerWidth` 为 _375_,屏幕物理像素宽度则为:375 * 3 = *1125*。 -渲染压力和屏幕物理像素高宽成正比,物理像素越大,渲染压力越大,也就越耗电。画布的高宽建议通过 [WebCanvas](/apis/rhi-webgl/WebCanvas) 暴露的 API 设置,不建议使用原生 canvas 的 API ,如 `canvas.width` 或 `canvas.style.width` 这些方法修改。 +渲染压力和屏幕物理像素高宽成正比,物理像素越大,渲染压力越大,也就越耗电。画布的高宽建议通过 [WebCanvas](/apis/rhi-webgl/WebCanvas) 暴露的 API 设置,不建议使用原生 canvas 的 API,如 `canvas.width` 或 `canvas.style.width` 这些方法修改。 -> ️ **注意**:有些前端脚手架会插入以下标签修改页面的缩放比: +> **注意**:有些前端脚手架会插入以下标签修改页面的缩放比: > > `` > > 这行代码会把 `window.innerWidth` 的值从 375 修改为 1125。 -除了 `resizeByClientSize` 自动适配,推荐使用以下两种模式: +### 自动分辨率工作原理 -### 节能模式 +```mermaid +flowchart TD + A[ResizeObserver] -->|监听| B[Canvas 元素] + B -->|devicePixelContentBoxSize| C[设备像素尺寸] + B -->|降级: clientWidth × devicePixelRatio| C + C -->|× scale| D[渲染缓冲区大小] +``` -考虑到移动端设备虽然是高清屏幕(设别像素比高)但实际显卡性能并不能很好地满足高清实时渲染的性能要求的情况(**3 倍屏和 2 倍屏渲染面积比是 9:4,3 倍屏较容易造成手机发烫**),此模式下引擎通过对画布缩放拉伸来达到适配的目的。代码如下: +引擎通过 `ResizeObserver` 监听 canvas 元素的尺寸变化。每次尺寸变化时,observer 捕获 canvas 的设备像素尺寸,然后引擎从以下两种来源之一计算渲染缓冲区大小: -```typescript -const canvas = document.getElementById("canvas"); -const webcanvas = new WebCanvas(canvas); -const pixelRatio = window.devicePixelRatio; // 如果已经设置 meta scale,请设置为 1 -const scale = 3 / 2; // 3 倍高清屏按 2 倍屏来计算画布尺寸 - -/** - * 设置节能模式,默认全屏,也可以自己设置任意高宽 - */ -webcanvas.width = (window.innerWidth * pixelRatio) / scale; -webcanvas.height = (window.innerHeight * pixelRatio) / scale; -webcanvas.setScale(scale, scale); // 拉伸画布 -``` +- **`devicePixelContentBoxSize`(Chrome/Edge/Firefox)**:已直接表示设备像素,只需乘以 `scale` +- **`clientWidth × devicePixelRatio`(Safari 降级方案)**:CSS 像素先乘以设备像素比,再乘以 `scale` -如果已经通过 CSS 设置了画布高宽(比如 `width: 100vw; height: 100vh;`),那么可以通过 `resizeByClientSize` 传参实现画布的缩放: +尺寸更新推迟到下一帧(`engine.update()` 时)执行,避免画面撕裂。 -```typescript -const canvas = document.getElementById("canvas"); -const webcanvas = new WebCanvas(canvas); -const scale = 2 / 3; // 3 倍高清屏按 2 倍屏来计算画布尺寸 +### 节能模式 + +考虑到移动端设备虽然是高清屏幕(设备像素比高)但实际显卡性能并不能很好地满足高清实时渲染的性能要求的情况(**3 倍屏和 2 倍屏渲染面积比是 9:4,3 倍屏较容易造成手机发烫**),可以通过缩放倍率来降低 GPU 负载: -webcanvas.resizeByClientSize(scale); // 拉伸画布 +```typescript +engine.canvas.setAutoResolution(2 / 3); ``` ### 固定宽度模式 -某些情况下,比如设计稿固定 750 宽度的情况,开发者有可能会写死画布宽度来降低适配成本。代码如下: +某些情况下,比如设计稿固定 750 宽度的情况,开发者有可能会写死画布宽度来降低适配成本: ```typescript import { WebCanvas } from "@galacean/engine"; -const canvas = document.getElementById("canvas"); -const webcanvas = new WebCanvas(canvas); -const fixedWidth = 750; // 固定 750 宽度 +const canvas = document.getElementById("canvas") as HTMLCanvasElement; +const webCanvas = new WebCanvas(canvas); +const fixedWidth = 750; -/** - * 设置固定宽度模式 - */ const scale = window.innerWidth / fixedWidth; -webcanvas.width = fixedWidth; -webcanvas.height = window.innerHeight / scale; -webcanvas.setScale(scale, scale); // 拉伸画布 +webCanvas.setResolution(fixedWidth, Math.round(window.innerHeight / scale)); +webCanvas.setScale(scale, scale); ``` diff --git a/docs/zh/physics/query/raycast.mdx b/docs/zh/physics/query/raycast.mdx index 942fb03c0b..d162446fe8 100644 --- a/docs/zh/physics/query/raycast.mdx +++ b/docs/zh/physics/query/raycast.mdx @@ -64,7 +64,6 @@ const engine = await WebGLEngine.create({ canvas: "canvas", physics: new PhysXPhysics() }); -engine.canvas.resizeByClientSize(); const scene = engine.scenes[0]; // 创建一条从原点向下的射线 diff --git a/docs/zh/platform/platform.mdx b/docs/zh/platform/platform.mdx index 04d54e053a..f77c66e91f 100644 --- a/docs/zh/platform/platform.mdx +++ b/docs/zh/platform/platform.mdx @@ -40,7 +40,7 @@ label: Platform | WebGL Mode | 选择使用 WebGL 的版本:
**Auto:** 优先使用 WebGL2.0, 如果运行环境不支持自动切到 WebGL1.0
**WebGL1.0:** 使用 WebGL1.0
**WebGL2.0:** 使用 WebGL2.0 | | Alpha | 画布是否支持透明背景,如果希望画布下方的内容可以透出可以开启 | | Preserve Drawing Buffer | 用于控制在调用 gl.clear() 方法后,绘图缓冲区是否保留其内容 | -| DPR Mode | [设备的像素比](/docs/core/canvas),通过调用 engine.canvas.resizeByClientSize 来控制画布的尺寸
**Auto:** 自动适配,即参数为 window.devicePixelRatio
**Fixed:** 开发者自行设置参数 选择 Fixed 后,开发者可以自行输入需要设置的参数 | +| DPR Mode | [设备的像素比](/docs/core/canvas),通过 `engine.canvas.setAutoResolution` 来控制画布的分辨率
**Auto:** 自动适配,即跟随显示尺寸并使用 devicePixelRatio
**Fixed:** 开发者自行设置缩放参数 选择 Fixed 后,开发者可以自行输入缩放倍率 | ## 支持的导出平台 目前 Galacean 支持导出的平台如下: diff --git a/packages/galacean/README.md b/packages/galacean/README.md index 71bc59c556..8551c2785b 100644 --- a/packages/galacean/README.md +++ b/packages/galacean/README.md @@ -39,9 +39,8 @@ import { Engine, Scene, Entity } from "@galacean/engine"; ## Usage ```typescript -// Create engine by passing in the HTMLCanvasElement id and adjust canvas size +// Create engine by passing in the HTMLCanvasElement id (canvas auto-resizes by default) const engine = await WebGLEngine.create({ canvas: "canvas-id" }); -engine.canvas.resizeByClientSize(); // Get scene and create root entity const scene = engine.sceneManager.activeScene;