169 lines
4.3 KiB
JavaScript
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()
|
|
}
|
|
|