Giter VIP home page Giter VIP logo

blog's Introduction

This blog is my tech blog deployed on coding pages. Main modules include:

  • 100+ Markdown posts.
  • Hexo configurations.
  • Custom theme with static front end demos. (Standalone repo maintained here).

Besides front end, I'm trying to combine technology and humanity in blog posts. Your involvement is welcomed if your're also interested in this.

Quick Links

Participation

Issues and pull requests are super welcomed for:

  • Propose ideas you're interested in.
  • Fix typos and mistakes.
  • Improve UX for the site.
  • Request new features.

For basic formatting, please use SBC case(全角) punctuation and keep space between Chinese and English words.

This blog is generated by Hexo. So to preview the change, simply follow these steps:

  1. Clone this repo, alone with its theme in themes/jacman path.
  2. Install and go.
npm install -g hexo-cli
npm install
hexo server

To update the site, hexo deploy helps. But generally please submit issue or pull request for this 🙂

The columns on juejin.im and zhihu.com are manually maintained by pasting content derived from here. For participation related to these columns, feel free to contact me 📪

Donate

BTC: 1NAggoiG9fkXqT9MgCjkaL1YAL2mqcQbWP

License

CC BY-ND 4.0

blog's People

Contributors

doodlewind avatar yifewind avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Forkers

wujcheng cgy1992

blog's Issues

Draw Call 排序场景抽象

在使用以 Draw + Resource 为核心的新版 API 后,我们就具备了给 Draw Call 排序来减少状态变更的能力。以绘制如下的的四个矩形为例:

image

它可以通过同一个矩形用红蓝 2 种纹理和 4 个不同 Uniform 矩阵,调用 4 次 Draw 得到:

image

我们将每次的 GL 状态变更计为一个 Command,那么在 WebGL 中常见的 Command 包括:

SetUniforms // 设置 Uniform

BindTextures // 切换纹理
SetTextures // 更新纹理

BindBuffer // 切换缓冲区
SetBuffer // 设置缓冲区

SetContextState // 设置其他 GL 状态

DrawCall // 启动渲染流水线绘制

对于上面的场景,我们可以列出从左上到右下,整个绘制流程所需的相应 Command 列表。下面用 S 表示着色器,T0 / T1 表示纹理,M1 / M2 / M3 / M4 表示 Uniform 矩阵:

// Commands for drawing 4 rects
Commands = [
  // BEGIN Top Left
  {
    type: BindTextures,
    shader: S,
    action: useTextureT0
  },
  {
    type: SetUniforms,
    shader: S,
    action: useMatrixM1
  },
  {
    type: DrawCall,
    shader: S,
    // On draw, compute (Shader, Textures, DataBuffers, IndexBuffer, Uniforms)
    // Get: (S, T0, M1)
    action: draw
  },
  // END Top Left

  // BEGIN Top Right
  {
    type: BindTextures,
    shader: S,
    action: useTextureT0
  },
  {
    type: SetUniforms,
    shader: S,
    action: useMatrixM2
  },
  {
    type: DrawCall,
    shader: S,
    // On draw, compute (Shader, Textures, DataBuffers, IndexBuffer, Uniforms)
    // Get: (S, T1, M2)
    action: draw
  },
  // END Upper Right

  // BEGIN Bottom Left
  {
    type: SetTextures,
    shader: S,
    action: useTextureT0
  },
  {
    type: SetUniforms,
    shader: S,
    action: useMaxtrixM3
  },
  {
    type: DrawCall,
    shader: S,
    // On draw, compute (Shader, Textures, DataBuffers, IndexBuffer, Uniforms)
    // Get: (S, T0, M3)
    action: draw
  },
  // END Bottom Left

  // BEGIN Bottom Right
  {
    type: BindTextures,
    shader: S,
    action: useTextureT1
  },
  {
    type: SetUniforms,
    shader: S,
    action: useMatrixM4
  },
  {
    type: DrawCall,
    shader: S,
    // On draw, compute (Shader, Textures, DataBuffers, IndexBuffer, Uniforms)
    // Get: (S, T1, M4)
    action: draw
  }
  // End Bottom Right
]

