ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
glyph_bench.c
Go to the documentation of this file.
1
31#include <signal.h>
32#include <stddef.h>
33#include <stdint.h>
34#include <string.h>
35#include <unistd.h>
36
37#include "ra8_attributes.h"
38#include "ra8_err.h"
39#include "ra8_glyph_atlas.h"
40#include "ra8_io_stream.h"
41#include "ra8_io_stream_posix.h"
42#include "ra8_keycache.h"
43#include "ra8_log.h"
44
68
70typedef enum : uint32_t {
71 k_gb_xs_a = 13U,
72 k_gb_xs_b = 7U,
73 k_gb_xs_c = 17U,
80
82typedef enum : uint64_t {
83 k_gb_seed = 0xD1B54A32D192ED03ULL,
84} gb_seed_t;
85
87static const uint32_t s_gb_budgets[] =
88 {16U, 32U, 48U, 64U, 96U, 128U, 192U, 256U}; /* MAGIC-OK: swept cache-budget data */
89
94static const uint16_t s_gb_letter_freq[26] = {
95 817U, 150U, 278U, 425U, 1270U, 223U, 202U, 609U, 697U, /* MAGIC-OK: frequency data */
96 15U, 77U, 403U, 241U, 675U, 751U, 193U, 10U, 599U, /* MAGIC-OK: frequency data */
97 633U, 906U, 276U, 98U, 236U, 15U, 197U, 7U}; /* MAGIC-OK: frequency data */
98
100typedef enum : uint32_t {
102} gb_freq_t;
103
112
121
140internal_write_parts(ra8_io_stream_t* stream, const char* const* parts, size_t count)
141{
142 for (size_t i = 0U; i < count; ++i) {
143 const ra8_err_t error = ra8_io_stream_puts(stream, parts[i]);
144 if (error != k_ra8_ok) {
145 return error;
146 }
147 }
148 return k_ra8_ok;
149}
150
165RA8_INTERNAL static void internal_log_byte(void* context, uint8_t byte)
166{
167 (void)ra8_io_stream_putc((ra8_io_stream_t*)context, (char)byte);
168}
169
186{
187 struct sigaction action = {.sa_handler = SIG_IGN};
188 if ((sigemptyset(&action.sa_mask) != 0) || (sigaction(SIGPIPE, &action, nullptr) != 0)) {
190 }
192 if (error == k_ra8_ok) {
194 }
195 if (error == k_ra8_ok) {
196 ra8_log_init();
198 }
199 return error;
200}
201
225RA8_INTERNAL static uint64_t internal_gb_rng(uint64_t* s)
226{
227 uint64_t x = *s;
228 x ^= x << (uint32_t)k_gb_xs_a;
229 x ^= x >> (uint32_t)k_gb_xs_b;
230 x ^= x << (uint32_t)k_gb_xs_c;
231 *s = x;
232 return x;
233}
234
257RA8_INTERNAL static uint32_t internal_gb_below(uint64_t* s, uint32_t span)
258{
259 return (span == 0U) ? 0U : (uint32_t)(internal_gb_rng(s) % (uint64_t)span);
260}
261
285RA8_INTERNAL static uint32_t internal_gb_pick_letter(uint64_t* s)
286{
287 uint32_t r = internal_gb_below(s, (uint32_t)k_gb_freq_total);
288 for (uint32_t i = 0U; i < (uint32_t)k_gb_ascii_az_z; ++i) {
289 if (r < (uint32_t)s_gb_letter_freq[i]) {
290 return (uint32_t)k_gb_ascii_az_a + i;
291 }
292 r -= (uint32_t)s_gb_letter_freq[i];
293 }
294 return (uint32_t)k_gb_ascii_az_a; /* fallthrough: 'a' */
295}
296
298static const uint32_t s_gb_punct[] = {' ', '.', ',', ';', ':', '\'', '"', '-', '!', '?'};
299
324RA8_INTERNAL static uint32_t internal_gb_pick_codepoint(uint64_t* s)
325{
326 const uint32_t roll = internal_gb_below(s, (uint32_t)k_gb_pct_base);
327 if (roll < (uint32_t)k_gb_punct_pct) {
328 const uint32_t n = (uint32_t)(sizeof(s_gb_punct) / sizeof(s_gb_punct[0]));
329 return s_gb_punct[internal_gb_below(s, n)];
330 }
331 if (roll < (uint32_t)k_gb_punct_pct + (uint32_t)k_gb_digit_pct) {
332 return (uint32_t)k_gb_ascii_0 + internal_gb_below(s, (uint32_t)k_gb_digits);
333 }
334 uint32_t cp = internal_gb_pick_letter(s);
335 if (internal_gb_below(s, (uint32_t)k_gb_pct_base) < (uint32_t)k_gb_cap_pct) {
336 cp -= (uint32_t)k_gb_case_delta; /* uppercase variant */
337 }
338 return cp;
339}
340
370 const ra8_glyph_key_t* key,
371 uint8_t* cell,
372 uint32_t cell_bytes,
373 uint16_t* out_w,
374 uint16_t* out_h)
375{
376 (void)ctx;
377 (void)key;
378 (void)memset(cell, 0, (size_t)cell_bytes);
379 *out_w = 1U;
380 *out_h = 1U;
381 return k_ra8_ok;
382}
383
385static const uint64_t s_gb_access_total =
386 (uint64_t)k_gb_pages * (uint64_t)k_gb_reread_pages * (uint64_t)k_gb_page_glyphs;
387
406{
407 const bool heading = internal_gb_below(rng, (uint32_t)k_gb_pct_base) < (uint32_t)k_gb_heading_pct;
408 ra8_glyph_key_t key = {.glyph_id = internal_gb_pick_codepoint(rng),
409 .size_px = heading ? (uint16_t)k_gb_heading_px : (uint16_t)k_gb_body_px};
410 ra8_glyph_t glyph = {};
411 ra8_err_t error = ra8_glyph_atlas_get(atlas, &key, &glyph);
412 if (error == k_ra8_ok) {
413 error = ra8_glyph_atlas_put(atlas, glyph.bitmap);
414 }
415 return error;
416}
417
435 uint64_t* page_rng)
436{
437 for (uint32_t glyph = 0U; glyph < (uint32_t)k_gb_page_glyphs; ++glyph) {
438 const ra8_err_t error = internal_submit_access(atlas, page_rng);
439 if (error != k_ra8_ok) {
440 return error;
441 }
442 }
443 return k_ra8_ok;
444}
445
462{
463 uint64_t session_rng = (uint64_t)k_gb_seed;
464 for (uint32_t page = 0U; page < (uint32_t)k_gb_pages; ++page) {
465 const uint64_t page_start = session_rng;
466 uint64_t next_page = page_start;
467 for (uint32_t repeat = 0U; repeat < (uint32_t)k_gb_reread_pages; ++repeat) {
468 uint64_t page_rng = page_start;
469 const ra8_err_t error = internal_replay_page_glyphs(atlas, &page_rng);
470 if (error != k_ra8_ok) {
471 return error;
472 }
473 if (repeat == 0U) {
474 next_page = page_rng;
475 }
476 }
477 session_rng = next_page;
478 }
479 return k_ra8_ok;
480}
481
500internal_run_budget(uint32_t budget, uint32_t* out_hits, uint32_t* out_rasters)
501{
502 (void)memset(&s_gb_workspace, 0, sizeof(s_gb_workspace));
503 const ra8_glyph_atlas_cfg_t cfg = {.cell_mem = s_gb_workspace.cells,
504 .cell_bytes = (uint32_t)k_gb_cell_bytes,
505 .cell_count = budget,
506 .meta = s_gb_workspace.meta,
507 .keys = s_gb_workspace.keys,
508 .dims = s_gb_workspace.dims,
509 .buckets = s_gb_workspace.buckets,
510 .bucket_count = (uint32_t)k_gb_buckets,
511 .render = internal_gb_render,
512 .render_ctx = nullptr};
513 ra8_glyph_atlas_t atlas = {};
514 ra8_err_t error = ra8_glyph_atlas_init(&atlas, &cfg);
515 if (error == k_ra8_ok) {
516 error = internal_replay_workload(&atlas);
517 }
518 if (error == k_ra8_ok) {
519 error = ra8_glyph_atlas_stats(&atlas, out_hits, out_rasters, nullptr);
520 }
521 return error;
522}
523
542internal_put_padded_u64(ra8_io_stream_t* stream, uint64_t value, uint32_t width)
543{
544 uint32_t digits = 1U;
545 for (uint64_t copy = value; copy >= (uint64_t)k_gb_dec_radix; copy /= (uint64_t)k_gb_dec_radix) {
546 ++digits;
547 }
548 for (uint32_t i = digits; i < width; ++i) {
549 const ra8_err_t error = ra8_io_stream_putc(stream, ' ');
550 if (error != k_ra8_ok) {
551 return error;
552 }
553 }
554 return ra8_io_stream_put_u64(stream, value);
555}
556
572{
573 const char* const prefix[] = {"# #147/#164 glyph-cache budget sweep\n\nWorkload: "};
574 ra8_err_t error = internal_write_parts(&s_gb_output, prefix, 1U);
575 if (error == k_ra8_ok) {
576 error = ra8_io_stream_put_u32(&s_gb_output, (uint32_t)k_gb_pages);
577 }
578 if (error == k_ra8_ok) {
579 error = ra8_io_stream_puts(&s_gb_output, " pages x ");
580 }
581 if (error == k_ra8_ok) {
583 }
584 if (error == k_ra8_ok) {
585 error = ra8_io_stream_puts(&s_gb_output, " re-renders x ");
586 }
587 if (error == k_ra8_ok) {
589 }
590 if (error == k_ra8_ok) {
591 error = ra8_io_stream_puts(&s_gb_output, " glyphs = ");
592 }
593 if (error == k_ra8_ok) {
595 }
596 if (error == k_ra8_ok) {
597 error =
599 " glyph gets\n(English letter frequencies + caps/punct/digits; body ");
600 }
601 if (error == k_ra8_ok) {
603 }
604 if (error == k_ra8_ok) {
605 error = ra8_io_stream_puts(&s_gb_output, "px, heading ");
606 }
607 if (error == k_ra8_ok) {
609 }
610 if (error == k_ra8_ok) {
611 error = ra8_io_stream_puts(
613 "px)\n\n| cells | RAM @36KiB/cell | hit rate % | rasterisations |\n|------:|----------------:|-----------:|---------------:|\n");
614 }
615 return error;
616}
617
635RA8_INTERNAL static ra8_err_t internal_report_row(uint32_t budget, uint32_t hits, uint32_t rasters)
636{
637 const uint64_t scaled =
638 (((uint64_t)hits * (uint64_t)k_gb_pct_base * (uint64_t)k_gb_decimal_scale) +
639 (s_gb_access_total / 2U)) /
641 const uint64_t whole = scaled / (uint64_t)k_gb_decimal_scale;
642 const uint32_t fraction = (uint32_t)(scaled % (uint64_t)k_gb_decimal_scale);
644 if (error == k_ra8_ok) {
645 error = internal_put_padded_u64(&s_gb_output, budget, (uint32_t)k_gb_budget_width);
646 }
647 if (error == k_ra8_ok) {
648 error = ra8_io_stream_puts(&s_gb_output, " | ");
649 }
650 if (error == k_ra8_ok) {
652 (uint64_t)budget * (uint64_t)k_gb_prod_cell_kib,
653 (uint32_t)k_gb_ram_width);
654 }
655 if (error == k_ra8_ok) {
656 error = ra8_io_stream_puts(&s_gb_output, " KiB | ");
657 }
658 if (error == k_ra8_ok) {
659 error = internal_put_padded_u64(&s_gb_output, whole, (uint32_t)k_gb_hit_width);
660 }
661 if (error == k_ra8_ok) {
662 error = ra8_io_stream_putc(&s_gb_output, '.');
663 }
664 if ((error == k_ra8_ok) && (fraction < (uint32_t)k_gb_dec_radix)) {
665 error = ra8_io_stream_putc(&s_gb_output, '0');
666 }
667 if (error == k_ra8_ok) {
668 error = ra8_io_stream_put_u32(&s_gb_output, fraction);
669 }
670 if (error == k_ra8_ok) {
671 error = ra8_io_stream_puts(&s_gb_output, " | ");
672 }
673 if (error == k_ra8_ok) {
674 error = internal_put_padded_u64(&s_gb_output, rasters, (uint32_t)k_gb_raster_width);
675 }
676 if (error == k_ra8_ok) {
677 error = ra8_io_stream_puts(&s_gb_output, " |\n");
678 }
679 return error;
680}
681
697{
699 const size_t count = sizeof(s_gb_budgets) / sizeof(s_gb_budgets[0]);
700 for (size_t i = 0U; (i < count) && (error == k_ra8_ok); ++i) {
701 uint32_t hits = 0U;
702 uint32_t rasters = 0U;
703 error = internal_run_budget(s_gb_budgets[i], &hits, &rasters);
704 if (error == k_ra8_ok) {
705 error = internal_report_row(s_gb_budgets[i], hits, rasters);
706 }
707 }
708 if (error == k_ra8_ok) {
710 "\nWithout a cache the renderer rasterises once per glyph get (");
711 }
712 if (error == k_ra8_ok) {
714 }
715 if (error == k_ra8_ok) {
716 error = ra8_io_stream_puts(&s_gb_output, ").\n");
717 }
718 return error;
719}
720
721int main(void)
722{
724 return 1;
725 }
726 const ra8_err_t error = internal_run_report();
727 if (error != k_ra8_ok) {
728 const char* const parts[] = {"glyph_bench: workload or output failed\n"};
729 (void)internal_write_parts(&s_gb_diagnostic, parts, 1U);
730 return 1;
731 }
732 return 0;
733}
static ra8_err_t internal_replay_page_glyphs(ra8_glyph_atlas_t *atlas, uint64_t *page_rng)
Replay one page's fixed glyph-access sequence against a running RNG.
gb_freq_t
Sum of s_gb_letter_freq (the selection range).
@ k_gb_freq_total
Sum of the 26 letter weights above.
static void internal_log_byte(void *context, uint8_t byte)
Forward one logger byte into the bound diagnostic stream.
static uint32_t internal_gb_pick_letter(uint64_t *s)
Pick a lowercase letter codepoint weighted by English frequency.
static ra8_err_t internal_submit_access(ra8_glyph_atlas_t *atlas, uint64_t *rng)
Generate and submit one deterministic glyph access.
static uint64_t internal_gb_rng(uint64_t *s)
Advance a fixed-seed xorshift64 generator and return the new state.
static ra8_err_t internal_report_header(void)
Write the fixed report preamble and workload dimensions.
static ra8_io_stream_t s_gb_diagnostic
static ra8_err_t internal_output_init(void)
Bind process output descriptors and make broken pipes observable.
static const uint64_t s_gb_access_total
Total glyph accesses in the deterministic session.
static ra8_err_t internal_report_row(uint32_t budget, uint32_t hits, uint32_t rasters)
Write one exact table row from integer hit/miss evidence.
static uint32_t internal_gb_below(uint64_t *s, uint32_t span)
Draw a uniform pseudo-random integer in [0, span).
static ra8_io_stream_t s_gb_output
Borrowed stdout/stderr portable stream handles.
static const uint32_t s_gb_budgets[]
Swept cache budgets (cell counts).
Definition glyph_bench.c:87
static ra8_err_t internal_put_padded_u64(ra8_io_stream_t *stream, uint64_t value, uint32_t width)
Write one unsigned value left-padded to a fixed field width.
static const uint32_t s_gb_punct[]
A small punctuation/space repertoire (codepoints).
int main(void)
The application entry point Reset_Handler hands control to.
static ra8_err_t internal_gb_render(void *ctx, const ra8_glyph_key_t *key, uint8_t *cell, uint32_t cell_bytes, uint16_t *out_w, uint16_t *out_h)
Atlas render-on-miss callback: stand in for one rasterisation.
static uint32_t internal_gb_pick_codepoint(uint64_t *s)
Choose one codepoint for the glyph stream from the weighted mix.
gb_dim_t
Workload model dimensions (no bare literals).
Definition glyph_bench.c:46
@ k_gb_dec_radix
Decimal renderer radix.
Definition glyph_bench.c:62
@ k_gb_buckets
Hash buckets for the atlas.
Definition glyph_bench.c:58
@ k_gb_decimal_scale
Two decimal digits for percentages.
Definition glyph_bench.c:61
@ k_gb_heading_pct
% of glyphs drawn at the heading size.
Definition glyph_bench.c:52
@ k_gb_punct_pct
% of glyphs drawn as punctuation.
Definition glyph_bench.c:54
@ k_gb_hit_width
Whole-part width before .NN.
Definition glyph_bench.c:65
@ k_gb_cell_bytes
Bench cell size (content-irrelevant).
Definition glyph_bench.c:57
@ k_gb_reread_pages
Re-render each page this many times (turns).
Definition glyph_bench.c:49
@ k_gb_ram_width
RAM report field width.
Definition glyph_bench.c:64
@ k_gb_page_glyphs
Glyph draws per page (a dense page).
Definition glyph_bench.c:48
@ k_gb_max_budget
Largest swept atlas capacity.
Definition glyph_bench.c:60
@ k_gb_digit_pct
% of glyphs drawn as digits.
Definition glyph_bench.c:55
@ k_gb_prod_cell_kib
Production cell cost (192x192 alpha8) KiB.
Definition glyph_bench.c:59
@ k_gb_pct_base
Percentage base.
Definition glyph_bench.c:56
@ k_gb_body_px
Body text font size.
Definition glyph_bench.c:50
@ k_gb_heading_px
Heading font size.
Definition glyph_bench.c:51
@ k_gb_raster_width
Rasterization report field width.
Definition glyph_bench.c:66
@ k_gb_budget_width
Budget report field width.
Definition glyph_bench.c:63
@ k_gb_cap_pct
% of letters drawn as capitals.
Definition glyph_bench.c:53
@ k_gb_pages
Pages rendered in the session.
Definition glyph_bench.c:47
static ra8_io_stream_posix_state_t s_gb_diagnostic_posix
gb_seed_t
Deterministic xorshift64 seed.
Definition glyph_bench.c:82
@ k_gb_seed
Fixed seed.
Definition glyph_bench.c:83
static ra8_err_t internal_run_report(void)
Run the complete deterministic cache-budget report.
static ra8_err_t internal_write_parts(ra8_io_stream_t *stream, const char *const *parts, size_t count)
Write a fixed sequence of NUL-terminated fragments exactly.
static ra8_err_t internal_replay_workload(ra8_glyph_atlas_t *atlas)
Regenerate and replay the exact legacy session without a trace buffer.
static ra8_err_t internal_run_budget(uint32_t budget, uint32_t *out_hits, uint32_t *out_rasters)
Execute one budget against the caller-visible fixed atlas backing.
gb_const_t
xorshift64 + fill constants.
Definition glyph_bench.c:70
@ k_gb_ascii_az_a
ASCII 'a'.
Definition glyph_bench.c:74
@ k_gb_ascii_0
ASCII '0'.
Definition glyph_bench.c:77
@ k_gb_case_delta
'a' - 'A'.
Definition glyph_bench.c:76
@ k_gb_xs_c
xorshift shift 3.
Definition glyph_bench.c:73
@ k_gb_xs_b
xorshift shift 2.
Definition glyph_bench.c:72
@ k_gb_ascii_az_z
Letters in a-z.
Definition glyph_bench.c:75
@ k_gb_digits
Digit count.
Definition glyph_bench.c:78
@ k_gb_xs_a
xorshift shift 1.
Definition glyph_bench.c:71
static ra8_io_stream_posix_state_t s_gb_output_posix
Raw-descriptor adapter states for the borrowed process streams.
static gb_workspace_t s_gb_workspace
Process-lifetime semantic backing for one measured atlas.
static const uint16_t s_gb_letter_freq[26]
English letter relative frequencies, scaled to a per-mille-ish table indexed by letter (a....
Definition glyph_bench.c:94
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
Error Code Definitions for ra8-firmware.
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
@ k_ra8_err_comm_error
Generic communication error (use a more specific code when possible).
Definition ra8_err.h:399
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
void * memset(void *dst, int value, size_t n)
Fill memory with a constant byte value.
Fixed-RAM-budget glyph cache with LRU eviction (Layer 3, #147).
ra8_err_t ra8_glyph_atlas_put(ra8_glyph_atlas_t *atlas, const uint8_t *bitmap)
Release one pin on a glyph previously returned by ra8_glyph_atlas_get.
ra8_err_t ra8_glyph_atlas_stats(const ra8_glyph_atlas_t *atlas, uint32_t *out_hits, uint32_t *out_misses, uint32_t *out_evictions)
Report the atlas hit / miss / eviction counters.
ra8_err_t ra8_glyph_atlas_get(ra8_glyph_atlas_t *atlas, const ra8_glyph_key_t *key, ra8_glyph_t *out_glyph)
Get (and pin) the rendered glyph for key.
ra8_err_t ra8_glyph_atlas_init(ra8_glyph_atlas_t *atlas, const ra8_glyph_atlas_cfg_t *cfg)
Initialise a glyph atlas over caller-supplied storage.
ra8_io targetable byte-stream facade – one writer, many destinations.
ra8_err_t ra8_io_stream_puts(ra8_io_stream_t *s, const char *str)
Write a NUL-terminated string (without the NUL) to the bound sink.
ra8_err_t ra8_io_stream_put_u32(ra8_io_stream_t *s, uint32_t value)
Write value as unsigned decimal ASCII (no leading zeros).
ra8_err_t ra8_io_stream_put_u64(ra8_io_stream_t *s, uint64_t value)
Write value as unsigned 64-bit decimal ASCII without leading zeros.
ra8_err_t ra8_io_stream_putc(ra8_io_stream_t *s, char c)
Write a single byte to the bound sink.
Borrowed raw-descriptor backend for the portable byte-stream facade.
ra8_err_t ra8_io_stream_posix_init(ra8_io_stream_t *stream, ra8_io_stream_posix_state_t *state, int fd)
Bind a borrowed writable descriptor as a byte-stream sink.
The one reusable hash + pin + evict cache engine (#147, #345).
Lightweight Logging Interface for ra8-firmware.
void ra8_log_init(void)
Initialise the logging backend.
Definition ra8_log.c:379
void ra8_log_set_byte_sink(ra8_log_byte_sink_fn_t fn, void *ctx)
Install (or clear) an optional byte sink for log output.
Definition ra8_log.c:56
Exact caller-owned backing for the largest measured atlas.
ra8_glyph_dims_t dims[k_gb_max_budget]
Cell geometry.
uint8_t cells[k_gb_max_budget *k_gb_cell_bytes]
Bitmap cells.
ra8_keycache_cell_t meta[k_gb_max_budget]
LRU metadata.
int32_t buckets[k_gb_buckets]
Hash buckets.
ra8_glyph_key_t keys[k_gb_max_budget]
Cell keys.
Caller-supplied storage + renderer for ra8_glyph_atlas_init.
Glyph-cache state (caller-owned; treat as private).
Per-cell user descriptor: the rendered glyph dimensions.
Identifies one rendered glyph.
A pinned view of a cached glyph bitmap returned by ra8_glyph_atlas_get.
const uint8_t * bitmap
Glyph coverage bitmap (width*height bytes).
Caller-owned state retained by one POSIX stream binding.
Caller-allocated byte-stream handle binding a sink to its context.
Per-cell link metadata (one caller-owned array entry per cell).