Slide 1

Slide 1 text

WebGPU 触ってみた てらだ @ キャディ株式会社

Slide 2

Slide 2 text

寺田 雄一郎 ● Twitter: @u_1roh ● GitHub: u1roh ● 3D CAD 周辺の業界に長くいて、3年弱前に CADDi に入社 ● キャディ株式会社 / テクノロジー本部 / MaP チーム ○ MaP = Manufacturing Protocol ● CADDi でのお仕事 ○ 図面の画像を認識するアルゴリズムを Rust で書く ○ 図面のCADデータ(DXF/DWG)を認識するアルゴリズムを F# で書く ○ 図面を描画する Viewer を React + Three.js で書く ○ 図面に替わる設計情報の machine-readable なプロトコルを考える

Slide 3

Slide 3 text

WebGPUというものがあるらしい (名前しか知らない)

Slide 4

Slide 4 text

OpenGL、WebGL なら使ったことある

Slide 5

Slide 5 text

この機会にWebGPUを 試しに触ってみよう! (つまりド素人) ※ 説明が間違っている個所がある可能性はかなり高いです。 いや、きっとあるでしょう。 見つけた方はやさしく教えて頂けると嬉しいです。

Slide 6

Slide 6 text

…というわけで、 細かい前置きは抜きにして、 まずは WebGPU を動かしてみたい

Slide 7

Slide 7 text

1. Chrome Canary をダウンロード ● Google Chrome の開発者版 ● https://www.google.com/intl/ja/chrome/canary/ ● 「Linuxはサポートされていません」Oh…

Slide 8

Slide 8 text

2. WebGPU を ON にする ● chrome://flags/#enable-unsafe-webgpu

Slide 9

Slide 9 text

3. Demo を動かしてみる ● https://austin-eng.com/webgpu-samples/

Slide 10

Slide 10 text

※ Firefox Nightly で動かせなかった… [at 2022-08-28] やったこと ● Firefox Nightly をダウンロード ● about:config で dom.webgpu.enabled を true に ● 再起動して WebGPU Samples のページを開く 😭

Slide 11

Slide 11 text

4. React + TypeScript のプロジェクトを作る ※ もちろん React でなくてもよい。ReactとWebGPUは関係ない。 1. $ npx create-react-app —template typescript 2. $ npm install –save-dev @webgpu/types 3. tsconfig.json の “typeRoots” を設定 package.json tsconfig.json

Slide 12

Slide 12 text

4. の確認 テキトーなところで下記のように入力して、navigator.gpu が補完されたら成功。

Slide 13

Slide 13 text

環境を確認 はい、、 Intel のCPU内蔵のGPUです、、

Slide 14

Slide 14 text

実装の話に突入する前に、この辺りで 「WebGPUって何?」 という話をします

Slide 15

Slide 15 text

DirectX 12, Metal, Vulkan を抽象化してくれるやつ(雑) https://developer.chrome.com/docs/web-platform/webgpu/ より引用

Slide 16

Slide 16 text

Wikipedia より https://ja.wikipedia.org/wiki/WebGPU ● W3C の GPU for Web コミュニティグループで開発 ○ Apple, Mozilla, Microsoft, Google 等が参画 ● 経緯 ○ 2017 年、Apple が WebGPU を提案 ■ Metal をベースとした提案 ○ その後、Apple の最初の提案は「WebMetal」と改称 ■ 将来の標準となる WebGPU との混同を避けるため

Slide 17

Slide 17 text

WebGL との違い ● 各種のグラフィックAPIを抽象化する、という意味では似ている ● GPGPU (General Purpose computing with GPU) がやりやすい ○ WebGL はグラフィック処理が基本。 GPGPUのために使うには適さなかった。 ○ WebGPU は GPGPU を “first-class” サポート。 ● より整理されたAPI体系 ○ OpenGL・WebGLの「グローバルな状態遷移のつらみ」を解消。 ○ CPUの処理とGPUの処理の区別が分かりやすいという印象。 ○ CPU のマルチスレッドからの制御が可能。 ○ 遅延レンダリング。

Slide 18

Slide 18 text

ブラウザ、レンダリングエンジン、WebGPU実装 Chrome Chromium C++ Rust Rust Servo Blink すみません、Safari は状況 がよく分からず…。今の Preview 版では動かない? Mac の環境を持ってないの で確認できず。

Slide 19

Slide 19 text

ブラウザ、レンダリングエンジン、WebGPU実装 Chrome C++ Rust Rust Servo この発表で使ったのはこっち Chromium Blink

Slide 20

Slide 20 text

ブラウザ、レンダリングエンジン、WebGPU実装 Chrome C++ Rust Rust Servo Chromium Blink 個人的にはこっちも気になる。 少し動かしてみたりもした。 サイトに載っている図も分かりやすい。 → 軽く紹介する