只要我们在调用 beam.draw() 时不去直接操作 GL 状态,而是改为提交一个 Command,就很容易收集得到如上的一份 Command 列表。实际代码形如:

beam
  .clear() // 开始收集 Command
  .draw(pluginA, ...resourcesX)
  .draw(pluginB, ...resourcesX)
  .draw(pluginB, ...resourcesY)
  .commit() // 处理 Command 列表,执行实际绘制

收集得到该 Command 列表后,我们在每个 typeDrawCall 的节点,推导出该状态下的几个关键状态。优先级大致为:

Shader > Textures > DataBuffers > IndexBuffer > Uniforms

这样就能得到一份默认情况下,四次 Draw Call 的状态列表:

// Draw States Before:
(S, T0, M1)
(S, T1, M2)
(S, T0, M3)
(S, T1, M4)

假设我们每次 Draw 都需要更新全部状态,则对应于如下的状态变更顺序:

// State Changes Before:
Set S -> Set T0 -> Set M1 -> Draw
Set S -> Set T1 -> Set M2 -> Draw
Set S -> Set T0 -> Set M3 -> Draw
Set S -> Set T1 -> Set M4 -> Draw
// 12 "Set" State Changes

可以看到,这一共带来了 12 次状态变更。但只需要基于基数排序,就可以得到一个新的 Draw Call 顺序,在保证绘制结果相同的前提下,使得两次 Draw Call 之间的状态尽可能接近

// Draw States After:
(S, T0, M1)
(S, T0, M3)
(S, T1, M2)
(S, T1, M4)

从而,我们就可以进一步地根据当前的绘制状态,增量进行 State Change 了:

// State Changes After:
Set S -> Set T0 -> Set M1 -> Draw
Set M3 -> Draw
Set T1 -> Set M2 -> Draw
Set M4 -> Draw
// 7 "Set" State Changes

这只需要 7 次状态变更!如果是 100 个像这样依次交错的 Draw Call,优化效果还会好得多。如果被合并的状态变更之中还包括图像采样等高耗操作,所能节约的资源就更可观了。

我们可以基于这个思路,来设计 Command 提交 + 排序机制,以及状态的差量更新机制。

Beam 框架资源管理机制重构

在 1.0 之前,我们准备在 #4 基础上再实现如下改动,来大幅提升 Beam 的资源管理能力:

  • 支持动态更新 Renderer 插件序列
  • 支持跨插件可选复用数据 Buffer
  • 支持跨插件可选复用 Index Buffer
  • 支持简化的插件 Schema.from 更新机制
  • 支持动态更新插件着色器

下面介绍特性概述、API 变更及开发进展

特性概述

动态更新 Renderer 插件序列

在目前,Beam 渲染器在 constructor 中分配固定的插件序列,它们无法在运行时动态改变,并且 pipelinePluginsoffscreenPlugins 引入了额外的概念,并造成了一些代码实现上不必要的重复。为了自由地在运行时切换着色器,我们做了 API 的改造。现有使用方式:

const pipelinePlugins = [meshPlugin, postEffectPlugin]
const offscreenPlugins = [filterPlugin]
const renderer = new Renderer(canvas, pipelinePlugins, offscreenPlugins)

我们引入了 Renderer.usePlugins API,改造后:

const renderer = new Renderer(canvas)
renderer.usePlugins([meshPlugin, postEffectPlugin])

注意我们当前还没有重构离屏绘制的 API,故而 offscreenPlugins 能力相当于被暂时废弃了。我们将在未来重新设计实现这一特性。

跨插件可选复用数据 Buffer

