乐趣区

关于gpu:如何创建前端-WebGPU-项目

如何创立前端 WebGPU 我的项目?

1. 丐版 HelloWebGPU

最简略的 WebGPU 程序应该是这样的:

<script>
const init = () => {if ('gpu' in navigator) {console.log(navigator.gpu)
  } else {console.log('浏览器不反对 WebGPU;浏览器未开启 WebGPU 性能。')
  }
}
init()
</script>

将下面的代码保留成 index.html,双击关上就能够在控制台看到输入的对象:

如果你是什么都不会的老手,那么接下来这几个问题我只说一次。当然,新手能够选择性跳过。

① 为什么是 index.html

不为什么,你喜爱能够改成 home.htmlmain.html,然而文件的后缀名得是 html,用 index.html 是习惯。

② 你这个代码为什么浏览器没有显示货色?

如果你没有浏览过我之前的文章,没有仔细阅读过,天然是不晓得为什么。

WebGPU 不像 WebGL,它解除了与 DOM – HTMLCanvasElement 的强关联。很简略:

WebGPU focus on GPU. Rendering is part of GPU’s features, not all.

意思是,渲染绘图只是 GPU 的一部分性能,WebGPU 更靠近古代 GPU 的功能定位。

所以,作为浏览器内置的一个 API,你只须要 JavaScript 就能够获取到这套全新设计的 API,它挂载于 navigator 上。

③ 为什么你不必 HTTP 协定关上 index.html

别急,待会就用。

双击 “index.html” 文件,浏览器关上这个页面,你能够在浏览器地址栏看到残缺的文件资源门路:

file:///C:/Users/<YourUserName>/Desktop/index.html

最后面的字母是 file:///,而后跟了个本地磁盘门路,这种形式叫做 应用 file 协定拜访(关上)页面 ,然而咱们很多时候都是应用 http(或 https,简略起见,下文全用 http)协定关上的,哪怕是在本地:

http://localhost:8080/index.html

具体起因不反复,网上有十分多这两种协定在 Web 开发时的差别。

而在本地启动一个 HTTP 服务器软件,伺服某个磁盘中的资源,在我是学生的年代材料不算多,也不算少,能够借助 Apache-HTTPD,也能够借助 Tomcat,甚至为了一个小小的 index.html 下载一个 XAMPP 的大有人在。

当初,你齐全能够借助:

  • VSCode 的 LiveServer 插件
  • 更易配置的 nginx / openresty
  • Python 的 http.server 模块
  • NodeJS 全局 http 插件 / NodeJS 写一个繁难的 HTTP 服务器
  • 借助各种前端工程化工具,应用 devServer

来伺服这个 index.html,我之后将抉择最初一种,作为开发时的辅助工具。

2. 我须要类型提醒

2202 年如果还有人倡议老手用 Notepad 写代码的都倡议去喝一口恒河水。软件开发至今,积攒了有数的技术和门槛,就不要再重叠了。

我抉择 VSCode 作为 JavaScript 代码编辑器,你也能够抉择别的。

写 JavaScript 这种动静类型的语言,调用函数、鼠标指针挪动到变量、函数上没有智能提醒时,纯猜、靠背是相当让人没有急躁的事件。

咱们能够借助 TypeScript 的 类型申明 来做到类型提醒。

很惋惜,在我写这篇文章的时候,正式频道的各大浏览器还没有正式裸露 WebGPU JavaScript API。

2.1. 应用 VSCode 的 jsconfig 获取类型提醒

VSCode 是 TypeScript 编写的,天然对类型申明文件(*.d.ts)有反对。在一般的 JavaScript 我的项目中,想要取得像 TypeScript 一样的类型提醒,只需在我的项目根目录下减少一个 jsconfig.json 文件,其领有 compilerOptions.typeRoots 配置项,通知 VSCode 要去哪里读取类型申明文件即可。

如上所示,jsconfig.json 通知 VSCode,类型申明文件从 ./src 文件夹下找。在那个 index.d.ts 文件中,你能够本人写类型申明;我就偷懒了,从官网仓库复制了一份 WebGPU 的类型申明文件,粘贴于此。

所以,在 main.js 文件就能够用 WebGPU 的类型提醒啦!

这种只是一种过渡办法,接下来将展现在 Vite + TypeScript 我的项目中应用 WebGPU 官网的类型提醒库。

2.2. 在前端工程化的我的项目中应用类型提醒

我的抉择是 Vite + TypeScript,抉择 Vite 的理由很简略:

  • 开发服务器应用 esbuild 速度足够快
  • 插件蓬勃发展,开箱反对很多 Webpack 须要 loader 能力实现的个性
  • 文档丰盛
  • 内置依赖少

最重要的一点,我比拟腻烦 polyfill 的过程。毕竟,

能运行 WebGPU 的浏览器,还须要思考向下兼容?

对于如何应用 Vite 脚手架工具开启各种模板我的项目就不复述了,我应用 Vanilla + TS 模板创立了一个工程“gpuweb-prj”:

随后,装置我的项目依赖:

