Slide 1

Slide 1 text

Copyright (C) 2024 Toranoana Lab Inc. All Rights Reserved. WebGPUで遊ぼう 虎の穴ラボ株式会社 藤原佳顕 T O R A N O A N A L a b 1

Slide 2

Slide 2 text

Copyright (C) 2024 Toranoana Lab Inc. All Rights Reserved. 目次 1. 自己紹介 2. 概要 3. denoでのWebGPU 4. 作ったもの紹介 5. まとめ 2

Slide 3

Slide 3 text

Copyright (C) 2024 Toranoana Lab Inc. All Rights Reserved. 自己紹介ページ(藤原) 藤原 佳顕(ふじわら よしあき): yoshiaki fujiwara ‣ Webエンジニア ‣ 新規事業担当(Fantia、Creatia)、アーキテクトチーム ‣ 前職:独立系ソフトウェア会社、主に GISとWeb、ライブラリ開発 ‣ TypeScript、Ruby on Rails、C#、C++ ‣ React、Vue、Angular ‣ 入社理由 ‣ 自分がスキルアップできそうな場所に行きたい ‣ オタク系の話ができるところに行きたい 好きなモノ ‣ シューティングゲーム、格闘ゲーム ‣ SF小説 ‣ プログラミング 3

Slide 4

Slide 4 text

Copyright (C) 2024 Toranoana Lab Inc. All Rights Reserved. 概要 4

Slide 5

Slide 5 text

Copyright (C) 2024 Toranoana Lab Inc. All Rights Reserved. 概要と経緯 ● 概要 ○ denoでのWebGPUが使えるようになったので色々作ってみた紹介になりま す ○ 実装と合わせて、何を作ったか、どうやったかについても紹介します ○ WebGPUに詳しい人ではありませんので、割とそこら辺は適当です 5

Slide 6

Slide 6 text

Copyright (C) 2024 Toranoana Lab Inc. All Rights Reserved. denoでのWebGPU 6

Slide 7

Slide 7 text

Copyright (C) 2024 Toranoana Lab Inc. All Rights Reserved. denoでのWebGPU ● そもそもWebGPUとは? ○ 元々Web(ブラウザ)でGPUを使う仕組みとしてWebGLが存在 ○ WebGPUはWebGLの後継として開発された API ○ あくまでWebGLはグラフィックを目的にしており、機械学習をはじめとした GPGPUなどの利用は あまりうまくできなかった ○ こういった問題を解決するために作られたのが WebGPU ○ 詳細はいつものMDNへ https://developer.mozilla.org/ja/docs/Web/API/WebGPU_API ○ 仕様はDraft時点から色々変わっている模様 ■ 古いもの https://www.w3.org/TR/2021/WD-webgpu-20210601/ ■ 今のもの https://www.w3.org/TR/webgpu/ ■ 古いものだとswapChainがあるが、現在だと無いなど ■ 検索とかAIとかたまに古いものが出てくると動かないので注意 7

Slide 8

Slide 8 text

Copyright (C) 2024 Toranoana Lab Inc. All Rights Reserved. denoでのWebGPU ● denoでの対応状況 ○ 2021年に導入されるがその後消される (1.8) ○ 1.39で再度導入される ■ サンプルを見る限りnavigator.gpuを使って直接GPUを操作している模様 ● https://github.com/denoland/webgpu-examples/blob/main/hello-triang le/mod.ts ● ブラウザ上だとCanvasといった描画対象に描画しますが、 deno単独だとこの時点 では標準では無い模様 ● この時点だと機械学習とかをターゲットにしているのかなぁといった印象 ○ 1.40で任意の描画先を設定できるように Deno.UnsafeWindowSurfaceが追加された ■ WebView以外のウインドウ作成手段が増えた 8

Slide 9

Slide 9 text

Copyright (C) 2024 Toranoana Lab Inc. All Rights Reserved. 作ったもの紹介 9

Slide 10

Slide 10 text