在目前,我们将 Buffer 与 Shader 做了一对一的绑定。这主要适合插件渲染内容各自独立的场景。例如依次渲染这些内容:

天空盒 -> 模型网格 -> 雪花粒子

但另一种常见的情形,是由多个着色器绘制同一份数据 Buffer。例如:

  1. 3D 模型有多种着色算法,需要在不同着色器中承载更新
  2. 后处理等场景下,需要多个 Pass 逐一渲染

目前 Beam 可以让元素 State 在插件之间复用,但每个 State 都会逐插件创建一份数据 Buffer。这导致了在展示 3D 模型线框辅助线的时候,数据量增加了一倍。在这个 PR 中,我们支持显式控制插件的数据 Buffer 共享,即 Beam.sharePluginResource API。在 renderer 使用插件之前调用这一 API 即可,形如:

Beam.sharePluginResource([pluginA, pluginB]) // 声明 A 与 B 的共享关系

const renderer = new Renderer(canvas)
renderer.usePlugins([pluginA, pluginB, pluginC]) // 实际使用插件
  • 若在初始化资源前调用该 API,则数据 Buffer 只会,其它使用方式完全不变。
  • 被共享的插件,其 Buffer Schema 会被自动合并,若各插件字段无法正确合并,则优化无法生效。
  • 不调用该 API 时,插件默认维护独立的 Buffer 资源。

跨插件可选复用 Index Buffer

上一复用数据 Buffer 的优化特性,并不能完全满足需求,例如:

  • 在模型上叠加线框时,线框的连线式绘制,其需要的顶点顺序与原始的三角形方式不同
  • 为模型的部分顶点添加描边时,描边数据并不会覆盖全部的模型,只是原有顶点的子集
  • 多个着色插件可能完全共享一个大型的数据 Buffer,基于不同索引,各自绘制一部分的子集

对于这些需求,我们在 sharePluginResource 的基础上追加了可选的 Index Buffer 复用能力。默认情况下,被共享的插件使用完全相同的 Index。但只要为该 API 传入第二个 shareIndex = false 参数,即可在这些插件复用数据 Buffer 的基础上,使用各自独立的 Index Buffer 来绘制。以在模型上叠加线框为例:

// 初始化插件与 Renderer
const shapePlugin = new BasicShapePlugin()
const wireframePlugin = new WireframeShapePlugin()
const renderer = new Basic3DRenderer(canvas)

// 打开资源共享优化,注意第二个 false 参数
Beam.sharePluginResource([wireframePlugin, shapePlugin], false)
// 由 Renderer 初始化插件
renderer.usePlugins([wireframePlugin, shapePlugin])

// 每个 Shape 元素会被这两个插件共享
const createShapeElement = state => (
  Beam.createElement(state, BasicShapePlugin, WireframeShapePlugin)
)

// 创建一个球体和三个周边的立方体
const shapeA = createShapeElement({ type: 'sphere', position: [0, 0, 0] })
const shapeB = createShapeElement({ position: [2, 0, 0] })
const shapeC = createShapeElement({ position: [-2, 0, 0] })
const shapeD = createShapeElement({ position: [0, -2, 0] })

// 加入元素并渲染
renderer.setCamera([0, 10, 10])
renderer.addElements([shapeA, shapeB, shapeC, shapeD])
renderer.render()

简化的插件 Schema.from 更新机制

目前,Beam 使用了 plugin.propsByElementplugin.propsByEnv 两个生命周期钩子,来将语义化的 State 数据转换为 WebGL 所需的资源数据。这一实现虽然简单,但带来了一些优化和使用上的问题:

  • propsByElementpropsByEnv 所返回的 Key,在 Schema 中都已经存在,必须写两遍。
  • 复用 Buffer 结构时,需要合并多个插件的 propsByElement 计算结果。这仍然需要进行多次潜在的高耗计算。
  • 我们不知道 propsByElement 中的哪些字段是高耗的 Buffer 字段,哪些是普通的 Uniform 字段,难以做更细粒度的更新优化。
  • 每个字段如果需要默认值,需要在钩子中自行控制,较为松散且不易二次定制。若插件使用者希望不修改插件源码的前提下更改字段默认值,则必须覆盖整个 propsBy 钩子。
  • Props 概念的语义不如 Resources 概念符合直觉,但 resourcesByElement 看起来很啰嗦。

