import { getPath } from "./common.js"; class FontRenderer { createBuffers(device) { this.maxGlyphs = 1024; this.glyphBufferSize = 4 * this.maxGlyphs; this.frames = []; for (let i = 0; i < 2; i++) { const glyphBuffer = device.createBuffer({ label: `glyph buffer ${i}`, size: this.glyphBufferSize, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST, }); const bindGroup = device.createBindGroup({ label: `font bind group ${i}`, layout: this.bindGroupLayout.glyph, entries: [{ binding: 0, resource: { buffer: glyphBuffer }, }] }); this.frames.push({ glyphBuffer: glyphBuffer, bindGroup: bindGroup, }); } } constructor(device, canvasFormat, viewUniformBuffer, wgsl, module) { const label = "font"; this.module = module; ////////////////////////////////////////////////////////////////////// // shader module ////////////////////////////////////////////////////////////////////// const shaderModule = device.createShaderModule({ label: `${label} shader`, code: wgsl, }); ////////////////////////////////////////////////////////////////////// // index buffer ////////////////////////////////////////////////////////////////////// const array = new Uint16Array(6); array[0] = 0; array[1] = 1; array[2] = 2; array[3] = 0; array[4] = 3; array[5] = 1; this.buffer = device.createBuffer({ label: `${label} renderer buffer`, size: array.byteLength, usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST, }); device.queue.writeBuffer(this.buffer, 0, array, 0, array.length); ////////////////////////////////////////////////////////////////////// // pipeline ////////////////////////////////////////////////////////////////////// this.bindGroupLayout = { view: device.createBindGroupLayout({ label: `${label} bind group layout view`, entries: [{ binding: 0, visibility: GPUShaderStage.VERTEX, buffer: { type: "uniform" } }] }), glyph: device.createBindGroupLayout({ label: `${label} bind group layout glyph`, entries: [{ // glyphs binding: 0, visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT, buffer: { type: "read-only-storage" } }/*, { // configuration binding: 1, visibility: GPUShaderStage.FRAGMENT, buffer: { type: "uniform" } }*/] }), }; const pipelineLayout = device.createPipelineLayout({ label: `${label} pipeline layout`, bindGroupLayouts: [ this.bindGroupLayout.view, this.bindGroupLayout.glyph ], }); this.renderPipeline = device.createRenderPipeline({ label: `${label} pipeline`, layout: pipelineLayout, vertex: { module: shaderModule, entryPoint: "vertexMain", }, fragment: { module: shaderModule, entryPoint: "fragmentMain", targets: [{ format: canvasFormat, }] }, primitive: { topology: "triangle-list", }, depthStencil: { depthWriteEnabled: true, depthCompare: "less", format: "depth24plus", }, }); ////////////////////////////////////////////////////////////////////// // buffers ////////////////////////////////////////////////////////////////////// this.createBuffers(device); ////////////////////////////////////////////////////////////////////// // bind group ////////////////////////////////////////////////////////////////////// this.viewBindGroup = device.createBindGroup({ label: `${label} bind group`, layout: this.bindGroupLayout.view, entries: [{ binding: 0, resource: { buffer: viewUniformBuffer }, }], }); } render(renderPass, frameNumber) { renderPass.setPipeline(this.renderPipeline); renderPass.setIndexBuffer(this.buffer, "uint16"); renderPass.setBindGroup(0, this.viewBindGroup); renderPass.setBindGroup(1, this.frames[frameNumber].bindGroup); renderPass.drawIndexed(6); } } async function loadFont(device, canvasFormat, viewUniformBuffer, module) { const fontWgsl = await getPath("font.wgsl"); const renderer = new FontRenderer(device, canvasFormat, viewUniformBuffer, fontWgsl, module); return renderer; } export { loadFont };