Skip to content

Commit

Permalink
Control button definitions via comments + phase 1 gradient tiles vari…
Browse files Browse the repository at this point in the history
…ant (#317)
  • Loading branch information
mhawryluk authored Sep 13, 2024
1 parent cc91d74 commit b4d3b51
Show file tree
Hide file tree
Showing 3 changed files with 220 additions and 1 deletion.
3 changes: 3 additions & 0 deletions apps/typegpu-docs/src/components/ExampleView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ export function ExampleView({ example, isPlayground = false }: Props) {
codeEditorShowing ? 'max-h-[calc(50vh-3rem)]' : '',
)}
>
{/* Note: This is a temporary measure to allow for simple examples that do not require the @typegpu/example-toolkit package. */}
{def.elements.length === 0 ? <Canvas aspectRatio={1} /> : null}

{def.elements.map((element) => {
if (element.type === 'canvas') {
return (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
/*
{
"title": "Gradient Tiles",
"category": "simple"
}
*/

import tgpu from 'typegpu';
import { struct, u32 } from 'typegpu/data';

if (!navigator.gpu) {
throw new Error('WebGPU is not supported by this browser.');
}

const adapter = await navigator.gpu.requestAdapter();
if (!adapter) {
throw new Error('Could not find a compatible GPU.');
}
const device = await adapter.requestDevice();
const canvas = document.querySelector('canvas') as HTMLCanvasElement;
const context = canvas.getContext('webgpu') as GPUCanvasContext;
const presentationFormat = navigator.gpu.getPreferredCanvasFormat();

const vertWGSL = `
struct Output {
@builtin(position) pos: vec4f,
@location(0) uv: vec2f,
}
@vertex
fn main(
@builtin(vertex_index) vertexIndex: u32,
) -> Output {
var pos = array<vec2f, 4>(
vec2(1, 1), // top-right
vec2(-1, 1), // top-left
vec2(1, -1), // bottom-right
vec2(-1, -1) // bottom-left
);
var uv = array<vec2f, 4>(
vec2(1., 1.), // top-right
vec2(0., 1.), // top-left
vec2(1., 0.), // bottom-right
vec2(0., 0.) // bottom-left
);
var out: Output;
out.pos = vec4f(pos[vertexIndex], 0.0, 1.0);
out.uv = uv[vertexIndex];
return out;
}`;

const fragWGSL = `
struct Span {
x: u32,
y: u32,
}
@group(0) @binding(0) var<uniform> span: Span;
@fragment
fn main(
@location(0) uv: vec2f,
) -> @location(0) vec4f {
let red = floor(uv.x * f32(span.x)) / f32(span.x);
let green = floor(uv.y * f32(span.y)) / f32(span.y);
return vec4(red, green, 0.5, 1.0);
}`;

context.configure({
device: device,
format: presentationFormat,
alphaMode: 'premultiplied',
});

const Span = struct({
x: u32,
y: u32,
});

const spanBuffer = tgpu
.createBuffer(Span, { x: 10, y: 10 })
.$device(device)
.$usage(tgpu.Uniform);

const pipeline = device.createRenderPipeline({
layout: 'auto',
vertex: {
module: device.createShaderModule({
code: vertWGSL,
}),
},
fragment: {
module: device.createShaderModule({
code: fragWGSL,
}),
targets: [
{
format: presentationFormat,
},
],
},
primitive: {
topology: 'triangle-strip',
},
});

const bindGroup = device.createBindGroup({
layout: pipeline.getBindGroupLayout(0),
entries: [
{
binding: 0,
resource: {
buffer: spanBuffer.buffer,
},
},
],
});

const draw = (spanXValue: number, spanYValue: number) => {
const textureView = context.getCurrentTexture().createView();
const renderPassDescriptor: GPURenderPassDescriptor = {
colorAttachments: [
{
view: textureView,
clearValue: [0, 0, 0, 0],
loadOp: 'clear',
storeOp: 'store',
},
],
};

tgpu.write(spanBuffer, { x: spanXValue, y: spanYValue });

const commandEncoder = device.createCommandEncoder();
const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
passEncoder.setPipeline(pipeline);
passEncoder.setBindGroup(0, bindGroup);
passEncoder.draw(4);
passEncoder.end();

device.queue.submit([commandEncoder.finish()]);
};

let spanX = 10;
let spanY = 10;

// deferring to wait for canvas to init
setTimeout(() => {
draw(spanX, spanY);
}, 100);

/** @button "↔️ -" */
export function spanXMinus() {
spanX = Math.max(1, spanX - 1);
draw(spanX, spanY);
}

/** @button "↔️ +" */
export function spanXPlus() {
spanX = Math.min(spanX + 1, 20);
draw(spanX, spanY);
}

/** @button "↕️ -" */
export function spanYMinus() {
spanY = Math.max(1, spanY - 1);
draw(spanX, spanY);
}

/** @button "↕️ +" */
export function spanYPlus() {
spanY = Math.min(spanY + 1, 20);
draw(spanX, spanY);
}
47 changes: 46 additions & 1 deletion apps/typegpu-docs/src/utils/examples/exampleRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,46 @@ const staticToDynamicImports = {
} satisfies TraverseOptions,
};

let addButtonParameterImportAdded = false;

const labeledFunctionToControlButtons = () => {
return {
visitor: {
ImportDeclaration(path) {
if (path.node.source.value === '@typegpu/example-toolkit') {
for (const imp of path.node.specifiers) {
if (imp.local.name === 'addButtonParameter') {
addButtonParameterImportAdded = true;
break;
}
}
}
},

ExportNamedDeclaration(path, state) {
// @ts-ignore
const code: string = state.file.code;
const declaration = path.node.declaration;
if (declaration?.type === 'FunctionDeclaration') {
for (const comment of path.node.leadingComments ?? []) {
const regExp = /.*@button.*\"(?<label>.*)\".*/;
const label = regExp.exec(comment.value)?.groups?.label;

if (label) {
path.replaceWith(
template.program.ast(
`${addButtonParameterImportAdded ? '' : "import { addButtonParameter } from '@typegpu/example-toolkit';"} addButtonParameter('${label}', ${code.slice(declaration.start ?? 0, declaration.end ?? 0)})`,
),
);
addButtonParameterImportAdded = true;
}
}
}
},
} satisfies TraverseOptions,
};
};

const MAX_ITERATIONS = 10000;

/**
Expand Down Expand Up @@ -303,11 +343,16 @@ export async function executeExample(

const jsCode = tsToJs(exampleCode);

addButtonParameterImportAdded = false;
const transformedCode =
Babel.transform(jsCode, {
compact: false,
retainLines: true,
plugins: [staticToDynamicImports, preventInfiniteLoops],
plugins: [
labeledFunctionToControlButtons,
staticToDynamicImports,
preventInfiniteLoops,
],
}).code ?? jsCode;

const mod = Function(`
Expand Down

0 comments on commit b4d3b51

Please sign in to comment.