diff --git a/.gitignore b/.gitignore index 12c3a82..c6cdda4 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,6 @@ __pycache__ *.o *.data .gdb_history -*.elf \ No newline at end of file +*.elf +favicon.ico +tools/ttf_outline/ttf_outline \ No newline at end of file diff --git a/font.js b/font.js index af9fe7f..caaa202 100644 --- a/font.js +++ b/font.js @@ -3,39 +3,115 @@ import { getPath } from "./common.js"; class FontRenderer { createBuffers(device) { - this.maxGlyphs = 1024; - this.glyphBufferSize = 4 * this.maxGlyphs; - this.frames = []; + + ////////////////////////////////////////////////////////////////////// + // glyph + ////////////////////////////////////////////////////////////////////// + + const glyphBufferSize = this.module.instance.exports.font_layout__glyph_buffer_size(this.fontLayoutAddress); + const glyphBuffer = device.createBuffer({ + label: `glyph buffer`, + size: glyphBufferSize, + usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST, + }); + + const glyphBufferAddress = this.module.instance.exports.font_layout__glyph_buffer_address(this.fontLayoutAddress); + device.queue.writeBuffer(glyphBuffer, 0, + this.module.memory.buffer, glyphBufferAddress, glyphBufferSize); + + ////////////////////////////////////////////////////////////////////// + // texture + ////////////////////////////////////////////////////////////////////// + + const textureWidth = this.module.instance.exports.font_layout__texture_width(this.fontLayoutAddress); + const textureHeight = this.module.instance.exports.font_layout__texture_height(this.fontLayoutAddress); + const textureAddress = this.module.instance.exports.font_layout__texture_address(this.fontLayoutAddress); + + //console.log(textureWidth, textureHeight, textureAddress); + const texture = device.createTexture({ + size: [textureWidth, textureHeight], + format: 'r8unorm', + usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST, + }); + + device.queue.writeTexture({ texture: texture }, + this.module.memory.buffer, + { offset: textureAddress, bytesPerRow: textureWidth }, + { width: textureWidth, height: textureHeight }); + + ////////////////////////////////////////////////////////////////////// + // sampler + ////////////////////////////////////////////////////////////////////// + + const sampler = device.createSampler({ + magFilter: "linear", + minFilter: "linear", + }); + + ////////////////////////////////////////////////////////////////////// + // bind group + ////////////////////////////////////////////////////////////////////// + + this.perFontBindGroup = device.createBindGroup({ + label: `font per-font bind group`, + layout: this.bindGroupLayout.perFont, + entries: [{ + binding: 0, + resource: sampler, + }, { + binding: 1, + resource: texture, + }, { + binding: 2, + resource: { buffer: glyphBuffer }, + }] + }); + + ////////////////////////////////////////////////////////////////////// + // layout + ////////////////////////////////////////////////////////////////////// + + const layoutBufferSize = this.module.instance.exports.font_layout__layout_buffer_size(this.fontLayoutAddress); for (let i = 0; i < 2; i++) { - const glyphBuffer = device.createBuffer({ - label: `glyph buffer ${i}`, - size: this.glyphBufferSize, + const layoutBuffer = device.createBuffer({ + label: `layout buffer ${i}`, + size: layoutBufferSize, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST, }); const bindGroup = device.createBindGroup({ - label: `font bind group ${i}`, - layout: this.bindGroupLayout.glyph, + label: `font per-frame bind group ${i}`, + layout: this.bindGroupLayout.perFrame, entries: [{ binding: 0, - resource: { buffer: glyphBuffer }, + resource: { buffer: layoutBuffer }, }] }); this.frames.push({ - glyphBuffer: glyphBuffer, + layoutBuffer: layoutBuffer, bindGroup: bindGroup, }); } } - constructor(device, canvasFormat, viewUniformBuffer, wgsl, module) + constructor(device, canvasFormat, viewUniformBuffer, wgsl, module, fontBufferSrc) { const label = "font"; this.module = module; + ////////////////////////////////////////////////////////////////////// + // font layout + ////////////////////////////////////////////////////////////////////// + + this.fontBufferAddress = this.module.instance.exports.mem_alloc(fontBufferSrc.byteLength); + const fontBufferDst = new Uint8Array(this.module.memory.buffer, this.fontBufferAddress, fontBufferSrc.byteLength); + fontBufferDst.set(new Uint8Array(fontBufferSrc)); + this.maxGlyphs = 1024; + this.fontLayoutAddress = this.module.instance.exports.font_layout__create(this.fontBufferAddress, this.maxGlyphs); + ////////////////////////////////////////////////////////////////////// // shader module ////////////////////////////////////////////////////////////////////// @@ -78,13 +154,32 @@ class FontRenderer { buffer: { type: "uniform" } }] }), - glyph: device.createBindGroupLayout({ - label: `${label} bind group layout glyph`, - entries: [{ // glyphs + perFont: device.createBindGroupLayout({ + label: `${label} bind group per-font`, + entries: [ + { // sampler + binding: 0, + visibility: GPUShaderStage.FRAGMENT, + sampler: {} + }, { // texture + binding: 1, + visibility: GPUShaderStage.FRAGMENT, + texture: {}, + }, { // glyph buffer + binding: 2, + visibility: GPUShaderStage.FRAGMENT, + buffer: { type: "read-only-storage" } + } + ] + }), + perFrame: device.createBindGroupLayout({ + label: `${label} bind group per-frame`, + entries: [{ // layout buffer binding: 0, - visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT, + visibility: GPUShaderStage.VERTEX, buffer: { type: "read-only-storage" } - }/*, { // configuration + } + /*, { // configuration binding: 1, visibility: GPUShaderStage.FRAGMENT, buffer: { type: "uniform" } @@ -96,7 +191,8 @@ class FontRenderer { label: `${label} pipeline layout`, bindGroupLayouts: [ this.bindGroupLayout.view, - this.bindGroupLayout.glyph + this.bindGroupLayout.perFont, + this.bindGroupLayout.perFrame, ], }); @@ -149,7 +245,8 @@ class FontRenderer { renderPass.setPipeline(this.renderPipeline); renderPass.setIndexBuffer(this.buffer, "uint16"); renderPass.setBindGroup(0, this.viewBindGroup); - renderPass.setBindGroup(1, this.frames[frameNumber].bindGroup); + renderPass.setBindGroup(1, this.perFontBindGroup); + renderPass.setBindGroup(2, this.frames[frameNumber].bindGroup); renderPass.drawIndexed(6); } } @@ -158,7 +255,10 @@ async function loadFont(device, canvasFormat, viewUniformBuffer, module) { const fontWgsl = await getPath("font.wgsl"); - const renderer = new FontRenderer(device, canvasFormat, viewUniformBuffer, fontWgsl, module); + const liberationResponse = await fetch("liberation.data"); + const liberationBuffer = await liberationResponse.arrayBuffer(); + + const renderer = new FontRenderer(device, canvasFormat, viewUniformBuffer, fontWgsl, module, liberationBuffer); return renderer; } diff --git a/font.wgsl b/font.wgsl index e90427c..40b4d5b 100644 --- a/font.wgsl +++ b/font.wgsl @@ -6,13 +6,22 @@ struct Configuration { aspect: f32, }; -struct GlyphBitmap { +struct GlyphBuffer { position: vec2f, - velocity: vec2f, + size: vec2f, +}; + +struct LayoutBuffer { + position: vec4f, // glyph_index w }; @group(0) @binding(0) var config: Configuration; -@group(1) @binding(0) var glyphs: array; + +@group(1) @binding(0) var linearSampler: sampler; +@group(1) @binding(1) var fontTexture: texture_2d; +@group(1) @binding(2) var glyphBuffer: array; + +@group(2) @binding(0) var layoutBuffer: array; struct VertexInput { @builtin(vertex_index) vertex_index: u32, @@ -35,11 +44,11 @@ fn vertexMain(input: VertexInput) -> VertexOutput { let texture = vertices[input.vertex_index]; - let position = texture * 2.0 - 1.0; + let position = vec2f(texture.x, 1.0 - texture.y) * 2.0 - 1.0; var output: VertexOutput; - output.position = vec4f(position, 0.0, 1.0); - output.texture = position * vec2f(config.aspect, 1); + output.position = vec4f(position * 0.5, 0.0, 1.0); + output.texture = texture; return output; } @@ -48,5 +57,12 @@ fn fragmentMain(input: VertexOutput) -> @location(0) vec4f { var color = vec3f(input.texture.xy, 0); - return vec4f(color, 1); + let p = glyphBuffer[66].position; + let s = glyphBuffer[66].size; + + let base = textureSample(fontTexture, linearSampler, input.texture * s + p); + + let t = f32(base.x > 0.5); + + return vec4f(t, t, t, 1); } diff --git a/src/Makefile b/src/Makefile index 36abffb..5f78d2b 100644 --- a/src/Makefile +++ b/src/Makefile @@ -47,11 +47,13 @@ LDFLAGS = \ -Wl,--export=font_layout__texture_address \ -Wl,--export=font_layout__glyph_buffer_size \ -Wl,--export=font_layout__glyph_buffer_address \ - -Wl,--export=font_layout__texture_size \ + -Wl,--export=font_layout__texture_width \ + -Wl,--export=font_layout__texture_height \ -Wl,--export=font_layout__texture_address \ -Wl,--export=font_layout__layout_buffer_size \ -Wl,--export=font_layout__layout_buffer_address \ -Wl,--export=font_layout__layout_buffer_index \ + -Wl,--export=font_layout__draw \ -Wl,--import-undefined \ -Wl,--print-map \ -Wl,--import-memory \ diff --git a/src/font_layout/api.cpp b/src/font_layout/api.cpp index 123009b..3bf8b03 100644 --- a/src/font_layout/api.cpp +++ b/src/font_layout/api.cpp @@ -19,10 +19,14 @@ extern "C" { return font_layout->glyph_buffer; } - size_t font_layout__texture_size(FontLayout * font_layout) + size_t font_layout__texture_width(FontLayout * font_layout) { - int const bytes_per_pixel = 1; - return font_layout->font->texture_width * font_layout->font->texture_width * bytes_per_pixel; + return font_layout->font->texture_width; + } + + size_t font_layout__texture_height(FontLayout * font_layout) + { + return font_layout->font->texture_height; } void * font_layout__texture_address(FontLayout * font_layout) @@ -44,4 +48,9 @@ extern "C" { { return font_layout->layout_buffer_index; } + + void font_layout__draw(FontLayout * font_layout) + { + font_layout->draw_string("@"); + } }; diff --git a/src/font_layout/font_layout.cpp b/src/font_layout/font_layout.cpp index 395efac..99400c8 100644 --- a/src/font_layout/font_layout.cpp +++ b/src/font_layout/font_layout.cpp @@ -3,7 +3,7 @@ static inline size_t texture_offset(font * font) { - return (sizeof (font)) * (sizeof (glyph)) * font->glyph_count; + return (sizeof (struct font)) + (sizeof (struct glyph)) * font->glyph_count; } void FontLayout::init(size_t buffer, int layout_buffer_length) @@ -26,3 +26,26 @@ void FontLayout::init(size_t buffer, int layout_buffer_length) this->glyph_buffer[i].size.y = float(bitmap.height) / float(this->font->texture_height); } } + +void FontLayout::draw_string(char const * string) +{ + int i = 0; + while (true) { + char c = string[i]; + if (c == 0) + return; + + if (c < font->first_char_code || c > font->last_char_code) { + continue; + } + int char_index = c - font->first_char_code; + glyph const & glyph = glyphs[char_index]; + + if (layout_buffer_index >= layout_buffer_length) + return; + + layout_buffer[layout_buffer_index].position = XMFLOAT3(0, 0, 0); + layout_buffer[layout_buffer_index].glyph_index = char_index; + layout_buffer_index += 1; + } +} diff --git a/src/font_layout/font_layout.h b/src/font_layout/font_layout.h index 0cf8cf3..2a308fb 100644 --- a/src/font_layout/font_layout.h +++ b/src/font_layout/font_layout.h @@ -23,4 +23,5 @@ struct FontLayout { LayoutBuffer * layout_buffer; void init(size_t buffer, int layout_buffer_length); + void draw_string(char const * string); }; diff --git a/tools/ttf_outline/ttf_outline.cpp b/tools/ttf_outline/ttf_outline.cpp index 9177987..3d00522 100644 --- a/tools/ttf_outline/ttf_outline.cpp +++ b/tools/ttf_outline/ttf_outline.cpp @@ -293,7 +293,7 @@ int main(int argc, char *argv[]) fwrite(reinterpret_cast(&font), (sizeof (font)), 1, out); fwrite(reinterpret_cast(&glyphs[0]), (sizeof (glyph)), num_glyphs, out); - fwrite(reinterpret_cast(&texture[0]), (sizeof (uint8_t)), texture_size, out); + //fwrite(reinterpret_cast(&texture[0]), (sizeof (uint8_t)), texture_size, out); fclose(out); }