This commit is contained in:
agra
2026-03-04 17:17:29 +02:00
parent 343ea4bf08
commit 0782353ffa
12 changed files with 1288 additions and 131 deletions

View File

@@ -25,6 +25,7 @@ UIRenderer :: struct {
dpi_scale: f32;
white_texture: u32;
current_texture: u32;
draw_calls: s64;
init :: (self: *UIRenderer) {
// Create shader (ES for WASM/WebGL2, Core for desktop)
@@ -70,11 +71,22 @@ UIRenderer :: struct {
self.white_texture = create_white_texture();
}
begin :: (self: *UIRenderer, width: f32, height: f32) {
begin :: (self: *UIRenderer, width: f32, height: f32, font_texture: u32) {
self.screen_width = width;
self.screen_height = height;
self.vertex_count = 0;
self.current_texture = self.white_texture;
self.current_texture = font_texture;
self.draw_calls = 0;
// Set up GL state once for the entire frame
glUseProgram(self.shader);
proj := Mat4.ortho(0.0, width, height, 0.0, -1.0, 1.0);
glUniformMatrix4fv(self.proj_loc, 1, 0, proj.data);
glUniform1i(self.tex_loc, 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, font_texture);
glBindVertexArray(self.vao);
glBindBuffer(GL_ARRAY_BUFFER, self.vbo);
}
bind_texture :: (self: *UIRenderer, tex: u32) {
@@ -148,8 +160,13 @@ UIRenderer :: struct {
}
case .image: {
self.bind_texture(node.texture_id);
self.push_quad(node.frame, COLOR_WHITE, 0.0, 0.0);
self.bind_texture(self.white_texture);
neg2 : f32 = 0.0 - 2.0;
self.push_quad(node.frame, COLOR_WHITE, neg2, 0.0);
// Re-bind font atlas after image
font := g_font;
if xx font != 0 {
self.bind_texture(font.texture_id);
}
}
case .clip_push: {
self.flush();
@@ -176,27 +193,16 @@ UIRenderer :: struct {
flush :: (self: *UIRenderer) {
if self.vertex_count == 0 { return; }
glUseProgram(self.shader);
// Orthographic projection: (0,0) top-left, (w,h) bottom-right
proj := Mat4.ortho(0.0, self.screen_width, self.screen_height, 0.0, -1.0, 1.0);
glUniformMatrix4fv(self.proj_loc, 1, 0, proj.data);
// Bind current texture
glActiveTexture(GL_TEXTURE0);
// Only bind the current texture (program, projection, VAO already bound in begin())
glBindTexture(GL_TEXTURE_2D, self.current_texture);
glUniform1i(self.tex_loc, 0);
glBindVertexArray(self.vao);
glBindBuffer(GL_ARRAY_BUFFER, self.vbo);
upload_size : s64 = self.vertex_count * UI_VERTEX_BYTES;
glBufferSubData(GL_ARRAY_BUFFER, 0, xx upload_size, self.vertices);
// Use glBufferData to orphan the old buffer and avoid GPU sync stalls
glBufferData(GL_ARRAY_BUFFER, xx upload_size, self.vertices, GL_DYNAMIC_DRAW);
glDrawArrays(GL_TRIANGLES, 0, xx self.vertex_count);
glBindVertexArray(0);
self.vertex_count = 0;
self.draw_calls += 1;
}
render_text :: (self: *UIRenderer, node: RenderNode) {
@@ -206,9 +212,8 @@ UIRenderer :: struct {
// Shape text into positioned glyphs
font.shape_text(node.text, node.font_size);
// Flush any new glyphs to the atlas texture before rendering
// Flush any new glyphs to the atlas texture (no texture switch needed — atlas is already bound)
font.flush();
self.bind_texture(font.texture_id);
r := node.text_color.rf();
g := node.text_color.gf();
@@ -256,7 +261,6 @@ UIRenderer :: struct {
// Flush any glyphs rasterized during this text draw
font.flush();
self.bind_texture(self.white_texture);
}
}
@@ -312,33 +316,37 @@ float roundedBoxSDF(vec2 center, vec2 half_size, float radius) {
}
void main() {
float radius = vParams.x;
float mode = vParams.x;
float border = vParams.y;
vec2 rectSize = vParams.zw;
if (radius < 0.0) {
if (mode < -1.5) {
// Image mode (mode == -2.0): sample texture
FragColor = texture(uTex, vUV) * vColor;
} else if (mode < 0.0) {
// Text mode (mode == -1.0): sample glyph atlas .r as alpha
float alpha = texture(uTex, vUV).r;
float ew = fwidth(alpha) * 0.7;
alpha = smoothstep(0.5 - ew, 0.5 + ew, alpha);
FragColor = vec4(vColor.rgb, vColor.a * pow(alpha, 0.9));
} else if (radius > 0.0 || border > 0.0) {
vec4 texColor = texture(uTex, vUV);
} else if (mode > 0.0 || border > 0.0) {
// Rounded rect: SDF alpha, vertex color only (no texture sample)
vec2 half_size = rectSize * 0.5;
vec2 center = (vUV - vec2(0.5)) * rectSize;
float dist = roundedBoxSDF(center, half_size, radius);
float dist = roundedBoxSDF(center, half_size, mode);
float aa = fwidth(dist);
float alpha = 1.0 - smoothstep(-aa, aa, dist);
if (border > 0.0) {
float inner = roundedBoxSDF(center, half_size - vec2(border), max(radius - border, 0.0));
float inner = roundedBoxSDF(center, half_size - vec2(border), max(mode - border, 0.0));
float border_alpha = smoothstep(-aa, aa, inner);
alpha = alpha * max(border_alpha, 0.0);
}
FragColor = vec4(texColor.rgb * vColor.rgb, texColor.a * vColor.a * alpha);
FragColor = vec4(vColor.rgb, vColor.a * alpha);
} else {
vec4 texColor = texture(uTex, vUV);
FragColor = texColor * vColor;
// Plain rect: vertex color only (no texture sample)
FragColor = vColor;
}
}
GLSL;
@@ -384,33 +392,37 @@ float roundedBoxSDF(vec2 center, vec2 half_size, float radius) {
}
void main() {
float radius = vParams.x;
float mode = vParams.x;
float border = vParams.y;
vec2 rectSize = vParams.zw;
if (radius < 0.0) {
if (mode < -1.5) {
// Image mode (mode == -2.0): sample texture
FragColor = texture(uTex, vUV) * vColor;
} else if (mode < 0.0) {
// Text mode (mode == -1.0): sample glyph atlas .r as alpha
float alpha = texture(uTex, vUV).r;
float ew = fwidth(alpha) * 0.7;
alpha = smoothstep(0.5 - ew, 0.5 + ew, alpha);
FragColor = vec4(vColor.rgb, vColor.a * pow(alpha, 0.9));
} else if (radius > 0.0 || border > 0.0) {
vec4 texColor = texture(uTex, vUV);
} else if (mode > 0.0 || border > 0.0) {
// Rounded rect: SDF alpha, vertex color only
vec2 half_size = rectSize * 0.5;
vec2 center = (vUV - vec2(0.5)) * rectSize;
float dist = roundedBoxSDF(center, half_size, radius);
float dist = roundedBoxSDF(center, half_size, mode);
float aa = fwidth(dist);
float alpha = 1.0 - smoothstep(-aa, aa, dist);
if (border > 0.0) {
float inner = roundedBoxSDF(center, half_size - vec2(border), max(radius - border, 0.0));
float inner = roundedBoxSDF(center, half_size - vec2(border), max(mode - border, 0.0));
float border_alpha = smoothstep(-aa, aa, inner);
alpha = alpha * max(border_alpha, 0.0);
}
FragColor = vec4(texColor.rgb * vColor.rgb, texColor.a * vColor.a * alpha);
FragColor = vec4(vColor.rgb, vColor.a * alpha);
} else {
vec4 texColor = texture(uTex, vUV);
FragColor = texColor * vColor;
// Plain rect: vertex color only
FragColor = vColor;
}
}
GLSL;