ref: 62bbc39e91d7f624ecf806d8b1b462c28ba206d5
parent: 5c3cfc53fa2b74c5f926a475b8cd73c7bad4beb5
author: Olav Sørensen <olav.sorensen@live.no>
date: Sat Apr 13 10:50:35 EDT 2024
HPC timer changes
--- a/src/ft2_hpc.c
+++ b/src/ft2_hpc.c
@@ -1,5 +1,13 @@
/*
-** Hardware Performance Counter delay routines
+** Hardware Performance Counter delay routines by 8bitbubsy.
+**
+** These are by no means well written, and are made for specific
+** usage cases. There may be soome hackish design choices here.
+**
+** NOTE: hpc_SetDurationInHz() has quite a bit of overhead, so it's
+** recommended to have one hpcFreq_t counter for each delay value,
+** then call hpc_SetDurationInHz()/hpc_SetDurationInMs() on program
+** init instead of every time you want to delay.
*/
#ifdef _WIN32
@@ -13,7 +21,7 @@
#include <stdbool.h>
#include "ft2_hpc.h"
-#define FRAC_BITS 63
+#define FRAC_BITS 63 /* leaves one bit for frac overflow test */
#define FRAC_SCALE (1ULL << FRAC_BITS)
#define FRAC_MASK (FRAC_SCALE-1)
@@ -31,7 +39,7 @@
static void (*usleep)(int32_t usec);
-static void usleepGood(int32_t usec)
+static void usleepAcceptable(int32_t usec)
{
LARGE_INTEGER delayInterval;
@@ -51,7 +59,7 @@
static void windowsSetupUsleep(void)
{
NtDelayExecution = (NTSTATUS (__stdcall *)(BOOL, PLARGE_INTEGER))GetProcAddress(GetModuleHandle("ntdll.dll"), "NtDelayExecution");
- usleep = (NtDelayExecution != NULL) ? usleepGood : usleepPoor;
+ usleep = (NtDelayExecution != NULL) ? usleepAcceptable : usleepPoor;
NtQueryTimerResolution = (NTSTATUS (__stdcall *)(PULONG, PULONG, PULONG))GetProcAddress(GetModuleHandle("ntdll.dll"), "NtQueryTimerResolution");
NtSetTimerResolution = (NTSTATUS (__stdcall *)(ULONG, BOOLEAN, PULONG))GetProcAddress(GetModuleHandle("ntdll.dll"), "NtSetTimerResolution");
@@ -92,15 +100,36 @@
return ((uint64_t)resultHi << 32) | resultLo;
}
-void hpc_SetDurationInHz(hpc_t *hpc, uint32_t hz)
+void hpc_SetDurationInHz(hpc_t *hpc, double dHz) // dHz = max 8191.999inf Hz (0.12ms)
{
+#define BITS_IN_UINT32 32
+
+ /* 19 = Good compensation between fraction bits and max integer size.
+ ** Non-realtime OSes probably can't do a thread delay with such a
+ ** high precision (0.12ms) anyway.
+ */
+#define INPUT_FRAC_BITS 19
+#define INPUT_FRAC_SCALE (1UL << INPUT_FRAC_BITS)
+#define INPUT_INT_MAX ((1UL << (BITS_IN_UINT32-INPUT_FRAC_BITS))-1)
+
+ if (dHz > INPUT_INT_MAX)
+ dHz = INPUT_INT_MAX;
+
+ const uint32_t fpHz = (uint32_t)((dHz * INPUT_FRAC_SCALE) + 0.5);
+
// set 64:63fp value
- hpc->durationInt = hpcFreq.freq64 / hz;
- hpc->durationFrac = getFrac64FromU64DivU32(hpcFreq.freq64, hz) >> 1;
+ const uint64_t fpFreq64 = hpcFreq.freq64 << INPUT_FRAC_BITS;
+ hpc->durationInt = fpFreq64 / fpHz;
+ hpc->durationFrac = getFrac64FromU64DivU32(fpFreq64, fpHz) >> 1; // 64 -> 63 bits (1 bit for frac overflow test)
- hpc->resetFrame = hz * (60 * 30); // reset counters every half an hour
+ hpc->resetFrame = ((uint64_t)fpHz * (60 * 30)) / INPUT_FRAC_SCALE; // reset counters every half an hour
}
+void hpc_SetDurationInMs(hpc_t *hpc, double dMs) // dMs = minimum 0.12208521548 ms
+{
+ hpc_SetDurationInHz(hpc, 1000.0 / dMs);
+}
+
void hpc_ResetCounters(hpc_t *hpc)
{
hpc->endTimeInt = SDL_GetPerformanceCounter() + hpc->durationInt;
@@ -152,7 +181,7 @@
/* The counter ("endTimeInt") can accumulate major errors after a couple of hours,
** since each frame is not happening at perfect intervals.
** To fix this, reset the counter's int & frac once every half an hour. We should only
- ** get up to one frame of stutter while they are resetting, then it's back to normal.
+ ** get up to one frame of jitter while they are resetting, then it's back to normal.
*/
hpc->frameCounter++;
if (hpc->frameCounter >= hpc->resetFrame)
--- a/src/ft2_hpc.h
+++ b/src/ft2_hpc.h
@@ -20,7 +20,8 @@
extern hpcFreq_t hpcFreq;
void hpc_Init(void);
-void hpc_SetDurationInHz(hpc_t *hpc, uint32_t dHz);
+void hpc_SetDurationInHz(hpc_t *hpc, double dHz); // dHz = max 8191.999inf Hz (0.12ms)
+void hpc_SetDurationInMs(hpc_t *hpc, double dMs); // dMs = minimum 0.12208521548 ms
void hpc_ResetCounters(hpc_t *hpc);
void hpc_Wait(hpc_t *hpc);