azrp: first version of text shader (slow!), based on topti-cg
This commit is contained in:
parent
2e7c076e60
commit
2f185a36d7
|
@ -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})
|
||||
|
|
|
@ -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
|
||||
//
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include <azur/gint/render.h>
|
||||
|
||||
uint AZRP_SHADER_RECT = -1;
|
||||
uint8_t AZRP_SHADER_RECT = -1;
|
||||
|
||||
static void configure(void)
|
||||
{
|
||||
|
|
|
@ -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
|
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue