import { GraphicsDevice } from './src/core/graphics-device.js' import { PowerPreference, VertexFormat } 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 shaderSource = ` @group(0) @binding(0) var transform : mat4x4; @vertex fn vs_main(@location(0) in_pos : vec3) -> @builtin(position) vec4 { return transform * 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( shaderSource, 'SquareShader', ) const vertices = new Float32Array([ -0.5, 0.5, 0.0, -0.5, -0.5, 0.0, 0.5, 0.5, 0.0, 0.5, 0.5, 0.0, -0.5, -0.5, 0.0, 0.5, -0.5, 0.0 ]) const indices = new Uint16Array([ 0, 1, 2, 3, 4, 5 ]) const indexBuffer = graphicsDevice.createBuffer({ size: indices.byteLength, usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST }) indexBuffer.write(indices) 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 material = graphicsDevice.createMaterial( { vertex: shaderModule }, ) const vertexBufferLayout = { arrayStride: 3 * 4, attributes: [ { shaderLocation: 0, offset: 0, format: VertexFormat.Float32x3 } ] } const pipelineDescriptor = material.getRenderPipelineDescriptor({ label: 'SquarePipeline', vertex: { buffers: [vertexBufferLayout] }, fragment: { targets: [{ format: graphicsDevice.swapChain.format }] } }) const pipeline = graphicsDevice.createRenderPipeline(pipelineDescriptor) /** @type {Array} */ const uniformBindings = [{ binding: 0, resource: uniformBuffer }] const uniformBindGroup = graphicsDevice.createBindGroup( material.bindGroupLayouts[0], uniformBindings, 'Uniforms' ) async function frame() { if (!graphicsDevice.isInitialized) { return } graphicsDevice.queue.writeBuffer(uniformBuffer.handle, 0, matrixData) const commandRecorder = graphicsDevice.createCommandRecorder('FrameCommands') const passEncoder = commandRecorder.beginRenderPass() if (passEncoder) { passEncoder.setPipeline(pipeline.handle) passEncoder.setVertexBuffer(0, vertexBuffer.handle) passEncoder.setIndexBuffer(indexBuffer.handle, 'uint16') passEncoder.setBindGroup(0, uniformBindGroup.handle) passEncoder.drawIndexed(indices.length) 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() }