Upgrade to Pro — share decks privately, control downloads, hide ads and more …

【toranoana.deno#15】WebGPUで遊ぼう

 【toranoana.deno#15】WebGPUで遊ぼう

toranoana.deno#15での発表資料です。
https://yumenosora.connpass.com/event/307235/

虎の穴ラボ株式会社

February 14, 2024
Tweet

More Decks by 虎の穴ラボ株式会社

Other Decks in Technology

Transcript

  1. Copyright (C) 2024 Toranoana Lab Inc. All Rights Reserved. WebGPUで遊ぼう

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

    1. 自己紹介 2. 概要 3. denoでのWebGPU 4. 作ったもの紹介 5. まとめ 2
  3. 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
  4. Copyright (C) 2024 Toranoana Lab Inc. All Rights Reserved. 概要と経緯

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

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

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

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

    17 // 頂点シェーダー( WGSL) const vertexShaderSource = ` struct Uniforms { modelViewProjectionMatrix : mat4x4<f32>, } @binding(0) @group(0) var<uniform> uniforms : Uniforms; struct VertexOutput { @builtin(position) Position : vec4<f32>, @location(0) fragUV : vec2<f32>, @location(1) fragPosition: vec4<f32>, } @vertex fn main( @location(0) position : vec4<f32>, @location(1) uv : vec2<f32> ) -> 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<f32>, @location(1) fragPosition: vec4<f32> ) -> @location(0) vec4<f32> { 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, });
  13. 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(); }
  14. Copyright (C) 2024 Toranoana Lab Inc. All Rights Reserved. 作ったもの紹介

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

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