Copyright (C) 2024 Toranoana Lab Inc. All Rights Reserved. 作ったもの紹介 ● 公式サンプル ○ そのままでは動かなかった(というより省略されている箇所がある)ので直してます ○ ウインドウ表示のベースも公式サンプル通りsdl2を利用しています ■ https://deno.land/x/[email protected] ■ 以降のサンプルも基本同じです。denoがWeb標準なおかげでsdl2に依存した 処理はほぼ不要 ■ sdl2のライブラリインストールは上記のURL通り 10

Slide 11

Slide 11 text

Copyright (C) 2024 Toranoana Lab Inc. All Rights Reserved. 作ったもの紹介 ● 公式サンプル ○ そのままでは動かなかった(というより省略されている箇所がある)ので直してます ○ ウインドウ表示のベースも公式サンプル通りsdl2を利用しています ■ https://deno.land/x/[email protected] ■ 以降のサンプルも基本同じです。denoがWeb標準なおかげでsdl2に依存した 処理はほぼ不要 ■ sdl2のライブラリインストールは上記のURL通り 11

Slide 12

Slide 12 text

Copyright (C) 2024 Toranoana Lab Inc. All Rights Reserved. 作ったもの紹介 12

Slide 13

Slide 13 text

Copyright (C) 2024 Toranoana Lab Inc. All Rights Reserved. 作ったもの紹介 13 import { EventType, Window, WindowBuilder, } from "https://deno.land/x/[email protected]/mod.ts"; const win: Window = new WindowBuilder("Hello, World!", 800, 600).build(); const adapter = await navigator.gpu.requestAdapter(); if (!adapter) { console.error("adapter is null"); Deno.exit(); } const device = await adapter.requestDevice(); const surface: Deno.UnsafeWindowSurface = win.windowSurface(); // フォーマットを contextの設定として用います const context = surface.getContext("webgpu"); context.configure({ device, format: navigator.gpu.getPreferredCanvasFormat(), width: 800, height: 600 }); for await (const event of win.events(false)) { if (event.type === EventType.Quit) break; // Sine wave const r = Math.sin(Date.now() / 1000) / 2 + 0.5; const g = Math.sin(Date.now() / 1000 + 2) / 2 + 0.5; const b = Math.sin(Date.now() / 1000 + 4) / 2 + 0.5; const textureView = context.getCurrentTexture().createView(); const renderPassDescriptor: GPURenderPassDescriptor = { colorAttachments: [ { view: textureView, clearValue: { r, g, b, a: 1.0 }, loadOp: "clear", storeOp: "store", }, ], }; const commandEncoder = device.createCommandEncoder(); const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor); passEncoder.end(); device.queue.submit([commandEncoder.finish()]); surface.present(); }

Slide 14

Slide 14 text

Copyright (C) 2024 Toranoana Lab Inc. All Rights Reserved. 作ったもの紹介 ● サンプルから変えたもの ○ context.configureの中身を入れた ■ formatに変なのいれるとその後でエラーになるので注意 ■ APIからデバイスに適したものが取れるのでそれを利用 ○ win.eventsがAsyncGeneratorになってたので変えた ■ for await にしただけ ■ ちなみに引数にtrueいれるとマウスがウインドウの中にあるときだけ動くように なる 14

Slide 15

Slide 15 text

Copyright (C) 2024 Toranoana Lab Inc. All Rights Reserved. 作ったもの紹介 ● WebGPUのサンプルからいくつか ○ https://webgpu.github.io/webgpu-samples/ ○ シェーダーを使うサンプルとして回転する立方体を利用 ■ https://webgpu.github.io/webgpu-samples/samples/rotatingCube ○ 動かない箇所のコード以外はそのまま使う 15

Slide 16

Slide 16 text

Copyright (C) 2024 Toranoana Lab Inc. All Rights Reserved. 作ったもの紹介 16

Slide 17

Slide 17 text