Slide 21

Slide 21 text

Rust 製 wgpu クレート https://github.com/gfx-rs/wgpu/blob/master/etc/big-picture.png より

Slide 22

Slide 22 text

Rust 製 wgpu クレート HAL = Hardware Abstraction Layer 各種グラフィックスを抽象化し、差異を吸収してくれるやつ

Slide 23

Slide 23 text

Rust 製 wgpu クレート 各種シェーダーを翻訳してくれるやつ (WGSL, GLSL, HLSL, MSL, SPIR-V)

Slide 24

Slide 24 text

Rust 製 wgpu クレート ブラウザへの組み込み

Slide 25

Slide 25 text

Rust 製 wgpu クレート ネイティブアプリから利用

Slide 26

Slide 26 text

GPUDevice (論理) GPU(物理) Driver (DirectX, Metal, Vulkan) GPUQueue GPUAdapter GPUCommandEncoder 計算パス GPUBuffer GPUBuffer GPUBuffer GPUComputePipeline or GPURenderPipeline WebGPU APIモデル gpu.requestAdapter() adapter.requestDevice() queue.submit(...)

Slide 27

Slide 27 text

ではコードに戻ります

Slide 28

Slide 28 text

GPUDevice を取得 const adapter = await navigator.gpu.requestAdapter(); const device = await adapter?.requestDevice(); GPUDevice (論理) GPU(物理) Driver (DirectX, Metal, Vulkan) GPUQueue GPUAdapter GPUCommandEncoder 計算パス GPUBuffer GPUBuffer GPUBuffer GPUComputePipeline or GPURenderPipeline gpu.requestAdapter() adapter.requestDevice() queue.submit(...)

Slide 29

Slide 29 text

compute pipeline を動かす流れ const myComputeShader = device.createShaderModule({ code: ` @compute @workgroup_size(16) fn main(…) { … } ` }); const pipeline = device.createComputePipeline({ layout: "auto", compute: { module: myComputeShader, entryPoint: "main" } }); const outputBuf = device.createBuffer({…}); const bindGroup = device.createBindGroup({ layout: pipeline.getBindGroupLayout(0), entries: [{ binding: 1, resource: { buffer: outputBuf } }] }); const encoder = device.createCommandEncoder(); const pass = encoder.beginComputePass(); pass.setPipeline(pipeline); pass.setBindGroup(0, bindGroup); pass.dispatchWorkgroups(…); pass.end(); device.queue.submit([encoder.finish()]); GPUDevice (論理) GPUQueue GPUCommandEncoder 計算パス GPUBuffer GPUBuffer GPUBuffer GPUComputePipeline or GPURenderPipeline queue.submit(...)

Slide 30

Slide 30 text

render pipeline を動かす流れ const myVertexShader = ` @vertex fn main(…) { … } `; const myFragmentShader = ` @fragment fn main(…) { … } `; const pipeline = device.createRenderPipeline({ layout: "auto", vertex: { … }, fragment: { … }, }); const buf= device.createBuffer({…}); const bindGroup = device.createBindGroup({ layout: pipeline.getBindGroupLayout(0), entries: [{ binding: 1, resource: { buffer: buf } }] }); const encoder = device.createCommandEncoder(); const pass = encoder.beginRenderPass({ … }); pass.setPipeline(pipeline); pass.setBindGroup(0, bindGroup); pass.draw(…); pass.end(); device.queue.submit([encoder.finish()]); GPUDevice (論理) GPUQueue GPUCommandEncoder 計算パス GPUBuffer GPUBuffer GPUBuffer GPUComputePipeline or GPURenderPipeline queue.submit(...)

Slide 31

Slide 31 text

まずは三角形を描いてみよう (定番)

Slide 32

Slide 32 text

GPUCanvasContext を取得 const device: GPUDevice = … const canvas: HTMLCanvasElement = … const context = canvas.getContext('webgpu'); context?.configure({ device, format: navigator.gpu.getPreferredCanvasFormat(), alphaMode: "opaque" }); const encoder = device.createCommandEncoder(); const pass = encoder.beginRenderPass({ colorAttachments: [{ view: context.getCurrentTexture().createView(), clearValue: clearColor, loadOp: "clear", storeOp: "store" } as GPURenderPassColorAttachment] }); … pass.end(); device.queue.submit([encoder.finish()]); 描画先の Canvas を指定

Slide 33

Slide 33 text

Vertex Shader と Fragment Shader Vertex Shader (WGSL) @vertex fn main( @builtin(vertex_index) i : u32 ) -> @builtin(position) vec4 { let pos = array, 3>( vec2(0.0, 0.5), vec2(-0.5, -0.5), vec2(0.5, -0.5), ); return vec4(pos[i], 0.0, 1.0); } Fragment Shader (WGSL) @fragment fn main() -> @location(0) vec4 { return vec4(1.0, 0.5, 0.0, 1.0); } 文法がそこはかとなく Rust っぽい

