diff --git a/index.html b/index.html new file mode 100644 index 0000000..8de64c5 --- /dev/null +++ b/index.html @@ -0,0 +1,11 @@ + + + + + + WebGPU Video Processor Demo + + + + + \ No newline at end of file diff --git a/src/core/webgpu-bridge.d.ts b/src/core/webgpu-bridge.d.ts new file mode 100644 index 0000000..8bda6e2 --- /dev/null +++ b/src/core/webgpu-bridge.d.ts @@ -0,0 +1,58 @@ +/// + +declare global { + interface Navigator extends NavigatorGPU {} +} + +// Bridge interfaces to handle type compatibility +interface GPUObjectBase { + label: string | undefined; +} + +interface GPUObjectDescriptorBase { + label?: string; +} + +interface GPUDeviceBridge extends GPUDevice, GPUObjectBase { + features: GPUFeatureSet; + limits: Required; + queue: GPUQueue; + lost: Promise; + pushErrorScope(filter: GPUErrorFilter): void; + popErrorScope(): Promise; + createBuffer(descriptor: GPUBufferDescriptor): GPUBuffer; + createTexture(descriptor: GPUTextureDescriptor): GPUTexture; + createSampler(descriptor?: GPUSamplerDescriptor): GPUSampler; + createBindGroupLayout(descriptor: GPUBindGroupLayoutDescriptor): GPUBindGroupLayout; + createPipelineLayout(descriptor: GPUPipelineLayoutDescriptor): GPUPipelineLayout; + createBindGroup(descriptor: GPUBindGroupDescriptor): GPUBindGroup; + createShaderModule(descriptor: GPUShaderModuleDescriptor): GPUShaderModule; + createComputePipeline(descriptor: GPUComputePipelineDescriptor): GPUComputePipeline; + createRenderPipeline(descriptor: GPURenderPipelineDescriptor): GPURenderPipeline; + createCommandEncoder(descriptor?: GPUCommandEncoderDescriptor): GPUCommandEncoder; + createRenderBundleEncoder(descriptor: GPURenderBundleEncoderDescriptor): GPURenderBundleEncoder; + destroy(): void; +} + +interface GPUQueueBridge extends GPUQueue { + submit(commandBuffers: Iterable): void; + onSubmittedWorkDone(): Promise; + writeBuffer( + buffer: GPUBuffer, + bufferOffset: number, + data: BufferSource, + dataOffset?: number, + size?: number + ): void; + writeTexture( + destination: GPUImageCopyTexture, + data: BufferSource, + dataLayout: GPUImageDataLayout, + size: GPUExtent3D + ): void; +} + +export { + GPUDeviceBridge as GPUDevice, + GPUQueueBridge as GPUQueue +}; \ No newline at end of file diff --git a/src/core/webgpu-types.d.ts b/src/core/webgpu-types.d.ts new file mode 100644 index 0000000..6e4878f --- /dev/null +++ b/src/core/webgpu-types.d.ts @@ -0,0 +1,19 @@ +/// + +declare global { + var GPU: { + prototype: GPU; + new(): GPU; + }; + + interface Navigator { + readonly gpu?: GPU; + } + + interface HTMLCanvasElement { + getContext(contextId: 'webgpu'): GPUCanvasContext | null; + } +} + +// This empty export makes this file a module +export {}; \ No newline at end of file diff --git a/src/demo.ts b/src/demo.ts new file mode 100644 index 0000000..255d927 --- /dev/null +++ b/src/demo.ts @@ -0,0 +1,82 @@ +import { WebGPUVideoProcessor } from './index'; + +async function main() { + // Create video element + const video = document.createElement('video'); + video.width = 640; + video.height = 480; + video.autoplay = true; + video.muted = true; // Required for autoplay in most browsers + + // Get user media (webcam) + try { + const stream = await navigator.mediaDevices.getUserMedia({ + video: { + width: { ideal: 640 }, + height: { ideal: 480 } + } + }); + video.srcObject = stream; + document.body.appendChild(video); + + // Create canvas for output + const canvas = document.createElement('canvas'); + canvas.width = video.width; + canvas.height = video.height; + document.body.appendChild(canvas); + + // Initialize video processor + const processor = new WebGPUVideoProcessor({ + enableWebGL2Fallback: false, + preferHighPerformance: true, + debug: true + }); + + // Wait for video to be ready + await new Promise((resolve) => { + video.onloadedmetadata = () => resolve(); + video.onerror = () => { + throw new Error('Failed to load video'); + }; + }); + + // Start processing + await processor.initialize(canvas); + + // Process frames in a loop + async function processFrame() { + if (processor.isInitialized) { + await processor.process(video); + } + requestAnimationFrame(processFrame); + } + + processFrame(); + + } catch (error) { + console.error('Error:', error); + } +} + +// Add some basic styling +const style = document.createElement('style'); +style.textContent = ` + body { + margin: 0; + display: flex; + justify-content: center; + align-items: center; + min-height: 100vh; + background: #1a1a1a; + color: white; + font-family: Arial, sans-serif; + } + video, canvas { + margin: 10px; + border: 2px solid #333; + border-radius: 4px; + } +`; +document.head.appendChild(style); + +main().catch(console.error); \ No newline at end of file diff --git a/src/minimal-demo.ts b/src/minimal-demo.ts new file mode 100644 index 0000000..5ffc14c --- /dev/null +++ b/src/minimal-demo.ts @@ -0,0 +1,73 @@ +async function main() { + // Create canvas + const canvas = document.createElement('canvas'); + canvas.width = 800; + canvas.height = 600; + document.body.appendChild(canvas); + + // Check WebGPU support + if (!navigator.gpu) { + throw new Error('WebGPU is not supported in this browser'); + } + + // Request adapter and device + const adapter = await navigator.gpu.requestAdapter({ + powerPreference: 'high-performance' + }); + + if (!adapter) { + throw new Error('No WebGPU adapter found'); + } + + const device = await adapter.requestDevice(); + const context = canvas.getContext('webgpu'); + + if (!context) { + throw new Error('Failed to get WebGPU context'); + } + + // Configure canvas + const format = navigator.gpu.getPreferredCanvasFormat(); + context.configure({ + device, + format, + alphaMode: 'premultiplied', + usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_DST + }); + + // Create command encoder and render pass + const commandEncoder = device.createCommandEncoder(); + const renderPassDescriptor: GPURenderPassDescriptor = { + colorAttachments: [{ + view: context.getCurrentTexture().createView(), + clearValue: { r: 0.0, g: 0.0, b: 1.0, a: 1.0 }, // Blue color + loadOp: 'clear', + storeOp: 'store' + }] + }; + + // Create and submit command buffer + const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor); + passEncoder.end(); + device.queue.submit([commandEncoder.finish()]); + + // Add some basic styling + const style = document.createElement('style'); + style.textContent = ` + body { + margin: 0; + display: flex; + justify-content: center; + align-items: center; + min-height: 100vh; + background: #1a1a1a; + } + canvas { + border: 2px solid #333; + border-radius: 4px; + } + `; + document.head.appendChild(style); +} + +main().catch(console.error); \ No newline at end of file diff --git a/src/types/webgpu.d.ts b/src/types/webgpu.d.ts new file mode 100644 index 0000000..0519ecb --- /dev/null +++ b/src/types/webgpu.d.ts @@ -0,0 +1 @@ + \ No newline at end of file