Copyright (C) 2024 Toranoana Lab Inc. All Rights Reserved. 作ったもの紹介 17 // 頂点シェーダー( WGSL) const vertexShaderSource = ` struct Uniforms { modelViewProjectionMatrix : mat4x4, } @binding(0) @group(0) var uniforms : Uniforms; struct VertexOutput { @builtin(position) Position : vec4, @location(0) fragUV : vec2, @location(1) fragPosition: vec4, } @vertex fn main( @location(0) position : vec4, @location(1) uv : vec2 ) -> VertexOutput { var output : VertexOutput; output.Position = uniforms.modelViewProjectionMatrix * position; output.fragUV = uv; output.fragPosition = 0.5 * (position + vec4(1.0, 1.0, 1.0, 1.0)); return output; }`; // フラグメントシェーダー( WGSL) const fragmentShaderSource = ` @fragment fn main( @location(0) fragUV: vec2, @location(1) fragPosition: vec4 ) -> @location(0) vec4 { return fragPosition; }`; // Create a vertex buffer from the cube data. const verticesBuffer = device.createBuffer({ size: cubeVertexArray.byteLength, usage: GPUBufferUsage.VERTEX, mappedAtCreation: true, }); new Float32Array(verticesBuffer.getMappedRange()).set(cubeVertexArray); verticesBuffer.unmap(); // シェーダーモジュールの作成 const vertexShaderModule = device.createShaderModule({ code: vertexShaderSource, }); const fragmentShaderModule = device.createShaderModule({ code: fragmentShaderSource, });

Slide 18

Slide 18 text

Copyright (C) 2024 Toranoana Lab Inc. All Rights Reserved. 作ったもの紹介 18 // eventからマウス位置を取って、座標位置から cube回転させればマウスでコ ントロール可能だが、幾何計算が必要なのでライブラリとかに頼りたい // 既存のWeb資産(npmとか)にあるにはあるが、ターゲットが Canvasとかで、 Canvas自体へのイベント追加とかで動くのでそのままだと流用できない (3d-view-controlsとか) // surfaceがAPI的にcanvasのWrapperだとラクできそうだけど・・・・ for await (const event of win.events(false)) { if (event.type === EventType.Quit) break; const transformationMatrix = getTransformationMatrix(); device.queue.writeBuffer( uniformBuffer, 0, transformationMatrix.buffer, transformationMatrix.byteOffset, transformationMatrix.byteLength, ); const renderPassDescriptor: GPURenderPassDescriptor = { colorAttachments: [ { view: context.getCurrentTexture().createView(), clearValue: { r: 0.5, g: 0.5, b: 0.5, a: 1.0 }, loadOp: "clear", storeOp: "store", }, ], depthStencilAttachment: { view: depthTexture.createView(), depthClearValue: 1.0, depthLoadOp: "clear", depthStoreOp: "store", }, }; const commandEncoder = device.createCommandEncoder(); const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor); passEncoder.setPipeline(pipeline); passEncoder.setBindGroup(0, uniformBindGroup); passEncoder.setVertexBuffer(0, verticesBuffer); passEncoder.draw(cubeVertexCount); passEncoder.end(); device.queue.submit([commandEncoder.finish()]); surface.present(); }

Slide 19

Slide 19 text

Copyright (C) 2024 Toranoana Lab Inc. All Rights Reserved. 作ったもの紹介 ● DOM系イベント使っている箇所以外はそのまま! ○ Web API準拠しているのが大きい ○ サンプルはWebブラウザのWebGPU処理だが、denoでそのまま動く ● requestAnimationFrameの部分だけ、一個前のサンプルと同様に、for awaitで処理す る必要がある 19

Slide 20

Slide 20 text

Copyright (C) 2024 Toranoana Lab Inc. All Rights Reserved. まとめ 20

Slide 21

Slide 21 text

Copyright (C) 2024 Toranoana Lab Inc. All Rights Reserved. まとめ ● WebGPUで色々作ってみました ● denoでGUIを作る手段の一つになりうると思います ○ あるいは、ゲームなんかも作れるかも? ○ その上、ブラウザでもローカルでも動くようにできるかも? ● 内部の処理を見てみるとFFIでdllとかdylib読んでるだけに近いっぽいので、将来的 にはGPU関係ないGUI関連も期待できそう 21