draw sdf glyph
This commit is contained in:
parent
1d76808f02
commit
71052ffb69
2
.gitignore
vendored
2
.gitignore
vendored
@ -5,3 +5,5 @@ __pycache__
|
|||||||
*.data
|
*.data
|
||||||
.gdb_history
|
.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 {
|
class FontRenderer {
|
||||||
createBuffers(device)
|
createBuffers(device)
|
||||||
{
|
{
|
||||||
this.maxGlyphs = 1024;
|
|
||||||
this.glyphBufferSize = 4 * this.maxGlyphs;
|
|
||||||
|
|
||||||
this.frames = [];
|
this.frames = [];
|
||||||
for (let i = 0; i < 2; i++) {
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// glyph
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
const glyphBufferSize = this.module.instance.exports.font_layout__glyph_buffer_size(this.fontLayoutAddress);
|
||||||
const glyphBuffer = device.createBuffer({
|
const glyphBuffer = device.createBuffer({
|
||||||
label: `glyph buffer ${i}`,
|
label: `glyph buffer`,
|
||||||
size: this.glyphBufferSize,
|
size: glyphBufferSize,
|
||||||
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
|
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
|
||||||
});
|
});
|
||||||
|
|
||||||
const bindGroup = device.createBindGroup({
|
const glyphBufferAddress = this.module.instance.exports.font_layout__glyph_buffer_address(this.fontLayoutAddress);
|
||||||
label: `font bind group ${i}`,
|
device.queue.writeBuffer(glyphBuffer, 0,
|
||||||
layout: this.bindGroupLayout.glyph,
|
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: [{
|
entries: [{
|
||||||
binding: 0,
|
binding: 0,
|
||||||
|
resource: sampler,
|
||||||
|
}, {
|
||||||
|
binding: 1,
|
||||||
|
resource: texture,
|
||||||
|
}, {
|
||||||
|
binding: 2,
|
||||||
resource: { buffer: glyphBuffer },
|
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 layoutBuffer = device.createBuffer({
|
||||||
|
label: `layout buffer ${i}`,
|
||||||
|
size: layoutBufferSize,
|
||||||
|
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
|
||||||
|
});
|
||||||
|
|
||||||
|
const bindGroup = device.createBindGroup({
|
||||||
|
label: `font per-frame bind group ${i}`,
|
||||||
|
layout: this.bindGroupLayout.perFrame,
|
||||||
|
entries: [{
|
||||||
|
binding: 0,
|
||||||
|
resource: { buffer: layoutBuffer },
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
|
||||||
this.frames.push({
|
this.frames.push({
|
||||||
glyphBuffer: glyphBuffer,
|
layoutBuffer: layoutBuffer,
|
||||||
bindGroup: bindGroup,
|
bindGroup: bindGroup,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(device, canvasFormat, viewUniformBuffer, wgsl, module)
|
constructor(device, canvasFormat, viewUniformBuffer, wgsl, module, fontBufferSrc)
|
||||||
{
|
{
|
||||||
const label = "font";
|
const label = "font";
|
||||||
|
|
||||||
this.module = module;
|
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
|
// shader module
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
@ -78,13 +154,32 @@ class FontRenderer {
|
|||||||
buffer: { type: "uniform" }
|
buffer: { type: "uniform" }
|
||||||
}]
|
}]
|
||||||
}),
|
}),
|
||||||
glyph: device.createBindGroupLayout({
|
perFont: device.createBindGroupLayout({
|
||||||
label: `${label} bind group layout glyph`,
|
label: `${label} bind group per-font`,
|
||||||
entries: [{ // glyphs
|
entries: [
|
||||||
|
{ // sampler
|
||||||
binding: 0,
|
binding: 0,
|
||||||
visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
|
visibility: GPUShaderStage.FRAGMENT,
|
||||||
|
sampler: {}
|
||||||
|
}, { // texture
|
||||||
|
binding: 1,
|
||||||
|
visibility: GPUShaderStage.FRAGMENT,
|
||||||
|
texture: {},
|
||||||
|
}, { // glyph buffer
|
||||||
|
binding: 2,
|
||||||
|
visibility: GPUShaderStage.FRAGMENT,
|
||||||
buffer: { type: "read-only-storage" }
|
buffer: { type: "read-only-storage" }
|
||||||
}/*, { // configuration
|
}
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
perFrame: device.createBindGroupLayout({
|
||||||
|
label: `${label} bind group per-frame`,
|
||||||
|
entries: [{ // layout buffer
|
||||||
|
binding: 0,
|
||||||
|
visibility: GPUShaderStage.VERTEX,
|
||||||
|
buffer: { type: "read-only-storage" }
|
||||||
|
}
|
||||||
|
/*, { // configuration
|
||||||
binding: 1,
|
binding: 1,
|
||||||
visibility: GPUShaderStage.FRAGMENT,
|
visibility: GPUShaderStage.FRAGMENT,
|
||||||
buffer: { type: "uniform" }
|
buffer: { type: "uniform" }
|
||||||
@ -96,7 +191,8 @@ class FontRenderer {
|
|||||||
label: `${label} pipeline layout`,
|
label: `${label} pipeline layout`,
|
||||||
bindGroupLayouts: [
|
bindGroupLayouts: [
|
||||||
this.bindGroupLayout.view,
|
this.bindGroupLayout.view,
|
||||||
this.bindGroupLayout.glyph
|
this.bindGroupLayout.perFont,
|
||||||
|
this.bindGroupLayout.perFrame,
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -149,7 +245,8 @@ class FontRenderer {
|
|||||||
renderPass.setPipeline(this.renderPipeline);
|
renderPass.setPipeline(this.renderPipeline);
|
||||||
renderPass.setIndexBuffer(this.buffer, "uint16");
|
renderPass.setIndexBuffer(this.buffer, "uint16");
|
||||||
renderPass.setBindGroup(0, this.viewBindGroup);
|
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);
|
renderPass.drawIndexed(6);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -158,7 +255,10 @@ async function loadFont(device, canvasFormat, viewUniformBuffer, module)
|
|||||||
{
|
{
|
||||||
const fontWgsl = await getPath("font.wgsl");
|
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;
|
return renderer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
30
font.wgsl
30
font.wgsl
@ -6,13 +6,22 @@ struct Configuration {
|
|||||||
aspect: f32,
|
aspect: f32,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GlyphBitmap {
|
struct GlyphBuffer {
|
||||||
position: vec2f,
|
position: vec2f,
|
||||||
velocity: vec2f,
|
size: vec2f,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LayoutBuffer {
|
||||||
|
position: vec4f, // glyph_index w
|
||||||
};
|
};
|
||||||
|
|
||||||
@group(0) @binding(0) var<uniform> config: Configuration;
|
@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 {
|
struct VertexInput {
|
||||||
@builtin(vertex_index) vertex_index: u32,
|
@builtin(vertex_index) vertex_index: u32,
|
||||||
@ -35,11 +44,11 @@ fn vertexMain(input: VertexInput) -> VertexOutput
|
|||||||
{
|
{
|
||||||
let texture = vertices[input.vertex_index];
|
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;
|
var output: VertexOutput;
|
||||||
output.position = vec4f(position, 0.0, 1.0);
|
output.position = vec4f(position * 0.5, 0.0, 1.0);
|
||||||
output.texture = position * vec2f(config.aspect, 1);
|
output.texture = texture;
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,5 +57,12 @@ fn fragmentMain(input: VertexOutput) -> @location(0) vec4f
|
|||||||
{
|
{
|
||||||
var color = vec3f(input.texture.xy, 0);
|
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__texture_address \
|
||||||
-Wl,--export=font_layout__glyph_buffer_size \
|
-Wl,--export=font_layout__glyph_buffer_size \
|
||||||
-Wl,--export=font_layout__glyph_buffer_address \
|
-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__texture_address \
|
||||||
-Wl,--export=font_layout__layout_buffer_size \
|
-Wl,--export=font_layout__layout_buffer_size \
|
||||||
-Wl,--export=font_layout__layout_buffer_address \
|
-Wl,--export=font_layout__layout_buffer_address \
|
||||||
-Wl,--export=font_layout__layout_buffer_index \
|
-Wl,--export=font_layout__layout_buffer_index \
|
||||||
|
-Wl,--export=font_layout__draw \
|
||||||
-Wl,--import-undefined \
|
-Wl,--import-undefined \
|
||||||
-Wl,--print-map \
|
-Wl,--print-map \
|
||||||
-Wl,--import-memory \
|
-Wl,--import-memory \
|
||||||
|
|||||||
@ -19,10 +19,14 @@ extern "C" {
|
|||||||
return font_layout->glyph_buffer;
|
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;
|
||||||
return font_layout->font->texture_width * font_layout->font->texture_width * bytes_per_pixel;
|
}
|
||||||
|
|
||||||
|
size_t font_layout__texture_height(FontLayout * font_layout)
|
||||||
|
{
|
||||||
|
return font_layout->font->texture_height;
|
||||||
}
|
}
|
||||||
|
|
||||||
void * font_layout__texture_address(FontLayout * font_layout)
|
void * font_layout__texture_address(FontLayout * font_layout)
|
||||||
@ -44,4 +48,9 @@ extern "C" {
|
|||||||
{
|
{
|
||||||
return font_layout->layout_buffer_index;
|
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)
|
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)
|
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);
|
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;
|
LayoutBuffer * layout_buffer;
|
||||||
|
|
||||||
void init(size_t buffer, int layout_buffer_length);
|
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*>(&font), (sizeof (font)), 1, out);
|
||||||
fwrite(reinterpret_cast<void*>(&glyphs[0]), (sizeof (glyph)), num_glyphs, 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);
|
fclose(out);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user