为此,我们借鉴了 REGL 的设计,引入新的 Schema 的 from API 来替代现有的 propsBy 钩子。它不仅降低了使用成本,还利于提高性能。现有的 API 使用方式形如这样:

export class ShapePlugin extends ShadePlugin {
  constructor () {
    // ...
    // 原有的 Schema 写法
    this.resourceSchema = {
      buffers: {
        pos: { type: vec4, n: 3 },
        color: { type: vec4 },
        index: { type: index }
      },
      uniforms: {
        modelMat: { type: mat4 },
        viewMat: { type: mat4 },
        projectionMat: { type: mat4 }
      }
    }
  }

  // 返回 Key 与 Schema 字段相互匹配的字段
  propsByElement (state) {
    const { type, position, color } = state
    const modelMat = create()
    if (state.translate) translate(modelMat, modelMat, state.translate)

    if (type === 'sphere') {
      const { positions, colors, indices } = getSphere(position)
      return { modelMat, pos: positions, color: colors, index: indices }
    } else {
      const { positions, colors, indices } = getCube(position, color)
      return { modelMat, pos: positions, color: colors, index: indices }
    }
  }

  // 此处相当于简单的字段名转换
  propsByEnv (state) {
    return {
      viewMat: state.camera,
      projectionMat: state.perspective
    }
  }
}

而新的 Schema from API 则将钩子整合到了 Schema 中,形如这样:

export class BasicShapePlugin extends ShadePlugin {
  constructor () {
    // ...
    this.resourceSchema = {
      buffers: {
        // getPos / getColor / getIndices 用于转换 state 数据
        pos: { type: vec4, n: 3, from: getPos },
        color: { type: vec4, from: getColor },
        index: { type: index, from: getIndices }
      },
      uniforms: {
        // from 为字符串时,直接转换 state 的相应字段即可
        viewMat: { type: mat4, from: 'camera' },
        projectionMat: { type: mat4, from: 'perspective' }
      }
    }
  }
}

这一改造使得框架能够追踪单个字段的更新,实现更细粒度的优化。同时,在简单情况下可以指定字符串形式的 from 转换方式,基于插件二次开发时也非常容易定制。

还有相关的两点更新:

  • 每个 Schema 项可以配套一个 default 字段,作为默认值。
  • 对 Textures 和 Uniforms 下的 Schema 项,它们还支持一个额外的 element: true 配置。这可以用于指定该字段是从 ElementState 还是 EnvState 中获取。

动态更新插件着色器

目前 Beam 的着色器是在 new Plugin 时固定,传入的 defines 宏也是固定的。这不利于较为灵活的着色器更新场景。为此,我们设计了 Plugin.setProgram API 来增强灵活性。现有 API 形如:

const fooPlugin = new FooPlugin({ FOO: true }) // 使用固定的 defines

更改后的 API 则是这样的:

const fooPlugin = new FooPlugin()
fooPlugin.setProgram({ vs, fs, defines  })

这里的 vsfs 都是可选的着色器字符串。插件有自己默认的着色器,可以不提供。而 defines 则是可以动态更新的宏定义。在调用 API 后,框架即会使用新的着色器来进行渲染。

API 变更

这一 PR 主要对应如下 API 变更:

  • new Renderer(canvas, plugins) 改造为 new Renderer(canvas)renderer.usePlugins 两步。
  • new ShadePlugin(defines) 改造为new ShadePlugin()
  • propsBy 风格 API 迁移到 from 风格。

