← 都来码 / Dolaima← 例程索引c/3d/bench3d.c原文

c/3d/bench3d.c · 12657 字节 · 下载原文

/* bench3d.c —— **VML 3D 保留模式压力测试**:物体越来越多,会怎么样?
/* bench3d.c -- **VML 3D retained-mode stress test**: what happens as objects pile up?
 *
 * 用户口径:「搞一堆物体在屏幕上运动,测试压力,物体越来越多,会怎么样」。
 * User's brief: "put a pile of objects moving on screen, stress-test it, see what happens as the objects pile up".
 *
 * ## 怎么测
 * ## How it measures
 * 每 60 帧**新建 20 个物体**(各自公转 + 自转 + 随机相位),到**本程序自己定的 250** 为止
 * Every 60 frames **creates 20 objects** (each with its own orbit + spin + random phase), up to **250, this program's own stop point**
 * ⚠ 宿主上限自 2026-10-08 起是 `MaxObjects`(**1024**)(从前 256);这里仍停在 250 是**刻意的** ——
 *   ⚠ The host cap has been `MaxObjects` (**1024**) since 2026-10-08 (256 before); stopping at 250 is **deliberate** -
 *   拉满要多跑 3000 多帧,而这个程序的用处是看"物体变多时的拐点",250 那一带已经够看。
 *   filling it would take 3000+ frames, and this program exists to find the knee, which 250 already shows.
 * 全程 `ui_msg_fetch_wait(40)` —— ⚠ **只是「等输入最多 40ms」**;
 * Throughout, `ui_msg_fetch_wait(40)` -- ⚠ **only "wait for input for at most 40ms"**;
 * 帧节流自 2026-10-04 起归**宿主**(`ui_2d_present()` 之后按显示预算走,并把它夹到帧截止)。
 * frame pacing belongs to the **host** since 2026-10-04 (after `ui_2d_present()` it follows the display budget and clamps it to the frame deadline).
 * 每 60 帧打一行 `BENCH obj=N frame=F` —— 帧率从**宿主的 VM 状态面板**读(那是真值,
 * Every 60 frames it prints `BENCH obj=N frame=F` -- read the frame rate from the **host's VM status panel** (that is the true value;
 * guest 自己算的"帧率"只是它自己的节拍,不含合成开销)。
 * the "frame rate" the guest computes is its own beat, excluding compositing cost).
 *
 * ## 为什么用**黄金角**散布相位
 * ## Why the phases are spread with the **golden angle**
 * 等差相位会与轨道数**共振**(本仓 E123:等差式与容器尺寸共振,26 颗星塌成 3 列)。
 * Evenly spaced phases **resonate** with the orbit count (repo issue E123: an arithmetic progression resonates with container size, collapsing 26 stars into 3 columns).
 * 黄金角 137.5°(`i * 0.381966` 圈)是低差异序列,任意个数下都散得开、不结块。
 * The golden angle 137.5° (`i * 0.381966` turns) is a low-discrepancy sequence: it spreads out for any count, never clumping.
 *
 * ## 判据(能分辨"谁先扛不住")
 * ## Criteria (they can tell which side gives out first)
 *   · 物体少时应当稳在**显示帧率**(宿主按显示预算节流;25fps 那个上限已经没有了);
 *   · with few objects it should hold the **display frame rate** (the host paces to the display budget; the old 25fps cap is gone);
 *   · 物体变多后帧率下滑 ⇒ 下滑的**拐点**就是这套链路(宿主合成)的容量;
 *   · as objects grow the frame rate drops => the **knee** of that drop is the capacity of this chain (host compositing);
 *   · 若帧率**不降反升**,说明节流没生效(嘉宾在空转),那是另一回事。
 *   · if the frame rate **rises instead of dropping**, pacing is not taking effect (the guest is spinning) -- a different problem.
 */
#include <vml_ui.h>
#include <vml_3d.h>   /* 3D 接口库(600–634)—— 包装器在库里,本文件不再自己写 */
                      /* the 3D interface library (600-634) -- the wrappers live in the library; this file does not write its own */
