|
| 1 | +# GL2GPU |
| 2 | + |
| 3 | +> 如何使用静态的GL2GPU? |
| 4 | +
|
| 5 | +Webpack编译。指令 `npm i; npx webpack w`,根据`webpack.config.js`中的配置,静态的GL2GPU将在`dist/`中生成最终代码。`dist/release`, `dist/development`, `dist/release-minimized` 里面的 `gl2gpu.js` 都可以被使用。 |
| 6 | + |
| 7 | +翻译shader代码,翻译样例:`src/components/shaders/shaders_info.json` |
| 8 | +- 翻译辅助脚本:`src/components/shaders/glsl_to_bindings.py` |
| 9 | +- uniform 需要被特殊翻译!(TBW) |
| 10 | + |
| 11 | +```typescript |
| 12 | +export function ShaderInfo2String(shaderInfo: ShaderInfoType): string { |
| 13 | + let res = ""; |
| 14 | + let offset = 0; |
| 15 | + if (shaderInfo.uniforms.length > 0) { |
| 16 | + res += "struct HydUniformObject {\n"; |
| 17 | + for (const uniform of shaderInfo.uniforms) { |
| 18 | + res += ` ${uniform.name}: ${uniform.wgsl_type},\n`; |
| 19 | + } |
| 20 | + res += "};\n\n"; |
| 21 | + res += "@binding(0) @group(0) var<uniform> _hyd_uniforms_ : HydUniformObject;\n\n"; |
| 22 | + offset = 1; |
| 23 | + } |
| 24 | + for (let i = 0; i < shaderInfo.samplers.length; i++) { |
| 25 | + const sampler = shaderInfo.samplers[i]; |
| 26 | + res += `@binding(${i * 2 + offset + 0}) @group(0) var ${sampler.name}S: ${sampler.wgsl_sampler_type};\n`; |
| 27 | + res += `@binding(${i * 2 + offset + 1}) @group(0) var ${sampler.name}T: ${sampler.wgsl_texture_type};\n`; |
| 28 | + } |
| 29 | + return res; |
| 30 | +} |
| 31 | +``` |
| 32 | + |
| 33 | + |
| 34 | + |
| 35 | +修改WebGL应用。需要进行5处修改: |
| 36 | +1. 引用上一步中得到的 `gl2gpu.js` 文件 |
| 37 | +2. 将 `getContext("webgl", arg0?)` 修改为 `GL2GPU.getContext(canvas, <shader_json_url>, arg0?, arg1?)` |
| 38 | + - `this._gl = await GL2GPU.hydGetContext(this.element, "../shaders_info.json", ["webgl", { preserveDrawingBuffer: true }], [1<<18, 200]);` |
| 39 | +3. 将同步函数修改为异步函数调用 `async`-`await` |
| 40 | +4. 将渲染函数 `render()` (通常被 `requestAnimationFrame` 所调用)修改为 `GL2GPU.beginFrame(); render(); GL2GPU.endFrame();` |
| 41 | + |
| 42 | +--- |
| 43 | + |
| 44 | +以下是动态GL2GPU文档。 |
| 45 | + |
| 46 | +Build Status: [](https://github.com/yudshj/GL2GPU/actions) |
| 47 | + |
| 48 | +# Evaluation 方法 |
| 49 | + |
| 50 | +Webpack编译指令 `npm i; npx webpack w` |
| 51 | + |
| 52 | +用Webpack编译完成后,将得到一个 hydWrapper.js 文件。只需要把这个文件以某种合适的方法注入到页面中就能加载GL2GPU。 |
| 53 | + |
| 54 | +加载完毕后为了驱动 GL2GPU 执行,还需要调用 `HYD.hydInstrument` 来修改 `canvas` 的 `getContext` 方法。比如我们可以添加以下代码(见 `/extension/inject.js`): |
| 55 | + |
| 56 | +```javascript |
| 57 | +const _GL2GPU_CONFIG = { |
| 58 | + cmbMode: "cmb", |
| 59 | + replayDelay: 5000, |
| 60 | + uniformBatchSize: 262144, // 256KB |
| 61 | +}; |
| 62 | + |
| 63 | +const proxy = new Proxy(window.requestAnimationFrame, { |
| 64 | + apply: function (target, thisArg, argumentsList) { |
| 65 | + const func = argumentsList[0]; |
| 66 | + const funcNew = function (time) { |
| 67 | + HYD.runFrameStartFunctions(); |
| 68 | + func(time); |
| 69 | + HYD.runFrameEndFunctions(); |
| 70 | + } |
| 71 | + target.apply(thisArg, [funcNew]); |
| 72 | + } |
| 73 | +}); |
| 74 | +window.requestAnimationFrame = proxy; |
| 75 | +HYD.hydInstrument( _GL2GPU_CONFIG["cmbMode"], _GL2GPU_CONFIG["replayDelay"], _GL2GPU_CONFIG["uniformBatchSize"] ); |
| 76 | +``` |
| 77 | + |
| 78 | +Extension 的实现中还使用 sessionStorage 来保存GL2GPU开启状态。保存的key名称为 'HYD_LOAD_KEY',可选项有 'on' 和 'off'。这样可以实现点击浏览器插件栏插件来开启/关闭GL2GPU。 |
| 79 | + |
| 80 | +`_GL2GPU_CONFIG` 定义了运行配置。 |
| 81 | + |
| 82 | +- `cmbMode` 表示优化开关,可选项有: |
| 83 | + - "cmb": cache + merge uniform + bundle |
| 84 | + - "c1mb": cache (without layer-2 cache) + merge uniform + bundle |
| 85 | + - "mb": merge uniform + bundle |
| 86 | + - "m": merge uniform |
| 87 | + - "cm": cache + merge uniform |
| 88 | +- `replayDelay` 表示等待多少毫秒之后开启 replay mode,$-1$ 表示关闭replay mode |
| 89 | +- `sessionStorage` 表示 merged uniform size |
| 90 | + |
| 91 | +## 关于 chrome extension |
| 92 | + |
| 93 | +目录为 `/extension`。插件旨在将目录下的 `inject.js` 注入当前页面。 |
| 94 | + |
| 95 | +修改 `inject.js` 然后在 chrome://extensions 里面刷新插件即可加载最新的配置。 |
| 96 | + |
| 97 | +## 关于 frame times |
| 98 | + |
| 99 | +现在三个 demo 都包含一段代码,可以记录 frameTimes. 记录原理如下: |
| 100 | + |
| 101 | +```javascript |
| 102 | +var frameTimes = []; |
| 103 | +function render() { |
| 104 | + const startTime = performance.now(); |
| 105 | + // render logic |
| 106 | + const endTime = performance.now(); |
| 107 | + frameTimes.push(endTime - startTime); |
| 108 | +} |
| 109 | + |
| 110 | +requestAnimationFrame(render); |
| 111 | +``` |
| 112 | + |
| 113 | +为了方便导出,我设置了获取 2000 个 frameTime 后保存 `frameTimes` 为json并下载。 |
| 114 | + |
| 115 | +## Demo |
| 116 | + |
| 117 | +- https://serverless.pku.edu.cn/gl2gpu/aquarium/?numFish=100 |
| 118 | +- https://serverless.pku.edu.cn/gl2gpu/sprites/?numSprites=100 |
| 119 | +- https://serverless.pku.edu.cn/gl2gpu/motionmark/?numTriangles=100 |
| 120 | + |
| 121 | +# 二次开发 |
| 122 | + |
| 123 | +## Shader |
| 124 | + |
| 125 | +WebGL 的 shader 需要在编译项目前单独翻译。翻译shader将得到一个json文件(`src/componenets/shaders/shaders_info.json`)。我们的翻译器会在 `shaderDB.ts` 中import这个 `shaders_info.json`。 |
| 126 | + |
| 127 | +目录 `src/componenets/shaders` 存放着与shader有关的所有代码。每个demo的shader代码在`src/componenets/shaders/<demo名称>`目录下。可以用 Python 3 执行 `src/componenets/shaders/glsl_to_bindings.py` 来自动生成 `src/componenets/shaders/shaders_info.json`。 |
| 128 | + |
| 129 | +> **由于变量alignment的问题,WebGL的shader无法“一对一”地翻译到WebGPU。**对于同一个 WebGL shader,可能有多种不同的WebGPU shader与之对应。这是因为GLSL(WebGL的shader语法)在开头不需要声明所有的全局变量,而WGSL(WebGPU的shader语法)则需要。不同的vertex和fragment的组合造成了翻译得到的WGSL的不一致。 |
| 130 | +
|
| 131 | +> 我们在翻译器的运行中解决这个 alignment 的问题。 |
| 132 | +
|
| 133 | +为了让 `glsl_to_bindings.py` 自动生成shader代码需要在编写每个项目的shader代码时注意以下两点: |
| 134 | + |
| 135 | +1. 把所有的uniform变量用 `_hyd_uniforms_` 给打包起来; |
| 136 | +2. 一个WebGL的texture uniform在WGSL中需要用两个bindgroup的变量表示。分别是sampler和texture。命名格式形如 xxxS, xxxT,其中xxx为GLSL中texture的变量名。 |
| 137 | + |
| 138 | +编写shader时只需保留运算逻辑,不需要声明 uniform binding group 和 storage binding group. 可以在 `hydProgram.ts` 的 `linkProgram()` 中打印alignment完成后的WGSL shader。 |
| 139 | + |
| 140 | +<!-- |
| 141 | +# Challenge |
| 142 | +
|
| 143 | +- 判断每个request animation frame的开始,frame和`clear()`的不对应。 |
| 144 | + - 不然createView耗时长。 |
| 145 | +- **shader的转换**和uniform binding的对齐。 |
| 146 | +- `mat4` can be a type of a vertex buffer. `in mat4 matrix`. |
| 147 | +- 一些小的uniform buffer可能不符合`minBindingSize`的要求。 |
| 148 | +- WebGPU不支持`gl_PointSize`。 |
| 149 | +- **mipmaps**. |
| 150 | +- WebGPU texture 的实现中 sampler (texture state) 和 内存 (texture data) 的分离 |
| 151 | +- `gl.LUMINANCE` |
| 152 | +- ==坐标变换== |
| 153 | +- uniform buffer performance? |
| 154 | +- 不支持 $\mathit{type}$ 为`gl.UNSIGNED_BYTE`的`drawElements`. |
| 155 | +- `instanceof` |
| 156 | +- WebGPU: texture, viewDimension / WebGL: `bindTexture()` |
| 157 | + - WebGL的bindTexture不能bind两次! |
| 158 | +- stencil attachments |
| 159 | +- 合并uniform buffer? |
| 160 | + - https://stackoverflow.com/questions/33589784/ssbo-as-bigger-ubo |
| 161 | + - https://community.khronos.org/t/uniform-buffer-memory-barriers/6777 |
| 162 | +- cache开销! |
| 163 | +
|
| 164 | +# To-do |
| 165 | +
|
| 166 | +## Functionality |
| 167 | +
|
| 168 | +### usability |
| 169 | +
|
| 170 | +- [ ] [做成插件](https://stackoverflow.com/questions/19191679/chrome-extension-inject-js-before-page-load) |
| 171 | +
|
| 172 | +### global state |
| 173 | +
|
| 174 | +- [x] Vertex Array Object |
| 175 | +- [x] Texture |
| 176 | +- [x] test5.html |
| 177 | +- [x] shadow mapping |
| 178 | +- [x] **framebuffer, renderbuffer (多pipeline?)** |
| 179 | +- [x] depth |
| 180 | +- [x] stencil |
| 181 | +- [x] blend |
| 182 | +- [x] cullFace |
| 183 | +- [x] CubeMap |
| 184 | +- [x] threejs example |
| 185 | +- [x] scissor box |
| 186 | +- [ ] dithering |
| 187 | +- [ ] buffer writemasks |
| 188 | +- [ ] multisample? |
| 189 | +- [ ] `gl.vertexAttrib4f()` |
| 190 | +- [ ] ~~生成 `BindGroupLayout`~~ |
| 191 | +- [ ] **`RenderPass` Cache** |
| 192 | +- [x] fuse `uniforms` into one struct |
| 193 | + - [alignment](https://www.w3.org/TR/WGSL/#alignment-and-size) |
| 194 | +
|
| 195 | +### shader |
| 196 | +
|
| 197 | +- [ ] GLSL -> WGSL |
| 198 | +- [ ] uniform & attribute location |
| 199 | +
|
| 200 | +## JS-tricks |
| 201 | +
|
| 202 | +- [ ] `instanceof` |
| 203 | +- [ ] fake `device.queue` |
| 204 | +
|
| 205 | +## Performance |
| 206 | +
|
| 207 | +- [ ] cache CommandEncoder&Pipeline&BindGroup&VertexState&VertexBufferLayout?TextureView? |
| 208 | +- [ ] performance of `writeUniform, draw, writeUniform, draw` ... |
| 209 | +
|
| 210 | +
|
| 211 | +# 优化方向 |
| 212 | +
|
| 213 | +- `BindGroup` 重用 |
| 214 | +- `BindGroupLayout` 重用 |
| 215 | +- 根据更改频率对资源进行分组 |
| 216 | + - `BindGroup(0)` 放更改频率最低的 |
| 217 | +--> |
0 commit comments