Files
webgpu/src/main.ts

234 lines
5.3 KiB
TypeScript

import shaderSource from './shaders/shader.wgsl?raw';
import { QuadGeometry } from './geometry';
import { Texture } from './texture';
class Renderer{
private device! : GPUDevice;
private context! : GPUCanvasContext;
private pipeline! : GPURenderPipeline;
private postitionBuffer! : GPUBuffer;
private colorBuffer! : GPUBuffer;
private texCoordBuffer! : GPUBuffer;
private textureBindGroup! : GPUBindGroup;
private testTexture! : Texture;
public async initialize()
{
const canvas = document.getElementById('canvas') as HTMLCanvasElement;
this.context = canvas.getContext('webgpu')!;
if(!this.context){
alert('WebGPU not supported');
return;
}
const adapter = await navigator.gpu.requestAdapter(
{
powerPreference: 'low-power'
}
);
if(!adapter)
{
alert('No WebGPU adapter found');
return;
}
this.device = await adapter.requestDevice()
this.context.configure({
device: this.device,
format: navigator.gpu.getPreferredCanvasFormat()
})
this.testTexture = await Texture.createTextureFromUrl(this.device, 'assets/UVEditorColorGrid.png');
this.prepareModel();
const geometry = new QuadGeometry();
this.postitionBuffer = this.CreateBuffer(new Float32Array(geometry.positions));
this.colorBuffer = this.CreateBuffer(new Float32Array(geometry.colors));
this.texCoordBuffer = this.CreateBuffer(new Float32Array(geometry.texCoords));
}
private CreateBuffer(data: Float32Array)
{
const buffer = this.device.createBuffer({
size: data.byteLength,
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
mappedAtCreation: true
});
new Float32Array(buffer.getMappedRange()).set(data);
buffer.unmap();
return buffer;
}
private prepareModel() {
const shaderModule = this.device.createShaderModule({code: shaderSource});
const postitionBufferLayout : GPUVertexBufferLayout =
{
arrayStride: 2 * Float32Array.BYTES_PER_ELEMENT,
attributes: [
{
shaderLocation: 0,
offset: 0,
format: 'float32x2'
}
],
stepMode: 'vertex'
}
const colorBufferLayout : GPUVertexBufferLayout =
{
arrayStride: 3 * Float32Array.BYTES_PER_ELEMENT,
attributes: [
{
shaderLocation: 1,
offset: 0,
format: 'float32x3'
}]
}
const textureCoordBufferLayout : GPUVertexBufferLayout =
{
arrayStride: 2 * Float32Array.BYTES_PER_ELEMENT,
attributes: [
{
shaderLocation: 2,
offset: 0,
format: 'float32x2'
}],
stepMode: 'vertex'
}
const vertexState: GPUVertexState = {
module : shaderModule,
entryPoint: "VertexMain",
buffers:
[
postitionBufferLayout,
colorBufferLayout,
textureCoordBufferLayout
]
}
const fragmentState: GPUFragmentState = {
module : shaderModule,
entryPoint: "FragmentMain",
targets: [
{
format: navigator.gpu.getPreferredCanvasFormat(),
blend: {
color: {
srcFactor: 'one',
dstFactor: 'zero',
operation: 'add'
},
alpha: {
srcFactor: 'one',
dstFactor: 'zero',
operation: 'add'
}
}
}
]
}
const textureBindGroupLayout = this.device.createBindGroupLayout({
entries: [
{
binding: 0,
visibility: GPUShaderStage.FRAGMENT,
sampler: {}
},
{
binding: 1,
visibility: GPUShaderStage.FRAGMENT,
texture: {}
}
]
});
const pipelineLayout = this.device.createPipelineLayout({
bindGroupLayouts: [
textureBindGroupLayout
]
});
this.textureBindGroup = this.device.createBindGroup({
layout: textureBindGroupLayout,
entries: [
{
binding: 0,
resource: this.testTexture.sampler
},
{
binding: 1,
resource: this.testTexture.texture.createView()
}
]
});
this.pipeline = this.device.createRenderPipeline({
vertex: vertexState,
fragment: fragmentState,
primitive:{
topology: 'triangle-list',
},
layout: pipelineLayout
});
}
public draw()
{
const commandEncoder = this.device.createCommandEncoder();
const textureView = this.context.getCurrentTexture().createView();
const renderPassDescriptor:GPURenderPassDescriptor = {
colorAttachments:[{
view: textureView,
clearValue: {r: 0, g: 0, b: 0, a: 1},
loadOp: 'clear',
storeOp:'store'
}]
}
const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
//Draw here
passEncoder.setPipeline(this.pipeline );
passEncoder.setVertexBuffer(0, this.postitionBuffer);
passEncoder.setVertexBuffer(1, this.colorBuffer);
passEncoder.setVertexBuffer(2, this.texCoordBuffer);
passEncoder.setBindGroup(0, this.textureBindGroup)
passEncoder.draw(6);
passEncoder.end();
this.device.queue.submit([commandEncoder.finish()]);
}
}
const renderer = new Renderer();
renderer.initialize()
.then(()=> renderer.draw());