开发进展

  • 所有新增 API 均已可用。
  • 自带示例中,Basic Shades / Textures / Lambert 已完成适配,PBR 等其它示例暂待跟进。
  • FBO 处理暂未支持,需额外的 PR 设计实现。
  • 文档暂未更新,预计需较大范围的重写。
  • 特殊边界情况处理,待示例适配过程中完善。

网站访问不了啦!

我这里多条线路访问任何 ewind.us 域下资源都是一直 waiting for connection,直到 connection reset。
FYI:

$ dig ewind.us

; <<>> DiG 9.11.5-P4-1-Debian <<>> ewind.us
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 12911
;; flags: qr rd ra; QUERY: 1, ANSWER: 6, AUTHORITY: 4, ADDITIONAL: 10

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;ewind.us.			IN	A

;; ANSWER SECTION:
ewind.us.		600	IN	CNAME	pages.coding.me.
pages.coding.me.	60	IN	A	128.1.135.25
pages.coding.me.	60	IN	A	23.248.162.18
pages.coding.me.	60	IN	A	23.91.98.84
pages.coding.me.	60	IN	A	107.150.122.20
pages.coding.me.	60	IN	A	128.1.135.18

;; AUTHORITY SECTION:
coding.me.		2858	IN	NS	lv3ns1.ffdns.net.
coding.me.		2858	IN	NS	lv3ns2.ffdns.net.
coding.me.		2858	IN	NS	lv3ns4.ffdns.net.
coding.me.		2858	IN	NS	lv3ns3.ffdns.net.

;; ADDITIONAL SECTION:
lv3ns1.ffdns.net.	559	IN	A	61.133.127.56
lv3ns1.ffdns.net.	559	IN	A	111.6.96.206
lv3ns1.ffdns.net.	559	IN	A	113.106.99.204
lv3ns2.ffdns.net.	559	IN	A	61.133.127.157
lv3ns3.ffdns.net.	559	IN	A	61.133.127.130
lv3ns3.ffdns.net.	559	IN	A	111.161.119.67
lv3ns3.ffdns.net.	559	IN	A	183.146.208.102
lv3ns4.ffdns.net.	559	IN	A	220.168.190.140
lv3ns4.ffdns.net.	559	IN	A	61.54.31.27

;; Query time: 1279 msec
;; SERVER: 8.8.8.8#53(8.8.8.8)
;; WHEN: Sun Mar 10 19:20:23 CST 2019
;; MSG SIZE  rcvd: 383

(要不要考虑使用一个定时检查首页变化并邮件通知的服务?)

期待有天能加入你们的前端大家庭

你好Yifeng Wang,我从事前端行业快五年了,由于一直在传统软件行业,做着任务紧项目多的苦差事😓,发现技术和你们这样的大厂存在一定差距,也影响了职业规划。今年希望能跳出这样的模式,看到你们团队研发出来的产品🌹,使用的技术👍,和有趣的团队氛围十分向往🔥,想把加入你们作为自己的一个目标去实现和努力。
想问问你,技术要达到什么样的标准能加入你们,你们招的都是技术全面的大牛,还是说也会培养一些有一定基础,充满热情的coder,希望能得到你的解答。
邮箱:[email protected]
实在找不到哪里可以留言了,只能在这了,抱歉。

浏览器环境下自动化测试的可行性?

本文内容尚不足以作为博客文章,故作为 Issue 提出。

在 2019 年,已经有成熟的工具来帮助前端开发者实现:

  • 纯 JS 环境下的自动化单元测试,例如很多 Node 下的 Test Runner。
  • 浏览器环境下的自动化 E2E 测试,例如基于 Puppeteer 或 Cypress 等的方案。

