azrp: first version of text shader (slow!), based on topti-cg

This commit is contained in:
Lephenixnoir 2023-06-12 19:00:28 +02:00
parent 2e7c076e60
commit 2f185a36d7
Signed by: Lephenixnoir
GPG Key ID: 1BBA026E13FC0495
5 changed files with 261 additions and 2 deletions

View File

@ -75,7 +75,10 @@ if(AZUR_GRAPHICS_GINT_CG)
src/gint/shaders/triangle.S
# Rectangle shader
src/gint/shaders/rect.c
src/gint/shaders/rect.S)
src/gint/shaders/rect.S
# Text shader
src/gint/shaders/text.c
src/gint/shaders/text.S)
endif()
add_library(azur STATIC ${SOURCES})

View File

@ -239,6 +239,14 @@ enum {
AZRP_RECT_WHITEN = -3,
};
/* azrp_text(): Render a string of text, like dtext(). */
void azrp_text(int x, int y, font_t const *f, char const *str, int fg,
int size);
/* azrp_text_opt(): Render text with options similar to dtext_opt(). */
void azrp_text_opt(int x, int y, font_t const *font, int fg, int halign,
int valign, char const *str, int size);
//---
// Performance indicators
//

View File

@ -1,6 +1,6 @@
#include <azur/gint/render.h>
uint AZRP_SHADER_RECT = -1;
uint8_t AZRP_SHADER_RECT = -1;
static void configure(void)
{

View File

@ -0,0 +1,83 @@
.global _azrp_text_glyph
/* Naive glyph rendering. Possible optimizations:
- Use a 1-bit bitmask instead of a longword index? */
/* Parameters:
r4: fragment
r5: data
r6: color
r7: height
@(4,r15): width
@(8,r15): dataw - width (stride)
@(12,r15): starting index in data
Stack:
@(0,r15): r8 save
Register allocation:
r0: (temporary)
r1: (temporary)
r2: x counter
r3: glyph data index
r4: fragment pointer
r5: glyph pointer
r6: color
r7: y counter
Callee-saved registers:
r8: fragment stride */
_azrp_text_glyph:
/* Compute fragment stride: 2 * (azrp_width-width) */
mov.l r8, @-r15
mov.l 1f, r8
mov.l @r8, r8
mov.l @(4, r15), r3
sub r3, r8
shll r8
/* Load the starting index */
mov.l @(12, r15), r3
.fg_y:
/* Initialize width counter */
mov.l @(4, r15), r2
.fg_x:
/* Load one bit of data in T */
mov r3, r0
mov #-5, r1
shld r1, r0
shll2 r0
mov.l @(r0, r5), r1
mov r3, r0
and #31, r0
shld r0, r1
shll r1
/* Write color to fragment only if it's a 1 bit */
bf .fg_next
mov.w r6, @r4
.fg_next:
/* Leave the x-loop if x counter reaches 0 */
add #2, r4
dt r2
bf/s .fg_x
add #1, r3
/* Move to next row, leave the y-loop if height reaches 0 */
dt r7
mov.l @(8, r15), r0
add r0, r3
bf/s .fg_y
add r8, r4
rts
mov.l @r15+, r8
.align 4
1: .long _azrp_width

View File

@ -0,0 +1,165 @@
#include <azur/gint/render.h>
#include <gint/defs/util.h>
#include <gint/display.h>
#include <string.h>
uint8_t AZRP_SHADER_TEXT = -1;
__attribute__((constructor))
static void register_shader(void)
{
extern azrp_shader_t azrp_shader_text;
AZRP_SHADER_TEXT = azrp_register_shader(azrp_shader_text, NULL);
}
//---
/* This version of the text shader is a very rough adaptation of dtext(). It
should be optimized in several important ways in the future:
1. Use left-shifting in azrp_text_glyph(), which is probably faster both for
partial and full glyphs.
2. Optimize the heck out of the full-width case, which is almost every
single call.
3. Precompute the set of glyphs so the list can be reused when crossing
fragment boundaries, the shader can be written entirely in assembler, and
the command can possibly be reused?
4. Provide noclip toplevel functions, which I believe should provide a
nontrivial speed boost. */
void azrp_text_glyph(uint16_t *fragment, uint32_t const *data, int color,
int height, int width, int stride, int index);
struct command {
uint8_t shader_id;
uint8_t _;
int16_t x, y;
int16_t height, top;
font_t const *font;
char const *str;
int fg;
int size;
};
void azrp_shader_text(void *uniforms0, void *cmd0, void *frag0)
{
struct command *cmd = cmd0;
int x = cmd->x;
int y = cmd->y;
font_t const *f = cmd->font;
int fg = cmd->fg;
int size = cmd->size;
/* Storage height, top position within glyph */
int height = min(cmd->height, azrp_frag_height - y);
int top = cmd->top;
uint8_t const *str = (void *)cmd->str;
uint8_t const *str0 = str;
/* Raw glyph data */
uint32_t const *data = f->data;
/* Update for next fragment */
cmd->height -= height;
cmd->top += height;
cmd->y = 0;
/* Move to top row */
uint16_t *frag = (uint16_t *)frag0 + azrp_width * y;
/* Read each character from the input string */
while(x < azrp_window.right)
{
uint32_t code_point = dtext_utf8_next(&str);
if(!code_point || (size >= 0 && str - str0 > size)) break;
int glyph = dfont_glyph_index(f, code_point);
if(glyph < 0) continue;
int dataw = f->prop ? f->glyph_width[glyph] : f->width;
int index = dfont_glyph_offset(f, glyph);
/* Compute horizontal intersection between glyph and screen */
int width = dataw, left = 0;
if(x + dataw <= azrp_window.left)
{
x += dataw + f->char_spacing;
continue;
}
if(x < azrp_window.left) {
left = azrp_window.left - x;
width -= left;
}
width = min(width, azrp_window.right - x);
/* Render glyph */
azrp_text_glyph(frag + x + left, data + index, fg, height, width,
dataw - width, top * dataw + left);
x += dataw + f->char_spacing;
}
}
void azrp_text(int x, int y, font_t const *f, char const *str,
int fg, int size)
{
prof_enter(azrp_perf_cmdgen);
/* Clipping */
if(x >= azrp_window.right || y >= azrp_window.bottom ||
y + f->data_height <= azrp_window.top) {
prof_leave(azrp_perf_cmdgen);
return;
}
int top_overflow = y - azrp_window.top;
int top = 0;
int height = f->data_height;
if(top_overflow < 0) {
top = -top_overflow;
height += top_overflow;
y -= top_overflow;
}
height = min(height, azrp_window.bottom - y);
if(height <= 0) {
prof_leave(azrp_perf_cmdgen);
return;
}
int frag_first, first_offset, frag_count;
azrp_config_get_lines(y, f->data_height,
&frag_first, &first_offset, &frag_count);
struct command cmd;
cmd.shader_id = AZRP_SHADER_TEXT;
cmd.x = x;
cmd.y = first_offset;
cmd.height = height;
cmd.top = top;
cmd.font = f;
cmd.str = str;
cmd.fg = fg;
cmd.size = size;
azrp_queue_command(&cmd, sizeof cmd, frag_first, frag_count);
prof_leave(azrp_perf_cmdgen);
}
void azrp_text_opt(int x, int y, font_t const *font, int fg, int halign,
int valign, char const *str, int size)
{
if(halign != DTEXT_LEFT || valign != DTEXT_TOP) {
int w, h;
dnsize(str, size, font, &w, &h);
if(halign == DTEXT_RIGHT) x -= w - 1;
if(halign == DTEXT_CENTER) x -= (w >> 1);
if(valign == DTEXT_BOTTOM) y -= h - 1;
if(valign == DTEXT_MIDDLE) y -= (h >> 1);
}
azrp_text(x, y, font, str, fg, size);
}