Orillusion / orillusion

Orillusion is a pure Web3D rendering engine which is fully developed based on the WebGPU standard.
https://www.orillusion.com
MIT License
4.47k stars 551 forks source link

[BUG]: 当 window.devicePixelRatio 不为 1 时,会不断触发 canvas 的 resize。 #403

Closed fenriliuguang closed 1 month ago

fenriliuguang commented 2 months ago

Bug描述

在如下代码块中不配置 devicePixelRatio 时默认取 window.devicePixelRatio,当其值大于 1 (可能是1.2几)时,canvas 会不断 resize 宽高,直到达到 ~ 2 ^ 32;小于 1 时则最终为 0;

await Engine3D.init({
  canvasConfig: {
    canvas,
    // devicePixelRatio
  },
})

Bug复现流程

通过以下步骤产生bug:

  1. 创建 vue + vite 默认项目
  2. 替换 HelloWorld.vue 文件为如下代码

    <template>
      <div>
        <div class="container">
          <canvas id="canvas" :width="700" :height="500"></canvas>
        </div>
        <button @click="run()">渲染</button>
      </div>
    </template>
    
    <script setup lang="ts">
    import {
      Engine3D,
    } from '@orillusion/core'
    const run = async () => {
      const canvas = document.getElementById('canvas') as HTMLCanvasElement   
      await Engine3D.init({
        canvasConfig: {
          canvas,
        },
      })
    }
    </script>
    <style>
    .container {
      width: 700px;
      height: 500px;
    }
    </style>
  3. 缩放浏览器页面 (ctrl + 鼠标滚轮)至 window.devicePixelRatio 大于 1.125 或 小于 0.875。
  4. 点击按钮
  5. 出现错误 canvas 元素的 宽高值 不断放大/缩小 至 2^32 / 0。

期待的结果

正常初始化一个 宽 700 高 500 的 画布元素。

现象

Screenshot 2024-06-03 152032

bug 原因猜测

可能是如下代码块出现问题

class Context3D extends CEventDispatcher {
  // ...
  get pixelRatio() {
    return this._pixelRatio;
  }
  // ...
  async init(canvasConfig) {
    // ...
    this._pixelRatio = this.canvasConfig?.devicePixelRatio || window.devicePixelRatio || 1;
    this._pixelRatio = Math.min(this._pixelRatio, 2);
    // ...
    const resizeObserver = new ResizeObserver(() => {
      this.updateSize();
      Texture.destroyTexture();
    });
    resizeObserver.observe(this.canvas);
    this.updateSize();
    return true;
  }
  updateSize() {
    let w = Math.floor(this.canvas.clientWidth * this.pixelRatio * this.super);
    let h = Math.floor(this.canvas.clientHeight * this.pixelRatio * this.super);
    console.log(w, h)
    if (w != this.windowWidth || h != this.windowHeight) {
      this.canvas.width = this.windowWidth = w;
      this.canvas.height = this.windowHeight = h;
      this.presentationSize[0] = this.windowWidth;
      this.presentationSize[1] = this.windowHeight;
      this.aspect = this.windowWidth / this.windowHeight;
      this._resizeEvent.data.width = this.windowWidth;
      this._resizeEvent.data.height = this.windowHeight;
      this.dispatchEvent(this._resizeEvent);
    }
  }
}

测试引擎版本:

本地运行出错的Orillusion引擎版本: 0.7.2

本机系统 (请填写完整):

本机配置

fenriliuguang commented 2 months ago

可能是这个方法有问题:https://github.com/Orillusion/orillusion/blob/49ad63a0e903bb23804386defa7bc2abaa4a3be5/src/gfx/graphics/webGpu/Context3D.ts#L124

lslzl3000 commented 2 months ago

使用外部 canvas 的话,一般需要开发者自己管理 canvas 的 style,这个例子中因为没有指定 canvas style 的大小,浏览器默认会根据 canvas 的画布 width 和 height 标签来设置 style size, 所以 updateSize 后会改变 context size 从而改变 style size, 又触发resize 这样反复循环...

如果是想让 canvas 占满父级 container 应该指定 canvas style width 和 height 100% ,其实本质是对 canvas style width/height 和 attribute width/height 的理解问题

fenriliuguang commented 2 months ago

感谢指出我的理解问题。设置了 canvas 的 style 宽高解决了这一现象。能否同步一下官方文档: 手动创建-canvas?因为按照文档的最小示例仍然会触发上述问题。

lslzl3000 commented 2 months ago

感谢指出我的理解问题。设置了 canvas 的 style 宽高解决了这一现象。能否同步一下官方文档: 手动创建-canvas?因为按照文档的最小示例仍然会触发上述问题。

感谢提醒,稍后会对文档进行补充

Davidyanlong commented 2 months ago

这个bug 似乎已经修改了