但是,DOM 环境下的单元测试似乎一直没有突破性的进展。真实世界里,单元测试还是主要集中在前端类库与框架中,而 E2E 测试则更多地为部分 QA 同学所使用。它们各自的痛点在于:

  • Node 中的单元测试很难还原浏览器环境,即便 jsdom 模拟了 DOM,还有网络请求、canvas 与 webgl 等能力难以测试。
  • E2E 测试对开发同学过于沉重。通过组件化开发编写的代码,却需要编码模拟一系列页面跳转、表单提交等代码才能覆盖,且工具链的维护成本也较高。

有没有能取二者之长的手段呢?虽然现在社区开源的 puppeteer-jest 仍然定位在 E2E 测试,但是在 puppeteer 中,我们已经能做到一个重要的点:同一份 Node 下运行的 js 文件里,其中的某个回调能够在 window 的上下文里执行。那么,能否实现这一能力:编写 UI 组件各种方法的 JS 单元测试,但所测试的对象都能访问到浏览器的宿主环境呢?比如,在 React 源码的 fixtures 目录,放置着一些为了在 DOM 环境下测试 fiber 而编写的静态 html 文件,它们应该还是供肉测的,能否用 puppeteer 来自动化这些测试呢?

一种类似 repeater 中使用的实践是这样的:业务项目的页面中接入测试工具的简单 API,在页面 mounted 时将组件对象传入该 API,从而在 Node 环境下接受这一对象,以便于用 Jest 等框架来实现对其的单元测试。这样,我们就能测试 UI 组件的各类方法,而无需 mock 环境了。

现在遇到的主要问题在于,如何处理测试的副作用与多个 DOM 环境时的问题呢?例如 Jest 的测试天生是并行的,那么在同一个 DOM 环境下同时测试这些用例,如何解决可能的用例执行时序影响结果、副作用污染等问题呢?要给每个测试用例一个独立页面 DOM 环境,开销似乎又太昂贵了。

Beam 1.0 规划

Beam 从诞生起,就被寄托了成为团队首个自研 WebGL 基础库的重任,并随着业务一同快速地迭代演化。令我们欣慰的是,Beam 的成长跟上了业务需求的脚步。我们在多次针对性重构优化后,确实地获得了在工业级项目中,每个重要的指标(初始化速度、帧率、包体积等)都完全追平或优于开源方案的成果。虽然 Beam 仍然尚未发布首个 Major 版本,但实战的检验已经使得我们对渲染引擎的理解、掌控和预期都成熟得多了。为了促进这一正向循环,保证日后更大范围、更深度、更稳定的使用,是时候将 1.0 版本提上日程了。下面列出相关的规划。

1.0 目标

  • 去除冗余概念,提供高度简明、一致的 API。
  • 引入开发工具,改善使用体验。
  • 保证在恰当使用时,能发挥出原生级的最优性能。

预期变更

Global / Element / State / Props 概念整合

着色插件涉及的这些核心抽象在 1.0 之前有些重叠而难以理解。现在这些概念得到了简化:

  • 场景由语义化的 JSON 数据构建,这些数据称之为 State
  • 若场景中有多个物体,则每个物体对应一个 Element,每个 Element 存储着自己的顶点、法向量、UV 贴图等 State 信息,这些逐物体的 State 称为 ElementState。同时,摄像机位置、环境贴图等状态是整个场景唯一的,这些 State 称为 GlobalState
  • 语义化的 State 通过插件,转换为能够提交到 WebGL 的资源,这些资源称为 Props。输入 State,返回 Props 的纯函数,即对应于插件的 propsByElementpropsByGlobal 两个方法。
  • 插件生成的 Props 如何提交到 WebGL?通过下面提及的 Schema 机制即可。

因此新设计下,渲染简化为了 State (Element / Global) -> Props -> WebGL 的过程。从而,上层 API 简化为 Renderer 的:

  • renderer.addElement(s)
  • renderer.removeElement(s)
  • renderer.setElementState
  • renderer.setGlobalState
  • renderer.render

