Skip to content

Commit

Permalink
Font rendering (used to render point numbers)
Browse files Browse the repository at this point in the history
  • Loading branch information
mhochsteger committed Oct 29, 2024
1 parent d2550ee commit f67b8ec
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 5 deletions.
1 change: 1 addition & 0 deletions dev.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ def main():
observer.stop()
if _have_dev_dependencies:
observer.join()
http_thread.join()


if __name__ == "__main__":
Expand Down
2 changes: 2 additions & 0 deletions init.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ const files = [
"colormap.py",
"compute.wgsl",
"eval.wgsl",
"font.py",
"fonts.json",
"gpu.py",
"input_handler.py",
"main.py",
Expand Down
10 changes: 8 additions & 2 deletions webgpu/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ async def main():

gpu = await init_webgpu(js.document.getElementById("canvas"))

if 1:
point_number_object = None

if 0:
# create new ngsolve mesh and evaluate arbitrary function on it
mesh = ngs.Mesh(unit_square.GenerateMesh(maxh=0.5))
order = 6
Expand All @@ -36,7 +38,7 @@ async def main():
# create testing mesh, this one also supports indexed or deferred rendering
# but has always P1 and 'x' hard-coded as function
query = urllib.parse.parse_qs(js.location.search[1:])
N = 100
N = 10
# N = int(5000/2**.5)
# N = int(2000 / 2**0.5)
# N = int(50/2**.5)
Expand All @@ -49,6 +51,7 @@ async def main():
# mesh_object = MeshRenderObjectIndexed(gpu, buffers, n_trigs)
# mesh_object = MeshRenderObjectDeferred(gpu, buffers, n_trigs)

point_number_object = PointNumbersRenderObject(gpu, buffers, font_size=16)
# move mesh to center and scale it
for i in [0, 5, 10]:
gpu.uniforms.mat[i] = 1.8
Expand Down Expand Up @@ -87,6 +90,9 @@ def render(time):

mesh_object.render(command_encoder)

if point_number_object is not None:
point_number_object.render(command_encoder)

gpu.device.queue.submit([command_encoder.finish()])
if frame_counter < 20:
js.requestAnimationFrame(render_function)
Expand Down
86 changes: 86 additions & 0 deletions webgpu/mesh.py
Original file line number Diff line number Diff line change
Expand Up @@ -508,3 +508,89 @@ def create_testing_square_mesh(gpu, n):
gpu.device.queue.submit([command_encoder.finish()])

return n_trigs, buffers


class PointNumbersRenderObject:
"""Render a point numbers of a mesh"""

def __init__(self, gpu, buffers, font_size=20):

self._buffers = buffers
self.gpu = gpu
self.device = Device(gpu.device)
self.n_verts = self._buffers["vertices"].size // (4 * 3)

self.set_font_size(font_size)
self.n_digits = 6

def get_bindings(self):
return [
*self.gpu.uniforms.get_bindings(),
TextureBinding(Binding.FONT_TEXTURE, self.texture, dim=2),
BufferBinding(Binding.VERTICES, self._buffers["vertices"]),
]

def _create_pipeline(self):
bind_layout, self._bind_group = self.device.create_bind_group(
self.get_bindings(), "PointNumbersRenderObject"
)
pipeline_layout = self.device.create_pipeline_layout(bind_layout)
shader_module = self.device.compile_files(
"webgpu/shader.wgsl", "webgpu/eval.wgsl"
)
self._pipeline = self.gpu.device.createRenderPipeline(
to_js(
{
"label": "PointNumbersRenderObject",
"layout": pipeline_layout,
"vertex": {
"module": shader_module,
"entryPoint": "mainVertexPointNumber",
},
"fragment": {
"module": shader_module,
"entryPoint": "mainFragmentText",
"targets": [
{
"format": self.gpu.format,
"blend": {
"color": {
"operation": "add",
"srcFactor": "one",
"dstFactor": "one-minus-src-alpha",
},
"alpha": {
"operation": "add",
"srcFactor": "one",
"dstFactor": "one-minus-src-alpha",
},
},
}
],
},
"primitive": {
"topology": "triangle-list",
"cullMode": "none",
"frontFace": "ccw",
},
"depthStencil": self.gpu.depth_stencil,
}
)
)

def render(self, encoder):
render_pass = self.gpu.begin_render_pass(encoder)
render_pass.setBindGroup(0, self._bind_group)
render_pass.setPipeline(self._pipeline)
render_pass.draw(self.n_digits * 6, self.n_verts, 0, 0)
render_pass.end()

def set_font_size(self, font_size: int):
from .font import create_font_texture

self.texture = create_font_texture(self.gpu.device, font_size)
char_width = self.texture.width // (127 - 32)
char_height = self.texture.height
self.gpu.uniforms.font_width = char_width
self.gpu.uniforms.font_height = char_height
self._create_pipeline()
78 changes: 76 additions & 2 deletions webgpu/shader.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ struct Uniforms {
aspect: f32,
eval_mode: u32,
do_clipping: u32,
padding: u32,
font_width: u32,
font_height: u32,
padding0: u32,
padding1: u32,
padding2: u32,
};

const VALUES_OFFSET: u32 = 2; // storing number of components and order of basis functions in first two entries
Expand All @@ -28,7 +32,7 @@ const VALUES_OFFSET: u32 = 2; // storing number of components and order of basis
@group(0) @binding(9) var<storage> index : array<u32>;

@group(0) @binding(10) var gBufferLam : texture_2d<f32>;
// @group(0) @binding(11) var gBufferDepth : texture_depth_2d;
@group(0) @binding(11) var font : texture_2d<f32>;

struct VertexOutput1d {
@builtin(position) fragPosition: vec4<f32>,
Expand Down Expand Up @@ -178,3 +182,73 @@ fn mainVertexDeferred(@builtin(vertex_index) vertexId: u32) -> VertexOutputDefer
}


struct FragmentTextInput {
@builtin(position) fragPosition: vec4<f32>,
@location(0) tex_coord: vec2<f32>,
};

@vertex
fn mainVertexPointNumber(@builtin(vertex_index) vertexId: u32, @builtin(instance_index) pointId: u32) -> FragmentTextInput {
var p = vec3<f32>(vertices[3 * pointId], vertices[3 * pointId + 1], vertices[3 * pointId + 2]);
if uniforms.do_clipping != 0 {
if dot(uniforms.clipping_plane, vec4<f32>(p, 1.0)) < 0 {
return FragmentTextInput(vec4<f32>(-1.0, -1.0, 0.0, 1.0), vec2<f32>(0.));
}
}

var position = calcPosition(p);
let i_digit = vertexId / 6;
let vi = vertexId % 6;

var length = 1u;
var n = 10u;
while n <= pointId + 1 {
length++;
n *= 10u;
}

if i_digit >= length {
return FragmentTextInput(vec4<f32>(-1.0, -1.0, 0.0, 1.0), vec2<f32>(0.));
}

var digit = pointId + 1;
for (var i = 0u; i < i_digit; i++) {
digit = digit / 10;
}
digit = digit % 10;

let w: f32 = 2 * f32(uniforms.font_width) / 1000.;
let h: f32 = 2 * f32(uniforms.font_height) / 800.;

var tex_coord = vec2<f32>(
f32((digit + 16) * uniforms.font_width),
f32(uniforms.font_height)
);

if vi == 2 || vi == 4 || vi == 5 {
position.y += h * position.w;
tex_coord.y = 0.0;
}

position.x += f32(length - i_digit -1) * w * position.w;

if vi == 1 || vi == 2 || vi == 4 {
position.x += w * position.w;
tex_coord.x += f32(uniforms.font_width);
}

return FragmentTextInput(position, tex_coord);
}

@fragment
fn mainFragmentText(@location(0) tex_coord: vec2<f32>) -> @location(0) vec4<f32> {
let alpha: f32 = textureLoad(
font,
vec2i(floor(tex_coord)),
0
).x;
if alpha < 0.01 {
discard;
}
return vec4(0., 0., 0., alpha);
}
7 changes: 6 additions & 1 deletion webgpu/uniforms.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class Binding:
VERTICES = 8
INDEX = 9
GBUFFERLAM = 10
FONT_TEXTURE = 11


class ClippingPlaneUniform(ct.Structure):
Expand All @@ -42,7 +43,11 @@ class Uniforms(ct.Structure):
("aspect", ct.c_float),
("eval_mode", ct.c_uint32),
("do_clipping", ct.c_uint32),
("padding", ct.c_uint32),
("font_width", ct.c_uint32),
("font_height", ct.c_uint32),
("padding0", ct.c_uint32),
("padding1", ct.c_uint32),
("padding2", ct.c_uint32),
]

def __init__(self, device):
Expand Down

0 comments on commit f67b8ec

Please sign in to comment.