Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Code-editor for examples #20

Merged
merged 16 commits into from
Jul 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
193 changes: 193 additions & 0 deletions apps/wigsill-examples/examples/camera-thresholding.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
/*
iwoplaza marked this conversation as resolved.
Show resolved Hide resolved
{
"title": "Camera Thresholding"
}
*/

import { f32, makeArena, ProgramBuilder, wgsl, WGSLRuntime } from 'wigsill';
import { addElement, addParameter, onFrame } from '@wigsill/example-toolkit';

// Layout
const [video, canvas] = await Promise.all([
addElement('video', { width: 500, height: 375 }),
addElement('canvas', { width: 500, height: 375 }),
]);

const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();

const thresholdData = wgsl.memory(f32).alias('threshold');

const shaderCode = wgsl`
@group(0) @binding(0) var sampler_ : sampler;
@group(0) @binding(1) var videoTexture : texture_external;

struct VertexOutput {
@builtin(position) Position : vec4f,
@location(0) fragUV : vec2f,
}

@vertex
fn vert_main(@builtin(vertex_index) VertexIndex : u32) -> VertexOutput {
const pos = array(
vec2( 1.0, 1.0),
vec2( 1.0, -1.0),
vec2(-1.0, -1.0),
vec2( 1.0, 1.0),
vec2(-1.0, -1.0),
vec2(-1.0, 1.0),
);

const uv = array(
vec2(1.0, 0.0),
vec2(1.0, 1.0),
vec2(0.0, 1.0),
vec2(1.0, 0.0),
vec2(0.0, 1.0),
vec2(0.0, 0.0),
);

var output : VertexOutput;
output.Position = vec4(pos[VertexIndex], 0.0, 1.0);
output.fragUV = uv[VertexIndex];
return output;
}

@fragment
fn frag_main(@location(0) fragUV : vec2f) -> @location(0) vec4f {
var color = textureSampleBaseClampToEdge(videoTexture, sampler_, fragUV);
let grey = 0.299*color.r + 0.587*color.g + 0.114*color.b;

if grey < ${thresholdData} {
return vec4f(0, 0, 0, 1);
}

return vec4f(1);
}
`;

if (navigator.mediaDevices.getUserMedia) {
video.srcObject = await navigator.mediaDevices.getUserMedia({
video: true,
});
}

const context = canvas.getContext('webgpu');
const presentationFormat = navigator.gpu.getPreferredCanvasFormat();

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

const runtime = new WGSLRuntime(device);

const arena = makeArena({
bufferBindingType: 'uniform',
memoryEntries: [thresholdData],
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.UNIFORM,
});

const program = new ProgramBuilder(runtime, shaderCode).build({
bindingGroup: 1,
shaderStage: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
arenas: [arena],
});

const shaderModule = device.createShaderModule({
code: program.code,
});

const layout = device.createPipelineLayout({
bindGroupLayouts: [
device.createBindGroupLayout({
entries: [
{
binding: 0,
visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
sampler: {},
},
{
binding: 1,
visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
externalTexture: {},
},
],
}),
program.bindGroupLayout,
],
});

const renderPipeline = device.createRenderPipeline({
layout: layout,
vertex: {
module: shaderModule,
},
fragment: {
module: shaderModule,
targets: [
{
format: presentationFormat,
},
],
},
primitive: {
topology: 'triangle-list',
},
});

const sampler = device.createSampler({
magFilter: 'linear',
minFilter: 'linear',
});

// UI

addParameter('threshold', { initial: 0.4, min: 0, max: 1 }, (threshold) =>
thresholdData.write(runtime, threshold),
);

onFrame(() => {
if (!(video.currentTime > 0)) {
return;
}

const resultTexture = device.importExternalTexture({
source: video,
});

const bindGroup = device.createBindGroup({
layout: renderPipeline.getBindGroupLayout(0),
entries: [
{
binding: 0,
resource: sampler,
},
{
binding: 1,
resource: resultTexture,
},
],
});

const commandEncoder = device.createCommandEncoder();

const passEncoder = commandEncoder.beginRenderPass({
colorAttachments: [
{
view: context.getCurrentTexture().createView(),
clearValue: [0, 0, 0, 1],
loadOp: 'clear',
storeOp: 'store',
},
],
});

passEncoder.setPipeline(renderPipeline);
passEncoder.setBindGroup(0, bindGroup);
passEncoder.setBindGroup(1, program.bindGroup);
passEncoder.draw(6);
passEncoder.end();
device.queue.submit([commandEncoder.finish()]);
});
150 changes: 150 additions & 0 deletions apps/wigsill-examples/examples/gradient-tiles.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
/*
{
"title": "Gradient Tiles"
}
*/