/* ⚠ **必须显式包含 math.h**(**2026-10-04 复验仍成立**)—— `<vml_ui.h>` 不代为包含它,而漏了它**编得过、跑得起来、
/* ⚠ **math.h must be included explicitly** (**re-verified 2026-10-04**) -- `<vml_ui.h>` does not pull it in, and omitting it **still compiles, still runs,
 * 退出码 0**,只是 `sin()` 变成隐式声明(C89)后返回垃圾(实测 `float s = sin(0.5f)` 打印 **50000** = 0.5,
 * and exits 0**, only `sin()` becomes an implicit declaration (C89) returning garbage (measured: `float s = sin(0.5f)` prints **50000** = 0.5,
 * 即"原样把实参返回")⇒ `gen_sphere` 的顶点全退化 ⇒ **球体一个像素都不画**,
 * i.e. "returns the argument unchanged") => every `gen_sphere` vertex degenerates => **not a single pixel of the sphere is drawn**,
 * 而 `ui_3d_obj_get_screen` 报的坐标却是对的(那是矩阵算的)。
 * while the coordinates reported by `ui_3d_obj_get_screen` are correct (those come from the matrix).
 * 症状是「读数说有、画面里没有」——最难查的一类(另一个示例 g3_all.c 实测踩到,见文件头)。
 * The symptom is "the readings say it is there, the screen shows nothing" -- the hardest kind to find (the other example g3_all.c hit it; see its header). */
#include <math.h>


/* ~~⚠ 前端缺陷绕过:cos 永远不返回负值 ⇒ 只用 sin~~
/* ~~⚠ frontend-bug workaround: cos never returned a negative value => use sin only~~
 * ✅ **已修好(2026-10-04 复验)**:实测 `cos(3.14159265f)` 的 `(int)(…*1000)` 得 **-1000**(= -1.000),
 * ✅ **Fixed (re-verified 2026-10-04)**: measured, `cos(3.14159265f)` through `(int)(...*1000)` gives **-1000** (= -1.000),
 *    即 cos 现在正常返回负值。这个 `vcos` 包一层**照旧保留**(能跑就别动);**新写的代码直接 `cos()` 即可**
 *    i.e. cos now returns negative values properly. This `vcos` wrapper is **kept as is** (do not touch what works); **new code can just use `cos()`**
 *    (同目录 race3d.c / air3d.c / pool3d.c / tank3d.c 就一直直接用 cos,一直是对的)。
 *    (race3d.c / air3d.c / pool3d.c / tank3d.c in this directory have always used cos directly, and are correct). */
float vcos(float x) { float t; t = sin(x + 1.5707963f); return t; }


/* ══ 网格:一个单位球就够(物体再多也是同一个 mesh —— 这正是保留模式该有的样子)══ */
/* ══ mesh: one unit sphere suffices (however many objects, it is the same mesh -- exactly what retained mode should look like) ══ */
float MB[4400];
int g_vn;
int g_in;

void mb_vertex(int i, float px, float py, float pz, float nx, float ny, float nz) {
    int b = i * 8;
    MB[b + 0] = px; MB[b + 1] = py; MB[b + 2] = pz;
    MB[b + 3] = nx; MB[b + 4] = ny; MB[b + 5] = nz;
    MB[b + 6] = 0.0f; MB[b + 7] = 0.0f;
}
void mb_index(int k, int v) {
    int* ip; ip = (int*)&MB[0]; ip[g_vn * 8 + k] = v;
}
int mb_ptr(void) { return (int)&MB[0]; }

void gen_sphere(int rings, int segs, float r) {
    int i; int j; int k; int v;
    v = 0;
    for (i = 0; i <= rings; i = i + 1) {
        float phi = 3.14159265f * i / rings;
        float sp = sin(phi);
        float cp = vcos(phi);
        for (j = 0; j <= segs; j = j + 1) {
            float th = 6.28318531f * j / segs;
            float nx = sp * vcos(th);
            float ny = cp;
            float nz = sp * sin(th);
            mb_vertex(v, nx * r, ny * r, nz * r, nx, ny, nz);
            v = v + 1;
        }
    }
    g_vn = v;
    k = 0;
    for (i = 0; i < rings; i = i + 1) {
        for (j = 0; j < segs; j = j + 1) {
            int a = i * (segs + 1) + j;
            int b = a + segs + 1;
            mb_index(k, a);     k = k + 1;
            mb_index(k, b);     k = k + 1;
            mb_index(k, a + 1); k = k + 1;
            mb_index(k, a + 1); k = k + 1;
            mb_index(k, b);     k = k + 1;
            mb_index(k, b + 1); k = k + 1;
        }
    }
    g_in = k;
}

/* 8 种颜色轮换,让画面能看清物体确实在增多 */
/* rotate through 8 colors so the screen makes it clear the objects really are increasing */
int palette(int i) {
    int c;
    c = i % 8;
    if (c == 0) return -65536;      /* 0xFFFF0000 红 */
                                    /* 0xFFFF0000 red */
    if (c == 1) return -16711936;   /* 0xFF00FF00 绿 */
                                    /* 0xFF00FF00 green */
    if (c == 2) return -16776961;   /* 0xFF0000FF 蓝 */
                                    /* 0xFF0000FF blue */
    if (c == 3) return -256;        /* 0xFFFFFF00 黄 */
                                    /* 0xFFFFFF00 yellow */
    if (c == 4) return -65535;      /* 0xFFFF00FF 品红 */
                                    /* 0xFFFF00FF magenta */
    if (c == 5) return -16711681;   /* 0xFF00FFFF 青 */
                                    /* 0xFF00FFFF cyan */
    if (c == 6) return -32768;      /* 0xFFFF8000 橙 */
                                    /* 0xFFFF8000 orange */
    return -1;                      /* 0xFFFFFFFF 白 */
                                    /* 0xFFFFFFFF white */
}

