Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
cfb72c7
refactor(rhi-webgl): extract isOffscreenCanvas helper
luo2430 Jun 17, 2026
c7d793a
feat(rhi-webgl): add WebGLEngine auto-resize support via ResizeObserver
luo2430 Jun 18, 2026
b794d54
chore(vitest): disable screenshotFailures to avoid taking blank scree…
luo2430 Jun 18, 2026
9fc8dce
docs(rhi-webgl): update isOffscreenCanvas and ResizeObserver.observe …
luo2430 Jun 18, 2026
a15b539
fix(rhi-webgl): defer auto-resize to prevent white flash
luo2430 Jun 22, 2026
7c5c1c1
refactor(rhi-webgl): move field declarations before methods
GuoLei1990 Jul 1, 2026
f7ad066
refactor(rhi-webgl): rework canvas resolution API, follow display siz…
GuoLei1990 Jul 1, 2026
25b7462
Merge branch 'dev/2.0' into pr3037-head
GuoLei1990 Jul 1, 2026
ada33cd
test(rhi-webgl): cover deprecated enableAutoResize/disableAutoResize …
GuoLei1990 Jul 1, 2026
a4c593b
test(e2e): rely on default auto-resolution instead of explicit setRes…
GuoLei1990 Jul 2, 2026
7a74b11
docs(core): drop redundant Read-only note from width/height remarks
GuoLei1990 Jul 2, 2026
4a23dd9
docs(core): drop migration pointer from width/height docs
GuoLei1990 Jul 2, 2026
83dd8c7
docs: consolidate resize comments; validate auto-resolution scale
GuoLei1990 Jul 2, 2026
b2fa6fe
docs: fix comment defects introduced by the comment audit itself
GuoLei1990 Jul 2, 2026
6a6b15b
docs(core): describe Canvas from the user's perspective
GuoLei1990 Jul 2, 2026
f7b5be9
docs: rewrite comments from the reader's perspective
GuoLei1990 Jul 2, 2026
90d5ce0
docs(core): align @throws format with the repo's existing convention
GuoLei1990 Jul 4, 2026
4350dd8
docs(core): drop self-evident comments on internal size methods
GuoLei1990 Jul 4, 2026
2fb80a8
docs(core): drop the _exitAutoResize note
GuoLei1990 Jul 4, 2026
7a5813f
docs(core): frame width/height as canvas resolution components
GuoLei1990 Jul 4, 2026
ddbe7a6
refactor(rhi-webgl): make isOffscreenCanvas internal
GuoLei1990 Jul 4, 2026
edadc3f
docs: drop unfounded warn-once TODO and trailing periods on line comm…
GuoLei1990 Jul 4, 2026
4c0db61
docs(rhi-webgl): drop self-evident comment on the resize observer cal…
GuoLei1990 Jul 4, 2026
8bfff96
docs(rhi-webgl): tighten _pumpPendingResize comments
GuoLei1990 Jul 4, 2026
daed532
docs(rhi-webgl): drop 0x0 guard comment
GuoLei1990 Jul 4, 2026
f557791
docs(rhi-webgl): note the compat constraint in the devicePixelContent…
GuoLei1990 Jul 4, 2026
e7a157e
docs(rhi-webgl): reframe the devicePixelContentBoxSize TODO as an add…
GuoLei1990 Jul 4, 2026
a139505
feat(rhi-webgl): use devicePixelContentBoxSize for exact auto-resize …
GuoLei1990 Jul 4, 2026
d0eb8e8
refactor(rhi-webgl): move _isOffscreenCanvas below the public members
GuoLei1990 Jul 4, 2026
49c1869
docs(rhi-webgl): drop the pending-device-pixel field comment
GuoLei1990 Jul 4, 2026
ba05525
fix(rhi-webgl): let the first auto-resize use exact device pixels
GuoLei1990 Jul 4, 2026
9df47a6
refactor(rhi-webgl): drop engine-level enableAutoResize/disableAutoRe…
GuoLei1990 Jul 4, 2026
5aacc85
refactor(rhi-webgl): move _releaseCanvas to the bottom, drop its comment
GuoLei1990 Jul 4, 2026
4889464
refactor: pump pending canvas resize in base Engine.update
GuoLei1990 Jul 4, 2026
24df15f
docs(core): drop the pump comment in Engine.update
GuoLei1990 Jul 4, 2026
f1dc487
docs(rhi-webgl): simplify the canvas getter doc
GuoLei1990 Jul 4, 2026
724dd3c
refactor(rhi-webgl): drop the autoResize create option
GuoLei1990 Jul 4, 2026
90a8755
chore(docs): migrate canvas resizing APIs and update guides
luo2430 Jul 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
103 changes: 56 additions & 47 deletions docs/en/core/canvas.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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:
>
> `<meta name="viewport" content="width=device-width, initial-scale=0.333333333">`
>
> 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
```
webCanvas.setResolution(fixedWidth, Math.round(window.innerHeight / scale));
webCanvas.setScale(scale, scale);
```
1 change: 0 additions & 1 deletion docs/en/physics/query/raycast.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion docs/en/platform/platform.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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:<br/> **Auto:** WebGL2.0 is preferred. If the operating environment does not support it, it will automatically switch to WebGL1.0 <br/> **WebGL1.0:** Using WebGL1.0 <br/> **WebGL2.0:** Using WebGL2.0 <Image src="https://mdn.alipayobjects.com/huamei_w6ifet/afts/img/A*HszfTJChrdEAAAAAAAAAAAAADjCHAQ/fmt.webp" style={{zoom: "50%"}} />|
| 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: <br/> **Auto:** Automatic adaptation, that is, the parameter is window.devicePixelRatio <br/> **Fixed:** Developers set their own parameters <Image src="https://mdn.alipayobjects.com/huamei_w6ifet/afts/img/A*EQOxSI8I8awAAAAAAAAAAAAADjCHAQ/fmt.webp" style={{zoom: "50%"}} /> After selecting Fixed, developers can enter the parameters they need to set. <Image src="https://mdn.alipayobjects.com/huamei_w6ifet/afts/img/A*-7YfTLegt_AAAAAAAAAAAAAADjCHAQ/fmt.webp" style={{zoom: "50%"}} />|
| DPR Mode | [Device pixel ratio](/en/docs/core/canvas),Control the canvas resolution via `engine.canvas.setAutoResolution`: <br/> **Auto:** Automatically follow the display size with devicePixelRatio <br/> **Fixed:** Developer sets a custom scale parameter <Image src="https://mdn.alipayobjects.com/huamei_w6ifet/afts/img/A*EQOxSI8I8awAAAAAAAAAAAAADjCHAQ/fmt.webp" style={{zoom: "50%"}} /> After selecting Fixed, developers can enter the scale value. <Image src="https://mdn.alipayobjects.com/huamei_w6ifet/afts/img/A*-7YfTLegt_AAAAAAAAAAAAAADjCHAQ/fmt.webp" style={{zoom: "50%"}} />|

## Supported export platforms
Currently, Galacean supports exporting to the following platforms:
Expand Down
103 changes: 56 additions & 47 deletions docs/zh/core/canvas.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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` 这些方法修改。

> **注意**:有些前端脚手架会插入以下标签修改页面的缩放比:
> **注意**:有些前端脚手架会插入以下标签修改页面的缩放比:
>
> `<meta name="viewport" content="width=device-width, initial-scale=0.333333333">`
>
> 这行代码会把 `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);
```
1 change: 0 additions & 1 deletion docs/zh/physics/query/raycast.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ const engine = await WebGLEngine.create({
canvas: "canvas",
physics: new PhysXPhysics()
});
engine.canvas.resizeByClientSize();
const scene = engine.scenes[0];

// 创建一条从原点向下的射线
Expand Down
Loading
Loading