diff --git a/dm/gfx/nvn/hle_nvn.c b/dm/gfx/nvn/hle_nvn.c index aba5a4f..700227f 100644 --- a/dm/gfx/nvn/hle_nvn.c +++ b/dm/gfx/nvn/hle_nvn.c @@ -1451,12 +1451,14 @@ NVN(nvnTextureViewSetSwizzle, void, NVNtextureView *v, NVNtextureSwizzle r, NVNt NVN(nvnColorStateSetAlphaTest, void, NVNcolorState *s, NVNalphaFunc f) { ST(color_st, s)->alpha_func = (uint32_t)f; } NVN(nvnColorStateSetLogicOp, void, NVNcolorState *s, NVNlogicOp op) { ST(color_st, s)->logic_op = (uint32_t)op; } -// 设备:组合贴图句柄 = 贴图号 | 采样器号 << 20(与分离句柄同一编码);计时器数据里的时间戳宿主已是纳秒 -// Device: combined texture handle = texture id | sampler id << 20 (same encoding as the separate handles); the timestamp in counter data is already in nanoseconds on the host +// 设备:组合贴图句柄 = 贴图号 | 采样器号 << 20(与分离句柄同一编码) +// Device: combined texture handle = texture id | sampler id << 20 (same encoding as the separate handles) NVN(nvnDeviceGetTextureHandle, NVNtextureHandle, const NVNdevice *d, int tex, int samp) { (void)d; return (NVNtextureHandle)((uint64_t)(uint32_t)tex | (uint64_t)(uint32_t)samp << 20); } -NVN(nvnDeviceGetTimestampInNanoseconds, uint64_t, const NVNdevice *d, const NVNcounterData *c) { (void)d; return c->timestamp; } -// 当前 GPU 时刻(纳秒):宿主 GPU 计时与计数器时戳同一时钟源(CLOCK_MONOTONIC) | Current GPU time (ns): the host's GPU timing and counter timestamps share one clock source (CLOCK_MONOTONIC) -NVN(nvnDeviceGetCurrentTimestampInNanoseconds, uint64_t, const NVNdevice *d) { (void)d; return clock_gettime_nsec_np(CLOCK_MONOTONIC); } +// 计数器时戳是 19.2 MHz 的 GPU 计数(nvn_mtl.m gpu_ticks:纳秒 × 12 / 625),换回纳秒。原先原样返回,比 GetCurrentTimestampInNanoseconds 小约 52 倍,两者一减就是乱数 +// Counter timestamps are 19.2 MHz GPU ticks (nvn_mtl.m gpu_ticks: ns × 12 / 625); convert back to ns. They used to be returned as is, about 52 times smaller than GetCurrentTimestampInNanoseconds, so subtracting one from the other gave garbage +NVN(nvnDeviceGetTimestampInNanoseconds, uint64_t, const NVNdevice *d, const NVNcounterData *c) { (void)d; return c->timestamp * 625 / 12; } +// 当前 GPU 时刻(纳秒):与计数器时戳同一时钟——客体时钟(guest_ns,nvn_mtl.m gpu_ticks) | Current GPU time (ns): the same clock as the counter timestamps -- the guest clock (guest_ns, nvn_mtl.m gpu_ticks) +NVN(nvnDeviceGetCurrentTimestampInNanoseconds, uint64_t, const NVNdevice *d) { (void)d; return guest_ns(); } NVN(nvnDeviceRegisterFastClearColor, NVNboolean, NVNdevice *d, const float *c, NVNformat f) { (void)d; (void)c; (void)f; return 1; } // 窗口:活动交换链张数(宿主按创建时张数轮转,只记一次) diff --git a/dm/gfx/nvn/nvn_mtl.m b/dm/gfx/nvn/nvn_mtl.m index d6b2887..5516424 100644 --- a/dm/gfx/nvn/nvn_mtl.m +++ b/dm/gfx/nvn/nvn_mtl.m @@ -633,7 +633,12 @@ void mtl_sub_event(volatile uint32_t *val, uint32_t mode, uint32_t v) { post((po void mtl_sub_counter(uint64_t addr, uint32_t type) { post((post_op){2, NULL, 0, 0, addr, type, NULL}); } void mtl_counter_stats(uint64_t rep[32], uint64_t rst[32]) { memcpy(rep, g_cnt_report, sizeof g_cnt_report); memcpy(rst, g_cnt_reset, sizeof g_cnt_reset); } -static uint64_t gpu_ticks(void) { struct timespec t; clock_gettime(CLOCK_MONOTONIC, &t); return ((uint64_t)t.tv_sec * 1000000000ull + t.tv_nsec) * 12 / 625; } +// 计数器时戳(19.2 MHz)取客体时钟(hle_os.c guest_ns,与 svc GetSystemTick / cntpct 同源):真机上 GPU 时戳与 CPU 计数同一时基,游戏会拿两者相比。 +// 原先取 CLOCK_MONOTONIC:它在 Mac 睡眠时照走,而客体时钟(mach_absolute_time)不走,两者差出睡眠时长;时戳换成纳秒后马力欧把帧率减半。图形单测里 guest_ns 是返回 0 的弱定义,退回单调时钟 +// Counter timestamps (19.2 MHz) are taken from the guest clock (hle_os.c guest_ns, the same source as svc GetSystemTick / cntpct): on hardware GPU timestamps and the CPU counter share one timebase, and games compare them. +// They used to come from CLOCK_MONOTONIC, which keeps running while the Mac sleeps while the guest clock (mach_absolute_time) does not, so the two differed by the sleep time; once timestamps were converted to ns, Mario halved its frame rate. In graphics unit tests guest_ns is a weak definition returning 0; fall back to the monotonic clock +uint64_t guest_ns(void); +static uint64_t gpu_ticks(void) { uint64_t g = guest_ns(); if (!g) { struct timespec t; clock_gettime(CLOCK_MONOTONIC, &t); g = (uint64_t)t.tv_sec * 1000000000ull + t.tv_nsec; } return g * 12 / 625; } // ---------- 启动埋点(DM_BOOTPROF=1):metallib 载入、管线创建(均在提交线程回放中同步发生)、贴图上传、首帧出图 ---------- // ---------- Boot instrumentation (DM_BOOTPROF=1): metallib loading, pipeline creation (both happen synchronously during replay on the submit thread), texture upload, first-frame present ----------