183 lines
4.4 KiB
JavaScript
183 lines
4.4 KiB
JavaScript
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<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 = 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()
|
|
}
|
|
|