pnpm install && pnpm add @webgpu/types -D

而后,在我的项目根目录下的 tsconfig.jsoncompilerOptions.typeRoots 增加类型提醒库的门路即可:

{
  "compilerOptions": {"typeRoots": [ "./node_modules/@webgpu/types", "./node_modules/@types"]
  }
}

“./node_modules/@types” 是一个类型提醒库仓库的默认门路,位于 GitHub – DefinitelyTyped/DefinitelyTyped: The repository for high quality TypeScript type definitions.,然而 WebGPU 没有提交到这个仓库,所以须要增加新的门路,与 2.1 大节中增加本人的门路是一个情理,只不过这里用的是 TypeScript 我的项目,所以是 tsconfig.json 文件。

而后,你就能在你的 ts 代码中欢快地用 WebGPU 类型了。

待 WebGPU 正式登陆各大浏览器的那一天,应该就不须要搞这么麻烦了,届时会像 WebGL 一样,将类型集成在 TypeScript 的内置类型申明中:

:) 应该快来了吧,心愿官网别鸽太久了。

2.3. 在 Vue/React 等我的项目中

也一样的,均是在 jsconfig.json / tsconfig.json 文件中配置类型申明文件的门路即可。

3. 画三角形的例子

基于上述应用 Vite 创立的我的项目,批改 main.ts 如下:

import {wgslShader} from './shader'
import './style.css'

const init = async () => {if (!('gpu' in navigator)) {return}
  const canvas = document.getElementById('canvas') as HTMLCanvasElement

  //#region 适配高 DPR 屏幕的 canvas
  const dpr = window.devicePixelRatio
  canvas.style.height = `${canvas.height / dpr}px`
  canvas.style.width = `${canvas.width / dpr}px`
  //#endregion

  const adapter = await navigator.gpu.requestAdapter()
  if (!adapter) {return}
  const device = await adapter.requestDevice()
  const ctx = (canvas as HTMLCanvasElement).getContext('webgpu')
  if (!ctx) {return}
  const preferFormat = navigator.gpu.getPreferredCanvasFormat()
  ctx.configure({
    device: device,
    format: preferFormat,
    alphaMode: 'opaque',
  })

  const triangleShader = device.createShaderModule({code: wgslShader})
  const pipeline = device.createRenderPipeline({
    layout: "auto",
    vertex: {
      module: triangleShader,
      entryPoint: 'vertex_main'
    },
    fragment: {
      module: triangleShader,
      entryPoint: 'frag_main',
      targets: [{format: preferFormat}]
    },
    primitive: {topology: 'triangle-list'}
  })

  const renderPassDescriptor = {
    colorAttachments: [{
      view: undefined,
      clearValue: [0, 0, 0, 1],
      loadOp: 'clear',
      storeOp: 'store',
    }],
  } as GPURenderPassDescriptor

  const frame = () => {if (!canvas) {return}

    const commandEncoder = device.createCommandEncoder();
    (renderPassDescriptor.colorAttachments as GPURenderPassColorAttachment[])[0].view = ctx.getCurrentTexture().createView()
    const renderPassEncoder = commandEncoder.beginRenderPass(renderPassDescriptor)
    renderPassEncoder.setPipeline(pipeline)
    renderPassEncoder.draw(3)
    renderPassEncoder.end()

    device.queue.submit([commandEncoder.finish()
    ])

    requestAnimationFrame(frame)
  }
  requestAnimationFrame(frame)
}

window.addEventListener('DOMContentLoaded', () => {init()
})

创立着色器文件 shader.ts 如下:

export const wgslShader = /* wgsl */`
@vertex
fn vertex_main(@builtin(vertex_index) vertex_index: u32
) -> @builtin(position) vec4<f32> {
  var pos = array<vec2<f32>, 3>(vec2<f32>(0.0, 0.5),
    vec2<f32>(-0.5, -0.5),
    vec2<f32>(0.5, -0.5)
  );

  return vec4<f32>(pos[vertex_index], 0.0, 1.0);
}

@fragment
fn frag_main() -> @location(0) vec4<f32> {return vec4<f32>(1.0, 0.0, 0.0, 1.0);
}
`

款式和 HTML 如下:

<div id="app">
  <canvas id="canvas" height="800" width="1800"></canvas>
</div>


<style>
html, body {
  margin: 0;
  padding: 0;
}

#app {
  height: 100vh;
  width: 100vw;
  display: flex;
  justify-content: center;
  align-items: center;
}
</style>

其余改变并不大。

4. 作者的话

前端工程化蓬勃发展离不开 NodeJS,尽管这玩意儿本意并不是前端,只是它为一系列前端构建、开发工具提供了一个不错的土壤。

前端系统化离不开 TypeScript,它应该算是呈现在了适合的工夫。

前端走向各种丰盛的利用,ng、react、vue 堪称是浓墨重彩的一笔了,然而调用 GPU 的 Web 图形开发者更要搞清楚的是: 你是在操作各种 DOM,还是操心图形 API?

退出移动版