Loading...
Loading...
This skill should be used when the user asks about "electrobun WebGPU", "GpuWindow", "WGPUView", "WGSL shaders", "KEEPALIVE pattern", "electrobun GPU", render loops in electrobun, FFI pointer management, or GPU buffer serialization.
npx skill4agent add dexploarer/plugin-electrobun-dev electrobun-webgpu// electrobun.config.ts
mac: { bundleWGPU: true },
win: { bundleWGPU: true },
linux: { bundleWGPU: true },import { GpuWindow } from "electrobun/bun";
const gpuWin = new GpuWindow({
title: "GPU App",
frame: { width: 800, height: 600 },
centered: true,
});
const view = gpuWin.createView(); // WGPUView// ALWAYS create this array and push every GPU object into it
const KEEPALIVE: unknown[] = [];
const adapter = await navigator.gpu.requestAdapter();
KEEPALIVE.push(adapter);
const device = await adapter.requestDevice();
KEEPALIVE.push(device);
const pipeline = device.createRenderPipeline({ /* ... */ });
KEEPALIVE.push(pipeline);
const buffer = device.createBuffer({ /* ... */ });
KEEPALIVE.push(buffer);const FRAME_MS = 16; // ~60fps
function renderFrame() {
// 1. Update uniform buffer with current state
const data = new ArrayBuffer(32);
const view = new DataView(data);
view.setFloat32(0, performance.now() / 1000, true); // time
view.setFloat32(4, canvas.width, true); // resolution.x
view.setFloat32(8, canvas.height, true); // resolution.y
view.setFloat32(12, mouseX, true); // mouse.x
view.setFloat32(16, mouseY, true); // mouse.y
device.queue.writeBuffer(uniformBuffer, 0, data);
// 2. Create command encoder
const encoder = device.createCommandEncoder();
KEEPALIVE.push(encoder);
// 3. Begin render pass
const pass = encoder.beginRenderPass({
colorAttachments: [{
view: context.getCurrentTexture().createView(),
clearValue: { r: 0, g: 0, b: 0, a: 1 },
loadOp: "clear",
storeOp: "store",
}],
});
// 4. Draw
pass.setPipeline(pipeline);
pass.setVertexBuffer(0, vertexBuffer);
pass.draw(3);
pass.end();
// 5. Submit
device.queue.submit([encoder.finish()]);
}
setInterval(renderFrame, FRAME_MS);// Vertex shader
struct VertexOutput {
@builtin(position) position: vec4f,
@location(0) uv: vec2f,
}
@vertex
fn vs_main(@builtin(vertex_index) idx: u32) -> VertexOutput {
// Full-screen triangle
var positions = array<vec2f, 3>(
vec2f(-1.0, -1.0),
vec2f( 3.0, -1.0),
vec2f(-1.0, 3.0),
);
var out: VertexOutput;
out.position = vec4f(positions[idx], 0.0, 1.0);
out.uv = (positions[idx] + vec2f(1.0)) * 0.5;
return out;
}
@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4f {
// TODO: replace with your shader logic
return vec4f(in.uv, 0.5, 1.0);
}[x, y, time, res.x, res.y, mouse.x, mouse.y]// 3 vertices × 7 floats × 4 bytes = 84 bytes
const vertexData = new ArrayBuffer(3 * 7 * 4);
const dv = new DataView(vertexData);
// Full-screen triangle vertices
const verts = [
[-1, -1], [3, -1], [-1, 3],
];
verts.forEach(([x, y], i) => {
const offset = i * 7 * 4;
dv.setFloat32(offset + 0, x, true); // position.x
dv.setFloat32(offset + 4, y, true); // position.y
dv.setFloat32(offset + 8, time, true); // time
dv.setFloat32(offset + 12, width, true);
dv.setFloat32(offset + 16, height, true);
dv.setFloat32(offset + 20, mouseX, true);
dv.setFloat32(offset + 24, mouseY, true);
});bundleWGPU: falselittle-endian: trueDataView.setFloat32