Slide 34

Slide 34 text

出来た!

Slide 35

Slide 35 text

回してみた

Slide 36

Slide 36 text

マンデルブロ集合を描画してみよう

Slide 37

Slide 37 text

やりかた GPUTexture compute pipeline render pipeline compute shader で マンデルブロ集合の テクスチャ画像を生成 スクリーン一杯に 四角形を描画して、 そこにテクスチャを貼り付ける もちろん、render pipeline の fragment shader でマンデルブロ集合を描画するほうが 簡単ですが、compute shader を試したかった ので、敢えてこの方法にトライ

Slide 38

Slide 38 text

compute pipeline Compute Shader (WGSL) @group(0) @binding(1) var output: texture_storage_2d; @compute @workgroup_size(16, 16) fn main( @builtin(global_invocation_id) global_id: vec3 ) { // ここでマンデルブロ集合の漸化式を用いて色を決める var color: vec4 = …; // テクスチャに色を書き込む textureStore( output, vec2(global_id.xy), color ); } パイプラインやパスの構築 const pipeline = device.createComputePipeline({ layout: "auto", compute: { … } }); const IMAGE_SIZE = 512; const texture = device.createTexture({ format: "rgba8unorm", size: [IMAGE_SIZE, IMAGE_SIZE], usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.STORAGE_BINDING }); const bindGroup = device.createBindGroup({ layout: pipeline.getBindGroupLayout(0), entries: [{ binding: 1, resource: texture.createView() }] }); … pass.setPipeline(pipeline); pass.setBindGroup(0, bindGroup); pass.dispatchWorkgroups( Math.ceil(IMAGE_SIZE / 16), Math.ceil(IMAGE_SIZE / 16));

Slide 39

Slide 39 text

render pipeline Fragment Shader (WGSL) @group(0) @binding(0) var mySampler: sampler; @group(0) @binding(1) var myTexture: texture_2d; @fragment fn main(@location(0) uv: vec2) -> @location(0) vec4 { return textureSample(myTexture, mySampler, uv); } パイプラインやパスの構築 const pipeline = device.createRenderPipeline({ layout: "auto", vertex: { … }, fragment: { … }, }); const sampler = device.createSampler(); const mandelbrot = createMandelbrotTexture(device); const bindGroup = device.createBindGroup({ layout: pipeline.getBindGroupLayout(0), entries: [ { binding: 0, resource: sampler } { binding: 1, resource: mandelbrot.texture.createView() } ] }); … pass.setPipeline(pipeline); pass.setBindGroup(0, bindGroup); pass.draw(4); Vertex Shader (WGSL) const POS = array, 4>( vec2(-1.0, -1.0), vec2(1.0, -1.0), vec2(-1.0, 1.0), vec2(1.0, 1.0), ); const UV = array, 4>( vec2(0.0, 0.0), vec2(1.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), ); struct VertexOutput { @builtin(position) position: vec4, @location(0) uv: vec2, } @vertex fn main(@builtin(vertex_index) i : u32) -> VertexOutput { var output: VertexOutput; output.position = vec4(POS[i], 0.0, 1.0); output.uv = UV[i]; return output; } 四角形を描くので 4頂点

Slide 40

Slide 40 text

出来た!

Slide 41

Slide 41 text

す、3Dじゃなかった…🤔

Slide 42

Slide 42 text

まとめ、所感 ● こんな未来が来たらステキ ○ GPGPU がブラウザで手軽に動かせる ○ プラットフォームに依存しないグラフィックス APIが確立される ● とはいえ各プラットフォームの対応状況が気になる ○ Firefox や wgpu (Rust)、頑張ってほしい ○ Appleさんも頑張ってほしい、発端は Appleさんなので ● wgpu も少し触ってみた ○ Rust でネイティブGUIアプリで動かせる ○ 三角形は普通にレンダリングできた。 compute shader はうまく動かなかった。(私がなにか間違えているだけかもしれない) ● APIは整理されていて良い感じ ○ compute pipeline と render pipeline が統一された設計になっていて使いやすい ● TypeScript で驚くほど手軽に始められる 3Dのネタが作れなくてゴメンナサイ …

Slide 43

Slide 43 text

ご清聴ありがとうございました 本日のコードはこちら https://github.com/u1roh/learn-webgpu-sandbox エンジニアを募集しています! ● なぜ製造業は設計情報を未だに図面で流通させる必要があるのだろうか ● 3DCADと図面を併用しなくてはならない理由は何だろうか ● 現在の3DCADには何か不足があるのだろうか ● 脱・図面を実現するために必要なイノベーションとは何だろうか こういった問いに技術で向き合ってくれる方、ご連絡ください! → Twitter: @u_1roh 寺田 まで