以及着色插件的:

  • plugin.propsByElement
  • plugin.propsByGlobal

使用方式形如:

class MyPlugin extends ShadePlugin {
  constructor () {
    // 配置 Schema
  }

  propsByElement (state) {
    return { propKeyA: state.keyA, propKeyB: state.keyB }
  }

  propsByGlobal (state) {
    return { propKeyC: state.keyC, propKeyD: state.keyD }
  }
}

const myPlugin = new MyPlugin()
const renderer = new Renderer(canvas, [myPlugin])

// 设置好供 propsByGlobal 获取的全局 State
renderer.setGlobalState('keyC', foo)
renderer.setGlobalState('keyD', bar)

// 两个不同的 Element,其 State 字段供 propsByElement 获取
const xElement = createElement({ keyA: fooX, keyB: barX }, MyPlugin)
const yElement = createElement({ keyA: fooY, keyB: barY }, MyPlugin)
renderer.addElement(xElement)
renderer.addElement(yElement)

// 根据 propKeyA / propKeyB / propKeyC / propKeyD 分组渲染物体
renderer.render()

Schema 合并

我们计划将 propSchemashaderSchema 合并为更简单的单个 PropSchema。PropSchema 分为 buffers / uniforms / textures 三种,每种下的字段 Key 均包含一个必填的 type 属性。形如:

import { SchemaTypes, ShadePlugin, Renderer } from '@gaoding/beam'

const { vec4, mat4, index, tex2D } = SchemaTypes
this.propSchema = {
  buffers: {
    pos: { type: vec4, n: 3 },
    color: { type: vec4 },
    index: { type: index } // 注意 index 类型
  },
  uniforms: {
    viewMat: { type: mat4 },
    projectionMat: { type: mat4 }
  },
  textures: {
    image: { type: tex2D, flip: true } // 注意不是 sampler2D
  }
}

这样的 API 既能符合直觉地与 GLSL 中的类型保持一致,又很易于处理 Index Buffer 和 Texture 这两种无法直接一一映射到着色器变量的资源内容。并且,sampler2DsamplerCube 只要给定了 tex2DtexCube 类型的 Schema 字段,相应纹理资源(图像、Canvas、TypedArray 等)就能由引擎自动管理。

开发者工具

由于 Beam 的核心代码几乎只关注 Happy Path,这导致了其虽然体积极轻(1000 行代码量级,仅有 regl 的约十分之一,Three 的几十分之一),但在调试时出错位置容易使人困惑。我们可以通过开发阶段的工具来改善这一问题。

Beam 的 Renderer 对象虽然紧密围绕 WebGL 设计,但所有操作 GL 实例的 Utils 代码都是从外部注入的。因此只需要替换掉这些耦合 GL 实例的 Utils,我们就能在其它环境中运行 Renderer。基于这一点,我们可以提供一个特殊定制的 DevRenderer,它不会执行实际渲染,但只要将上层代码改为使用这一 Renderer 执行,就能提供充分的 Schema 校验、反模式警告等特性。具体提示内容尚在规划中。

性能优化

1.0 中规划的性能优化包括如下:

  • setElementState 可以根据类似 Proxy 的机制,判断更新的 key 是否属于 Buffers,从而按需执行 GL 状态的更改操作。
  • removeElement(s) 可以在移除的元素位于 Elements 序列尾部时,实现增量的 Buffer 传输优化。
  • 大段 Buffer 的复制可改为 TypedArray 的 set 方法实现以优化效率。

它可以保证如下场景的性能达到最优:

  • 初始化阶段,向场景中插入数十个物体,每个物体具备万级的顶点数量。
  • 每帧高频更新物体的变换矩阵等 ElementState,以及摄像机位置等 GlobalState。
  • 每帧叠加阴影、后处理等离屏渲染。
  • 物体可以做低频的增删操作。

欢迎在评论中 Review 或讨论目前的 1.0 规划与设计 :)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.