c/test/block_bench.c · 14158 字节 · 下载原文
/* block_bench.c —— 矢量图块(ui_2d_create_block / ui_2d_end_block / ui_2d_draw_block)的压力测试。
* block_bench.c -- a stress test for vector blocks (ui_2d_create_block / ui_2d_end_block /
* ui_2d_draw_block).
*
* ## 它测什么
* ## What it measures
*
* 用 10 种**样式各异的飞机图块**在屏幕上飞(各朝各的方向、速度不同),
* Ten **differently styled plane blocks** fly around the screen (each heading its own
* 数量从 1 架一路涨到 100 架,右上角实时显示:
* way, at its own speed); the count grows from 1 plane up to 100, and the top right
* corner shows live:
*
* 帧率 / 飞机数 / 虚拟机内存 / 栈大小 / 堆基址 / 场景图元数
* fps / plane count / VM memory / stack size / heap base / scene primitive count
*
* 用来回答一个问题:**贴 100 个带旋转的矢量图块,帧率还剩多少。**
* It answers one question: **with 100 rotated vector blocks blitted, how much frame
* rate is left.**
*
* ## 为什么这是图块的好用例
* ## Why this is a good use case for blocks
*
* 每架飞机每帧都要"转 + 移",而矢量图块贴一次的代价是
* Every plane must "rotate + move" every frame, and the cost of one block blit is
* `push + translate + rotate + scale + 块内行 + pop` —— 旋转是**变换矩阵**,
* `push + translate + rotate + scale + the block's rows + pop` -- the rotation is a
* **transformation matrix**, not a resample; and scaling up is not blurry either (its
* 不是重采样;放大也不糊(它的上限是图元预算,不是像素)。
* limit is the primitive budget, not pixels).
*
* ## 三条要知道的
* ## Three things to know
*
* 1. **颜色写完整 ARGB**(0xAARRGGBB)—— 写 `0xFF0000` 会被当成 alpha=0 的全透明色。
* 1. **Write the full ARGB colour** (0xAARRGGBB) -- writing `0xFF0000` is taken as a
* fully transparent colour with alpha=0.
* 2. 图块**造一次、一直贴**;块表**不随 ui_2d_clear 清**(每帧 create 一遍会在 128 帧后拿不到句柄)。
* 2. A block is **created once and blitted forever**; the block table is **not cleared by
* ui_2d_clear** (creating one every frame runs out of handles after about 128 frames).
* 3. 结尾**不能调 ui_win_close()** —— 那会把场景置空,而桌面 `--frame` 是跑完之后才取场景的。
* 3. Do **not call ui_win_close()** at the end -- it blanks the scene, and the desktop
* `--frame` only grabs the scene after the program has finished.
*/
#include <vml_ui.h>
/* `Lib/shared/src/sysinfo.c` 的包装(第一个参数按调用约定放 R0,所以 asm 里不用占位符)。 */
/* Wrappers for `Lib/shared/src/sysinfo.c` (the first argument goes in R0 by the calling
* convention, so the asm needs no placeholder). */
int getconfig(int type);
// ⚠ 这里原先自己写了一条 `int ui_time_get_ms(void);` —— 那个声明**库头里就有**
// ⚠ This file used to declare `int ui_time_get_ms(void);` itself -- but that declaration **already exists in the library header**
// (本文件第 46 行已经 `#include <vml_ui.h>`)。用户 2026-10-01 的口径:
// (this file already does `#include <vml_ui.h>` on line 46). The user's 2026-10-01 rule:
// 「例程不需要自己声明函数」—— 声明只留库那一份,删掉这条重复的。
// "an example does not declare library functions itself" -- keep the declaration only in the library and drop this duplicate.
#define MAX_PLANES 1000
#define KINDS 10
/* 飞机状态(文件级全局变量是好的,见 shot.c 的说明) */
/* Plane state (file level globals are fine here -- see the notes in shot.c) */
int planeX[MAX_PLANES];
int planeY[MAX_PLANES];
int planeKind[MAX_PLANES];
int planeSpd[MAX_PLANES];
int planeDir[MAX_PLANES]; /* 朝向角度(度) */
/* heading angle in degrees */
int planeAlive[MAX_PLANES];
int blockOf[KINDS]; /* 10 种样式的图块句柄 */
/* block handles for the 10 styles */
int sw;
int sh;
int planeCount;
/* ── 手写整数转字符串(不去碰 printf 那套,避免格式化的坑)── */
/* ── Hand rolled int to string (avoid the whole printf path, and its formatting traps) ── */
char* fmtInt(int v, char* buf) {
int n;
int i;
int neg;
n = 0;
neg = 0;
if (v < 0) { neg = 1; v = -v; }
if (v == 0) { buf[0] = '0'; buf[1] = 0; return buf; }
while (v > 0) {
buf[n] = '0' + (v - (v / 10) * 10);
n = n + 1;
v = v / 10;
}
if (neg) { buf[n] = '-'; n = n + 1; }
/* 反转 */
/* reverse it */
for (i = 0; i < n / 2; i++) {
char t = buf[i];
buf[i] = buf[n - 1 - i];
buf[n - 1 - i] = t;
}
buf[n] = 0;
return buf;
}
/* ── 10 种飞机样式:每种一组图元拼出来,录成一个图块 ── */
/* ── 10 plane styles: each is drawn from a set of primitives and recorded into a block ── */
void buildKind(int k) {
int c;
int body;
/* 每种一个主色(完整 ARGB) */
/* one main colour per style (full ARGB) */
if (k == 0) { c = 0xFFFF5555; }
else if (k == 1) { c = 0xFFFFAA33; }
else if (k == 2) { c = 0xFFFFEE55; }
else if (k == 3) { c = 0xFF66DD66; }
else if (k == 4) { c = 0xFF44DDBB; }
else if (k == 5) { c = 0xFF55BBFF; }
else if (k == 6) { c = 0xFF6688FF; }
else if (k == 7) { c = 0xFFAA77FF; }
else if (k == 8) { c = 0xFFEE77DD; }
else { c = 0xFFFFFFFF; }
blockOf[k] = ui_2d_create_block(28, 28, 0);
/* 机身:一个竖条(机头朝上,方便按角度旋转) */
/* fuselage: a vertical bar (nose up, so it rotates cleanly by angle) */
ui_2d_rect(12, 2, 4, 22, c, 1, 0, 2);
/* 机翼:横向渐短的条 */
/* wings: horizontal bars that get shorter */
ui_2d_rect(2, 12, 24, 4, c, 1, 0, 1);
/* 尾翼 */
/* tail fin */
ui_2d_rect(9, 20, 10, 3, c, 1, 0, 1);
/* 驾驶舱(白点) */
/* cockpit (a white dot) */
ui_2d_circle(14, 9, 2, 0xFFFFFFFF, 1, 0);
/* 样式区分:每种加一个不同的配饰(k 决定位置/形状) */
/* Style marker: each kind gets a different ornament (k decides position and shape) */
body = 3 + (k - (k / 4) * 4) * 3;
if (k < 5) {
ui_2d_circle(body, 6, 2, 0xFFFFFFFF, 1, 0);
} else {
ui_2d_rect(body, 17, 3, 3, 0xFFFFFFFF, 1, 0, 0);
}
ui_2d_end_block();
}
void spawnPlane(int i) {
planeX[i] = 20 + (i * 37 - (i * 37 / (sw - 60)) * (sw - 60));
planeY[i] = 40 + (i * 53 - (i * 53 / (sh - 120)) * (sh - 120));
planeKind[i] = i - (i / KINDS) * KINDS;
planeSpd[i] = 2 + (i - (i / 5) * 5); /* 2..6 */
planeDir[i] = (i * 37) - ((i * 37) / 360) * 360;
planeAlive[i] = 1;
}
void drawPlane(int i) {
int k;
k = blockOf[planeKind[i]];
ui_2d_draw_block(k, planeX[i], planeY[i], 1000, 1000, planeDir[i]);
}
/* 一步:按朝向飞,出界就绕回对面 */
/* One step: fly along the heading, and wrap around to the far side when out of bounds */
void stepPlane(int i) {
int d;
d = planeDir[i];
/* 用查表避免 sin/cos 的坑(本仓 BASIC 前端那套 sin/cos 有问题;C 这边也统一走整数推进) */
/* Use a lookup table to dodge the sin/cos traps (this repo's BASIC front end has
* problems with sin/cos; on the C side too we advance with integers only) */
if (d < 45) { planeX[i] = planeX[i] + planeSpd[i]; }
else if (d < 90) { planeX[i] = planeX[i] + planeSpd[i]; planeY[i] = planeY[i] - planeSpd[i] / 2; }
else if (d < 135) { planeY[i] = planeY[i] - planeSpd[i]; }
else if (d < 180) { planeX[i] = planeX[i] - planeSpd[i] / 2; planeY[i] = planeY[i] - planeSpd[i]; }
else if (d < 225) { planeX[i] = planeX[i] - planeSpd[i]; }
else if (d < 270) { planeX[i] = planeX[i] - planeSpd[i]; planeY[i] = planeY[i] + planeSpd[i] / 2; }
else if (d < 315) { planeY[i] = planeY[i] + planeSpd[i]; }
else { planeX[i] = planeX[i] + planeSpd[i] / 2; planeY[i] = planeY[i] + planeSpd[i]; }
if (planeX[i] < -20) { planeX[i] = sw + 10; }
if (planeX[i] > sw + 20) { planeX[i] = -10; }
if (planeY[i] < 20) { planeY[i] = sh - 60; }
if (planeY[i] > sh - 40) { planeY[i] = 30; }
}
/* ── 主循环 ── */
/* ── Main loop ── */
int main() {
int i;
int k;
int m;
int t0;
int t1;
int frames;
int fps;
int grow;
char buf[24];
char line[128];
int lang = ui_scr_get_language(); /* 界面语言:开局查一次(ui_scr_get_language 是 syscall,别每帧调) */
/* UI language: queried once at start (ui_scr_get_language is a syscall, do not call it every frame) */
/* ⚠ 尺寸必须传 `ui_scr_get_w()/ui_scr_get_h()`(可用绘图区),**不能传 0** ——
* ⚠ The size must be `ui_scr_get_w()/ui_scr_get_h()` (the usable drawing area), **not 0** --
传 0 时窗口拿到的尺寸与程序以为的不是一回事,飞机全画到画布外面去了。
* passing 0 gives the window a size different from what the program assumes, and
* every plane gets drawn outside the canvas. */
ui_win_open(lang == 0 ? "图块压力测试" : "Block stress test", ui_scr_get_w(), ui_scr_get_h());
sw = ui_scr_get_w();
sh = ui_scr_get_h();
for (k = 0; k < KINDS; k++) {
buildKind(k);
}
planeCount = 1;
for (i = 0; i < MAX_PLANES; i++) { planeAlive[i] = 0; }
spawnPlane(0);
frames = 0;
grow = 0;
t0 = ui_time_get_ms();
for (;;) {
if (ui_win_is_closed() != 0) { break; }
ui_2d_clear(0xFF0A0A14);
/* 画所有飞机(每架一次 block 贴图,带旋转) */
/* Draw all the planes (one rotated block blit each) */
for (i = 0; i < planeCount; i++) {
if (planeAlive[i] != 0) {
stepPlane(i);
drawPlane(i);
}
}
/* ── 信息面板 ── */
/* ── Info panel ── */
/* 底衬:飞机上千之后,没底衬的文字根本读不清(半透明黑,压在文字下面) */
/* Backing plate: with a thousand planes around, text with no backing is unreadable
* (semi transparent black, drawn under the text) */
ui_2d_rect(0, 0, 168, 84, 0xCC000000, 1, 0, 0);
frames = frames + 1;
grow = grow + 1;
t1 = ui_time_get_ms();
if (t1 - t0 >= 500) {
fps = (frames * 1000) / (t1 - t0);
frames = 0;
t0 = t1;
}
{
char b1[24]; char b2[24]; char b3[24]; char b4[24]; char b5[24];
int p; int q;
p = 0;
q = 0;
/* 手工拼一行信息(避开 sprintf) */
/* Build one info line by hand (dodging sprintf) */
line[q] = 'F'; q = q + 1;
line[q] = 'P'; q = q + 1;
line[q] = 'S'; q = q + 1;
line[q] = ' '; q = q + 1;
fmtInt(fps, b1);
p = 0; while (b1[p] != 0) { line[q] = b1[p]; q = q + 1; p = p + 1; }
line[q] = ' '; q = q + 1;
line[q] = '|'; q = q + 1;
line[q] = ' '; q = q + 1;
fmtInt(planeCount, b1);
p = 0; while (b1[p] != 0) { line[q] = b1[p]; q = q + 1; p = p + 1; }
line[q] = ' '; q = q + 1;
line[q] = 'j'; q = q + 1;
line[q] = 'i'; q = q + 1;
line[q] = 0;
ui_2d_text(8, 20, line, 0xFFCCDDFF, 14, VML_ANCHOR_LEFT);
}
{
/* 资源读数:内存总量 / 栈大小 / 堆基址(都来自 GetConfig) */
/* Resource readings: total memory / stack size / heap base (all from GetConfig) */
int memKB; int stkKB; int heapBase; int p; int q;
char b[24];
memKB = getconfig(7) / 1024;
stkKB = getconfig(12) / 1024;
heapBase = getconfig(13);
q = 0;
line[q] = 'M'; q = q + 1; line[q] = 'E'; q = q + 1;
line[q] = 'M'; q = q + 1; line[q] = ' '; q = q + 1;
fmtInt(memKB, b);
p = 0; while (b[p] != 0) { line[q] = b[p]; q = q + 1; p = p + 1; }
line[q] = 'K'; q = q + 1; line[q] = 0;
ui_2d_text(8, 40, line, 0xFF88AACC, 13, VML_ANCHOR_LEFT);
q = 0;
line[q] = 'S'; q = q + 1; line[q] = 'T'; q = q + 1;
line[q] = 'K'; q = q + 1; line[q] = ' '; q = q + 1;
fmtInt(stkKB, b);
p = 0; while (b[p] != 0) { line[q] = b[p]; q = q + 1; p = p + 1; }
line[q] = 'K'; q = q + 1; line[q] = 0;
ui_2d_text(8, 60, line, 0xFF88AACC, 13, VML_ANCHOR_LEFT);
q = 0;
line[q] = 'H'; q = q + 1; line[q] = 'P'; q = q + 1;
line[q] = ' '; q = q + 1;
fmtInt(heapBase, b);
p = 0; while (b[p] != 0) { line[q] = b[p]; q = q + 1; p = p + 1; }
line[q] = 0;
ui_2d_text(8, sh - 80, line, 0xFF88AACC, 13, VML_ANCHOR_LEFT);
}
ui_2d_present();
/* 每 3 帧加一架,加到 MAX_PLANES 架为止
* Add one plane every 3 frames, up to MAX_PLANES
(1000 架按每 12 帧加要跑一万多帧,手机上太慢;3 帧一架约 20~30 秒跑满)
* (adding one every 12 frames would take over ten thousand frames for 1000
* planes, far too slow on a phone; one every 3 frames fills up in 20-30 seconds) */
if (grow >= 3) {
grow = 0;
if (planeCount < MAX_PLANES) {
spawnPlane(planeCount);
planeCount = planeCount + 1;
}
}
ui_msg_wait(m, 8);
}
return 0;
}