Files
astromaddie--HaloCE-VR/port/android/host/host_debug.c
T
MadisonandClaude Opus 5.5 12c74e43f2 Read the visibility counters once a frame; fix the HUD pass's fog
The Android renderer answered each visibility test (lens-flare
occlusion) by mapping the counter buffer for its one word, and each map
waits for the GPU's queue to drain; on the Steam Frame's Zink it also
flushes and logs a performance warning. In stereo, with the flare tests
of two eyes, that took 45% of the game thread: 13-20 frames a second in
the first level's cryo bay. All counters are now read together into a
copy the first time a result is asked for after a test ends (one wait a
frame), and the same scene runs at the headset's 72.

The headset's HUD pass took the planar fog, whose animation offset only
a structure pass consumes, and the next frame's assertion stopped the
game in fogged clusters; the HUD pass leaves the fog alone.

In VR the head's turns count as looking for the scripts that wait for
the player to look (the first level's look test).

The host's thread sampler takes fractions of a second.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 10:28:08 +09:00

122 lines
3.2 KiB
C

/*
HOST_DEBUG.C
A sampler for finding where the guest spends its time (or hangs) on a
device without root, where debuggerd cannot attach: with sample_seconds in
config.toml's [debug], every guest thread is interrupted that often and its program
counter, link register and frame chain are written to logcat. The addresses
symbolize against build/android/halo_guest.elf (llvm-symbolizer
--obj=build/android/halo_guest.elf 0x...).
*/
#include "host.h"
#include <pthread.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <sys/syscall.h>
#include <ucontext.h>
#include <unistd.h>
#define MAXIMUM_THREADS 64
#define SAMPLE_SIGNAL SIGURG
static pid_t guest_threads[MAXIMUM_THREADS];
static pthread_mutex_t threads_lock = PTHREAD_MUTEX_INITIALIZER;
void host_debug_thread_started(void)
{
int index;
pthread_mutex_lock(&threads_lock);
for (index = 0; index < MAXIMUM_THREADS; index++)
{
if (!guest_threads[index])
{
guest_threads[index] = gettid();
break;
}
}
pthread_mutex_unlock(&threads_lock);
}
void host_debug_thread_exited(void)
{
pid_t self = gettid();
int index;
pthread_mutex_lock(&threads_lock);
for (index = 0; index < MAXIMUM_THREADS; index++)
{
if (guest_threads[index] == self)
guest_threads[index] = 0;
}
pthread_mutex_unlock(&threads_lock);
}
static void sample_handler(int signal_number, siginfo_t *information, void *context)
{
const ucontext_t *ucontext = context;
const struct sigcontext *registers = (const struct sigcontext *)&ucontext->uc_mcontext;
char line[512];
int length, depth;
uint64_t fp = registers->regs[29];
(void)signal_number;
(void)information;
length = snprintf(line, sizeof(line), "sample tid %d: pc %llx lr %llx", gettid(),
(unsigned long long)registers->pc, (unsigned long long)registers->regs[30]);
for (depth = 0; depth < 12 && fp && fp < 0x100000000ULL && !(fp & 7) && host_low_owns(fp, 16); depth++)
{
const uint64_t *frame = (const uint64_t *)fp;
length += snprintf(line + length, sizeof(line) - length, " %llx", (unsigned long long)frame[1]);
if (frame[0] <= fp)
break;
fp = frame[0];
}
host_logf(HOST_LOG_INFO, "%s", line);
}
static void *sampler(void *context)
{
unsigned int milliseconds = (unsigned int)(uintptr_t)context;
for (;;)
{
pid_t threads[MAXIMUM_THREADS];
int index;
usleep(milliseconds * 1000u);
pthread_mutex_lock(&threads_lock);
memcpy(threads, guest_threads, sizeof(threads));
pthread_mutex_unlock(&threads_lock);
for (index = 0; index < MAXIMUM_THREADS; index++)
{
if (threads[index])
syscall(SYS_tgkill, getpid(), threads[index], SAMPLE_SIGNAL);
}
}
return NULL;
}
void host_debug_start_sampler(const char *setting)
{
struct sigaction action;
pthread_t thread;
/* fractions of a second too: a profile wants many samples */
unsigned int milliseconds = setting ? (unsigned int)(atof(setting) * 1000.0) : 0;
if (!milliseconds)
return;
memset(&action, 0, sizeof(action));
action.sa_sigaction = sample_handler;
action.sa_flags = SA_SIGINFO | SA_RESTART;
sigemptyset(&action.sa_mask);
sigaction(SAMPLE_SIGNAL, &action, NULL);
if (pthread_create(&thread, NULL, sampler, (void *)(uintptr_t)milliseconds) == 0)
pthread_detach(thread);
host_logf(HOST_LOG_INFO, "sampling guest threads every %u ms", milliseconds);
}