WebGL から WebGPU へ変更してスピードアップ
はじめに
Web アプリを作って スピードアップしたい場合に 有効な手段です
準備
設定の変更 アドレスに入力
chrome://flags/#enable-unsafe-webgpu
edge://flags/#enable-unsafe-webgpuサーバーを立ち上げて起動する
call D:\WinPython\scripts\env_for_icons.bat %*
Start python -m http.server 8000
"C:\Program Files\Google\Chrome\Application\chrome.exe" http://localhost:8000/WebGPU.html
説明
WebGPU が動かないときのチェックリスト
- ブラウザの対応状況を確認
- WebGPU は 最新の Google Chrome(バージョン 113 以降) や Microsoft Edge(バージョン 113 以降) でサポート。
- Firefox や Safari ではまだ正式対応していないことが多いから注意!
- WebGPU が有効になっているか確認
- Chrome のアドレスバーに
chrome://flags/#enable-unsafe-webgpuと入力して、WebGPUを Enabled に設定。 - その後、ブラウザを再起動。
- Chrome のアドレスバーに
- HTTPS でアクセスしてる?
- WebGPU は HTTPS または localhost でしか動かない。
- ファイルを直接開く(
file://)だと動かないから、ローカルサーバーを使って。
python -m http.serverその後、ブラウザでhttp://localhost:8000にアクセス! - GPU が対応しているか確認
- 一部の古い GPU や仮想環境では WebGPU が使えないこともある。
navigator.gpuがundefinedになっていたら、対応していない可能性がある。
デバッグ用コード追加
WebGPU が使えるかどうかをチェックするコードを追加:
if (!navigator.gpu) {
alert("このブラウザは WebGPU をサポートしていません。最新の Chrome または Edge を使用してください。");
return;
}
ソース
WebGL vs WebGPU:12色三角形描画&時間比較
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>WebGL vs WebGPU 12色比較</title>
<style>
canvas { border: 1px solid #ccc; margin: 10px; }
button { margin: 5px; }
</style>
</head>
<body>
<h2>WebGL vs WebGPU:12色三角形描画&時間比較</h2>
<button onclick="runWebGL()">WebGLで描画</button>
<button onclick="runWebGPU()">WebGPUで描画</button>
<p id="result"></p>
<canvas id="canvas-webgl" width="400" height="400" style="display:none;"></canvas>
<canvas id="canvas-webgpu" width="400" height="400" style="display:none;"></canvas>
<script>
function showCanvas(idToShow) {
document.getElementById("canvas-webgl").style.display = "none";
document.getElementById("canvas-webgpu").style.display = "none";
document.getElementById(idToShow).style.display = "block";
}
function runWebGL() {
showCanvas("canvas-webgl");
drawWebGL();
}
function runWebGPU() {
showCanvas("canvas-webgpu");
drawWebGPU();
}
function drawWebGL() {
const canvas = document.getElementById("canvas-webgl");
const gl = canvas.getContext("webgl");
if (!gl) {
alert("WebGL がサポートされていません");
return;
}
const start = performance.now();
const vert = `
attribute vec2 position;
uniform float angle;
void main() {
float rad = radians(angle);
mat2 rot = mat2(cos(rad), -sin(rad), sin(rad), cos(rad));
gl_Position = vec4(rot * position, 0, 1);
}
`;
const frag = `
precision mediump float;
uniform vec3 color;
void main() {
gl_FragColor = vec4(color, 1.0);
}
`;
function compile(type, source) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
return shader;
}
const vs = compile(gl.VERTEX_SHADER, vert);
const fs = compile(gl.FRAGMENT_SHADER, frag);
const program = gl.createProgram();
gl.attachShader(program, vs);
gl.attachShader(program, fs);
gl.linkProgram(program);
gl.useProgram(program);
const vertices = new Float32Array([
0, 0.5,
-0.2, -0.3,
0.2, -0.3
]);
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
const loc = gl.getAttribLocation(program, "position");
gl.enableVertexAttribArray(loc);
gl.vertexAttribPointer(loc, 2, gl.FLOAT, false, 0, 0);
const angleLoc = gl.getUniformLocation(program, "angle");
const colorLoc = gl.getUniformLocation(program, "color");
gl.clearColor(1, 1, 1, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
for (let i = 0; i < 12; i++) {
const angle = i * 30;
const hue = i / 12;
const rgb = hsvToRgb(hue, 1, 1);
gl.uniform1f(angleLoc, angle);
gl.uniform3fv(colorLoc, rgb);
gl.drawArrays(gl.TRIANGLES, 0, 3);
}
const end = performance.now();
document.getElementById("result").textContent = `WebGL: ${(end - start).toFixed(2)} ms`;
}
async function drawWebGPU() {
if (!navigator.gpu) {
alert("このブラウザは WebGPU をサポートしていません。最新の Chrome または Edge を使用してください。");
return;
}
const canvas = document.getElementById("canvas-webgpu");
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
const context = canvas.getContext("webgpu");
const format = navigator.gpu.getPreferredCanvasFormat();
context.configure({
device: device,
format: format,
alphaMode: "opaque"
});
const start = performance.now();
const vertices = [];
const colors = [];
for (let i = 0; i < 12; i++) {
const angle = (i * 30) * Math.PI / 180;
const cos = Math.cos(angle);
const sin = Math.sin(angle);
const base = [
[0, 0.5],
[-0.2, -0.3],
[0.2, -0.3]
];
const hue = i / 12;
const rgb = hsvToRgb(hue, 1, 1);
for (const [x, y] of base) {
vertices.push(
x * cos - y * sin,
x * sin + y * cos
);
colors.push(...rgb);
}
}
const vertexBuffer = device.createBuffer({
size: vertices.length * 4,
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST
});
device.queue.writeBuffer(vertexBuffer, 0, new Float32Array(vertices));
const colorBuffer = device.createBuffer({
size: colors.length * 4,
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST
});
device.queue.writeBuffer(colorBuffer, 0, new Float32Array(colors));
const shaderModule = device.createShaderModule({
code: `
struct VertexOutput {
@builtin(position) position: vec4f,
@location(0) color: vec3f
};
@vertex
fn vs_main(@location(0) position: vec2f, @location(1) color: vec3f) -> VertexOutput {
var out: VertexOutput;
out.position = vec4f(position, 0.0, 1.0);
out.color = color;
return out;
}
@fragment
fn fs_main(@location(0) color: vec3f) -> @location(0) vec4f {
return vec4f(color, 1.0);
}
`
});
const pipeline = device.createRenderPipeline({
layout: "auto",
vertex: {
module: shaderModule,
entryPoint: "vs_main",
buffers: [
{
arrayStride: 8,
attributes: [{ shaderLocation: 0, offset: 0, format: "float32x2" }]
},
{
arrayStride: 12,
attributes: [{ shaderLocation: 1, offset: 0, format: "float32x3" }]
}
]
},
fragment: {
module: shaderModule,
entryPoint: "fs_main",
targets: [{ format }]
},
primitive: { topology: "triangle-list" }
});
const commandEncoder = device.createCommandEncoder();
const textureView = context.getCurrentTexture().createView();
const renderPass = commandEncoder.beginRenderPass({
colorAttachments: [{
view: textureView,
loadOp: "clear",
clearValue: { r: 1, g: 1, b: 1, a: 1 },
storeOp: "store"
}]
});
renderPass.setPipeline(pipeline);
renderPass.setVertexBuffer(0, vertexBuffer);
renderPass.setVertexBuffer(1, colorBuffer);
renderPass.draw(12 * 3);
renderPass.end();
device.queue.submit([commandEncoder.finish()]);
const end = performance.now();
document.getElementById("result").textContent = `WebGPU: ${(end - start).toFixed(2)} ms`;
}
function hsvToRgb(h, s, v) {
let r, g, b;
let i = Math.floor(h * 6);
let f = h * 6 - i;
let p = v * (1 - s);
let q = v * (1 - f * s);
let t = v * (1 - (1 - f) * s);
switch (i % 6) {
case 0: r = v; g = t; b = p; break;
case 1: r = q; g = v; b = p; break;
case 2: r = p; g = v; b = t; break;
case 3: r = p; g = q; b = v; break;
case 4: r = t; g = p; b = v; break;
case 5: r = v; g = p; b = q; break;
}
return [r, g, b];
}
</script>
</body>
</html>








ディスカッション
コメント一覧
まだ、コメントがありません