import { makeArena, ProgramBuilder, u32, wgsl, WGSLRuntime } from 'wigsill';
import {
addElement,
addParameter,
onCleanup,
onFrame,
} from '@wigsill/example-toolkit';

const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
const runtime = new WGSLRuntime(device);

const xSpanData = wgsl.memory(u32).alias('x-span');
const ySpanData = wgsl.memory(u32).alias('y-span');

const mainArena = makeArena({
bufferBindingType: 'uniform',
memoryEntries: [xSpanData, ySpanData],
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.UNIFORM,
});

const canvas = await addElement('canvas');

const context = canvas.getContext('webgpu');

const devicePixelRatio = window.devicePixelRatio;
canvas.width = canvas.clientWidth * devicePixelRatio;
canvas.height = canvas.clientHeight * devicePixelRatio;
const presentationFormat = navigator.gpu.getPreferredCanvasFormat();

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

const mainCode = wgsl`
struct VertexOutput {
@builtin(position) pos: vec4f,
@location(0) uv: vec2f,
}

@vertex
fn main_vert(
@builtin(vertex_index) VertexIndex: u32
) -> VertexOutput {
var pos = array<vec2f, 4>(
vec2(0.5, 0.5), // top-right
vec2(-0.5, 0.5), // top-left
vec2(0.5, -0.5), // bottom-right
vec2(-0.5, -0.5) // 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 output: VertexOutput;
output.pos = vec4f(pos[VertexIndex], 0.0, 1.0);
output.uv = uv[VertexIndex];
return output;
}

@fragment
fn main_frag(
@builtin(position) Position: vec4f,
@location(0) uv: vec2f,
) -> @location(0) vec4f {
let red = floor(uv.x * f32(${xSpanData})) / f32(${xSpanData});
let green = floor(uv.y * f32(${ySpanData})) / f32(${ySpanData});
return vec4(red, green, 0.5, 1.0);
}
`;

const program = new ProgramBuilder(runtime, mainCode).build({
bindingGroup: 0,
shaderStage: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
arenas: [mainArena],
});

const shaderModule = device.createShaderModule({
code: program.code,
});

const pipeline = device.createRenderPipeline({
layout: device.createPipelineLayout({
bindGroupLayouts: [program.bindGroupLayout],
}),
vertex: {
module: shaderModule,
entryPoint: 'main_vert',
},
fragment: {
module: shaderModule,
entryPoint: 'main_frag',
targets: [
{
format: presentationFormat,
},
],
},
primitive: {
topology: 'triangle-strip',
},
});

addParameter('x-span', { initial: 16, min: 1, max: 16, step: 1 }, (xSpan) =>
xSpanData.write(runtime, xSpan),
);

addParameter('y-span', { initial: 16, min: 1, max: 16, step: 1 }, (ySpan) =>
ySpanData.write(runtime, ySpan),
);

onFrame(() => {
const commandEncoder = device.createCommandEncoder();
const textureView = context.getCurrentTexture().createView();

const renderPassDescriptor = {
colorAttachments: [
{
view: textureView,
clearValue: [0, 0, 0, 1],
loadOp: 'clear',
storeOp: 'store',
},
],
};

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

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

onCleanup(() => {
console.log(`All cleaned up`);
});
13 changes: 11 additions & 2 deletions apps/wigsill-examples/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,24 @@
"preview": "vite preview"
},
"dependencies": {
"@babel/standalone": "^7.24.7",
"@monaco-editor/react": "^4.6.0",
"classnames": "^2.5.1",
"dat.gui": "^0.7.9",
"jotai": "^2.8.4",
"jotai-location": "^0.5.5",
"monaco-editor": "^0.50.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"remeda": "^2.3.0",
"typed-binary": "^4.0.0",
"wigsill": "workspace:*"
"wigsill": "workspace:*",
"zod": "^3.23.8"
},
"devDependencies": {
"@types/babel__standalone": "^7.1.7",
"@types/babel__template": "^7.4.4",
"@types/babel__traverse": "^7.20.6",
"@types/dat.gui": "^0.7.13",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
Expand All @@ -34,6 +42,7 @@
"postcss": "^8.4.39",
"tailwindcss": "^3.4.4",
"typescript": "^5.2.2",
"vite": "^5.3.1"
"vite": "^5.3.1",
"vitest": "0.33.0"
}
}
Loading