wgpu/index.js
2025-04-15 05:34:18 -05:00

169 lines
4.3 KiB
JavaScript

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<f32>) -> @builtin(position) vec4<f32> {
return vec4<f32>(in_pos, 1.0);
}
@fragment
fn fs_main() -> @location(0) vec4<f32> {
return vec4<f32>(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<import('./src/core/graphics-device.js').BindGroupEntry>} */
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()
}