import { GraphicsDevice } from './src/core/graphics-device.js' import { PowerPreference } from './src/enum.js' async function main() { const canvas = /** @type {HTMLCanvasElement} */ (document.getElementById('webgpu-canvas')) if (!canvas) { console.error("Canvas element not found!") return } canvas.width = 800 canvas.height = 600 const graphicsDevice = await GraphicsDevice.build() .withCanvas(canvas) .withAdapter({ powerPreference: PowerPreference.HighPerformance }) .build() const success = await graphicsDevice.initialize() if (!success) { console.error("Failed to initialize WebGPU.") document.body.innerHTML = "WebGPU initialization failed. Please use a supported browser and ensure hardware acceleration is enabled." return } const shaderCode = ` @vertex fn vs_main(@location(0) in_pos : vec3) -> @builtin(position) vec4 { return vec4(in_pos, 1.0); } @fragment fn fs_main() -> @location(0) vec4 { return vec4(0.0, 0.5, 1.0, 1.0); } ` const shaderModule = graphicsDevice.createShaderModule(shaderCode, 'TriangleShader') const vertices = new Float32Array([ 0.0, 0.5, 0.0, -0.5, -0.5, 0.0, 0.5, -0.5, 0.0 ]) const vertexBuffer = graphicsDevice.createBuffer( { label: 'TriangleVertexBuffer', size: vertices.byteLength, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST, }, vertices ) const matrixSize = 4 * 4 * Float32Array.BYTES_PER_ELEMENT const matrixData = new Float32Array(16) matrixData[0] = 1 matrixData[5] = 1 matrixData[10] = 1 matrixData[15] = 1 const uniformBuffer = graphicsDevice.createBuffer( { label: 'SceneUniformsBuffer', size: matrixSize, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST, }, matrixData ) const bindings = [{ binding: 0, visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT, buffer: /** @type {GPUBufferBindingLayout} */ ({ type: 'uniform' }) }] const bindGroupLayout = graphicsDevice.createBindGroupLayout(bindings, 'UniformLayout') const pipelineLayout = graphicsDevice.createPipelineLayout([bindGroupLayout], 'PipelineLayout') const pipeline = graphicsDevice.createRenderPipeline({ label: 'TrianglePipeline', layout: pipelineLayout, vertex: { module: shaderModule.handle, entryPoint: 'vs_main', buffers: [ { arrayStride: 3 * 4, attributes: [ { shaderLocation: 0, offset: 0, format: 'float32x3' } ] } ] }, fragment: { module: shaderModule.handle, entryPoint: 'fs_main', targets: [ { format: graphicsDevice.swapChain.format } ] }, primitive: { topology: 'triangle-list', }, }) /** @type {Array} */ const entries = [{ binding: 0, resource: uniformBuffer }] const uniformBindGroup = graphicsDevice.createBindGroup(bindGroupLayout, entries, 'Uniforms') async function frame() { if (!graphicsDevice.isInitialized) { return } const commandRecorder = graphicsDevice.createCommandRecorder('FrameCommands') const passEncoder = commandRecorder.beginRenderPass() if (passEncoder) { passEncoder.setPipeline(pipeline.handle) passEncoder.setVertexBuffer(0, vertexBuffer.handle) passEncoder.setBindGroup(0, uniformBindGroup.handle) passEncoder.draw(3) commandRecorder.endRenderPass() } const commandBuffer = commandRecorder.finish() graphicsDevice.submitCommands([commandBuffer]) } requestAnimationFrame(frame) // TODO: move to graphics device or somewhere else const resizeObserver = new ResizeObserver(entries => { for (let entry of entries) { const { width, height } = entry.contentRect if (graphicsDevice.isInitialized && width > 0 && height > 0) { graphicsDevice.swapChain.resize(width, height) } } }) resizeObserver.observe(canvas) window.addEventListener('beforeunload', () => { resizeObserver.disconnect() graphicsDevice.destroy() }) } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', main) } else { main() }