int main(void) {
    int msg[8];
    int vp; int sw; int sh; int frame; int m;
    int created;                    /* 已创建的物体数 */
                                    /* number of objects created so far */
    int lastReport;

    ui_win_open_ex("3D 压力测试 BENCH", 0, 0, VML_WIN_ROTATABLE, VML_WIN_NO_GAMEPAD);
    sw = ui_scr_get_w_ex(ui_scr_get_orientation(), VML_CHROME_NONE);
    sh = ui_scr_get_h_ex(ui_scr_get_orientation(), VML_CHROME_NONE);
    if (sw <= 0) sw = 640;
    if (sh <= 0) sh = 640;

    vp = ui_3d_viewport_create(0, 0, sw, sh, 0);
    if (vp <= 0) {
        printf("NO_3D_DEVICE=1\n");
        while (ui_win_is_closed() == 0) { ui_msg_fetch_wait(100); }
        return 0;
    }

    ui_3d_viewport_set_camera(vp, 0.0f, 400.0f, 500.0f, 0.0f, 0.0f, 0.0f);
    ui_3d_viewport_set_fov(vp, 60.0f, 1.0f, 8000.0f);
    ui_3d_viewport_fit(vp, 320.0f);
    ui_3d_viewport_set_bg(vp, -16250870);
    ui_3d_viewport_set_light(vp, 0.45f, 1.0f, 0.35f, -1, 0.45f);
    ui_3d_viewport_set_depth(vp, 1);

    gen_sphere(10, 14, 1.0f);
    m = ui_3d_mesh_create(mb_ptr(), g_vn, g_in);
    printf("MESH=%d vn=%d in=%d\n", m, g_vn, g_in);
    if (m <= 0) { printf("NO_MESH=1\n"); return 0; }

    created = 0;
    lastReport = 0;
    frame = 0;
    while (ui_win_is_closed() == 0) {
        if (ui_key_is_down(VML_KEY_ESCAPE)) break;

        /* ── 加物体:每 60 帧 20 个,到 250 停(宿主上限 1024,250 是本程序 **刻意** 的停点) ── */
        /* ── add objects: 20 every 60 frames, stop at 250 (host cap is 1024; 250 is this program's **deliberate** stop point) ── */
        if (frame % 60 == 0 && created < 250) {
            int k;
            for (k = 0; k < 20 && created < 250; k = k + 1) {
                int o;
                float orbR;
                float phase;
                o = ui_3d_obj_create(vp, m);
                /* 轨道半径分 8 档;相位用**黄金角**散开(等差会与档数共振,见文件头) */
                /* orbit radius in 8 steps; phases spread with the **golden angle** (an arithmetic progression resonates with the step count; see the header) */
                orbR = 60.0f + (created % 8) * 32.0f;
                phase = created * 137.5f;
                ui_3d_obj_set_color(o, palette(created));
                ui_3d_obj_set_scale(o, 6.0f + (created % 5) * 2.0f);
                ui_3d_obj_set_spin(o, 0.0f, 30.0f + (created % 7) * 10.0f, 0.0f);
                ui_3d_obj_set_orbit(o, 0.0f, 0.0f, 0.0f, orbR,
                             3000.0f + (created % 8) * 1500.0f, phase);
                created = created + 1;
            }
        }

        ui_2d_clear(0);
        ui_2d_text(14, 12, "3D 压力测试 —— 物体越来越多", -1, 16, VML_ANCHOR_LEFT);
        ui_2d_text(14, 34, "物体数 / 帧号见下方;Esc 退出", -8355712, 11, VML_ANCHOR_LEFT);
        if (created < 100) ui_2d_text(14, 52, "物体 < 100", -256, 12, VML_ANCHOR_LEFT);
        ui_2d_present();

        frame = frame + 1;
        if (frame - lastReport >= 60) {
            lastReport = frame;
            printf("BENCH obj=%d frame=%d\n", created, frame);
        }
        ui_msg_fetch_wait(40);            /* 只是「等输入最多 40ms」—— 上屏节奏由宿主管。 */
                                          /* just "wait for input for at most 40ms" -- on-screen pacing is the host's job. */
    }
    printf("DONE obj=%d frame=%d\n", created, frame);
    return 0;
}