wgpu/index.js

189 lines
4.7 KiB
JavaScript

import { GraphicsDevice } from './src/core/graphics-device.js'
import { PowerPreference, VertexFormat } from './src/enum.js'
import { MipGenerator } from './src/utils/mip-generator.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 mipGenerator = new MipGenerator(graphicsDevice.device)
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<uniform> transform : mat4x4<f32>;
@vertex
fn vs_main(@location(0) in_pos : vec3<f32>) -> @builtin(position) vec4<f32> {
return transform * 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(
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<import('./src/core/graphics-device.js').BindGroupEntry>} */
const uniformBindings = [{
binding: 0,
resource: uniformBuffer
}]
const uniformBindGroup = material.createBindGroup(
0,
{ transform: uniformBuffer },
'Uniforms'
)
//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()
}