draw sdf glyph
This commit is contained in:
parent
1d76808f02
commit
71052ffb69
4
.gitignore
vendored
4
.gitignore
vendored
@ -4,4 +4,6 @@ __pycache__
|
||||
*.o
|
||||
*.data
|
||||
.gdb_history
|
||||
*.elf
|
||||
*.elf
|
||||
favicon.ico
|
||||
tools/ttf_outline/ttf_outline
|
||||
138
font.js
138
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;
|
||||
}
|
||||
|
||||
|
||||
30
font.wgsl
30
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<uniform> config: Configuration;
|
||||
@group(1) @binding(0) var<storage> glyphs: array<GlyphBitmap>;
|
||||
|
||||
@group(1) @binding(0) var linearSampler: sampler;
|
||||
@group(1) @binding(1) var fontTexture: texture_2d<f32>;
|
||||
@group(1) @binding(2) var<storage> glyphBuffer: array<GlyphBuffer>;
|
||||
|
||||
@group(2) @binding(0) var<storage> layoutBuffer: array<LayoutBuffer>;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@ -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 \
|
||||
|
||||
@ -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("@");
|
||||
}
|
||||
};
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,4 +23,5 @@ struct FontLayout {
|
||||
LayoutBuffer * layout_buffer;
|
||||
|
||||
void init(size_t buffer, int layout_buffer_length);
|
||||
void draw_string(char const * string);
|
||||
};
|
||||
|
||||
@ -293,7 +293,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
fwrite(reinterpret_cast<void*>(&font), (sizeof (font)), 1, out);
|
||||
fwrite(reinterpret_cast<void*>(&glyphs[0]), (sizeof (glyph)), num_glyphs, out);
|
||||
fwrite(reinterpret_cast<void*>(&texture[0]), (sizeof (uint8_t)), texture_size, out);
|
||||
//fwrite(reinterpret_cast<void*>(&texture[0]), (sizeof (uint8_t)), texture_size, out);
|
||||
|
||||
fclose(out);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user