#ifndef RAY_PROFILE_H
#define RAY_PROFILE_H
#include <stdint.h>
#include <stdbool.h>
#if defined(RAY_OS_WINDOWS)
#include <windows.h>
#else
#include <time.h>
#ifndef CLOCK_MONOTONIC
#define CLOCK_MONOTONIC 1
int clock_gettime(int clk_id, struct timespec *tp);
#endif
#endif
#define RAY_PROFILE_SPANS_MAX 2048
typedef enum {
RAY_PROF_SPAN_START,
RAY_PROF_SPAN_END,
RAY_PROF_SPAN_TICK
} ray_prof_span_type_t;
typedef struct {
ray_prof_span_type_t type;
const char* msg;
int64_t ts;
} ray_prof_span_t;
typedef void (*ray_progress_fn)(int64_t done, int64_t total, const char* label);
typedef struct {
bool active;
int32_t n;
int64_t progress_total;
int64_t progress_done;
const char* progress_label;
int64_t progress_last_render;
ray_progress_fn progress_cb;
ray_prof_span_t spans[RAY_PROFILE_SPANS_MAX];
} ray_profile_t;
extern ray_profile_t g_ray_profile;
static inline int64_t ray_profile_now_ns(void) {
#if defined(RAY_OS_WINDOWS)
LARGE_INTEGER freq, cnt;
QueryPerformanceFrequency(&freq);
QueryPerformanceCounter(&cnt);
return (int64_t)((double)cnt.QuadPart / (double)freq.QuadPart * 1e9);
#else
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (int64_t)ts.tv_sec * 1000000000LL + (int64_t)ts.tv_nsec;
#endif
}
static inline void ray_profile_reset(void) {
g_ray_profile.n = 0;
g_ray_profile.progress_total = 0;
g_ray_profile.progress_done = 0;
g_ray_profile.progress_label = NULL;
g_ray_profile.progress_last_render = 0;
}
static inline void ray_profile_span_start(const char* name) {
if (!g_ray_profile.active) return;
if (g_ray_profile.n >= RAY_PROFILE_SPANS_MAX) return;
ray_prof_span_t* s = &g_ray_profile.spans[g_ray_profile.n++];
s->type = RAY_PROF_SPAN_START;
s->msg = name;
s->ts = ray_profile_now_ns();
}
static inline void ray_profile_span_end(const char* name) {
if (!g_ray_profile.active) return;
if (g_ray_profile.n >= RAY_PROFILE_SPANS_MAX) return;
ray_prof_span_t* s = &g_ray_profile.spans[g_ray_profile.n++];
s->type = RAY_PROF_SPAN_END;
s->msg = name;
s->ts = ray_profile_now_ns();
}
static inline void ray_profile_tick(const char* msg) {
if (!g_ray_profile.active) return;
if (g_ray_profile.n >= RAY_PROFILE_SPANS_MAX) return;
ray_prof_span_t* s = &g_ray_profile.spans[g_ray_profile.n++];
s->type = RAY_PROF_SPAN_TICK;
s->msg = msg;
s->ts = ray_profile_now_ns();
}
static inline void ray_profile_progress_begin(const char* label, int64_t total) {
if (!g_ray_profile.active) return;
g_ray_profile.progress_label = label;
g_ray_profile.progress_total = total;
g_ray_profile.progress_done = 0;
}
#define RAY_PROGRESS_RENDER_INTERVAL_NS (100 * 1000000LL)
static inline void ray_profile_progress_advance(int64_t delta) {
if (!g_ray_profile.active) return;
g_ray_profile.progress_done += delta;
if (g_ray_profile.progress_cb && g_ray_profile.progress_total > 0) {
int64_t now = ray_profile_now_ns();
if (now - g_ray_profile.progress_last_render > RAY_PROGRESS_RENDER_INTERVAL_NS) {
g_ray_profile.progress_last_render = now;
g_ray_profile.progress_cb(g_ray_profile.progress_done,
g_ray_profile.progress_total,
g_ray_profile.progress_label);
}
}
}
static inline void ray_profile_progress_end(void) {
if (!g_ray_profile.active) return;
g_ray_profile.progress_label = NULL;
g_ray_profile.progress_total = 0;
g_ray_profile.progress_done = 0;
}
#endif