benchmark-toolbox: add benchmark-toolbox
a unified benchmarking tool
This commit is contained in:
390
Source/Benchmark-Toolbox/source/bench.c
Normal file
390
Source/Benchmark-Toolbox/source/bench.c
Normal file
@@ -0,0 +1,390 @@
|
||||
/*
|
||||
* bench.c — CPU bandwidth + latency benchmarks and system info.
|
||||
* Refactored from Membench-NX/main.c into result-returning functions with no
|
||||
* console I/O, so a GUI (borealis) can drive them from a worker thread.
|
||||
*
|
||||
* Original bandwidth/latency methodology:
|
||||
* Copyright (c) 2011 Siarhei Siamashka, (c) 20xx KazushiMe, (c) 2025 Souldbminer
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <switch.h>
|
||||
|
||||
#include "bench.h"
|
||||
#include "gpu_bw.h"
|
||||
#include <sys/time.h>
|
||||
|
||||
#define SIZE (32 * 1024 * 1024)
|
||||
#define MAXREPEATS 10
|
||||
#define LATBENCH_COUNT 10000000
|
||||
#define ALIGN_PADDING 0x100000
|
||||
#define CACHE_LINE_SIZE 128
|
||||
|
||||
struct f_data {
|
||||
void (*func)(int64_t *, int64_t *, int);
|
||||
int64_t *arg1;
|
||||
int64_t *arg2;
|
||||
int arg3;
|
||||
};
|
||||
|
||||
static pthread_cond_t p_ready, p_start;
|
||||
static pthread_mutex_t p_lock;
|
||||
static pthread_t *p_worker = NULL;
|
||||
static struct f_data *worker_data = NULL;
|
||||
static int p_worker_not_ready, p_workers_ready;
|
||||
|
||||
static void *thread_func(void *data) {
|
||||
struct f_data *d = data;
|
||||
pthread_mutex_lock(&p_lock);
|
||||
p_worker_not_ready--;
|
||||
if (!p_worker_not_ready)
|
||||
pthread_cond_signal(&p_ready);
|
||||
while (p_workers_ready != 1)
|
||||
pthread_cond_wait(&p_start, &p_lock);
|
||||
pthread_mutex_unlock(&p_lock);
|
||||
(d->func)(d->arg1, d->arg2, d->arg3);
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
static void parallel_run(void) {
|
||||
pthread_mutex_lock(&p_lock);
|
||||
p_workers_ready = 1;
|
||||
pthread_mutex_unlock(&p_lock);
|
||||
pthread_cond_broadcast(&p_start);
|
||||
}
|
||||
|
||||
static void parallel_init(int threads) {
|
||||
pthread_attr_t attr;
|
||||
pthread_cond_init(&p_ready, NULL);
|
||||
pthread_cond_init(&p_start, NULL);
|
||||
pthread_mutex_init(&p_lock, NULL);
|
||||
p_worker_not_ready = threads;
|
||||
p_workers_ready = 0;
|
||||
pthread_attr_init(&attr);
|
||||
if (!p_worker || !worker_data) {
|
||||
p_worker = malloc(threads * sizeof(pthread_t));
|
||||
worker_data = malloc(threads * sizeof(struct f_data));
|
||||
}
|
||||
for (int i = 0; i < threads; i++)
|
||||
pthread_create(p_worker + i, &attr, thread_func, worker_data + i);
|
||||
pthread_mutex_lock(&p_lock);
|
||||
while (p_worker_not_ready != 0)
|
||||
pthread_cond_wait(&p_ready, &p_lock);
|
||||
pthread_mutex_unlock(&p_lock);
|
||||
}
|
||||
|
||||
static void aligned_block_copy(int64_t *__restrict dst_, int64_t *__restrict src, int size) {
|
||||
volatile int64_t *dst = dst_;
|
||||
int64_t t1, t2, t3, t4;
|
||||
while ((size -= 64) >= 0) {
|
||||
t1 = *src++;
|
||||
t2 = *src++;
|
||||
t3 = *src++;
|
||||
t4 = *src++;
|
||||
*dst++ = t1;
|
||||
*dst++ = t2;
|
||||
*dst++ = t3;
|
||||
*dst++ = t4;
|
||||
t1 = *src++;
|
||||
t2 = *src++;
|
||||
t3 = *src++;
|
||||
t4 = *src++;
|
||||
*dst++ = t1;
|
||||
*dst++ = t2;
|
||||
*dst++ = t3;
|
||||
*dst++ = t4;
|
||||
}
|
||||
}
|
||||
|
||||
static void aligned_block_fetch(int64_t *__restrict dst, int64_t *__restrict src_, int size) {
|
||||
volatile int64_t *src = src_;
|
||||
(void)dst;
|
||||
while ((size -= 64) >= 0) {
|
||||
*src++;
|
||||
*src++;
|
||||
*src++;
|
||||
*src++;
|
||||
*src++;
|
||||
*src++;
|
||||
*src++;
|
||||
*src++;
|
||||
}
|
||||
}
|
||||
|
||||
static void aligned_block_fill(int64_t *__restrict dst_, int64_t *__restrict src, int size) {
|
||||
volatile int64_t *dst = dst_;
|
||||
int64_t data = *src;
|
||||
while ((size -= 64) >= 0) {
|
||||
*dst++ = data;
|
||||
*dst++ = data;
|
||||
*dst++ = data;
|
||||
*dst++ = data;
|
||||
*dst++ = data;
|
||||
*dst++ = data;
|
||||
*dst++ = data;
|
||||
*dst++ = data;
|
||||
}
|
||||
}
|
||||
|
||||
static double gettime(void) {
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
return (double)((int64_t)tv.tv_sec * 1000000 + tv.tv_usec) / 1000000.;
|
||||
}
|
||||
|
||||
static double bandwidth_bench_helper(int threads, int64_t *dstbuf, int64_t *srcbuf, int size, void (*f)(int64_t *, int64_t *, int)) {
|
||||
int i, loopcount, innerloopcount, n;
|
||||
double t, t1, t2, speed, maxspeed, s, s0, s1, s2;
|
||||
|
||||
s = s0 = s1 = s2 = 0.;
|
||||
maxspeed = 0.;
|
||||
for (n = 0; n < MAXREPEATS; n++) {
|
||||
loopcount = 0;
|
||||
innerloopcount = 1;
|
||||
t = 0.;
|
||||
do {
|
||||
loopcount += innerloopcount;
|
||||
for (i = 0; i < innerloopcount; i++) {
|
||||
parallel_init(threads);
|
||||
for (int pt = 0; pt < threads; pt++) {
|
||||
(worker_data + pt)->func = f;
|
||||
(worker_data + pt)->arg1 = dstbuf + size * pt / sizeof(int64_t);
|
||||
(worker_data + pt)->arg2 = srcbuf + size * pt / sizeof(int64_t);
|
||||
(worker_data + pt)->arg3 = size;
|
||||
}
|
||||
t1 = gettime();
|
||||
parallel_run();
|
||||
for (int pt = 0; pt < threads; pt++)
|
||||
pthread_join(p_worker[pt], NULL);
|
||||
t2 = gettime();
|
||||
t += t2 - t1;
|
||||
}
|
||||
innerloopcount *= 2;
|
||||
} while (t < 0.5);
|
||||
|
||||
speed = (double)size * threads * loopcount / t / 1000000.;
|
||||
s0 += 1.;
|
||||
s1 += speed;
|
||||
s2 += speed * speed;
|
||||
if (speed > maxspeed)
|
||||
maxspeed = speed;
|
||||
if (s0 > 2.) {
|
||||
s = sqrt((s0 * s2 - s1 * s1) / (s0 * (s0 - 1)));
|
||||
if (s < maxspeed / 1000.)
|
||||
break;
|
||||
}
|
||||
}
|
||||
return maxspeed;
|
||||
}
|
||||
|
||||
static char *align_up(char *ptr, int align) {
|
||||
return (char *)(((uintptr_t)ptr + align - 1) & ~(uintptr_t)(align - 1));
|
||||
}
|
||||
|
||||
static void *alloc_nonaliased_buffers(void **buf1_, int size1, void **buf2_, int size2, void **buf3_, int size3) {
|
||||
char **buf1 = (char **)buf1_, **buf2 = (char **)buf2_, **buf3 = (char **)buf3_;
|
||||
int mask = (ALIGN_PADDING - 1) & ~(CACHE_LINE_SIZE - 1);
|
||||
char *buf = malloc(size1 + size2 + size3 + 9 * ALIGN_PADDING);
|
||||
char *ptr = buf;
|
||||
memset(buf, 0xCC, size1 + size2 + size3 + 9 * ALIGN_PADDING);
|
||||
ptr = align_up(ptr, ALIGN_PADDING);
|
||||
if (buf1) {
|
||||
*buf1 = ptr + (0xAAAAAAAA & mask);
|
||||
ptr = align_up(*buf1 + size1, ALIGN_PADDING);
|
||||
}
|
||||
if (buf2) {
|
||||
*buf2 = ptr + (0x55555555 & mask);
|
||||
ptr = align_up(*buf2 + size2, ALIGN_PADDING);
|
||||
}
|
||||
if (buf3) {
|
||||
*buf3 = ptr + (0xCCCCCCCC & mask);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
static void __attribute__((noinline)) random_read_test(char *buf, int count, int nbits) {
|
||||
uint32_t seed = 0;
|
||||
uintptr_t mask = (1 << nbits) - 1;
|
||||
uint32_t v;
|
||||
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
|
||||
static volatile uint32_t dummy;
|
||||
#define RMA() \
|
||||
seed = seed * 1103515245 + 12345; \
|
||||
v = (seed >> 16) & 0xFF; \
|
||||
seed = seed * 1103515245 + 12345; \
|
||||
v |= (seed >> 8) & 0xFF00; \
|
||||
seed = seed * 1103515245 + 12345; \
|
||||
v |= seed & 0x7FFF0000; \
|
||||
seed |= buf[v & mask];
|
||||
while (count >= 16) {
|
||||
RMA() RMA() RMA() RMA() RMA() RMA() RMA() RMA() RMA() RMA() RMA() RMA() RMA() RMA() RMA() RMA() count -= 16;
|
||||
}
|
||||
dummy = seed;
|
||||
#undef RMA
|
||||
}
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
static double latency_measure(char *buf, int nbits, int count) {
|
||||
double t_noaccess = 0, t_before, t_after, t, xs1 = 0, xs2 = 0, min_t = 0;
|
||||
double xs;
|
||||
int n;
|
||||
|
||||
for (n = 1; n <= MAXREPEATS; n++) {
|
||||
t_before = gettime();
|
||||
random_read_test(buf, count, 1);
|
||||
t_after = gettime();
|
||||
if (n == 1 || t_after - t_before < t_noaccess)
|
||||
t_noaccess = t_after - t_before;
|
||||
}
|
||||
for (n = 1; n <= MAXREPEATS; n++) {
|
||||
t_before = gettime();
|
||||
random_read_test(buf, count, nbits);
|
||||
t_after = gettime();
|
||||
t = t_after - t_before - t_noaccess;
|
||||
if (t < 0)
|
||||
t = 0;
|
||||
xs1 += t;
|
||||
xs2 += t * t;
|
||||
if (n == 1 || t < min_t)
|
||||
min_t = t;
|
||||
if (n > 2) {
|
||||
xs = sqrt((xs2 * n - xs1 * xs1) / (n * (n - 1)));
|
||||
if (xs < min_t / 1000.)
|
||||
break;
|
||||
}
|
||||
}
|
||||
return min_t * 1000000000.0 / count;
|
||||
}
|
||||
|
||||
static void latency_bench(double *l2_out, double *ram_out) {
|
||||
char *buf_alloc = malloc(0x2001000);
|
||||
char *buf = (char *)(((uintptr_t)buf_alloc + 4095) & ~(uintptr_t)4095);
|
||||
memset(buf, 0, 0x2000000);
|
||||
*l2_out = latency_measure(buf, 20, LATBENCH_COUNT);
|
||||
*ram_out = latency_measure(buf, 25, LATBENCH_COUNT);
|
||||
free(buf_alloc);
|
||||
}
|
||||
|
||||
void bench_get_sysinfo(sysinfo_t *out) {
|
||||
memset(out, 0, sizeof(*out));
|
||||
out->threads = 3;
|
||||
out->is_4gb = (appletGetAppletType() == AppletType_Application);
|
||||
if (R_SUCCEEDED(clkrstInitialize())) {
|
||||
ClkrstSession s;
|
||||
clkrstOpenSession(&s, PcvModuleId_CpuBus, 3);
|
||||
clkrstGetClockRate(&s, &out->cpu_hz);
|
||||
clkrstCloseSession(&s);
|
||||
clkrstOpenSession(&s, PcvModuleId_GPU, 3);
|
||||
clkrstGetClockRate(&s, &out->gpu_hz);
|
||||
clkrstCloseSession(&s);
|
||||
clkrstOpenSession(&s, PcvModuleId_EMC, 3);
|
||||
clkrstGetClockRate(&s, &out->mem_hz);
|
||||
clkrstCloseSession(&s);
|
||||
clkrstExit();
|
||||
}
|
||||
}
|
||||
|
||||
void bench_run_full(bench_results_t *out, bench_progress_fn progress, void *user) {
|
||||
const int threads = 3;
|
||||
const int size = SIZE;
|
||||
bool is_4gb = (appletGetAppletType() == AppletType_Application);
|
||||
memset(out, 0, sizeof(*out));
|
||||
|
||||
#define STEP(label, frac) \
|
||||
do { \
|
||||
if (progress) \
|
||||
progress((label), (frac), user); \
|
||||
} while (0)
|
||||
|
||||
STEP("GPU bandwidth", 0.05f);
|
||||
gpu_bw_run(is_4gb, &out->gpu_copy, &out->gpu_read, &out->gpu_write);
|
||||
|
||||
int64_t *srcbuf, *dstbuf;
|
||||
void *poolbuf = alloc_nonaliased_buffers((void **)&srcbuf, size * threads, (void **)&dstbuf, size * threads, NULL, 0);
|
||||
|
||||
STEP("CPU copy", 0.40f);
|
||||
out->cpu_copy = bandwidth_bench_helper(threads, dstbuf, srcbuf, size, aligned_block_copy);
|
||||
STEP("CPU read", 0.55f);
|
||||
out->cpu_read = bandwidth_bench_helper(threads, dstbuf, srcbuf, size, aligned_block_fetch);
|
||||
STEP("CPU write", 0.70f);
|
||||
out->cpu_write = bandwidth_bench_helper(threads, dstbuf, srcbuf, size, aligned_block_fill);
|
||||
free(poolbuf);
|
||||
|
||||
STEP("Latency", 0.85f);
|
||||
latency_bench(&out->l2_ns, &out->ram_ns);
|
||||
|
||||
STEP("Done", 1.0f);
|
||||
#undef STEP
|
||||
}
|
||||
|
||||
struct bench_ctx {
|
||||
int phase;
|
||||
bool is_4gb;
|
||||
void *pool;
|
||||
int64_t *src;
|
||||
int64_t *dst;
|
||||
};
|
||||
|
||||
bench_ctx *bench_begin(void) {
|
||||
bench_ctx *c = (bench_ctx *)calloc(1, sizeof(bench_ctx));
|
||||
if (!c)
|
||||
return NULL;
|
||||
c->is_4gb = (appletGetAppletType() == AppletType_Application);
|
||||
c->pool = alloc_nonaliased_buffers((void **)&c->src, SIZE * 3, (void **)&c->dst, SIZE * 3, NULL, 0);
|
||||
return c;
|
||||
}
|
||||
|
||||
bool bench_step(bench_ctx *c, bench_results_t *out, const char **label, float *frac) {
|
||||
const int threads = 3;
|
||||
const int size = SIZE;
|
||||
switch (c->phase) {
|
||||
case 0:
|
||||
gpu_bw_run(c->is_4gb, &out->gpu_copy, &out->gpu_read, &out->gpu_write);
|
||||
*label = "GPU bandwidth";
|
||||
*frac = 0.25f;
|
||||
break;
|
||||
case 1:
|
||||
out->cpu_copy = bandwidth_bench_helper(threads, c->dst, c->src, size, aligned_block_copy);
|
||||
*label = "CPU copy";
|
||||
*frac = 0.45f;
|
||||
break;
|
||||
case 2:
|
||||
out->cpu_read = bandwidth_bench_helper(threads, c->dst, c->src, size, aligned_block_fetch);
|
||||
*label = "CPU read";
|
||||
*frac = 0.60f;
|
||||
break;
|
||||
case 3:
|
||||
out->cpu_write = bandwidth_bench_helper(threads, c->dst, c->src, size, aligned_block_fill);
|
||||
*label = "CPU write";
|
||||
*frac = 0.75f;
|
||||
break;
|
||||
case 4:
|
||||
if (c->pool) {
|
||||
free(c->pool);
|
||||
c->pool = NULL;
|
||||
}
|
||||
latency_bench(&out->l2_ns, &out->ram_ns);
|
||||
*label = "Latency";
|
||||
*frac = 0.95f;
|
||||
break;
|
||||
default:
|
||||
*label = "Done";
|
||||
*frac = 1.0f;
|
||||
return false;
|
||||
}
|
||||
c->phase++;
|
||||
return true;
|
||||
}
|
||||
|
||||
void bench_end(bench_ctx *c) {
|
||||
if (!c)
|
||||
return;
|
||||
if (c->pool)
|
||||
free(c->pool);
|
||||
free(c);
|
||||
}
|
||||
36
Source/Benchmark-Toolbox/source/bench.h
Normal file
36
Source/Benchmark-Toolbox/source/bench.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
uint32_t cpu_hz;
|
||||
uint32_t gpu_hz;
|
||||
uint32_t mem_hz;
|
||||
bool is_4gb;
|
||||
int threads;
|
||||
} sysinfo_t;
|
||||
|
||||
void bench_get_sysinfo(sysinfo_t *out);
|
||||
|
||||
typedef struct {
|
||||
double gpu_copy, gpu_read, gpu_write;
|
||||
double cpu_copy, cpu_read, cpu_write;
|
||||
double l2_ns, ram_ns;
|
||||
} bench_results_t;
|
||||
|
||||
typedef void (*bench_progress_fn)(const char *stage, float frac, void *user);
|
||||
|
||||
void bench_run_full(bench_results_t *out, bench_progress_fn progress, void *user);
|
||||
|
||||
typedef struct bench_ctx bench_ctx;
|
||||
bench_ctx *bench_begin(void);
|
||||
bool bench_step(bench_ctx *ctx, bench_results_t *out, const char **label, float *frac);
|
||||
void bench_end(bench_ctx *ctx);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
2105
Source/Benchmark-Toolbox/source/furmark/BHRT.cpp
Normal file
2105
Source/Benchmark-Toolbox/source/furmark/BHRT.cpp
Normal file
File diff suppressed because it is too large
Load Diff
3
Source/Benchmark-Toolbox/source/furmark/BHRT.h
Normal file
3
Source/Benchmark-Toolbox/source/furmark/BHRT.h
Normal file
@@ -0,0 +1,3 @@
|
||||
void BHRTSceneInit();
|
||||
void BHRTRender();
|
||||
void BHRTExit();
|
||||
1089
Source/Benchmark-Toolbox/source/furmark/CPURB.cpp
Normal file
1089
Source/Benchmark-Toolbox/source/furmark/CPURB.cpp
Normal file
File diff suppressed because it is too large
Load Diff
3
Source/Benchmark-Toolbox/source/furmark/CPURB.h
Normal file
3
Source/Benchmark-Toolbox/source/furmark/CPURB.h
Normal file
@@ -0,0 +1,3 @@
|
||||
void CPURBSceneinit();
|
||||
void CPURBRender();
|
||||
void CPURBExit();
|
||||
1048
Source/Benchmark-Toolbox/source/furmark/CPURT.cpp
Normal file
1048
Source/Benchmark-Toolbox/source/furmark/CPURT.cpp
Normal file
File diff suppressed because it is too large
Load Diff
3
Source/Benchmark-Toolbox/source/furmark/CPURT.h
Normal file
3
Source/Benchmark-Toolbox/source/furmark/CPURT.h
Normal file
@@ -0,0 +1,3 @@
|
||||
void CPURTSceneinit();
|
||||
void CPURTRender();
|
||||
void CPURTExit();
|
||||
770
Source/Benchmark-Toolbox/source/furmark/GPUPT.cpp
Normal file
770
Source/Benchmark-Toolbox/source/furmark/GPUPT.cpp
Normal file
@@ -0,0 +1,770 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <switch.h>
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
#include <glad/glad.h>
|
||||
|
||||
#define GLM_FORCE_PURE
|
||||
#include "sates.h"
|
||||
#include "stb_image.h"
|
||||
#include <glm/gtc/constants.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <glm/mat4x4.hpp>
|
||||
#include <glm/vec3.hpp>
|
||||
#include <glm/vec4.hpp>
|
||||
|
||||
#ifndef ENABLE_NXLINK
|
||||
#define TRACE(fmt, ...) ((void)0)
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#define TRACE(fmt, ...) printf("%s: " fmt "\n", __PRETTY_FUNCTION__, ##__VA_ARGS__)
|
||||
|
||||
static int s_nxlinkSock = -1;
|
||||
|
||||
static void initNxLink() {
|
||||
if (R_FAILED(socketInitializeDefault()))
|
||||
return;
|
||||
|
||||
s_nxlinkSock = nxlinkStdio();
|
||||
if (s_nxlinkSock >= 0)
|
||||
TRACE("printf output now goes to nxlink server");
|
||||
else
|
||||
socketExit();
|
||||
}
|
||||
|
||||
static void deinitNxLink() {
|
||||
if (s_nxlinkSock >= 0) {
|
||||
close(s_nxlinkSock);
|
||||
socketExit();
|
||||
s_nxlinkSock = -1;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void userAppInit() {
|
||||
initNxLink();
|
||||
}
|
||||
|
||||
extern "C" void userAppExit() {
|
||||
deinitNxLink();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static EGLDisplay s_display;
|
||||
static EGLContext s_context;
|
||||
static EGLSurface s_surface;
|
||||
|
||||
static bool initEgl(NWindow *win) {
|
||||
|
||||
s_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
||||
if (!s_display) {
|
||||
TRACE("Could not connect to display! error: %d", eglGetError());
|
||||
goto _fail0;
|
||||
}
|
||||
|
||||
eglInitialize(s_display, nullptr, nullptr);
|
||||
|
||||
if (eglBindAPI(EGL_OPENGL_API) == EGL_FALSE) {
|
||||
TRACE("Could not set API! error: %d", eglGetError());
|
||||
goto _fail1;
|
||||
}
|
||||
|
||||
EGLConfig config;
|
||||
EGLint numConfigs;
|
||||
static const EGLint framebufferAttributeList[] = { EGL_RENDERABLE_TYPE,
|
||||
EGL_OPENGL_BIT,
|
||||
EGL_RED_SIZE,
|
||||
8,
|
||||
EGL_GREEN_SIZE,
|
||||
8,
|
||||
EGL_BLUE_SIZE,
|
||||
8,
|
||||
EGL_ALPHA_SIZE,
|
||||
8,
|
||||
EGL_DEPTH_SIZE,
|
||||
24,
|
||||
EGL_STENCIL_SIZE,
|
||||
8,
|
||||
EGL_NONE };
|
||||
eglChooseConfig(s_display, framebufferAttributeList, &config, 1, &numConfigs);
|
||||
if (numConfigs == 0) {
|
||||
TRACE("No config found! error: %d", eglGetError());
|
||||
goto _fail1;
|
||||
}
|
||||
|
||||
s_surface = eglCreateWindowSurface(s_display, config, win, nullptr);
|
||||
if (!s_surface) {
|
||||
TRACE("Surface creation failed! error: %d", eglGetError());
|
||||
goto _fail1;
|
||||
}
|
||||
|
||||
static const EGLint contextAttributeList[] = { EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR,
|
||||
EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR,
|
||||
EGL_CONTEXT_MAJOR_VERSION_KHR,
|
||||
4,
|
||||
EGL_CONTEXT_MINOR_VERSION_KHR,
|
||||
3,
|
||||
EGL_NONE };
|
||||
s_context = eglCreateContext(s_display, config, EGL_NO_CONTEXT, contextAttributeList);
|
||||
if (!s_context) {
|
||||
TRACE("Context creation failed! error: %d", eglGetError());
|
||||
goto _fail2;
|
||||
}
|
||||
|
||||
eglMakeCurrent(s_display, s_surface, s_surface, s_context);
|
||||
return true;
|
||||
|
||||
_fail2:
|
||||
eglDestroySurface(s_display, s_surface);
|
||||
s_surface = nullptr;
|
||||
_fail1:
|
||||
eglTerminate(s_display);
|
||||
s_display = nullptr;
|
||||
_fail0:
|
||||
return false;
|
||||
}
|
||||
|
||||
static void deinitEgl() {
|
||||
if (s_display) {
|
||||
eglMakeCurrent(s_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
if (s_context) {
|
||||
eglDestroyContext(s_display, s_context);
|
||||
s_context = nullptr;
|
||||
}
|
||||
if (s_surface) {
|
||||
eglDestroySurface(s_display, s_surface);
|
||||
s_surface = nullptr;
|
||||
}
|
||||
eglTerminate(s_display);
|
||||
s_display = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *const text_vs = R"text(
|
||||
#version 330 core
|
||||
layout(location=0) in vec2 inPos;
|
||||
layout(location=1) in vec3 inColor;
|
||||
out vec3 color;
|
||||
void main() {
|
||||
color = inColor;
|
||||
gl_Position = vec4(inPos, 0.0, 1.0);
|
||||
}
|
||||
)text";
|
||||
|
||||
static const char *const text_fs = R"text(
|
||||
#version 330 core
|
||||
in vec3 color;
|
||||
out vec4 fragColor;
|
||||
void main() {
|
||||
fragColor = vec4(color, 1.0);
|
||||
}
|
||||
)text";
|
||||
|
||||
static GLuint s_textProgram = 0;
|
||||
static GLuint s_textVao = 0;
|
||||
static GLuint s_textVbo = 0;
|
||||
|
||||
static const unsigned char font8x8[11][8] = { { 0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00 }, { 0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00 },
|
||||
{ 0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00 }, { 0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00 },
|
||||
{ 0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00 }, { 0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00 },
|
||||
{ 0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00 }, { 0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00 },
|
||||
{ 0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00 }, { 0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00 },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00 } };
|
||||
|
||||
static GLuint createAndCompileShader(GLenum type, const char *source);
|
||||
|
||||
static void initTextRenderer() {
|
||||
GLuint vsh = createAndCompileShader(GL_VERTEX_SHADER, text_vs);
|
||||
GLuint fsh = createAndCompileShader(GL_FRAGMENT_SHADER, text_fs);
|
||||
|
||||
s_textProgram = glCreateProgram();
|
||||
glAttachShader(s_textProgram, vsh);
|
||||
glAttachShader(s_textProgram, fsh);
|
||||
glLinkProgram(s_textProgram);
|
||||
glDeleteShader(vsh);
|
||||
glDeleteShader(fsh);
|
||||
|
||||
glGenVertexArrays(1, &s_textVao);
|
||||
glGenBuffers(1, &s_textVbo);
|
||||
}
|
||||
|
||||
static void drawTextPixel(float x, float y, float size, float r, float g, float b, float *vertexData, int *offset) {
|
||||
float verts[] = { x, y, r, g, b, x + size, y, r, g, b, x + size, y + size, r, g, b,
|
||||
x, y, r, g, b, x + size, y + size, r, g, b, x, y + size, r, g, b };
|
||||
memcpy(&vertexData[*offset], verts, sizeof(verts));
|
||||
*offset += 30;
|
||||
}
|
||||
|
||||
static void drawChar(char c, float x, float y, float scale, float r, float g, float b, float *vertexData, int *offset) {
|
||||
int idx = -1;
|
||||
if (c >= '0' && c <= '9')
|
||||
idx = c - '0';
|
||||
else if (c == '.')
|
||||
idx = 10;
|
||||
else
|
||||
return;
|
||||
|
||||
const unsigned char *glyph = font8x8[idx];
|
||||
for (int row = 0; row < 8; row++) {
|
||||
for (int col = 0; col < 8; col++) {
|
||||
if (glyph[row] & (1 << col)) {
|
||||
float px = x + col * scale;
|
||||
float py = y - row * scale;
|
||||
drawTextPixel(px, py, scale, r, g, b, vertexData, offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void drawText(const char *text, float x, float y, float scale, float r, float g, float b) {
|
||||
float *vertexData = (float *)malloc(100 * 64 * 6 * 5 * sizeof(float));
|
||||
int offset = 0;
|
||||
float cx = x;
|
||||
|
||||
while (*text) {
|
||||
drawChar(*text, cx, y, scale, r, g, b, vertexData, &offset);
|
||||
cx += 8 * scale;
|
||||
text++;
|
||||
}
|
||||
|
||||
if (offset > 0) {
|
||||
glUseProgram(s_textProgram);
|
||||
glBindVertexArray(s_textVao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, s_textVbo);
|
||||
glBufferData(GL_ARRAY_BUFFER, offset * sizeof(float), vertexData, GL_DYNAMIC_DRAW);
|
||||
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *)0);
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *)(2 * sizeof(float)));
|
||||
glEnableVertexAttribArray(1);
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, offset / 5);
|
||||
}
|
||||
|
||||
free(vertexData);
|
||||
}
|
||||
|
||||
static void cleanupTextRenderer() {
|
||||
if (s_textVbo) {
|
||||
glDeleteBuffers(1, &s_textVbo);
|
||||
s_textVbo = 0;
|
||||
}
|
||||
if (s_textVao) {
|
||||
glDeleteVertexArrays(1, &s_textVao);
|
||||
s_textVao = 0;
|
||||
}
|
||||
if (s_textProgram) {
|
||||
glDeleteProgram(s_textProgram);
|
||||
s_textProgram = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void setMesaConfig() {
|
||||
|
||||
setenv("EGL_LOG_LEVEL", "debug", 1);
|
||||
setenv("MESA_VERBOSE", "all", 1);
|
||||
setenv("NOUVEAU_MESA_DEBUG", "1", 1);
|
||||
|
||||
setenv("NV50_PROG_OPTIMIZE", "0", 1);
|
||||
setenv("NV50_PROG_DEBUG", "1", 1);
|
||||
setenv("NV50_PROG_CHIPSET", "0x120", 1);
|
||||
}
|
||||
|
||||
static const char *const vertexShaderSource = R"text(
|
||||
#version 330 core
|
||||
out vec2 uv;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec2 pos = vec2(
|
||||
(gl_VertexID == 2) ? 3.0 : -1.0,
|
||||
(gl_VertexID == 1) ? 3.0 : -1.0
|
||||
);
|
||||
|
||||
uv = 0.5 * (pos + 1.0);
|
||||
gl_Position = vec4(pos, 0.0, 1.0);
|
||||
}
|
||||
)text";
|
||||
|
||||
static const char *const fragmentShaderSource = R"text(
|
||||
#version 330 core
|
||||
// Alright so originally this was supposed to be a way more complex shader
|
||||
// It was supposed to be a proceduraly generated path tracing scene
|
||||
// Unfortunately, my stupidity knows no bounds, and well over 5000 lines in I decided that I was better off waterboarding myself
|
||||
// To anyone who even wants to ask, no you don't want to see the original, it was a fucking war crime
|
||||
// Ask Adam why he thought it was a good idea to copy entire libraries into this shit, and then acted surprised when it didn't work
|
||||
// so, have a cornel box instead, because I cannot be fucked to make a proper shader file loader, despite needing one soon anyway.
|
||||
|
||||
in vec2 uv;
|
||||
out vec4 fragColor;
|
||||
|
||||
uniform vec2 u_resolution;
|
||||
uniform float u_time;
|
||||
|
||||
// Sampler variables
|
||||
uniform int u_frame;
|
||||
uniform sampler2D u_prevFrame;
|
||||
|
||||
#define WHITE 0
|
||||
#define RED 1
|
||||
#define GREEN 2
|
||||
#define LIGHT 3
|
||||
#define MIRROR 4
|
||||
|
||||
struct Hit {
|
||||
float dist;
|
||||
int material;
|
||||
vec3 normal;
|
||||
};
|
||||
|
||||
float hash(vec2 p){
|
||||
return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5453);
|
||||
}
|
||||
|
||||
vec3 rand3(vec3 p){
|
||||
float f = float(u_frame);
|
||||
|
||||
return vec3(
|
||||
hash(p.xy + f),
|
||||
hash(p.yz + f*1.37),
|
||||
hash(p.zx + f*2.17)
|
||||
) * 2.0 - 1.0;
|
||||
}
|
||||
|
||||
float sdSphere(vec3 p, float r){
|
||||
return length(p) - r;
|
||||
}
|
||||
|
||||
Hit map(vec3 p)
|
||||
{
|
||||
Hit h;
|
||||
h.dist = 1e9;
|
||||
h.material = WHITE;
|
||||
|
||||
// ---------- mirror sphere ----------
|
||||
float s = sdSphere(p - vec3(0,1.0,-0.5), 1.0);
|
||||
if(s < h.dist){
|
||||
h.dist = s;
|
||||
h.material = MIRROR;
|
||||
h.normal = normalize(p - vec3(0,1.0,-0.5));
|
||||
}
|
||||
|
||||
// ---------- room walls (inward planes) ----------
|
||||
|
||||
// left (red)
|
||||
float left = p.x + 2.0;
|
||||
if(left < h.dist){
|
||||
h.dist = left;
|
||||
h.material = RED;
|
||||
h.normal = vec3(1, 0, 0);
|
||||
}
|
||||
|
||||
// right (green)
|
||||
float right = 2.0 - p.x;
|
||||
if(right < h.dist){
|
||||
h.dist = right;
|
||||
h.material = GREEN;
|
||||
h.normal = vec3(-1, 0, 0);
|
||||
}
|
||||
|
||||
// floor
|
||||
float floor = p.y;
|
||||
if(floor < h.dist){
|
||||
h.dist = floor;
|
||||
h.material = WHITE;
|
||||
h.normal = vec3(0, 1, 0);
|
||||
}
|
||||
|
||||
// ceiling
|
||||
float ceil = 4.0 - p.y;
|
||||
if(ceil < h.dist){
|
||||
h.dist = ceil;
|
||||
h.material = WHITE;
|
||||
h.normal = vec3(0, -1, 0);
|
||||
}
|
||||
|
||||
// back wall
|
||||
float back = 2.0 - p.z;
|
||||
if(back < h.dist){
|
||||
h.dist = back;
|
||||
h.material = WHITE;
|
||||
h.normal = vec3(0, 0, -1);
|
||||
}
|
||||
|
||||
vec3 lCenter = vec3(0.0, 3.98, -1.2);
|
||||
vec3 lSize = vec3(0.9, 0.01, 0.9);
|
||||
|
||||
vec3 d = abs(p - lCenter) - lSize;
|
||||
float light = length(max(d,0.0)) + min(max(d.x,max(d.y,d.z)),0.0);
|
||||
|
||||
if(light < h.dist){
|
||||
h.dist = light;
|
||||
h.material = LIGHT;
|
||||
}
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
Hit raymarch(vec3 ro, vec3 rd)
|
||||
{
|
||||
float t = 0.0;
|
||||
|
||||
// 80 steps, lower values give better performance, while higher values increase load
|
||||
// increasing load beyond this only decreases power draw, while lowering results in graphical glitches
|
||||
for(int i=0;i<80;i++){
|
||||
vec3 p = ro + rd*t;
|
||||
Hit h = map(p);
|
||||
|
||||
if(h.dist < 0.001){
|
||||
h.dist = t;
|
||||
return h;
|
||||
}
|
||||
|
||||
t += h.dist;
|
||||
if(t > 50.0) break;
|
||||
}
|
||||
|
||||
Hit miss;
|
||||
miss.dist = -1.0;
|
||||
return miss;
|
||||
}
|
||||
|
||||
vec3 getColor(int m){
|
||||
if(m==RED) return vec3(1,0.2,0.2);
|
||||
if(m==GREEN) return vec3(0.2,1,0.2);
|
||||
return vec3(0.9);
|
||||
}
|
||||
|
||||
bool isLight(int m){ return m==LIGHT; }
|
||||
bool isMirror(int m){ return m==MIRROR; }
|
||||
|
||||
vec3 trace(vec3 ro, vec3 rd)
|
||||
{
|
||||
vec3 color = vec3(0);
|
||||
vec3 throughput = vec3(1);
|
||||
|
||||
// Controls the amount of bounces made by rays in the scene
|
||||
// reduced to 3 for parody with CPU mode
|
||||
for(int bounce=0; bounce<3; bounce++)
|
||||
{
|
||||
Hit h = raymarch(ro, rd);
|
||||
|
||||
// sky fallback
|
||||
if(h.dist < 0.0){
|
||||
color += throughput * vec3(0.7,0.8,1.0);
|
||||
break;
|
||||
}
|
||||
|
||||
vec3 pos = ro + rd*h.dist;
|
||||
vec3 n = h.normal;
|
||||
|
||||
// hit light
|
||||
if(isLight(h.material)){
|
||||
color += throughput * vec3(12.0);
|
||||
break;
|
||||
}
|
||||
|
||||
// mirror bounce
|
||||
if(isMirror(h.material)){
|
||||
rd = reflect(rd, n);
|
||||
}
|
||||
else{
|
||||
// diffuse bounce (hemisphere)
|
||||
vec3 r = rand3(pos + float(bounce) + float(u_frame));
|
||||
rd = normalize(n + r);
|
||||
// cosine weighting (energy can only be transfered)
|
||||
// I mean if you want to change this sure, but you'll get flashbanged.
|
||||
|
||||
float cosTheta = max(dot(rd, n), 0.0);
|
||||
|
||||
throughput *= getColor(h.material) * cosTheta;
|
||||
}
|
||||
|
||||
ro = pos + n*0.001;
|
||||
}
|
||||
|
||||
// Clamp color values so we don't get a concussion simulator
|
||||
return color;
|
||||
}
|
||||
|
||||
vec3 pathTrace(vec2 uv)
|
||||
{
|
||||
// convert uv to screen space values
|
||||
vec2 p = uv * 2.0 - 1.0;
|
||||
p.x *= u_resolution.x / u_resolution.y;
|
||||
|
||||
// Camera setup
|
||||
vec3 ro = vec3(0,2,-6);
|
||||
vec3 target = vec3(0,2,0);
|
||||
|
||||
vec3 forward = normalize(target - ro);
|
||||
vec3 right = normalize(cross(forward, vec3(0,1,0)));
|
||||
vec3 up = cross(right, forward);
|
||||
|
||||
vec3 rd = normalize(forward + p.x*right + p.y*up);
|
||||
|
||||
// Add pixel jittering to reduce noise
|
||||
vec2 jitter = vec2(
|
||||
hash(gl_FragCoord.xy + float(u_frame)),
|
||||
hash(gl_FragCoord.yx + float(u_frame))
|
||||
) / u_resolution;
|
||||
|
||||
return trace(ro, rd + vec3(jitter, 00));
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
//setup output
|
||||
vec2 uv = gl_FragCoord.xy / u_resolution;
|
||||
vec3 newSample = pathTrace(uv);
|
||||
|
||||
// Accumulation
|
||||
vec3 prev = texture(u_prevFrame, gl_FragCoord.xy / u_resolution).rgb;
|
||||
vec3 accumulated;
|
||||
|
||||
if(u_frame == 0)
|
||||
accumulated = newSample;
|
||||
else
|
||||
accumulated = (prev *float(u_frame) + newSample) / float(u_frame + 1);
|
||||
|
||||
// Output final pixels, send to vertex
|
||||
fragColor = vec4(accumulated, 1.0);
|
||||
}
|
||||
)text";
|
||||
|
||||
static GLuint s_program;
|
||||
static GLuint s_vao, s_vbo;
|
||||
|
||||
static GLint resolutionLoc;
|
||||
|
||||
static GLint loc_mdlvMtx, loc_projMtx;
|
||||
static GLint loc_time;
|
||||
|
||||
static u64 s_startTicks;
|
||||
|
||||
static u64 s_lastFrameTime = 0;
|
||||
static float s_fps = 0.0f;
|
||||
static int s_frameCount = 0;
|
||||
static u64 s_fpsUpdateTime = 0;
|
||||
|
||||
static GLuint tex[2], fbo[2];
|
||||
static GLuint frameLoc, prevframeLoc;
|
||||
static int frame = 0;
|
||||
|
||||
static GLuint createAndCompileShader(GLenum type, const char *source) {
|
||||
GLint success;
|
||||
GLchar msg[512];
|
||||
|
||||
GLuint handle = glCreateShader(type);
|
||||
if (!handle) {
|
||||
TRACE("%u: cannot create shader", type);
|
||||
return 0;
|
||||
}
|
||||
glShaderSource(handle, 1, &source, nullptr);
|
||||
glCompileShader(handle);
|
||||
glGetShaderiv(handle, GL_COMPILE_STATUS, &success);
|
||||
|
||||
if (!success) {
|
||||
glGetShaderInfoLog(handle, sizeof(msg), nullptr, msg);
|
||||
TRACE("%u: %s\n", type, msg);
|
||||
glDeleteShader(handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
void GPUPTSceneInit() {
|
||||
GLint vsh = createAndCompileShader(GL_VERTEX_SHADER, vertexShaderSource);
|
||||
GLint fsh = createAndCompileShader(GL_FRAGMENT_SHADER, fragmentShaderSource);
|
||||
if (!vsh || !fsh) {
|
||||
TRACE("Shader compile failed — aborting");
|
||||
return;
|
||||
}
|
||||
|
||||
s_program = glCreateProgram();
|
||||
glViewport(0, 0, 1280, 720);
|
||||
glAttachShader(s_program, vsh);
|
||||
glAttachShader(s_program, fsh);
|
||||
glBindFragDataLocation(s_program, 0, "fragColor");
|
||||
glLinkProgram(s_program);
|
||||
|
||||
frameLoc = glGetUniformLocation(s_program, "u_frame");
|
||||
prevframeLoc = glGetUniformLocation(s_program, "u_prevFrame");
|
||||
resolutionLoc = glGetUniformLocation(s_program, "u_resolution");
|
||||
loc_time = glGetUniformLocation(s_program, "u_time");
|
||||
|
||||
GLint success;
|
||||
glGetProgramiv(s_program, GL_LINK_STATUS, &success);
|
||||
if (!success) {
|
||||
char buf[512];
|
||||
glGetProgramInfoLog(s_program, sizeof(buf), nullptr, buf);
|
||||
TRACE("Link error: %s", buf);
|
||||
}
|
||||
glDeleteShader(vsh);
|
||||
glDeleteShader(fsh);
|
||||
|
||||
loc_mdlvMtx = glGetUniformLocation(s_program, "mdlvMtx");
|
||||
loc_projMtx = glGetUniformLocation(s_program, "projMtx");
|
||||
loc_time = glGetUniformLocation(s_program, "u_time");
|
||||
|
||||
static float vertices[] = {
|
||||
-1.0f, -1.0f, 3.0f, -1.0f, -1.0f, 3.0f,
|
||||
};
|
||||
|
||||
glGenTextures(2, tex);
|
||||
glGenFramebuffers(2, fbo);
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, tex[i]);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, 1280, 720, 0, GL_RGBA, GL_FLOAT, nullptr);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo[i]);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex[i], 0);
|
||||
|
||||
glClearColor(0, 0, 0, 1);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
|
||||
glGenVertexArrays(1, &s_vao);
|
||||
glGenBuffers(1, &s_vbo);
|
||||
|
||||
glBindVertexArray(s_vao);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, s_vbo);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
|
||||
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void *)0);
|
||||
glEnableVertexAttribArray(0);
|
||||
|
||||
glEnable(GL_FRAMEBUFFER_SRGB);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
glBindVertexArray(0);
|
||||
|
||||
glUseProgram(s_program);
|
||||
auto projMtx = glm::perspective(glm::radians(40.0f), 1280.0f / 720.0f, 0.01f, 1000.0f);
|
||||
glUniformMatrix4fv(loc_projMtx, 1, GL_FALSE, glm::value_ptr(projMtx));
|
||||
|
||||
s_startTicks = armGetSystemTick();
|
||||
|
||||
s_lastFrameTime = s_startTicks;
|
||||
s_fpsUpdateTime = s_startTicks;
|
||||
s_frameCount = 0;
|
||||
|
||||
initTextRenderer();
|
||||
}
|
||||
float getTime2() {
|
||||
u64 elapsed = armGetSystemTick() - s_startTicks;
|
||||
return (elapsed * 625 / 12) / 2000000000.0;
|
||||
}
|
||||
|
||||
void GPUPTRender() {
|
||||
static int X = 0;
|
||||
static int Y = 1;
|
||||
|
||||
u64 currentTime = armGetSystemTick();
|
||||
s_frameCount++;
|
||||
u64 timeSinceUpdate = currentTime - s_fpsUpdateTime;
|
||||
float secondsSinceUpdate = (timeSinceUpdate * 625.0f / 12.0f) / 1000000000.0f;
|
||||
|
||||
if (secondsSinceUpdate >= 0.01f) {
|
||||
s_fps = s_frameCount / secondsSinceUpdate;
|
||||
s_frameCount = 0;
|
||||
s_fpsUpdateTime = currentTime;
|
||||
}
|
||||
|
||||
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_CULL_FACE);
|
||||
|
||||
glUseProgram(s_program);
|
||||
glUniform1f(loc_time, getTime2());
|
||||
glUniform2f(resolutionLoc, 1280.0f, 720.0f);
|
||||
glBindVertexArray(s_vao);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, tex[X]);
|
||||
glUniform1i(prevframeLoc, 0);
|
||||
glUniform1i(frameLoc, frame);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo[Y]);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo[Y]);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
glBlitFramebuffer(0, 0, 1280, 720, 0, 0, 1280, 720, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
|
||||
if (frame == 0) {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo[X]);
|
||||
glClearColor(0, 0, 0, 1);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
|
||||
std::swap(X, Y);
|
||||
frame++;
|
||||
|
||||
glBindVertexArray(0);
|
||||
char fpsText[32];
|
||||
snprintf(fpsText, sizeof(fpsText), "%.3f", s_fps);
|
||||
drawText(fpsText, -0.95f, 0.90f, 0.02f, 1.0f, 0.0f, 0.0f);
|
||||
|
||||
char sampleText[64];
|
||||
snprintf(sampleText, sizeof(sampleText), "Samples: %d", frame);
|
||||
drawText(sampleText, -0.95f, 0.90f, 0.02f, 1.0f, 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
void GPUPTExit() {
|
||||
cleanupTextRenderer();
|
||||
glDeleteBuffers(1, &s_vbo);
|
||||
glDeleteVertexArrays(1, &s_vao);
|
||||
glDeleteProgram(s_program);
|
||||
frame = 0;
|
||||
}
|
||||
|
||||
int GPUPTMain(int argc, char *argv[]) {
|
||||
|
||||
setMesaConfig();
|
||||
|
||||
if (!initEgl(nwindowGetDefault()))
|
||||
return EXIT_FAILURE;
|
||||
|
||||
gladLoadGLLoader((GLADloadproc)eglGetProcAddress);
|
||||
|
||||
GPUPTSceneInit();
|
||||
|
||||
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
|
||||
|
||||
PadState pad;
|
||||
padInitializeDefault(&pad);
|
||||
|
||||
while (appletMainLoop()) {
|
||||
|
||||
padUpdate(&pad);
|
||||
u32 kDown = padGetButtonsDown(&pad);
|
||||
if (kDown & HidNpadButton_B) {
|
||||
GPUPTExit();
|
||||
deinitEgl();
|
||||
state = STATE_MENU;
|
||||
return 0;
|
||||
}
|
||||
|
||||
GPUPTRender();
|
||||
eglSwapBuffers(s_display, s_surface);
|
||||
}
|
||||
|
||||
GPUPTExit();
|
||||
|
||||
deinitEgl();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
4
Source/Benchmark-Toolbox/source/furmark/GPUPT.h
Normal file
4
Source/Benchmark-Toolbox/source/furmark/GPUPT.h
Normal file
@@ -0,0 +1,4 @@
|
||||
void GPUPTSceneInit();
|
||||
void GPUPTRender();
|
||||
void GPUPTExit();
|
||||
void getTime2();
|
||||
691
Source/Benchmark-Toolbox/source/furmark/fmrkRAM.cpp
Normal file
691
Source/Benchmark-Toolbox/source/furmark/fmrkRAM.cpp
Normal file
@@ -0,0 +1,691 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <switch.h>
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
#include <glad/glad.h>
|
||||
|
||||
#define GLM_FORCE_PURE
|
||||
#include "fur_png.h"
|
||||
#include "noise_png.h"
|
||||
#include "sates.h"
|
||||
#include "stb_image.h"
|
||||
#include "wall_png.h"
|
||||
#include "wunk_png.h"
|
||||
#include <glm/gtc/constants.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <glm/mat4x4.hpp>
|
||||
#include <glm/vec3.hpp>
|
||||
#include <glm/vec4.hpp>
|
||||
|
||||
#ifndef ENABLE_NXLINK
|
||||
#define TRACE(fmt, ...) ((void)0)
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#define TRACE(fmt, ...) printf("%s: " fmt "\n", __PRETTY_FUNCTION__, ##__VA_ARGS__)
|
||||
|
||||
static int s_nxlinkSock = -1;
|
||||
|
||||
static void initNxLink() {
|
||||
if (R_FAILED(socketInitializeDefault()))
|
||||
return;
|
||||
|
||||
s_nxlinkSock = nxlinkStdio();
|
||||
if (s_nxlinkSock >= 0)
|
||||
TRACE("printf output now goes to nxlink server");
|
||||
else
|
||||
socketExit();
|
||||
}
|
||||
|
||||
static void deinitNxLink() {
|
||||
if (s_nxlinkSock >= 0) {
|
||||
close(s_nxlinkSock);
|
||||
socketExit();
|
||||
s_nxlinkSock = -1;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void userAppInit() {
|
||||
initNxLink();
|
||||
}
|
||||
|
||||
extern "C" void userAppExit() {
|
||||
deinitNxLink();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static EGLDisplay s_display;
|
||||
static EGLContext s_context;
|
||||
static EGLSurface s_surface;
|
||||
|
||||
static bool initEgl(NWindow *win) {
|
||||
|
||||
s_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
||||
if (!s_display) {
|
||||
TRACE("Could not connect to display! error: %d", eglGetError());
|
||||
goto _fail0;
|
||||
}
|
||||
|
||||
eglInitialize(s_display, nullptr, nullptr);
|
||||
|
||||
if (eglBindAPI(EGL_OPENGL_API) == EGL_FALSE) {
|
||||
TRACE("Could not set API! error: %d", eglGetError());
|
||||
goto _fail1;
|
||||
}
|
||||
|
||||
EGLConfig config;
|
||||
EGLint numConfigs;
|
||||
static const EGLint framebufferAttributeList[] = { EGL_RENDERABLE_TYPE,
|
||||
EGL_OPENGL_BIT,
|
||||
EGL_RED_SIZE,
|
||||
8,
|
||||
EGL_GREEN_SIZE,
|
||||
8,
|
||||
EGL_BLUE_SIZE,
|
||||
8,
|
||||
EGL_ALPHA_SIZE,
|
||||
8,
|
||||
EGL_DEPTH_SIZE,
|
||||
24,
|
||||
EGL_STENCIL_SIZE,
|
||||
8,
|
||||
EGL_NONE };
|
||||
eglChooseConfig(s_display, framebufferAttributeList, &config, 1, &numConfigs);
|
||||
if (numConfigs == 0) {
|
||||
TRACE("No config found! error: %d", eglGetError());
|
||||
goto _fail1;
|
||||
}
|
||||
|
||||
s_surface = eglCreateWindowSurface(s_display, config, win, nullptr);
|
||||
if (!s_surface) {
|
||||
TRACE("Surface creation failed! error: %d", eglGetError());
|
||||
goto _fail1;
|
||||
}
|
||||
|
||||
static const EGLint contextAttributeList[] = { EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR,
|
||||
EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR,
|
||||
EGL_CONTEXT_MAJOR_VERSION_KHR,
|
||||
4,
|
||||
EGL_CONTEXT_MINOR_VERSION_KHR,
|
||||
3,
|
||||
EGL_NONE };
|
||||
s_context = eglCreateContext(s_display, config, EGL_NO_CONTEXT, contextAttributeList);
|
||||
if (!s_context) {
|
||||
TRACE("Context creation failed! error: %d", eglGetError());
|
||||
goto _fail2;
|
||||
}
|
||||
|
||||
eglMakeCurrent(s_display, s_surface, s_surface, s_context);
|
||||
return true;
|
||||
|
||||
_fail2:
|
||||
eglDestroySurface(s_display, s_surface);
|
||||
s_surface = nullptr;
|
||||
_fail1:
|
||||
eglTerminate(s_display);
|
||||
s_display = nullptr;
|
||||
_fail0:
|
||||
return false;
|
||||
}
|
||||
|
||||
static void deinitEgl() {
|
||||
if (s_display) {
|
||||
eglMakeCurrent(s_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
if (s_context) {
|
||||
eglDestroyContext(s_display, s_context);
|
||||
s_context = nullptr;
|
||||
}
|
||||
if (s_surface) {
|
||||
eglDestroySurface(s_display, s_surface);
|
||||
s_surface = nullptr;
|
||||
}
|
||||
eglTerminate(s_display);
|
||||
s_display = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *const text_vs = R"text(
|
||||
#version 330 core
|
||||
layout(location=0) in vec2 inPos;
|
||||
layout(location=1) in vec3 inColor;
|
||||
out vec3 color;
|
||||
void main() {
|
||||
color = inColor;
|
||||
gl_Position = vec4(inPos, 0.0, 1.0);
|
||||
}
|
||||
)text";
|
||||
|
||||
static const char *const text_fs = R"text(
|
||||
#version 330 core
|
||||
in vec3 color;
|
||||
out vec4 fragColor;
|
||||
void main() {
|
||||
fragColor = vec4(color, 1.0);
|
||||
}
|
||||
)text";
|
||||
|
||||
static GLuint s_textProgram = 0;
|
||||
static GLuint s_textVao = 0;
|
||||
static GLuint s_textVbo = 0;
|
||||
|
||||
static const unsigned char font8x8[11][8] = { { 0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00 }, { 0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00 },
|
||||
{ 0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00 }, { 0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00 },
|
||||
{ 0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00 }, { 0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00 },
|
||||
{ 0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00 }, { 0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00 },
|
||||
{ 0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00 }, { 0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00 },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00 } };
|
||||
|
||||
static GLuint createAndCompileShader(GLenum type, const char *source);
|
||||
|
||||
static void initTextRenderer() {
|
||||
GLuint vsh = createAndCompileShader(GL_VERTEX_SHADER, text_vs);
|
||||
GLuint fsh = createAndCompileShader(GL_FRAGMENT_SHADER, text_fs);
|
||||
|
||||
s_textProgram = glCreateProgram();
|
||||
glAttachShader(s_textProgram, vsh);
|
||||
glAttachShader(s_textProgram, fsh);
|
||||
glLinkProgram(s_textProgram);
|
||||
glDeleteShader(vsh);
|
||||
glDeleteShader(fsh);
|
||||
|
||||
glGenVertexArrays(1, &s_textVao);
|
||||
glGenBuffers(1, &s_textVbo);
|
||||
}
|
||||
|
||||
static void drawTextPixel(float x, float y, float size, float r, float g, float b, float *vertexData, int *offset) {
|
||||
float verts[] = { x, y, r, g, b, x + size, y, r, g, b, x + size, y + size, r, g, b,
|
||||
x, y, r, g, b, x + size, y + size, r, g, b, x, y + size, r, g, b };
|
||||
memcpy(&vertexData[*offset], verts, sizeof(verts));
|
||||
*offset += 30;
|
||||
}
|
||||
|
||||
static void drawChar(char c, float x, float y, float scale, float r, float g, float b, float *vertexData, int *offset) {
|
||||
int idx = -1;
|
||||
if (c >= '0' && c <= '9')
|
||||
idx = c - '0';
|
||||
else if (c == '.')
|
||||
idx = 10;
|
||||
else
|
||||
return;
|
||||
|
||||
const unsigned char *glyph = font8x8[idx];
|
||||
for (int row = 0; row < 8; row++) {
|
||||
for (int col = 0; col < 8; col++) {
|
||||
if (glyph[row] & (1 << col)) {
|
||||
float px = x + col * scale;
|
||||
float py = y - row * scale;
|
||||
drawTextPixel(px, py, scale, r, g, b, vertexData, offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void drawText(const char *text, float x, float y, float scale, float r, float g, float b) {
|
||||
float *vertexData = (float *)malloc(100 * 64 * 6 * 5 * sizeof(float));
|
||||
int offset = 0;
|
||||
float cx = x;
|
||||
|
||||
while (*text) {
|
||||
drawChar(*text, cx, y, scale, r, g, b, vertexData, &offset);
|
||||
cx += 8 * scale;
|
||||
text++;
|
||||
}
|
||||
|
||||
if (offset > 0) {
|
||||
glUseProgram(s_textProgram);
|
||||
glBindVertexArray(s_textVao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, s_textVbo);
|
||||
glBufferData(GL_ARRAY_BUFFER, offset * sizeof(float), vertexData, GL_DYNAMIC_DRAW);
|
||||
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *)0);
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *)(2 * sizeof(float)));
|
||||
glEnableVertexAttribArray(1);
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, offset / 5);
|
||||
}
|
||||
|
||||
free(vertexData);
|
||||
}
|
||||
|
||||
static void cleanupTextRenderer() {
|
||||
if (s_textVbo) {
|
||||
glDeleteBuffers(1, &s_textVbo);
|
||||
s_textVbo = 0;
|
||||
}
|
||||
if (s_textVao) {
|
||||
glDeleteVertexArrays(1, &s_textVao);
|
||||
s_textVao = 0;
|
||||
}
|
||||
if (s_textProgram) {
|
||||
glDeleteProgram(s_textProgram);
|
||||
s_textProgram = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void setMesaConfig() {
|
||||
}
|
||||
|
||||
static const char *const vertexShaderSource = R"text(
|
||||
#version 330 core
|
||||
|
||||
out vec2 v_uv;
|
||||
|
||||
void main() {
|
||||
vec2 pos = vec2(
|
||||
(gl_VertexID == 1) ? 3.0 : -1.0,
|
||||
(gl_VertexID == 2) ? 3.0 : -1.0
|
||||
);
|
||||
v_uv = pos * 0.5 + 0.5;
|
||||
gl_Position = vec4(pos, 0.0, 1.0);
|
||||
}
|
||||
)text";
|
||||
|
||||
static const char *const fragmentShaderSource = R"text(
|
||||
#version 330 core
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
uniform vec2 u_resolution;
|
||||
uniform float u_time;
|
||||
uniform sampler2D u_texture1;
|
||||
uniform sampler2D u_texture2;
|
||||
uniform sampler2D u_texture3;
|
||||
uniform sampler2D u_texture4;
|
||||
|
||||
const float PI = 3.1416;
|
||||
const float TAU = 2 * PI;
|
||||
|
||||
float displace(vec3 p, sampler2D tex) {
|
||||
float s = 4.5;
|
||||
float u = s / TAU * atan(p.y / p.x);
|
||||
float v = sign(p.z) / TAU *
|
||||
acos((p.z * p.z * sqrt(s * s + 1) + sqrt(1 - p.z * p.z * s * s)) / (p.z * p.z + 1));
|
||||
vec2 uv = 2.0 * vec2(u, v);
|
||||
float disp = texture(tex, uv).r;
|
||||
return disp * 0.06;
|
||||
}
|
||||
|
||||
mat2 rot2D(float a) {
|
||||
float sa = sin(a);
|
||||
float ca = cos(a);
|
||||
return mat2(ca, sa, -sa, ca);
|
||||
}
|
||||
|
||||
void rotate(inout vec3 p) {
|
||||
p.xy *= rot2D(sin(u_time * 0.8) * 0.25);
|
||||
p.yz *= rot2D(sin(u_time * 0.7) * 0.2);
|
||||
}
|
||||
|
||||
float map(vec3 p) {
|
||||
float dist = length(vec2(length(p.xy) - 0.6, p.z)) - 0.22;
|
||||
return dist * 0.7;
|
||||
}
|
||||
|
||||
vec3 getNormal(vec3 p) {
|
||||
vec2 e = vec2(0.01, 0.0);
|
||||
vec3 n = vec3(map(p)) - vec3(map(p - e.xyy), map(p - e.yxy), map(p - e.yyx));
|
||||
return normalize(n);
|
||||
}
|
||||
|
||||
float rayMarch(vec3 ro, vec3 rd) {
|
||||
float dist = 0.0;
|
||||
for (int i = 0; i < 32; i++) {
|
||||
vec3 p = ro + dist * rd;
|
||||
|
||||
rotate(p);
|
||||
float hit = map(p);
|
||||
dist += hit;
|
||||
|
||||
// displace
|
||||
dist -= displace(0.5 * p, u_texture2);
|
||||
|
||||
vec2 uv = fract(p.xy * 0.317);
|
||||
float t39 = texture(u_texture1, uv.yx).r;
|
||||
float texNoise = texture(u_texture2, uv).r;
|
||||
float t3 = texture(u_texture3, uv * 0.73).r;
|
||||
float car = texture(u_texture4, uv * 0.37).r;
|
||||
|
||||
dist += (texNoise + t39 + t3 + car) * 1e-4;
|
||||
dist += (texNoise + t39 + t3 + car) * 1e-5;
|
||||
dist += (texNoise + t39 + t3 + car) * 1e-6;
|
||||
|
||||
if (dist > 100.0 || abs(hit) < 0.0001) break;
|
||||
}
|
||||
return dist;
|
||||
}
|
||||
|
||||
vec3 triPlanar(sampler2D tex, vec3 p, vec3 normal) {
|
||||
normal = abs(normal);
|
||||
normal = pow(normal, vec3(15));
|
||||
normal /= normal.x + normal.y + normal.z;
|
||||
p = p * 0.5 + 0.5;
|
||||
return (texture(tex, p.xy) * normal.z +
|
||||
texture(tex, p.xz) * normal.y +
|
||||
texture(tex, p.yz) * normal.x).rgb;
|
||||
}
|
||||
|
||||
vec3 render(vec2 offset) {
|
||||
vec2 uv = (4.0 * (gl_FragCoord.xy + offset) - u_resolution.xy) / u_resolution.y;
|
||||
vec3 col = vec3(0);
|
||||
|
||||
vec3 ro = vec3(0, 0, -1.0);
|
||||
vec3 rd = normalize(vec3(uv, 1.0));
|
||||
|
||||
// return to normal rendering path
|
||||
float dist = rayMarch(ro, rd);
|
||||
|
||||
if (dist < 100.0) {
|
||||
vec3 p = ro + dist * rd;
|
||||
rotate(p);
|
||||
col += triPlanar(u_texture1, p * 1.0, getNormal(p));
|
||||
} else {
|
||||
float phi = atan(uv.y, uv.x);
|
||||
float rho = length(uv) + 0.2;
|
||||
|
||||
phi += sin(0.3 * rho - 0.5 * u_time);
|
||||
|
||||
float h = sin(8.0 * phi) * 0.5 + 0.5;
|
||||
|
||||
vec2 st;
|
||||
st.x = 3.0 * phi / PI;
|
||||
st.y = u_time * 0.5 + PI / (rho + 0.1 * smoothstep(0.45, 0.5, h));
|
||||
|
||||
col += texture(u_texture3, st).rgb;
|
||||
|
||||
float occ = smoothstep(0.0, 0.45, h) - smoothstep(0.5, 1.0, h);
|
||||
col *= 1.0 - 0.45 * occ * rho;
|
||||
col *= rho;
|
||||
}
|
||||
return col;
|
||||
}
|
||||
|
||||
vec3 renderAAx4() {
|
||||
vec4 e = vec4(0.125, -0.125, 0.375, -0.375);
|
||||
vec3 colAA = render(e.xz) + render(e.yw) + render(e.wx) + render(e.zy);
|
||||
return colAA /= 4.0;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec3 color = renderAAx4();
|
||||
|
||||
fragColor = vec4(color, 1.0);
|
||||
}
|
||||
)text";
|
||||
|
||||
static GLuint s_program;
|
||||
static GLuint s_vao, s_vbo;
|
||||
|
||||
static GLint resolutionLoc;
|
||||
static GLuint tex1;
|
||||
static GLuint tex2;
|
||||
static GLuint tex3;
|
||||
static GLuint tex4;
|
||||
|
||||
static GLint loc_mdlvMtx, loc_projMtx;
|
||||
static GLint loc_lightPos, loc_ambient, loc_diffuse, loc_specular, loc_tex_diffuse;
|
||||
static GLint loc_time;
|
||||
|
||||
static u64 s_startTicks;
|
||||
|
||||
static u64 s_lastFrameTime = 0;
|
||||
static float s_fps = 0.0f;
|
||||
static int s_frameCount = 0;
|
||||
static u64 s_fpsUpdateTime = 0;
|
||||
|
||||
static GLuint createAndCompileShader(GLenum type, const char *source) {
|
||||
GLint success;
|
||||
GLchar msg[512];
|
||||
|
||||
GLuint handle = glCreateShader(type);
|
||||
if (!handle) {
|
||||
TRACE("%u: cannot create shader", type);
|
||||
return 0;
|
||||
}
|
||||
glShaderSource(handle, 1, &source, nullptr);
|
||||
glCompileShader(handle);
|
||||
glGetShaderiv(handle, GL_COMPILE_STATUS, &success);
|
||||
|
||||
if (!success) {
|
||||
glGetShaderInfoLog(handle, sizeof(msg), nullptr, msg);
|
||||
TRACE("%u: %s\n", type, msg);
|
||||
glDeleteShader(handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
void frRamSceneInit() {
|
||||
GLint vsh = createAndCompileShader(GL_VERTEX_SHADER, vertexShaderSource);
|
||||
GLint fsh = createAndCompileShader(GL_FRAGMENT_SHADER, fragmentShaderSource);
|
||||
|
||||
s_program = glCreateProgram();
|
||||
glAttachShader(s_program, vsh);
|
||||
glAttachShader(s_program, fsh);
|
||||
glLinkProgram(s_program);
|
||||
resolutionLoc = glGetUniformLocation(s_program, "u_resolution");
|
||||
GLuint tex1Loc = glGetUniformLocation(s_program, "u_texture1");
|
||||
GLuint tex2Loc = glGetUniformLocation(s_program, "u_texture2");
|
||||
GLuint tex3Loc = glGetUniformLocation(s_program, "u_texture3");
|
||||
GLuint tex4Loc = glGetUniformLocation(s_program, "u_texture4");
|
||||
loc_time = glGetUniformLocation(s_program, "u_time");
|
||||
|
||||
GLint success;
|
||||
glGetProgramiv(s_program, GL_LINK_STATUS, &success);
|
||||
if (!success) {
|
||||
char buf[512];
|
||||
glGetProgramInfoLog(s_program, sizeof(buf), nullptr, buf);
|
||||
TRACE("Link error: %s", buf);
|
||||
}
|
||||
glDeleteShader(vsh);
|
||||
glDeleteShader(fsh);
|
||||
|
||||
loc_mdlvMtx = glGetUniformLocation(s_program, "mdlvMtx");
|
||||
loc_projMtx = glGetUniformLocation(s_program, "projMtx");
|
||||
loc_lightPos = glGetUniformLocation(s_program, "lightPos");
|
||||
loc_ambient = glGetUniformLocation(s_program, "ambient");
|
||||
loc_diffuse = glGetUniformLocation(s_program, "diffuse");
|
||||
loc_specular = glGetUniformLocation(s_program, "specular");
|
||||
loc_tex_diffuse = glGetUniformLocation(s_program, "tex_diffuse");
|
||||
loc_time = glGetUniformLocation(s_program, "u_time");
|
||||
|
||||
struct Vertex {
|
||||
float position[3];
|
||||
float color[3];
|
||||
glm::vec2 texcoord;
|
||||
glm::vec3 normal;
|
||||
};
|
||||
|
||||
static const Vertex vertices[] = {
|
||||
{ { -0.5f, -0.5f, 0.0f }, { 1.0f, 0.0f, 0.0f } },
|
||||
{ { 0.5f, -0.5f, 0.0f }, { 0.0f, 1.0f, 0.0f } },
|
||||
{ { 0.0f, 0.5f, 0.0f }, { 0.0f, 0.0f, 1.0f } },
|
||||
};
|
||||
|
||||
glGenVertexArrays(1, &s_vao);
|
||||
glGenBuffers(1, &s_vbo);
|
||||
|
||||
glBindVertexArray(s_vao);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, s_vbo);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
|
||||
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void *)offsetof(Vertex, position));
|
||||
glEnableVertexAttribArray(0);
|
||||
|
||||
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void *)offsetof(Vertex, color));
|
||||
glEnableVertexAttribArray(1);
|
||||
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void *)offsetof(Vertex, texcoord));
|
||||
glEnableVertexAttribArray(2);
|
||||
|
||||
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void *)offsetof(Vertex, normal));
|
||||
glEnableVertexAttribArray(3);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
glBindVertexArray(0);
|
||||
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
|
||||
int width, height, nchan;
|
||||
stbi_set_flip_vertically_on_load(true);
|
||||
|
||||
glGenTextures(1, &tex1);
|
||||
glBindTexture(GL_TEXTURE_2D, tex1);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
stbi_uc *img = stbi_load_from_memory((const stbi_uc *)fur_png, fur_png_size, &width, &height, &nchan, 4);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img);
|
||||
stbi_image_free(img);
|
||||
|
||||
glGenTextures(1, &tex2);
|
||||
glBindTexture(GL_TEXTURE_2D, tex2);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
img = stbi_load_from_memory((const stbi_uc *)noise_png, noise_png_size, &width, &height, &nchan, 4);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img);
|
||||
stbi_image_free(img);
|
||||
|
||||
glGenTextures(1, &tex3);
|
||||
glBindTexture(GL_TEXTURE_2D, tex3);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
img = stbi_load_from_memory((const stbi_uc *)wall_png, wall_png_size, &width, &height, &nchan, 4);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img);
|
||||
stbi_image_free(img);
|
||||
|
||||
glGenTextures(1, &tex4);
|
||||
glBindTexture(GL_TEXTURE_2D, tex4);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
img = stbi_load_from_memory((const stbi_uc *)wunk_png, wunk_png_size, &width, &height, &nchan, 4);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img);
|
||||
stbi_image_free(img);
|
||||
|
||||
glUseProgram(s_program);
|
||||
auto projMtx = glm::perspective(glm::radians(40.0f), 1280.0f / 720.0f, 0.01f, 500.0f);
|
||||
glUniformMatrix4fv(loc_projMtx, 1, GL_FALSE, glm::value_ptr(projMtx));
|
||||
glUniform4f(loc_lightPos, 0.0f, 0.0f, 0.5f, 1.0f);
|
||||
glUniform3f(loc_ambient, 0.1f, 0.1f, 0.1f);
|
||||
glUniform3f(loc_diffuse, 0.4f, 0.4f, 0.4f);
|
||||
glUniform4f(loc_specular, 0.5f, 0.5f, 0.5f, 20.0f);
|
||||
|
||||
s_startTicks = armGetSystemTick();
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, tex1);
|
||||
glUniform1i(tex1Loc, 0);
|
||||
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, tex2);
|
||||
glUniform1i(tex2Loc, 1);
|
||||
|
||||
glActiveTexture(GL_TEXTURE2);
|
||||
glBindTexture(GL_TEXTURE_2D, tex3);
|
||||
glUniform1i(tex3Loc, 2);
|
||||
|
||||
glActiveTexture(GL_TEXTURE3);
|
||||
glBindTexture(GL_TEXTURE_2D, tex4);
|
||||
glUniform1i(tex4Loc, 3);
|
||||
|
||||
s_lastFrameTime = s_startTicks;
|
||||
s_fpsUpdateTime = s_startTicks;
|
||||
s_frameCount = 0;
|
||||
|
||||
initTextRenderer();
|
||||
}
|
||||
float getTime1() {
|
||||
u64 elapsed = armGetSystemTick() - s_startTicks;
|
||||
return (elapsed * 625 / 12) / 2000000000.0;
|
||||
}
|
||||
|
||||
void frRamRender() {
|
||||
|
||||
u64 currentTime = armGetSystemTick();
|
||||
s_frameCount++;
|
||||
u64 timeSinceUpdate = currentTime - s_fpsUpdateTime;
|
||||
float secondsSinceUpdate = (timeSinceUpdate * 625.0f / 12.0f) / 1000000000.0f;
|
||||
|
||||
if (secondsSinceUpdate >= 0.01f) {
|
||||
s_fps = s_frameCount / secondsSinceUpdate;
|
||||
s_frameCount = 0;
|
||||
s_fpsUpdateTime = currentTime;
|
||||
}
|
||||
|
||||
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_CULL_FACE);
|
||||
|
||||
glUseProgram(s_program);
|
||||
|
||||
glViewport(0, 0, 1280, 720);
|
||||
glUniform1f(loc_time, getTime1());
|
||||
glUniform2f(resolutionLoc, 640.0f, 360.0f);
|
||||
glBindVertexArray(s_vao);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
|
||||
glBindVertexArray(0);
|
||||
char fpsText[32];
|
||||
snprintf(fpsText, sizeof(fpsText), "%.3f", s_fps);
|
||||
drawText(fpsText, -0.95f, 0.90f, 0.02f, 1.0f, 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
void frRamExit() {
|
||||
cleanupTextRenderer();
|
||||
glDeleteBuffers(1, &s_vbo);
|
||||
glDeleteVertexArrays(1, &s_vao);
|
||||
glDeleteProgram(s_program);
|
||||
}
|
||||
|
||||
int frRamMain(int argc, char *argv[]) {
|
||||
|
||||
setMesaConfig();
|
||||
|
||||
if (!initEgl(nwindowGetDefault()))
|
||||
return EXIT_FAILURE;
|
||||
|
||||
gladLoadGLLoader((GLADloadproc)eglGetProcAddress);
|
||||
|
||||
frRamSceneInit();
|
||||
|
||||
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
|
||||
|
||||
PadState pad;
|
||||
padInitializeDefault(&pad);
|
||||
|
||||
while (appletMainLoop()) {
|
||||
|
||||
padUpdate(&pad);
|
||||
u32 kDown = padGetButtonsDown(&pad);
|
||||
if (kDown & HidNpadButton_B) {
|
||||
frRamExit();
|
||||
deinitEgl();
|
||||
state = STATE_MENU;
|
||||
return 0;
|
||||
}
|
||||
|
||||
frRamRender();
|
||||
eglSwapBuffers(s_display, s_surface);
|
||||
}
|
||||
|
||||
frRamExit();
|
||||
|
||||
deinitEgl();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
4
Source/Benchmark-Toolbox/source/furmark/fmrkRAM.h
Normal file
4
Source/Benchmark-Toolbox/source/furmark/fmrkRAM.h
Normal file
@@ -0,0 +1,4 @@
|
||||
void frRamSceneInit();
|
||||
void frRamRender();
|
||||
void frRamExit();
|
||||
void getTime1();
|
||||
682
Source/Benchmark-Toolbox/source/furmark/furmark.cpp
Normal file
682
Source/Benchmark-Toolbox/source/furmark/furmark.cpp
Normal file
@@ -0,0 +1,682 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <switch.h>
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
#include <glad/glad.h>
|
||||
|
||||
#define GLM_FORCE_PURE
|
||||
#include "fur_png.h"
|
||||
#include "noise_png.h"
|
||||
#include "sates.h"
|
||||
#include "stb_image.h"
|
||||
#include "wall_png.h"
|
||||
#include <glm/gtc/constants.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <glm/mat4x4.hpp>
|
||||
#include <glm/vec3.hpp>
|
||||
#include <glm/vec4.hpp>
|
||||
|
||||
#ifndef ENABLE_NXLINK
|
||||
#define TRACE(fmt, ...) ((void)0)
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#define TRACE(fmt, ...) printf("%s: " fmt "\n", __PRETTY_FUNCTION__, ##__VA_ARGS__)
|
||||
|
||||
static int s_nxlinkSock = -1;
|
||||
|
||||
static void initNxLink() {
|
||||
if (R_FAILED(socketInitializeDefault()))
|
||||
return;
|
||||
|
||||
s_nxlinkSock = nxlinkStdio();
|
||||
if (s_nxlinkSock >= 0)
|
||||
TRACE("printf output now goes to nxlink server");
|
||||
else
|
||||
socketExit();
|
||||
}
|
||||
|
||||
static void deinitNxLink() {
|
||||
if (s_nxlinkSock >= 0) {
|
||||
close(s_nxlinkSock);
|
||||
socketExit();
|
||||
s_nxlinkSock = -1;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void userAppInit() {
|
||||
initNxLink();
|
||||
}
|
||||
|
||||
extern "C" void userAppExit() {
|
||||
deinitNxLink();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static EGLDisplay s_display;
|
||||
static EGLContext s_context;
|
||||
static EGLSurface s_surface;
|
||||
|
||||
static bool initEgl(NWindow *win) {
|
||||
|
||||
s_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
||||
if (!s_display) {
|
||||
TRACE("Could not connect to display! error: %d", eglGetError());
|
||||
goto _fail0;
|
||||
}
|
||||
|
||||
eglInitialize(s_display, nullptr, nullptr);
|
||||
|
||||
if (eglBindAPI(EGL_OPENGL_API) == EGL_FALSE) {
|
||||
TRACE("Could not set API! error: %d", eglGetError());
|
||||
goto _fail1;
|
||||
}
|
||||
|
||||
EGLConfig config;
|
||||
EGLint numConfigs;
|
||||
static const EGLint framebufferAttributeList[] = { EGL_RENDERABLE_TYPE,
|
||||
EGL_OPENGL_BIT,
|
||||
EGL_RED_SIZE,
|
||||
8,
|
||||
EGL_GREEN_SIZE,
|
||||
8,
|
||||
EGL_BLUE_SIZE,
|
||||
8,
|
||||
EGL_ALPHA_SIZE,
|
||||
8,
|
||||
EGL_DEPTH_SIZE,
|
||||
24,
|
||||
EGL_STENCIL_SIZE,
|
||||
8,
|
||||
EGL_NONE };
|
||||
eglChooseConfig(s_display, framebufferAttributeList, &config, 1, &numConfigs);
|
||||
if (numConfigs == 0) {
|
||||
TRACE("No config found! error: %d", eglGetError());
|
||||
goto _fail1;
|
||||
}
|
||||
|
||||
s_surface = eglCreateWindowSurface(s_display, config, win, nullptr);
|
||||
if (!s_surface) {
|
||||
TRACE("Surface creation failed! error: %d", eglGetError());
|
||||
goto _fail1;
|
||||
}
|
||||
|
||||
static const EGLint contextAttributeList[] = { EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR,
|
||||
EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR,
|
||||
EGL_CONTEXT_MAJOR_VERSION_KHR,
|
||||
4,
|
||||
EGL_CONTEXT_MINOR_VERSION_KHR,
|
||||
3,
|
||||
EGL_NONE };
|
||||
s_context = eglCreateContext(s_display, config, EGL_NO_CONTEXT, contextAttributeList);
|
||||
if (!s_context) {
|
||||
TRACE("Context creation failed! error: %d", eglGetError());
|
||||
goto _fail2;
|
||||
}
|
||||
|
||||
eglMakeCurrent(s_display, s_surface, s_surface, s_context);
|
||||
|
||||
eglSwapInterval(s_display, 0);
|
||||
return true;
|
||||
|
||||
_fail2:
|
||||
eglDestroySurface(s_display, s_surface);
|
||||
s_surface = nullptr;
|
||||
_fail1:
|
||||
eglTerminate(s_display);
|
||||
s_display = nullptr;
|
||||
_fail0:
|
||||
return false;
|
||||
}
|
||||
|
||||
static void deinitEgl() {
|
||||
if (s_display) {
|
||||
eglMakeCurrent(s_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
if (s_context) {
|
||||
eglDestroyContext(s_display, s_context);
|
||||
s_context = nullptr;
|
||||
}
|
||||
if (s_surface) {
|
||||
eglDestroySurface(s_display, s_surface);
|
||||
s_surface = nullptr;
|
||||
}
|
||||
eglTerminate(s_display);
|
||||
s_display = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *const text_vs = R"text(
|
||||
#version 330 core
|
||||
layout(location=0) in vec2 inPos;
|
||||
layout(location=1) in vec3 inColor;
|
||||
out vec3 color;
|
||||
void main() {
|
||||
color = inColor;
|
||||
gl_Position = vec4(inPos, 0.0, 1.0);
|
||||
}
|
||||
)text";
|
||||
|
||||
static const char *const text_fs = R"text(
|
||||
#version 330 core
|
||||
in vec3 color;
|
||||
out vec4 fragColor;
|
||||
void main() {
|
||||
fragColor = vec4(color, 1.0);
|
||||
}
|
||||
)text";
|
||||
|
||||
static GLuint s_textProgram = 0;
|
||||
static GLuint s_textVao = 0;
|
||||
static GLuint s_textVbo = 0;
|
||||
|
||||
static const unsigned char font8x8[11][8] = { { 0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00 }, { 0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00 },
|
||||
{ 0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00 }, { 0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00 },
|
||||
{ 0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00 }, { 0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00 },
|
||||
{ 0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00 }, { 0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00 },
|
||||
{ 0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00 }, { 0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00 },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00 } };
|
||||
|
||||
GLuint createAndCompileShader(GLenum type, const char *source);
|
||||
|
||||
static void initTextRenderer() {
|
||||
GLuint vsh = createAndCompileShader(GL_VERTEX_SHADER, text_vs);
|
||||
GLuint fsh = createAndCompileShader(GL_FRAGMENT_SHADER, text_fs);
|
||||
|
||||
s_textProgram = glCreateProgram();
|
||||
glAttachShader(s_textProgram, vsh);
|
||||
glAttachShader(s_textProgram, fsh);
|
||||
glLinkProgram(s_textProgram);
|
||||
glDeleteShader(vsh);
|
||||
glDeleteShader(fsh);
|
||||
|
||||
glGenVertexArrays(1, &s_textVao);
|
||||
glGenBuffers(1, &s_textVbo);
|
||||
}
|
||||
|
||||
static void drawTextPixel(float x, float y, float size, float r, float g, float b, float *vertexData, int *offset) {
|
||||
float verts[] = { x, y, r, g, b, x + size, y, r, g, b, x + size, y + size, r, g, b,
|
||||
x, y, r, g, b, x + size, y + size, r, g, b, x, y + size, r, g, b };
|
||||
memcpy(&vertexData[*offset], verts, sizeof(verts));
|
||||
*offset += 30;
|
||||
}
|
||||
|
||||
static void drawChar(char c, float x, float y, float scale, float r, float g, float b, float *vertexData, int *offset) {
|
||||
int idx = -1;
|
||||
if (c >= '0' && c <= '9')
|
||||
idx = c - '0';
|
||||
else if (c == '.')
|
||||
idx = 10;
|
||||
else
|
||||
return;
|
||||
|
||||
const unsigned char *glyph = font8x8[idx];
|
||||
for (int row = 0; row < 8; row++) {
|
||||
for (int col = 0; col < 8; col++) {
|
||||
if (glyph[row] & (1 << col)) {
|
||||
float px = x + col * scale;
|
||||
float py = y - row * scale;
|
||||
drawTextPixel(px, py, scale, r, g, b, vertexData, offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void drawText(const char *text, float x, float y, float scale, float r, float g, float b) {
|
||||
float *vertexData = (float *)malloc(100 * 64 * 6 * 5 * sizeof(float));
|
||||
int offset = 0;
|
||||
float cx = x;
|
||||
|
||||
while (*text) {
|
||||
drawChar(*text, cx, y, scale, r, g, b, vertexData, &offset);
|
||||
cx += 8 * scale;
|
||||
text++;
|
||||
}
|
||||
|
||||
if (offset > 0) {
|
||||
glUseProgram(s_textProgram);
|
||||
glBindVertexArray(s_textVao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, s_textVbo);
|
||||
glBufferData(GL_ARRAY_BUFFER, offset * sizeof(float), vertexData, GL_DYNAMIC_DRAW);
|
||||
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *)0);
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *)(2 * sizeof(float)));
|
||||
glEnableVertexAttribArray(1);
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, offset / 5);
|
||||
}
|
||||
|
||||
free(vertexData);
|
||||
}
|
||||
|
||||
static void cleanupTextRenderer() {
|
||||
if (s_textVbo) {
|
||||
glDeleteBuffers(1, &s_textVbo);
|
||||
s_textVbo = 0;
|
||||
}
|
||||
if (s_textVao) {
|
||||
glDeleteVertexArrays(1, &s_textVao);
|
||||
s_textVao = 0;
|
||||
}
|
||||
if (s_textProgram) {
|
||||
glDeleteProgram(s_textProgram);
|
||||
s_textProgram = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void setMesaConfig() {
|
||||
|
||||
setenv("MESA_NO_ERROR", "1", 1);
|
||||
|
||||
setenv("NV50_PROG_CHIPSET", "0", 1);
|
||||
}
|
||||
|
||||
static const char *const vertexShaderSource = R"text(
|
||||
#version 330 core
|
||||
|
||||
out vec2 v_uv;
|
||||
|
||||
void main() {
|
||||
vec2 pos = vec2(
|
||||
(gl_VertexID == 1) ? 3.0 : -1.0,
|
||||
(gl_VertexID == 2) ? 3.0 : -1.0
|
||||
);
|
||||
v_uv = pos * 0.5 + 0.5;
|
||||
gl_Position = vec4(pos, 0.0, 1.0);
|
||||
}
|
||||
)text";
|
||||
|
||||
static const char *const fragmentShaderSource = R"text(
|
||||
#version 330 core
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
uniform vec2 u_resolution;
|
||||
uniform float u_time;
|
||||
uniform sampler2D u_texture1;
|
||||
uniform sampler2D u_texture2;
|
||||
uniform sampler2D u_texture3;
|
||||
|
||||
const float PI = 3.1416;
|
||||
const float TAU = 2 * PI;
|
||||
|
||||
float displace(vec3 p, sampler2D tex) {
|
||||
float s = 4.5;
|
||||
float u = s / TAU * atan(p.y / p.x);
|
||||
float v = sign(p.z) / TAU *
|
||||
acos((p.z * p.z * sqrt(s * s + 1) + sqrt(1 - p.z * p.z * s * s)) / (p.z * p.z + 1));
|
||||
vec2 uv = 2.0 * vec2(u, v);
|
||||
float disp = texture(tex, uv).r;
|
||||
return disp * 0.06;
|
||||
}
|
||||
|
||||
mat2 rot2D(float a) {
|
||||
float sa = sin(a);
|
||||
float ca = cos(a);
|
||||
return mat2(ca, sa, -sa, ca);
|
||||
}
|
||||
|
||||
void rotate(inout vec3 p) {
|
||||
p.xy *= rot2D(sin(u_time * 0.8) * 0.25);
|
||||
p.yz *= rot2D(sin(u_time * 0.7) * 0.2);
|
||||
}
|
||||
|
||||
float map(vec3 p) {
|
||||
float dist = length(vec2(length(p.xy) - 0.6, p.z)) - 0.22;
|
||||
return dist * 0.7;
|
||||
}
|
||||
|
||||
vec3 getNormal(vec3 p) {
|
||||
vec2 e = vec2(0.01, 0.0);
|
||||
vec3 n = vec3(map(p)) - vec3(map(p - e.xyy), map(p - e.yxy), map(p - e.yyx));
|
||||
return normalize(n);
|
||||
}
|
||||
|
||||
float rayMarch(vec3 ro, vec3 rd) {
|
||||
float dist = 0.0;
|
||||
for (int i = 0; i < 48; i++) {
|
||||
vec3 p = ro + dist * rd;
|
||||
|
||||
rotate(p);
|
||||
float hit = map(p);
|
||||
dist += hit;
|
||||
|
||||
// displace
|
||||
dist -= displace(0.5 * p, u_texture2);
|
||||
|
||||
// Saturate FP32
|
||||
vec3 q = p;
|
||||
q = q * 1.37 + 0.13;
|
||||
q = q * q - 0.17;
|
||||
q = q * 0.91 + q.yzx * 0.09;
|
||||
dist += dot(q, q) * 1e-5;
|
||||
|
||||
// Register stress
|
||||
vec4 r0 = vec4(p, dist);
|
||||
vec4 r1 = sin(r0 * 3.1);
|
||||
vec4 r2 = cos(r1 * 2.7);
|
||||
vec4 r3 = r2 * r1;
|
||||
vec4 r4 = normalize(r3);
|
||||
dist += dot(r4, vec4(1e-5));
|
||||
|
||||
if (dist > 100.0 || abs(hit) < 0.0001) break;
|
||||
}
|
||||
return dist;
|
||||
}
|
||||
|
||||
vec3 triPlanar(sampler2D tex, vec3 p, vec3 normal) {
|
||||
normal = abs(normal);
|
||||
normal = pow(normal, vec3(15));
|
||||
normal /= normal.x + normal.y + normal.z;
|
||||
p = p * 0.5 + 0.5;
|
||||
return (texture(tex, p.xy) * normal.z +
|
||||
texture(tex, p.xz) * normal.y +
|
||||
texture(tex, p.yz) * normal.x).rgb;
|
||||
}
|
||||
|
||||
vec3 render(vec2 offset) {
|
||||
vec2 uv = (2.0 * (gl_FragCoord.xy + offset) - u_resolution.xy) / u_resolution.y;
|
||||
vec3 col = vec3(0);
|
||||
|
||||
vec3 ro = vec3(0, 0, -1.0);
|
||||
vec3 rd = normalize(vec3(uv, 1.0));
|
||||
|
||||
// return to normal rendering path
|
||||
float dist = rayMarch(ro, rd);
|
||||
|
||||
if (dist < 100.0) {
|
||||
vec3 p = ro + dist * rd;
|
||||
rotate(p);
|
||||
col += triPlanar(u_texture1, p * 1.0, getNormal(p));
|
||||
} else {
|
||||
float phi = atan(uv.y, uv.x);
|
||||
float rho = length(uv) + 0.2;
|
||||
|
||||
phi += sin(0.3 * rho - 0.5 * u_time);
|
||||
|
||||
float h = sin(8.0 * phi) * 0.5 + 0.5;
|
||||
|
||||
vec2 st;
|
||||
st.x = 3.0 * phi / PI;
|
||||
st.y = u_time * 0.5 + PI / (rho + 0.1 * smoothstep(0.45, 0.5, h));
|
||||
|
||||
col += texture(u_texture3, st).rgb;
|
||||
|
||||
float occ = smoothstep(0.0, 0.45, h) - smoothstep(0.5, 1.0, h);
|
||||
col *= 1.0 - 0.45 * occ * rho;
|
||||
col *= rho;
|
||||
}
|
||||
return col;
|
||||
}
|
||||
|
||||
vec3 renderAAx4() {
|
||||
vec4 e = vec4(0.125, -0.125, 0.375, -0.375);
|
||||
vec3 colAA = render(e.xz) + render(e.yw) + render(e.wx) + render(e.zy);
|
||||
return colAA /= 4.0;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec3 color = renderAAx4();
|
||||
|
||||
fragColor = vec4(color, 1.0);
|
||||
}
|
||||
)text";
|
||||
|
||||
static GLuint s_program;
|
||||
static GLuint s_vao, s_vbo;
|
||||
|
||||
static GLint resolutionLoc;
|
||||
static GLuint tex1;
|
||||
static GLuint tex2;
|
||||
static GLuint tex3;
|
||||
|
||||
static GLint loc_mdlvMtx, loc_projMtx;
|
||||
static GLint loc_lightPos, loc_ambient, loc_diffuse, loc_specular, loc_tex_diffuse;
|
||||
static GLint loc_time;
|
||||
|
||||
static u64 s_startTicks;
|
||||
|
||||
static u64 s_lastFrameTime = 0;
|
||||
static float s_fps = 0.0f;
|
||||
static int s_frameCount = 0;
|
||||
static u64 s_fpsUpdateTime = 0;
|
||||
|
||||
GLuint createAndCompileShader(GLenum type, const char *source) {
|
||||
GLint success;
|
||||
GLchar msg[512];
|
||||
|
||||
GLuint handle = glCreateShader(type);
|
||||
if (!handle) {
|
||||
TRACE("%u: cannot create shader", type);
|
||||
return 0;
|
||||
}
|
||||
glShaderSource(handle, 1, &source, nullptr);
|
||||
glCompileShader(handle);
|
||||
glGetShaderiv(handle, GL_COMPILE_STATUS, &success);
|
||||
|
||||
if (!success) {
|
||||
glGetShaderInfoLog(handle, sizeof(msg), nullptr, msg);
|
||||
TRACE("%u: %s\n", type, msg);
|
||||
glDeleteShader(handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
void frSceneInit() {
|
||||
GLint vsh = createAndCompileShader(GL_VERTEX_SHADER, vertexShaderSource);
|
||||
GLint fsh = createAndCompileShader(GL_FRAGMENT_SHADER, fragmentShaderSource);
|
||||
|
||||
s_program = glCreateProgram();
|
||||
glAttachShader(s_program, vsh);
|
||||
glAttachShader(s_program, fsh);
|
||||
glLinkProgram(s_program);
|
||||
resolutionLoc = glGetUniformLocation(s_program, "u_resolution");
|
||||
GLuint tex1Loc = glGetUniformLocation(s_program, "u_texture1");
|
||||
GLuint tex2Loc = glGetUniformLocation(s_program, "u_texture2");
|
||||
GLuint tex3Loc = glGetUniformLocation(s_program, "u_texture3");
|
||||
loc_time = glGetUniformLocation(s_program, "u_time");
|
||||
|
||||
GLint success;
|
||||
glGetProgramiv(s_program, GL_LINK_STATUS, &success);
|
||||
if (!success) {
|
||||
char buf[512];
|
||||
glGetProgramInfoLog(s_program, sizeof(buf), nullptr, buf);
|
||||
TRACE("Link error: %s", buf);
|
||||
}
|
||||
glDeleteShader(vsh);
|
||||
glDeleteShader(fsh);
|
||||
|
||||
loc_mdlvMtx = glGetUniformLocation(s_program, "mdlvMtx");
|
||||
loc_projMtx = glGetUniformLocation(s_program, "projMtx");
|
||||
loc_lightPos = glGetUniformLocation(s_program, "lightPos");
|
||||
loc_ambient = glGetUniformLocation(s_program, "ambient");
|
||||
loc_diffuse = glGetUniformLocation(s_program, "diffuse");
|
||||
loc_specular = glGetUniformLocation(s_program, "specular");
|
||||
loc_tex_diffuse = glGetUniformLocation(s_program, "tex_diffuse");
|
||||
loc_time = glGetUniformLocation(s_program, "u_time");
|
||||
|
||||
struct Vertex {
|
||||
float position[3];
|
||||
float color[3];
|
||||
glm::vec2 texcoord;
|
||||
glm::vec3 normal;
|
||||
};
|
||||
|
||||
static const Vertex vertices[] = {
|
||||
{ { -0.5f, -0.5f, 0.0f }, { 1.0f, 0.0f, 0.0f } },
|
||||
{ { 0.5f, -0.5f, 0.0f }, { 0.0f, 1.0f, 0.0f } },
|
||||
{ { 0.0f, 0.5f, 0.0f }, { 0.0f, 0.0f, 1.0f } },
|
||||
};
|
||||
|
||||
glGenVertexArrays(1, &s_vao);
|
||||
glGenBuffers(1, &s_vbo);
|
||||
|
||||
glBindVertexArray(s_vao);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, s_vbo);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
|
||||
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void *)offsetof(Vertex, position));
|
||||
glEnableVertexAttribArray(0);
|
||||
|
||||
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void *)offsetof(Vertex, color));
|
||||
glEnableVertexAttribArray(1);
|
||||
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void *)offsetof(Vertex, texcoord));
|
||||
glEnableVertexAttribArray(2);
|
||||
|
||||
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void *)offsetof(Vertex, normal));
|
||||
glEnableVertexAttribArray(3);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
glBindVertexArray(0);
|
||||
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
|
||||
int width, height, nchan;
|
||||
stbi_set_flip_vertically_on_load(true);
|
||||
|
||||
glGenTextures(1, &tex1);
|
||||
glBindTexture(GL_TEXTURE_2D, tex1);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
stbi_uc *img = stbi_load_from_memory((const stbi_uc *)fur_png, fur_png_size, &width, &height, &nchan, 4);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img);
|
||||
stbi_image_free(img);
|
||||
|
||||
glGenTextures(1, &tex2);
|
||||
glBindTexture(GL_TEXTURE_2D, tex2);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
img = stbi_load_from_memory((const stbi_uc *)noise_png, noise_png_size, &width, &height, &nchan, 4);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img);
|
||||
stbi_image_free(img);
|
||||
|
||||
glGenTextures(1, &tex3);
|
||||
glBindTexture(GL_TEXTURE_2D, tex3);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
img = stbi_load_from_memory((const stbi_uc *)wall_png, wall_png_size, &width, &height, &nchan, 4);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img);
|
||||
stbi_image_free(img);
|
||||
|
||||
glUseProgram(s_program);
|
||||
auto projMtx = glm::perspective(glm::radians(40.0f), 1280.0f / 720.0f, 0.01f, 1000.0f);
|
||||
glUniformMatrix4fv(loc_projMtx, 1, GL_FALSE, glm::value_ptr(projMtx));
|
||||
glUniform4f(loc_lightPos, 0.0f, 0.0f, 0.5f, 1.0f);
|
||||
glUniform3f(loc_ambient, 0.1f, 0.1f, 0.1f);
|
||||
glUniform3f(loc_diffuse, 0.4f, 0.4f, 0.4f);
|
||||
glUniform4f(loc_specular, 0.5f, 0.5f, 0.5f, 20.0f);
|
||||
|
||||
s_startTicks = armGetSystemTick();
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, tex1);
|
||||
glUniform1i(tex1Loc, 0);
|
||||
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, tex2);
|
||||
glUniform1i(tex2Loc, 1);
|
||||
|
||||
glActiveTexture(GL_TEXTURE2);
|
||||
glBindTexture(GL_TEXTURE_2D, tex3);
|
||||
glUniform1i(tex3Loc, 2);
|
||||
|
||||
s_lastFrameTime = s_startTicks;
|
||||
s_fpsUpdateTime = s_startTicks;
|
||||
s_frameCount = 0;
|
||||
|
||||
initTextRenderer();
|
||||
}
|
||||
float getTime() {
|
||||
u64 elapsed = armGetSystemTick() - s_startTicks;
|
||||
return (elapsed * 625 / 12) / 2000000000.0;
|
||||
}
|
||||
|
||||
void frRender() {
|
||||
|
||||
u64 currentTime = armGetSystemTick();
|
||||
s_frameCount++;
|
||||
u64 timeSinceUpdate = currentTime - s_fpsUpdateTime;
|
||||
float secondsSinceUpdate = (timeSinceUpdate * 625.0f / 12.0f) / 1000000000.0f;
|
||||
|
||||
if (secondsSinceUpdate >= 0.01f) {
|
||||
s_fps = s_frameCount / secondsSinceUpdate;
|
||||
s_frameCount = 0;
|
||||
s_fpsUpdateTime = currentTime;
|
||||
}
|
||||
|
||||
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_CULL_FACE);
|
||||
|
||||
glUseProgram(s_program);
|
||||
glUniform1f(loc_time, getTime());
|
||||
glUniform2f(resolutionLoc, 1280.0f, 720.0f);
|
||||
glBindVertexArray(s_vao);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
|
||||
glBindVertexArray(0);
|
||||
char fpsText[32];
|
||||
snprintf(fpsText, sizeof(fpsText), "%.3f", s_fps);
|
||||
drawText(fpsText, -0.95f, 0.90f, 0.02f, 1.0f, 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
void frExit() {
|
||||
cleanupTextRenderer();
|
||||
glDeleteBuffers(1, &s_vbo);
|
||||
glDeleteVertexArrays(1, &s_vao);
|
||||
glDeleteProgram(s_program);
|
||||
}
|
||||
|
||||
int frMain(int argc, char *argv[]) {
|
||||
|
||||
setMesaConfig();
|
||||
|
||||
if (!initEgl(nwindowGetDefault()))
|
||||
return EXIT_FAILURE;
|
||||
|
||||
gladLoadGLLoader((GLADloadproc)eglGetProcAddress);
|
||||
|
||||
frSceneInit();
|
||||
|
||||
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
|
||||
|
||||
PadState pad;
|
||||
padInitializeDefault(&pad);
|
||||
|
||||
while (appletMainLoop()) {
|
||||
|
||||
padUpdate(&pad);
|
||||
u32 kDown = padGetButtonsDown(&pad);
|
||||
if (kDown & HidNpadButton_B) {
|
||||
frExit();
|
||||
deinitEgl();
|
||||
state = STATE_MENU;
|
||||
return 0;
|
||||
}
|
||||
|
||||
frRender();
|
||||
eglSwapBuffers(s_display, s_surface);
|
||||
}
|
||||
|
||||
frExit();
|
||||
|
||||
deinitEgl();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
4
Source/Benchmark-Toolbox/source/furmark/furmark.h
Normal file
4
Source/Benchmark-Toolbox/source/furmark/furmark.h
Normal file
@@ -0,0 +1,4 @@
|
||||
void frSceneInit();
|
||||
void frRender();
|
||||
void frExit();
|
||||
void getTime();
|
||||
239
Source/Benchmark-Toolbox/source/furmark/run_furmark.cpp
Normal file
239
Source/Benchmark-Toolbox/source/furmark/run_furmark.cpp
Normal file
@@ -0,0 +1,239 @@
|
||||
#include <atomic>
|
||||
#include <switch.h>
|
||||
#include <thread>
|
||||
|
||||
#include "run_furmark.h"
|
||||
#include "sates.h"
|
||||
#include <EGL/egl.h>
|
||||
#include <glad/glad.h>
|
||||
|
||||
extern void frSceneInit();
|
||||
extern void frRender();
|
||||
extern void frExit();
|
||||
extern void frRamSceneInit();
|
||||
extern void frRamRender();
|
||||
extern void frRamExit();
|
||||
extern void GPUPTSceneInit();
|
||||
extern void GPUPTRender();
|
||||
extern void GPUPTExit();
|
||||
extern void BHRTSceneInit();
|
||||
extern void BHRTRender();
|
||||
extern void BHRTExit();
|
||||
extern void CPURTSceneinit();
|
||||
extern void CPURTRender();
|
||||
extern void CPURTExit();
|
||||
extern void CPURBSceneinit();
|
||||
extern void CPURBRender();
|
||||
extern void CPURBExit();
|
||||
|
||||
AppState state = STATE_MENU;
|
||||
|
||||
namespace {
|
||||
|
||||
std::thread g_thread;
|
||||
std::atomic<bool> g_stop{ false };
|
||||
std::atomic<bool> g_running{ false };
|
||||
|
||||
EGLDisplay s_dpy = EGL_NO_DISPLAY;
|
||||
EGLContext s_ctx = EGL_NO_CONTEXT;
|
||||
EGLSurface s_surf = EGL_NO_SURFACE;
|
||||
|
||||
bool eglUp() {
|
||||
s_dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
||||
if (s_dpy == EGL_NO_DISPLAY)
|
||||
return false;
|
||||
if (!eglInitialize(s_dpy, nullptr, nullptr))
|
||||
return false;
|
||||
if (!eglBindAPI(EGL_OPENGL_API))
|
||||
return false;
|
||||
|
||||
const EGLint cfgAttr[] = { EGL_RENDERABLE_TYPE,
|
||||
EGL_OPENGL_BIT,
|
||||
EGL_RED_SIZE,
|
||||
8,
|
||||
EGL_GREEN_SIZE,
|
||||
8,
|
||||
EGL_BLUE_SIZE,
|
||||
8,
|
||||
EGL_ALPHA_SIZE,
|
||||
8,
|
||||
EGL_DEPTH_SIZE,
|
||||
24,
|
||||
EGL_STENCIL_SIZE,
|
||||
8,
|
||||
EGL_NONE };
|
||||
EGLConfig cfg;
|
||||
EGLint n = 0;
|
||||
if (!eglChooseConfig(s_dpy, cfgAttr, &cfg, 1, &n) || n == 0)
|
||||
return false;
|
||||
|
||||
const EGLint ctxAttr[] = { EGL_CONTEXT_MAJOR_VERSION, 4, EGL_CONTEXT_MINOR_VERSION, 3, EGL_NONE };
|
||||
s_ctx = eglCreateContext(s_dpy, cfg, EGL_NO_CONTEXT, ctxAttr);
|
||||
if (s_ctx == EGL_NO_CONTEXT)
|
||||
return false;
|
||||
|
||||
s_surf = EGL_NO_SURFACE;
|
||||
if (eglMakeCurrent(s_dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, s_ctx) != EGL_TRUE)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void eglDown() {
|
||||
if (s_dpy) {
|
||||
eglMakeCurrent(s_dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
if (s_ctx)
|
||||
eglDestroyContext(s_dpy, s_ctx);
|
||||
if (s_surf != EGL_NO_SURFACE)
|
||||
eglDestroySurface(s_dpy, s_surf);
|
||||
eglTerminate(s_dpy);
|
||||
}
|
||||
s_ctx = EGL_NO_CONTEXT;
|
||||
s_surf = EGL_NO_SURFACE;
|
||||
s_dpy = EGL_NO_DISPLAY;
|
||||
eglReleaseThread();
|
||||
}
|
||||
|
||||
void sceneInit(int which) {
|
||||
switch (which) {
|
||||
case 0:
|
||||
frSceneInit();
|
||||
break;
|
||||
case 1:
|
||||
frRamSceneInit();
|
||||
break;
|
||||
case 2:
|
||||
GPUPTSceneInit();
|
||||
break;
|
||||
case 3:
|
||||
BHRTSceneInit();
|
||||
break;
|
||||
case 4:
|
||||
CPURTSceneinit();
|
||||
break;
|
||||
case 5:
|
||||
CPURBSceneinit();
|
||||
break;
|
||||
}
|
||||
}
|
||||
void sceneRender(int which) {
|
||||
switch (which) {
|
||||
case 0:
|
||||
frRender();
|
||||
break;
|
||||
case 1:
|
||||
frRamRender();
|
||||
break;
|
||||
case 2:
|
||||
GPUPTRender();
|
||||
break;
|
||||
case 3:
|
||||
BHRTRender();
|
||||
break;
|
||||
case 4:
|
||||
CPURTRender();
|
||||
break;
|
||||
case 5:
|
||||
CPURBRender();
|
||||
break;
|
||||
}
|
||||
}
|
||||
void sceneExit(int which) {
|
||||
switch (which) {
|
||||
case 0:
|
||||
frExit();
|
||||
break;
|
||||
case 1:
|
||||
frRamExit();
|
||||
break;
|
||||
case 2:
|
||||
GPUPTExit();
|
||||
break;
|
||||
case 3:
|
||||
BHRTExit();
|
||||
break;
|
||||
case 4:
|
||||
CPURTExit();
|
||||
break;
|
||||
case 5:
|
||||
CPURBExit();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void worker(int which) {
|
||||
if (!eglUp()) {
|
||||
eglDown();
|
||||
g_running.store(false);
|
||||
return;
|
||||
}
|
||||
gladLoadGLLoader((GLADloadproc)eglGetProcAddress);
|
||||
|
||||
GLuint fbo = 0, rbColor = 0, rbDepth = 0;
|
||||
glGenFramebuffers(1, &fbo);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||
glGenRenderbuffers(1, &rbColor);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, rbColor);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, 1280, 720);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbColor);
|
||||
glGenRenderbuffers(1, &rbDepth);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, rbDepth);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 1280, 720);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbDepth);
|
||||
glViewport(0, 0, 1280, 720);
|
||||
|
||||
sceneInit(which);
|
||||
|
||||
const u64 frameNs = 16666666ULL;
|
||||
while (!g_stop.load()) {
|
||||
u64 t0 = armGetSystemTick();
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||
glViewport(0, 0, 1280, 720);
|
||||
sceneRender(which);
|
||||
|
||||
GLsync fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
glFlush();
|
||||
while (!g_stop.load()) {
|
||||
GLenum r = glClientWaitSync(fence, 0, 0);
|
||||
if (r != GL_TIMEOUT_EXPIRED)
|
||||
break;
|
||||
svcSleepThread(500000ULL);
|
||||
}
|
||||
glDeleteSync(fence);
|
||||
|
||||
u64 dt = armTicksToNs(armGetSystemTick() - t0);
|
||||
if (dt < frameNs)
|
||||
svcSleepThread(frameNs - dt);
|
||||
}
|
||||
sceneExit(which);
|
||||
|
||||
glDeleteFramebuffers(1, &fbo);
|
||||
glDeleteRenderbuffers(1, &rbColor);
|
||||
glDeleteRenderbuffers(1, &rbDepth);
|
||||
eglDown();
|
||||
g_running.store(false);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" void run_furmark_start(int which) {
|
||||
if (g_running.load())
|
||||
return;
|
||||
if (g_thread.joinable())
|
||||
g_thread.join();
|
||||
g_stop.store(false);
|
||||
g_running.store(true);
|
||||
appletSetAutoSleepDisabled(true);
|
||||
g_thread = std::thread(worker, which);
|
||||
}
|
||||
|
||||
extern "C" void run_furmark_stop(void) {
|
||||
g_stop.store(true);
|
||||
if (g_thread.joinable())
|
||||
g_thread.join();
|
||||
g_running.store(false);
|
||||
appletSetAutoSleepDisabled(false);
|
||||
}
|
||||
|
||||
extern "C" int run_furmark_running(void) {
|
||||
return g_running.load() ? 1 : 0;
|
||||
}
|
||||
13
Source/Benchmark-Toolbox/source/furmark/run_furmark.h
Normal file
13
Source/Benchmark-Toolbox/source/furmark/run_furmark.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void run_furmark_start(int which);
|
||||
void run_furmark_stop(void);
|
||||
int run_furmark_running(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
3
Source/Benchmark-Toolbox/source/furmark/sates.h
Normal file
3
Source/Benchmark-Toolbox/source/furmark/sates.h
Normal file
@@ -0,0 +1,3 @@
|
||||
enum AppState { STATE_MENU, STATE_FURMARK, STATE_FURMARK_RB, STATE_GPU_PT, STATE_BH_RT, STATE_CPU_RT, STATE_CPU_RB };
|
||||
|
||||
extern AppState state;
|
||||
6751
Source/Benchmark-Toolbox/source/furmark/stb_image.h
Normal file
6751
Source/Benchmark-Toolbox/source/furmark/stb_image.h
Normal file
File diff suppressed because it is too large
Load Diff
314
Source/Benchmark-Toolbox/source/furmark/vec23.h
Normal file
314
Source/Benchmark-Toolbox/source/furmark/vec23.h
Normal file
@@ -0,0 +1,314 @@
|
||||
#pragma once
|
||||
#include <arm_neon.h>
|
||||
|
||||
struct vec2f {
|
||||
float x, y;
|
||||
|
||||
inline vec2f() {
|
||||
}
|
||||
inline vec2f(float x_, float y_) : x(x_), y(y_) {
|
||||
}
|
||||
};
|
||||
|
||||
struct vec3x4 {
|
||||
float32x4_t x, y, z;
|
||||
};
|
||||
|
||||
inline float32x4_t dot(const vec3x4 &a, const vec3x4 &b) {
|
||||
float32x4_t acc = vmulq_f32(a.x, b.x);
|
||||
acc = vfmaq_f32(acc, a.y, b.y);
|
||||
acc = vfmaq_f32(acc, a.z, b.z);
|
||||
return acc;
|
||||
}
|
||||
|
||||
inline vec3x4 normalize(vec3x4 v) {
|
||||
float32x4_t len2 = vfmaq_f32(vfmaq_f32(vmulq_f32(v.x, v.x), v.y, v.y), v.z, v.z);
|
||||
|
||||
float32x4_t invLen = vrsqrteq_f32(len2);
|
||||
|
||||
invLen = vmulq_f32(vrsqrtsq_f32(vmulq_f32(len2, invLen), invLen), invLen);
|
||||
|
||||
return { vmulq_f32(v.x, invLen), vmulq_f32(v.y, invLen), vmulq_f32(v.z, invLen) };
|
||||
}
|
||||
|
||||
inline vec3x4 reflect(const vec3x4 &v, const vec3x4 &n) {
|
||||
float32x4_t two_d = vmulq_n_f32(dot(v, n), 2.0f);
|
||||
vec3x4 r;
|
||||
r.x = vfmsq_f32(v.x, n.x, two_d);
|
||||
r.y = vfmsq_f32(v.y, n.y, two_d);
|
||||
r.z = vfmsq_f32(v.z, n.z, two_d);
|
||||
return r;
|
||||
}
|
||||
|
||||
struct vec3f {
|
||||
float x, y, z;
|
||||
|
||||
inline vec3f() {
|
||||
}
|
||||
inline vec3f(float v) : x(v), y(v), z(v) {
|
||||
}
|
||||
inline vec3f(float x_, float y_, float z_) : x(x_), y(y_), z(z_) {
|
||||
}
|
||||
};
|
||||
|
||||
inline vec3f operator+(const vec3f &a, const vec3f &b) {
|
||||
return vec3f(a.x + b.x, a.y + b.y, a.z + b.z);
|
||||
}
|
||||
|
||||
inline vec3f operator-(const vec3f &a, const vec3f &b) {
|
||||
return vec3f(a.x - b.x, a.y - b.y, a.z - b.z);
|
||||
}
|
||||
|
||||
inline vec3f operator-(const vec3f &v) {
|
||||
return vec3f(-v.x, -v.y, -v.z);
|
||||
}
|
||||
|
||||
inline vec3f &operator*=(vec3f &a, const vec3f &b) {
|
||||
a.x *= b.x;
|
||||
a.y *= b.y;
|
||||
a.z *= b.z;
|
||||
return a;
|
||||
}
|
||||
|
||||
inline vec3f &operator*=(vec3f &a, float b) {
|
||||
a.x *= b;
|
||||
a.y *= b;
|
||||
a.z *= b;
|
||||
return a;
|
||||
}
|
||||
|
||||
inline vec3f operator*(const vec3f &a, float b) {
|
||||
return vec3f(a.x * b, a.y * b, a.z * b);
|
||||
}
|
||||
|
||||
inline vec3f operator*(float b, const vec3f &a) {
|
||||
return a * b;
|
||||
}
|
||||
|
||||
inline vec3f operator*(const vec3f &a, const vec3f &b) {
|
||||
return vec3f(a.x * b.x, a.y * b.y, a.z * b.z);
|
||||
}
|
||||
|
||||
inline vec3f &operator+=(vec3f &a, const vec3f &b) {
|
||||
a.x += b.x;
|
||||
a.y += b.y;
|
||||
a.z += b.z;
|
||||
return a;
|
||||
}
|
||||
|
||||
inline vec3f operator/(const vec3f &a, float b) {
|
||||
float inv = 1.0f / b;
|
||||
return vec3f(a.x * inv, a.y * inv, a.z * inv);
|
||||
}
|
||||
|
||||
inline vec3f &operator/=(vec3f &a, float b) {
|
||||
float inv = 1.0f / b;
|
||||
a.x *= inv;
|
||||
a.y *= inv;
|
||||
a.z *= inv;
|
||||
return a;
|
||||
}
|
||||
|
||||
inline float dot(const vec3f &a, const vec3f &b) {
|
||||
return a.x * b.x + a.y * b.y + a.z * b.z;
|
||||
}
|
||||
|
||||
inline vec3f normalize(const vec3f &v) {
|
||||
float len2 = dot(v, v);
|
||||
float invLen = 1.0f / sqrtf(len2 + 1e-20f);
|
||||
return v * invLen;
|
||||
}
|
||||
|
||||
inline vec3f cross(const vec3f &a, const vec3f &b) {
|
||||
return vec3f(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x);
|
||||
}
|
||||
|
||||
static inline float32x4_t fastRecip(float32x4_t x) {
|
||||
float32x4_t r = vrecpeq_f32(x);
|
||||
r = vmulq_f32(vrecpsq_f32(x, r), r);
|
||||
r = vmulq_f32(vrecpsq_f32(x, r), r);
|
||||
return r;
|
||||
}
|
||||
|
||||
static inline float32x4_t fastRsqrt(float32x4_t x) {
|
||||
float32x4_t r = vrsqrteq_f32(x);
|
||||
r = vmulq_f32(r, vrsqrtsq_f32(vmulq_f32(x, r), r));
|
||||
r = vmulq_f32(r, vrsqrtsq_f32(vmulq_f32(x, r), r));
|
||||
return r;
|
||||
}
|
||||
|
||||
static inline vec3x4 normalizeFast(const vec3x4 &v) {
|
||||
float32x4_t len2 = vfmaq_f32(vmulq_f32(v.z, v.z), v.y, v.y);
|
||||
|
||||
len2 = vfmaq_f32(len2, v.x, v.x);
|
||||
|
||||
float32x4_t invLen = fastRsqrt(len2);
|
||||
|
||||
vec3x4 out;
|
||||
out.x = vmulq_f32(v.x, invLen);
|
||||
out.y = vmulq_f32(v.y, invLen);
|
||||
out.z = vmulq_f32(v.z, invLen);
|
||||
return out;
|
||||
}
|
||||
|
||||
inline uint32x4_t xorshift4(uint32x4_t &s) {
|
||||
s = veorq_u32(s, vshlq_n_u32(s, 13));
|
||||
s = veorq_u32(s, vshrq_n_u32(s, 17));
|
||||
s = veorq_u32(s, vshlq_n_u32(s, 5));
|
||||
return s;
|
||||
}
|
||||
|
||||
inline float32x4_t toFloat01(uint32x4_t x) {
|
||||
uint32x4_t m = vandq_u32(x, vdupq_n_u32(0xFFFFFF));
|
||||
return vmulq_n_f32(vcvtq_f32_u32(m), 1.0f / float(0xFFFFFF));
|
||||
}
|
||||
|
||||
inline float32x4_t sin4(float32x4_t phi) {
|
||||
|
||||
const float32x4_t pi = vdupq_n_f32(3.14159265f);
|
||||
const float32x4_t pi_2 = vdupq_n_f32(1.57079633f);
|
||||
// const float32x4_t two_pi = vdupq_n_f32(6.28318531f);
|
||||
|
||||
uint32x4_t neg = vcgeq_f32(phi, pi);
|
||||
float32x4_t xr = vbslq_f32(neg, vsubq_f32(phi, pi), phi);
|
||||
|
||||
uint32x4_t fold = vcgtq_f32(xr, pi_2);
|
||||
xr = vbslq_f32(fold, vsubq_f32(pi, xr), xr);
|
||||
|
||||
float32x4_t x2 = vmulq_f32(xr, xr);
|
||||
float32x4_t r = vfmsq_f32(vdupq_n_f32(1.0f / 120.0f), x2, vdupq_n_f32(1.0f / 5040.0f));
|
||||
r = vfmsq_f32(vdupq_n_f32(1.0f / 6.0f), x2, r);
|
||||
r = vfmsq_f32(vdupq_n_f32(1.0f), x2, r);
|
||||
r = vmulq_f32(xr, r);
|
||||
|
||||
uint32x4_t signBit = vshlq_n_u32(neg, 31);
|
||||
return vreinterpretq_f32_u32(veorq_u32(vreinterpretq_u32_f32(r), signBit));
|
||||
}
|
||||
|
||||
inline float32x4_t cos4(float32x4_t phi) {
|
||||
|
||||
float32x4_t shifted = vaddq_f32(phi, vdupq_n_f32(1.57079633f));
|
||||
float32x4_t two_pi = vdupq_n_f32(6.28318531f);
|
||||
uint32x4_t wrap = vcgeq_f32(shifted, two_pi);
|
||||
shifted = vbslq_f32(wrap, vsubq_f32(shifted, two_pi), shifted);
|
||||
return sin4(shifted);
|
||||
}
|
||||
|
||||
inline void buildONB4(const vec3x4 &n, vec3x4 &t, vec3x4 &b) {
|
||||
const float32x4_t one = vdupq_n_f32(1.0f);
|
||||
const float32x4_t neg = vdupq_n_f32(-1.0f);
|
||||
|
||||
uint32x4_t pos = vcgeq_f32(n.z, vdupq_n_f32(0.0f));
|
||||
float32x4_t sgn = vbslq_f32(pos, one, neg);
|
||||
|
||||
float32x4_t denom = vaddq_f32(sgn, n.z);
|
||||
float32x4_t r = vrecpeq_f32(denom);
|
||||
r = vmulq_f32(vrecpsq_f32(denom, r), r);
|
||||
r = vmulq_f32(vrecpsq_f32(denom, r), r);
|
||||
float32x4_t a = vnegq_f32(r);
|
||||
|
||||
float32x4_t bc = vmulq_f32(vmulq_f32(n.x, n.y), a);
|
||||
|
||||
t.x = vaddq_f32(one, vmulq_f32(sgn, vmulq_f32(vmulq_f32(n.x, n.y), a)));
|
||||
t.y = vmulq_f32(sgn, bc);
|
||||
t.z = vnegq_f32(vmulq_f32(sgn, n.x));
|
||||
|
||||
b.x = bc;
|
||||
b.y = vaddq_f32(sgn, vmulq_f32(vmulq_f32(n.y, n.y), a));
|
||||
b.z = vnegq_f32(n.y);
|
||||
}
|
||||
|
||||
inline vec3x4 cosineSampleHemisphere4(const vec3x4 &n, uint32x4_t &rng) {
|
||||
|
||||
float32x4_t u1 = toFloat01(xorshift4(rng));
|
||||
float32x4_t u2 = toFloat01(xorshift4(rng));
|
||||
|
||||
float32x4_t r = vsqrtq_f32(u1);
|
||||
float32x4_t phi = vmulq_n_f32(u2, 6.28318531f);
|
||||
|
||||
float32x4_t lx = vmulq_f32(r, cos4(phi));
|
||||
float32x4_t ly = vmulq_f32(r, sin4(phi));
|
||||
float32x4_t lz = vsqrtq_f32(vmaxq_f32(vsubq_f32(vdupq_n_f32(1.0f), u1), vdupq_n_f32(0.0)));
|
||||
|
||||
vec3x4 t, b;
|
||||
buildONB4(n, t, b);
|
||||
|
||||
vec3x4 d;
|
||||
d.x = vaddq_f32(vaddq_f32(vmulq_f32(lx, t.x), vmulq_f32(ly, b.x)), vmulq_f32(lz, n.x));
|
||||
d.y = vaddq_f32(vaddq_f32(vmulq_f32(lx, t.y), vmulq_f32(ly, b.y)), vmulq_f32(lz, n.y));
|
||||
d.z = vaddq_f32(vaddq_f32(vmulq_f32(lx, t.z), vmulq_f32(ly, b.z)), vmulq_f32(lz, n.z));
|
||||
return d;
|
||||
}
|
||||
|
||||
inline float neon_dot3(float32x4_t a, float32x4_t b) {
|
||||
float32x4_t mul = vmulq_f32(a, b);
|
||||
float32x2_t lo = vget_low_f32(mul);
|
||||
float32x2_t hi = vget_high_f32(mul);
|
||||
float32x2_t sum = vpadd_f32(lo, lo);
|
||||
return vget_lane_f32(vadd_f32(sum, hi), 0);
|
||||
}
|
||||
|
||||
inline float32x4_t neon_cross3(float32x4_t a, float32x4_t b) {
|
||||
float32x4_t a_yzx = __builtin_shufflevector(a, a, 1, 2, 0, 3);
|
||||
float32x4_t a_zxy = __builtin_shufflevector(a, a, 2, 0, 1, 3);
|
||||
float32x4_t b_yzx = __builtin_shufflevector(b, b, 1, 2, 0, 3);
|
||||
float32x4_t b_zxy = __builtin_shufflevector(b, b, 2, 0, 1, 3);
|
||||
return vsubq_f32(vmulq_f32(a_yzx, b_zxy), vmulq_f32(a_zxy, b_yzx));
|
||||
}
|
||||
|
||||
inline float32x4_t neon_normalize3(float32x4_t v) {
|
||||
float32x2_t lenSq = vdup_n_f32(neon_dot3(v, v));
|
||||
float32x2_t est = vrsqrte_f32(lenSq);
|
||||
est = vmul_f32(est, vrsqrts_f32(vmul_f32(lenSq, est), est));
|
||||
return vmulq_f32(v, vcombine_f32(est, est));
|
||||
}
|
||||
|
||||
inline void neon_store3(float *p, float32x4_t v) {
|
||||
vst1q_lane_f32(p + 0, v, 0);
|
||||
vst1q_lane_f32(p + 1, v, 1);
|
||||
vst1q_lane_f32(p + 2, v, 2);
|
||||
}
|
||||
|
||||
inline float32x4_t vpermute(float32x4_t x) {
|
||||
float32x4_t v34 = vdupq_n_f32(34.0f);
|
||||
float32x4_t v1 = vdupq_n_f32(1.0f);
|
||||
float32x4_t v289 = vdupq_n_f32(289.0f);
|
||||
float32x4_t inv289 = vdupq_n_f32(1.0f / 289.0f);
|
||||
|
||||
float32x4_t res = vmlaq_f32(v1, x, v34);
|
||||
res = vmulq_f32(res, x);
|
||||
|
||||
float32x4_t quotient = vmulq_f32(res, inv289);
|
||||
|
||||
float32x4_t floored = vrndmq_f32(quotient);
|
||||
res = vmlsq_f32(res, floored, v289);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
inline float32x4_t vtaylorInvSqrt(float32x4_t x) {
|
||||
float32x4_t c1 = vdupq_n_f32(1.79284291400159f);
|
||||
float32x4_t c2 = vdupq_n_f32(0.85373472095314f);
|
||||
return vmlsq_f32(c1, x, c2);
|
||||
}
|
||||
|
||||
static inline void fastSinCos(float angle, float *s, float *c) {
|
||||
|
||||
const float INV_TWO_PI = 0.15915494309f;
|
||||
const float TWO_PI = 6.28318530718f;
|
||||
// const float PI = 3.14159265359f;
|
||||
|
||||
float x = angle - TWO_PI * floorf(angle * INV_TWO_PI + 0.5f);
|
||||
|
||||
float x2 = x * x;
|
||||
float x3 = x2 * x;
|
||||
float x5 = x3 * x2;
|
||||
float x7 = x5 * x3;
|
||||
|
||||
*s = x + x3 * (-0.16666667163f) + x5 * (0.00833333842f) + x7 * (-0.00019840680f);
|
||||
|
||||
float x4 = x2 * x2;
|
||||
float x6 = x4 * x2;
|
||||
|
||||
*c = 1.0f + x2 * (-0.49999997020f) + x4 * (0.04166664556f) + x6 * (-0.00138873165f);
|
||||
}
|
||||
251
Source/Benchmark-Toolbox/source/gpu_bw.c
Normal file
251
Source/Benchmark-Toolbox/source/gpu_bw.c
Normal file
@@ -0,0 +1,251 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <switch.h>
|
||||
|
||||
#include "gpu_bw.h"
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
#include <GLES3/gl31.h>
|
||||
|
||||
static EGLDisplay s_display = EGL_NO_DISPLAY;
|
||||
static EGLContext s_context = EGL_NO_CONTEXT;
|
||||
|
||||
static bool egl_init(void) {
|
||||
s_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
||||
if (s_display == EGL_NO_DISPLAY) {
|
||||
printf("EGL: no display\n");
|
||||
consoleUpdate(NULL);
|
||||
return false;
|
||||
}
|
||||
if (!eglInitialize(s_display, NULL, NULL)) {
|
||||
printf("EGL: initialize failed (0x%x)\n", eglGetError());
|
||||
consoleUpdate(NULL);
|
||||
return false;
|
||||
}
|
||||
if (!eglBindAPI(EGL_OPENGL_API)) {
|
||||
printf("EGL: bindAPI(OPENGL) failed (0x%x)\n", eglGetError());
|
||||
consoleUpdate(NULL);
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *exts = eglQueryString(s_display, EGL_EXTENSIONS);
|
||||
if (!exts || !strstr(exts, "EGL_KHR_surfaceless_context")) {
|
||||
printf("EGL: no surfaceless_context\n");
|
||||
consoleUpdate(NULL);
|
||||
return false;
|
||||
}
|
||||
|
||||
static const EGLint cfg_attribs[] = {
|
||||
EGL_RENDERABLE_TYPE,
|
||||
EGL_OPENGL_BIT,
|
||||
EGL_RED_SIZE,
|
||||
8,
|
||||
EGL_GREEN_SIZE,
|
||||
8,
|
||||
EGL_BLUE_SIZE,
|
||||
8,
|
||||
EGL_ALPHA_SIZE,
|
||||
8,
|
||||
EGL_DEPTH_SIZE,
|
||||
24,
|
||||
EGL_STENCIL_SIZE,
|
||||
8,
|
||||
EGL_NONE,
|
||||
};
|
||||
EGLConfig cfg;
|
||||
EGLint n;
|
||||
if (!eglChooseConfig(s_display, cfg_attribs, &cfg, 1, &n) || !n) {
|
||||
printf("EGL: chooseConfig failed n=%d (0x%x)\n", (int)n, eglGetError());
|
||||
consoleUpdate(NULL);
|
||||
return false;
|
||||
}
|
||||
|
||||
static const EGLint ctx_attribs[] = {
|
||||
EGL_CONTEXT_MAJOR_VERSION_KHR, 4, EGL_CONTEXT_MINOR_VERSION_KHR, 3, EGL_NONE,
|
||||
};
|
||||
s_context = eglCreateContext(s_display, cfg, EGL_NO_CONTEXT, ctx_attribs);
|
||||
if (s_context == EGL_NO_CONTEXT) {
|
||||
printf("EGL: createContext failed (0x%x)\n", eglGetError());
|
||||
consoleUpdate(NULL);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (eglMakeCurrent(s_display, EGL_NO_SURFACE, EGL_NO_SURFACE, s_context) != EGL_TRUE) {
|
||||
printf("EGL: makeCurrent failed (0x%x)\n", eglGetError());
|
||||
consoleUpdate(NULL);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void egl_exit(void) {
|
||||
eglMakeCurrent(s_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
if (s_context != EGL_NO_CONTEXT) {
|
||||
eglDestroyContext(s_display, s_context);
|
||||
s_context = EGL_NO_CONTEXT;
|
||||
}
|
||||
if (s_display != EGL_NO_DISPLAY) {
|
||||
eglTerminate(s_display);
|
||||
s_display = EGL_NO_DISPLAY;
|
||||
}
|
||||
eglReleaseThread();
|
||||
}
|
||||
|
||||
static GLuint compile_compute(const char *src, const char *name) {
|
||||
GLuint sh = glCreateShader(GL_COMPUTE_SHADER);
|
||||
glShaderSource(sh, 1, &src, NULL);
|
||||
glCompileShader(sh);
|
||||
GLint ok = 0;
|
||||
glGetShaderiv(sh, GL_COMPILE_STATUS, &ok);
|
||||
if (!ok) {
|
||||
char log[256] = { 0 };
|
||||
glGetShaderInfoLog(sh, sizeof(log), NULL, log);
|
||||
printf("Compute shader compile failed: %s\n", log[0] ? log : name);
|
||||
glDeleteShader(sh);
|
||||
return 0;
|
||||
}
|
||||
GLuint prog = glCreateProgram();
|
||||
glAttachShader(prog, sh);
|
||||
glLinkProgram(prog);
|
||||
glDeleteShader(sh);
|
||||
glGetProgramiv(prog, GL_LINK_STATUS, &ok);
|
||||
if (!ok) {
|
||||
glDeleteProgram(prog);
|
||||
return 0;
|
||||
}
|
||||
return prog;
|
||||
}
|
||||
|
||||
static double run_pass(GLuint prog, GLuint ssbo_src, GLuint ssbo_dst, size_t buf_bytes, int loops) {
|
||||
GLuint groups = (GLuint)(buf_bytes >> 10);
|
||||
|
||||
glUseProgram(prog);
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, ssbo_src);
|
||||
if (ssbo_dst)
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, ssbo_dst);
|
||||
|
||||
glDispatchCompute(groups, 1, 1);
|
||||
glFinish();
|
||||
if (glGetError() != GL_NO_ERROR)
|
||||
return 0.0;
|
||||
|
||||
uint64_t t0, t1;
|
||||
asm volatile("mrs %0, cntpct_el0" : "=r"(t0));
|
||||
for (int i = 0; i < loops; i++)
|
||||
glDispatchCompute(groups, 1, 1);
|
||||
glFinish();
|
||||
asm volatile("mrs %0, cntpct_el0" : "=r"(t1));
|
||||
|
||||
if (glGetError() != GL_NO_ERROR)
|
||||
return 0.0;
|
||||
|
||||
double elapsed = (double)(t1 - t0) * 625.0 / 12.0 / 1000000000.0;
|
||||
if (elapsed <= 0.0)
|
||||
return 0.0;
|
||||
return ((double)buf_bytes * (double)loops) / elapsed / 1000000.0;
|
||||
}
|
||||
|
||||
bool gpu_bw_run(bool is_4gb, double *copy_out, double *read_out, double *write_out) {
|
||||
|
||||
static const char *src_copy = "\n#version 430\n"
|
||||
"layout(std430, binding = 0) buffer srcBuffer { volatile uint src[]; };\n"
|
||||
"layout(std430, binding = 1) buffer dstBuffer { volatile uint dst[]; };\n"
|
||||
"layout(local_size_x = 256, local_size_y = 1, local_size_z = 1) in;\n"
|
||||
"void main() {\n"
|
||||
" dst[gl_GlobalInvocationID.x] = src[gl_GlobalInvocationID.x];\n"
|
||||
"}\n";
|
||||
|
||||
static const char *src_read = "\n#version 430\n"
|
||||
"layout(std430, binding = 0) buffer srcBuffer { volatile uint src[]; };\n"
|
||||
"shared uint tmp;\n"
|
||||
"layout(local_size_x = 256, local_size_y = 1, local_size_z = 1) in;\n"
|
||||
"void main() {\n"
|
||||
" tmp |= src[gl_GlobalInvocationID.x];\n"
|
||||
"}\n";
|
||||
|
||||
static const char *src_write = "\n#version 430\n"
|
||||
"layout(std430, binding = 0) buffer srcBuffer { volatile uint src[]; };\n"
|
||||
"layout(local_size_x = 256, local_size_y = 1, local_size_z = 1) in;\n"
|
||||
"void main() {\n"
|
||||
" src[gl_GlobalInvocationID.x] = 0xAAAAAAAAu;\n"
|
||||
"}\n";
|
||||
|
||||
if (!egl_init()) {
|
||||
printf("Failed to initialize EGL/GL for GPU benchmark.\n");
|
||||
consoleUpdate(NULL);
|
||||
return false;
|
||||
}
|
||||
|
||||
GLint max_ssbo = 0;
|
||||
glGetIntegerv(GL_MAX_SHADER_STORAGE_BLOCK_SIZE, &max_ssbo);
|
||||
if (max_ssbo < 1) {
|
||||
printf("Failed to query GPU SSBO size.\n");
|
||||
consoleUpdate(NULL);
|
||||
egl_exit();
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t buf_bytes = (size_t)max_ssbo;
|
||||
if (buf_bytes > 0x8000000)
|
||||
buf_bytes = 0x8000000;
|
||||
if (!is_4gb)
|
||||
buf_bytes >>= 1;
|
||||
buf_bytes &= ~(size_t)0x3FF;
|
||||
|
||||
if (buf_bytes < 0x400) {
|
||||
printf("GPU benchmark buffer is too small.\n");
|
||||
consoleUpdate(NULL);
|
||||
egl_exit();
|
||||
return false;
|
||||
}
|
||||
|
||||
int loops = is_4gb ? 400 : 800;
|
||||
int wloops = loops / 10;
|
||||
|
||||
GLuint prog_copy = compile_compute(src_copy, "GPU Copy");
|
||||
GLuint prog_read = compile_compute(src_read, "GPU Read");
|
||||
GLuint prog_write = compile_compute(src_write, "GPU Write");
|
||||
if (!prog_copy || !prog_read || !prog_write) {
|
||||
if (prog_copy)
|
||||
glDeleteProgram(prog_copy);
|
||||
if (prog_read)
|
||||
glDeleteProgram(prog_read);
|
||||
if (prog_write)
|
||||
glDeleteProgram(prog_write);
|
||||
egl_exit();
|
||||
return false;
|
||||
}
|
||||
|
||||
GLuint ssbo[2] = { 0, 0 };
|
||||
glGenBuffers(2, ssbo);
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo[0]);
|
||||
glBufferData(GL_SHADER_STORAGE_BUFFER, (GLsizeiptr)buf_bytes, NULL, GL_DYNAMIC_COPY);
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo[1]);
|
||||
glBufferData(GL_SHADER_STORAGE_BUFFER, (GLsizeiptr)buf_bytes, NULL, GL_DYNAMIC_COPY);
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
|
||||
if (glGetError() != GL_NO_ERROR) {
|
||||
printf("Failed to allocate GPU buffers!\n");
|
||||
consoleUpdate(NULL);
|
||||
glDeleteBuffers(2, ssbo);
|
||||
glDeleteProgram(prog_copy);
|
||||
glDeleteProgram(prog_read);
|
||||
glDeleteProgram(prog_write);
|
||||
egl_exit();
|
||||
return false;
|
||||
}
|
||||
|
||||
run_pass(prog_write, ssbo[0], 0, buf_bytes, wloops);
|
||||
run_pass(prog_read, ssbo[0], 0, buf_bytes, wloops);
|
||||
|
||||
*copy_out = run_pass(prog_copy, ssbo[0], ssbo[1], buf_bytes, loops);
|
||||
*read_out = run_pass(prog_read, ssbo[0], 0, buf_bytes, loops);
|
||||
*write_out = run_pass(prog_write, ssbo[0], 0, buf_bytes, loops);
|
||||
|
||||
glDeleteBuffers(2, ssbo);
|
||||
glDeleteProgram(prog_copy);
|
||||
glDeleteProgram(prog_read);
|
||||
glDeleteProgram(prog_write);
|
||||
egl_exit();
|
||||
return true;
|
||||
}
|
||||
4
Source/Benchmark-Toolbox/source/gpu_bw.h
Normal file
4
Source/Benchmark-Toolbox/source/gpu_bw.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
#include <stdbool.h>
|
||||
|
||||
bool gpu_bw_run(bool is_4gb, double *copy_out, double *read_out, double *write_out);
|
||||
305
Source/Benchmark-Toolbox/source/gpu_stress.cpp
Normal file
305
Source/Benchmark-Toolbox/source/gpu_stress.cpp
Normal file
@@ -0,0 +1,305 @@
|
||||
extern "C" {
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <switch.h>
|
||||
|
||||
#include "gpu_stress.h"
|
||||
}
|
||||
#include <deko3d.hpp>
|
||||
|
||||
#include "compute_shader_bin.h"
|
||||
|
||||
static constexpr uint32_t kDispatchX = 96u;
|
||||
static constexpr uint32_t kLocalX = 128u;
|
||||
static constexpr uint32_t kThreads = kDispatchX * kLocalX;
|
||||
static constexpr uint32_t kSeedBytes = kThreads * 4u;
|
||||
static constexpr uint32_t kOutU32s = kThreads * 4u;
|
||||
static constexpr uint32_t kOutBytes = kOutU32s * 4u;
|
||||
static constexpr uint32_t kScrBytes = 0x400000u;
|
||||
static constexpr uint32_t kUboAlloc = 0x1000u;
|
||||
static constexpr uint32_t kUboBindSz = 0x100u;
|
||||
static constexpr uint32_t kCmdbufSz = 0x20000u;
|
||||
|
||||
static constexpr uint32_t kDataFlags = DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached;
|
||||
static constexpr uint32_t kCodeFlags = DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached | DkMemBlockFlags_Code;
|
||||
|
||||
static constexpr double kOpsPerIter = 259.0;
|
||||
|
||||
static constexpr uint32_t kTargetMs = 15u;
|
||||
|
||||
static constexpr uint32_t kWindowIters = 16u;
|
||||
static constexpr bool kCpuDetect = true;
|
||||
static constexpr bool kCvDetect = true;
|
||||
|
||||
static uint64_t read_cntpct(void) {
|
||||
uint64_t t;
|
||||
asm volatile("mrs %0, cntpct_el0" : "=r"(t));
|
||||
return t;
|
||||
}
|
||||
static uint64_t cntpct_to_ms(uint64_t ticks) {
|
||||
|
||||
return (ticks * 625ULL) / 12000000ULL;
|
||||
}
|
||||
static uint32_t round_up_4k(uint32_t v) {
|
||||
return (v + 4095u) & ~4095u;
|
||||
}
|
||||
|
||||
static void init_seed_block(uint32_t *out, uint32_t count) {
|
||||
for (uint32_t a = 0; a < count; a++) {
|
||||
uint32_t m = a * 0x9e3779b9u;
|
||||
uint8_t b0 = (uint8_t)(m >> 16) ^ (uint8_t)(a) ^ 0xa5u;
|
||||
uint8_t b1 = (uint8_t)(m >> 24) ^ (uint8_t)(a >> 8) ^ 0xa5u;
|
||||
uint32_t v = ((uint32_t)b1 << 24) | ((uint32_t)b0 << 16) | ((uint32_t)((uint8_t)(b1 ^ (uint8_t)(m >> 8) ^ 0x5au)) << 8) |
|
||||
(uint32_t)((uint8_t)(b0 ^ (uint8_t)m ^ 0x5au));
|
||||
uint32_t u = v * 0x7feb352du;
|
||||
uint8_t c1 = (uint8_t)(u >> 24);
|
||||
uint32_t w = ((uint32_t)c1 << 24) | ((uint32_t)((uint8_t)((c1 >> 7) ^ (uint8_t)(u >> 16))) << 16) |
|
||||
((uint32_t)((uint8_t)((uint8_t)(u >> 23) ^ (uint8_t)(u >> 8))) << 8) | (uint32_t)((uint8_t)((uint8_t)(u >> 15) ^ (uint8_t)u));
|
||||
out[a] = (w * 0x846ca68bu) | 1u;
|
||||
}
|
||||
}
|
||||
|
||||
static void init_scratch(uint32_t *out, uint32_t count) {
|
||||
uint32_t s1 = 0x6d2b79f5u;
|
||||
uint32_t s2 = 0xc2b2ae35u;
|
||||
uint32_t acc = 0u;
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
s1 += acc;
|
||||
uint32_t os2 = s2;
|
||||
acc += 0x9e3779b9u;
|
||||
s2 = os2 + 0x85ebca6bu;
|
||||
uint32_t m1 = os2;
|
||||
m1 = (m1 ^ (m1 >> 16u)) * 0x7feb352du;
|
||||
m1 = (m1 ^ (m1 >> 15u)) * 0x846ca68bu;
|
||||
m1 ^= m1 >> 16u;
|
||||
uint32_t m2 = s1;
|
||||
m2 = (m2 ^ (m2 >> 16u)) * 0x7feb352du;
|
||||
m2 = (m2 ^ (m2 >> 15u)) * 0x846ca68bu;
|
||||
m2 ^= m2 >> 16u;
|
||||
out[i] = m1 ^ m2;
|
||||
}
|
||||
}
|
||||
|
||||
struct StressState {
|
||||
dk::UniqueDevice device;
|
||||
dk::UniqueQueue queue;
|
||||
dk::UniqueMemBlock codeBlock;
|
||||
dk::UniqueMemBlock paramsBlock;
|
||||
dk::UniqueMemBlock seedBlock;
|
||||
dk::UniqueMemBlock scratchBlock;
|
||||
dk::UniqueMemBlock outABlock;
|
||||
dk::UniqueMemBlock outBBlock;
|
||||
dk::UniqueMemBlock cmdmemBlock;
|
||||
dk::UniqueCmdBuf cmdbuf;
|
||||
dk::Shader computeShader;
|
||||
DkCmdList listA = 0;
|
||||
DkCmdList listB = 0;
|
||||
uint32_t *params = nullptr;
|
||||
uint32_t *outA = nullptr;
|
||||
uint32_t *outB = nullptr;
|
||||
uint32_t *golden = nullptr;
|
||||
uint32_t batch_size = 1024u;
|
||||
uint64_t cum_dispatches = 0;
|
||||
uint64_t start_ms = 0;
|
||||
bool initialized = false;
|
||||
bool failed = false;
|
||||
};
|
||||
static StressState g;
|
||||
|
||||
static bool stress_init(void) {
|
||||
g.device = dk::DeviceMaker{}.create();
|
||||
if (!g.device) {
|
||||
printf("deko3d: device create failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
g.queue = dk::QueueMaker{ g.device }.setFlags(DkQueueFlags_Compute).create();
|
||||
if (!g.queue) {
|
||||
printf("deko3d: queue create failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t codeSize = round_up_4k(compute_shader_bin_size);
|
||||
g.codeBlock = dk::MemBlockMaker{ g.device, codeSize }.setFlags(kCodeFlags).create();
|
||||
if (!g.codeBlock) {
|
||||
printf("deko3d: code memblock failed\n");
|
||||
return false;
|
||||
}
|
||||
memcpy(g.codeBlock.getCpuAddr(), compute_shader_bin, compute_shader_bin_size);
|
||||
dk::ShaderMaker{ g.codeBlock, 0 }.initialize(g.computeShader);
|
||||
|
||||
g.paramsBlock = dk::MemBlockMaker{ g.device, kUboAlloc }.setFlags(kDataFlags).create();
|
||||
g.seedBlock = dk::MemBlockMaker{ g.device, kSeedBytes }.setFlags(kDataFlags).create();
|
||||
g.scratchBlock = dk::MemBlockMaker{ g.device, kScrBytes }.setFlags(kDataFlags).create();
|
||||
g.outABlock = dk::MemBlockMaker{ g.device, kOutBytes }.setFlags(kDataFlags).create();
|
||||
g.outBBlock = dk::MemBlockMaker{ g.device, kOutBytes }.setFlags(kDataFlags).create();
|
||||
g.cmdmemBlock = dk::MemBlockMaker{ g.device, kCmdbufSz }.setFlags(kDataFlags).create();
|
||||
if (!g.paramsBlock || !g.seedBlock || !g.scratchBlock || !g.outABlock || !g.outBBlock || !g.cmdmemBlock) {
|
||||
printf("deko3d: memblock alloc failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
g.params = (uint32_t *)g.paramsBlock.getCpuAddr();
|
||||
auto *seeds = (uint32_t *)g.seedBlock.getCpuAddr();
|
||||
auto *scratch = (uint32_t *)g.scratchBlock.getCpuAddr();
|
||||
g.outA = (uint32_t *)g.outABlock.getCpuAddr();
|
||||
g.outB = (uint32_t *)g.outBBlock.getCpuAddr();
|
||||
|
||||
g.batch_size = 1024u;
|
||||
g.params[0] = g.batch_size;
|
||||
g.params[1] = 0;
|
||||
g.params[2] = 0;
|
||||
g.params[3] = 0;
|
||||
init_seed_block(seeds, kThreads);
|
||||
|
||||
init_scratch(scratch, kScrBytes / 4u);
|
||||
for (uint32_t i = 0; i < kOutU32s; i++) {
|
||||
g.outA[i] = 0xcafebabeu;
|
||||
g.outB[i] = 0xcafebabeu;
|
||||
}
|
||||
|
||||
g.cmdbuf = dk::CmdBufMaker{ g.device }.create();
|
||||
if (!g.cmdbuf) {
|
||||
printf("deko3d: cmdbuf create failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto record = [&](dk::MemBlock outputBlock, uint32_t memOffset) -> DkCmdList {
|
||||
g.cmdbuf.addMemory(g.cmdmemBlock, memOffset, kCmdbufSz / 2);
|
||||
g.cmdbuf.bindShaders(DkStageFlag_Compute, { &g.computeShader });
|
||||
DkBufExtents ubo = { g.paramsBlock.getGpuAddr(), kUboBindSz };
|
||||
g.cmdbuf.bindUniformBuffers(DkStage_Compute, 0, { ubo });
|
||||
DkBufExtents sb0 = { g.seedBlock.getGpuAddr(), kSeedBytes };
|
||||
DkBufExtents sb1 = { outputBlock.getGpuAddr(), kOutBytes };
|
||||
DkBufExtents sb2 = { g.scratchBlock.getGpuAddr(), kScrBytes };
|
||||
g.cmdbuf.bindStorageBuffers(DkStage_Compute, 0, { sb0, sb1, sb2 });
|
||||
g.cmdbuf.dispatchCompute(kDispatchX, 1, 1);
|
||||
return g.cmdbuf.finishList();
|
||||
};
|
||||
g.listA = record(g.outABlock, 0);
|
||||
g.listB = record(g.outBBlock, kCmdbufSz / 2);
|
||||
|
||||
uint64_t t0 = read_cntpct();
|
||||
g.queue.submitCommands(g.listA);
|
||||
g.queue.waitIdle();
|
||||
uint64_t cal_ms = cntpct_to_ms(read_cntpct() - t0);
|
||||
if (cal_ms == 0) {
|
||||
g.batch_size = 1024u;
|
||||
} else {
|
||||
double scaled = ((double)kTargetMs / (double)cal_ms) * 1024.0;
|
||||
if (scaled < 256.0)
|
||||
g.batch_size = 256u;
|
||||
else if (scaled > 65536.0)
|
||||
g.batch_size = 65536u;
|
||||
else
|
||||
g.batch_size = (uint32_t)scaled;
|
||||
}
|
||||
g.params[0] = g.batch_size;
|
||||
|
||||
for (uint32_t i = 0; i < kOutU32s; i++)
|
||||
g.outA[i] = 0xcafebabeu;
|
||||
g.queue.submitCommands(g.listA);
|
||||
g.queue.waitIdle();
|
||||
|
||||
bool gpu_wrote = false;
|
||||
for (uint32_t i = 0; i < kOutU32s && !gpu_wrote; i++)
|
||||
if (g.outA[i] != 0xcafebabeu)
|
||||
gpu_wrote = true;
|
||||
if (!gpu_wrote) {
|
||||
printf("deko3d: golden dispatch produced no GPU writes\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
g.golden = (uint32_t *)malloc(kOutBytes);
|
||||
if (!g.golden) {
|
||||
printf("deko3d: OOM for golden\n");
|
||||
return false;
|
||||
}
|
||||
memcpy(g.golden, g.outA, kOutBytes);
|
||||
|
||||
g.start_ms = cntpct_to_ms(read_cntpct());
|
||||
g.cum_dispatches = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
extern "C" bool gpu_stress_run(double *gflops_out, uint64_t *dispatches_out, uint64_t *mismatches_out) {
|
||||
*gflops_out = 0.0;
|
||||
*dispatches_out = 0;
|
||||
*mismatches_out = 0;
|
||||
|
||||
if (g.failed)
|
||||
return false;
|
||||
if (!g.initialized) {
|
||||
if (!stress_init()) {
|
||||
g.failed = true;
|
||||
return false;
|
||||
}
|
||||
g.initialized = true;
|
||||
}
|
||||
|
||||
uint64_t window_dispatches = 0;
|
||||
uint64_t window_mismatches = 0;
|
||||
for (uint32_t n = 0; n < kWindowIters; n++) {
|
||||
g.queue.submitCommands(g.listA);
|
||||
window_dispatches++;
|
||||
if (kCvDetect) {
|
||||
g.queue.submitCommands(g.listB);
|
||||
window_dispatches++;
|
||||
}
|
||||
g.queue.waitIdle();
|
||||
|
||||
if (kCpuDetect) {
|
||||
|
||||
if (memcmp(g.outA, g.golden, kOutBytes) != 0)
|
||||
for (uint32_t i = 0; i < kOutU32s; i++)
|
||||
if (g.outA[i] != g.golden[i])
|
||||
window_mismatches++;
|
||||
}
|
||||
if (kCvDetect) {
|
||||
|
||||
if (memcmp(g.outA, g.outB, kOutBytes) != 0)
|
||||
for (uint32_t i = 0; i < kOutU32s; i++)
|
||||
if (g.outB[i] != g.outA[i])
|
||||
window_mismatches++;
|
||||
}
|
||||
}
|
||||
g.cum_dispatches += window_dispatches;
|
||||
|
||||
uint64_t now_ms = cntpct_to_ms(read_cntpct());
|
||||
double elapsed_s = (double)(now_ms - g.start_ms) / 1000.0;
|
||||
double gflops = (elapsed_s > 0.0) ? ((double)g.batch_size * kOpsPerIter * (double)kThreads * (double)g.cum_dispatches) / (elapsed_s * 1e9) : 0.0;
|
||||
|
||||
*gflops_out = gflops;
|
||||
*dispatches_out = window_dispatches;
|
||||
*mismatches_out = window_mismatches;
|
||||
return true;
|
||||
}
|
||||
|
||||
extern "C" void gpu_stress_shutdown(void) {
|
||||
if (g.golden) {
|
||||
free(g.golden);
|
||||
g.golden = nullptr;
|
||||
}
|
||||
if (g.queue)
|
||||
g.queue.waitIdle();
|
||||
|
||||
g.cmdbuf = {};
|
||||
g.cmdmemBlock = {};
|
||||
g.outBBlock = {};
|
||||
g.outABlock = {};
|
||||
g.scratchBlock = {};
|
||||
g.seedBlock = {};
|
||||
g.paramsBlock = {};
|
||||
g.codeBlock = {};
|
||||
g.queue = {};
|
||||
g.device = {};
|
||||
|
||||
g.listA = g.listB = 0;
|
||||
g.params = g.outA = g.outB = nullptr;
|
||||
g.batch_size = 1024u;
|
||||
g.cum_dispatches = 0;
|
||||
g.start_ms = 0;
|
||||
g.initialized = false;
|
||||
g.failed = false;
|
||||
}
|
||||
6
Source/Benchmark-Toolbox/source/gpu_stress.h
Normal file
6
Source/Benchmark-Toolbox/source/gpu_stress.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
bool gpu_stress_run(double *gflops_out, uint64_t *dispatches_out, uint64_t *mismatches_out);
|
||||
void gpu_stress_shutdown(void);
|
||||
35
Source/Benchmark-Toolbox/source/hoc_clk.c
Normal file
35
Source/Benchmark-Toolbox/source/hoc_clk.c
Normal file
@@ -0,0 +1,35 @@
|
||||
#define NX_SERVICE_ASSUME_NON_DOMAIN
|
||||
#include <assert.h>
|
||||
#include <switch.h>
|
||||
|
||||
#include "hoc_clk.h"
|
||||
#include <hocclk/clock_manager.h>
|
||||
|
||||
#define HOCCLK_SERVICE "hoc:clk"
|
||||
#define HOCCLK_CMD_GET_CURRENT_CONTEXT 2
|
||||
|
||||
static Service g_srv;
|
||||
static bool g_active = false;
|
||||
|
||||
bool hocclk_init(void) {
|
||||
if (g_active)
|
||||
return true;
|
||||
Result rc = smGetService(&g_srv, HOCCLK_SERVICE);
|
||||
g_active = R_SUCCEEDED(rc);
|
||||
return g_active;
|
||||
}
|
||||
|
||||
void hocclk_exit(void) {
|
||||
if (g_active) {
|
||||
serviceClose(&g_srv);
|
||||
g_active = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool hocclk_get(HocClkContext *out) {
|
||||
if (!g_active)
|
||||
return false;
|
||||
Result rc = serviceDispatch(&g_srv, HOCCLK_CMD_GET_CURRENT_CONTEXT, .buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out },
|
||||
.buffers = { { out, sizeof(*out) } }, );
|
||||
return R_SUCCEEDED(rc);
|
||||
}
|
||||
16
Source/Benchmark-Toolbox/source/hoc_clk.h
Normal file
16
Source/Benchmark-Toolbox/source/hoc_clk.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <hocclk/clock_manager.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
bool hocclk_init(void);
|
||||
void hocclk_exit(void);
|
||||
bool hocclk_get(HocClkContext *out);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
585
Source/Benchmark-Toolbox/source/main.cpp
Normal file
585
Source/Benchmark-Toolbox/source/main.cpp
Normal file
@@ -0,0 +1,585 @@
|
||||
#include <atomic>
|
||||
#include <borealis.hpp>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
extern "C" {
|
||||
#include <switch.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "bench.h"
|
||||
#include "gpu_bw.h"
|
||||
#include "gpu_stress.h"
|
||||
#include "hoc_clk.h"
|
||||
#include "run_furmark.h"
|
||||
}
|
||||
|
||||
static std::string fstr(const char *f, double v) {
|
||||
char b[64];
|
||||
std::snprintf(b, sizeof(b), f, v);
|
||||
return b;
|
||||
}
|
||||
static std::string fstru(const char *f, unsigned long long v) {
|
||||
char b[64];
|
||||
std::snprintf(b, sizeof(b), f, v);
|
||||
return b;
|
||||
}
|
||||
|
||||
static brls::Label *makeRow(brls::Box *parent, const std::string &name) {
|
||||
auto *row = new brls::Box(brls::Axis::ROW);
|
||||
row->setMarginBottom(8.0f);
|
||||
auto *n = new brls::Label();
|
||||
n->setText(name);
|
||||
n->setGrow(1.0f);
|
||||
auto *v = new brls::Label();
|
||||
v->setText("-");
|
||||
row->addView(n);
|
||||
row->addView(v);
|
||||
parent->addView(row);
|
||||
return v;
|
||||
}
|
||||
|
||||
struct StatCells {
|
||||
brls::Label *load = nullptr, *clock = nullptr, *volt = nullptr, *temp = nullptr;
|
||||
};
|
||||
|
||||
static brls::Label *statCell(brls::Box *row, float fs) {
|
||||
auto *l = new brls::Label();
|
||||
if (fs > 0.0f)
|
||||
l->setFontSize(fs);
|
||||
row->addView(l);
|
||||
return l;
|
||||
}
|
||||
static void statSep(brls::Box *row, float fs) {
|
||||
auto *s = new brls::Label();
|
||||
s->setText("|");
|
||||
if (fs > 0.0f)
|
||||
s->setFontSize(fs);
|
||||
s->setTextColor(nvgRGB(120, 120, 120));
|
||||
s->setMarginLeft(7.0f);
|
||||
s->setMarginRight(7.0f);
|
||||
row->addView(s);
|
||||
}
|
||||
static void fmtLoad(brls::Label *l, unsigned val, bool isRam) {
|
||||
char b[32];
|
||||
if (isRam)
|
||||
std::snprintf(b, sizeof b, "%u.%u GB/s", val / 1000u, (val % 1000u) / 100u);
|
||||
else
|
||||
std::snprintf(b, sizeof b, "%u%%", val);
|
||||
l->setText(b);
|
||||
}
|
||||
static void fmtClock1(brls::Label *l, uint32_t hz) {
|
||||
char b[32];
|
||||
std::snprintf(b, sizeof b, "%u.%u MHz", hz / 1000000u, (hz / 100000u) % 10u);
|
||||
l->setText(b);
|
||||
}
|
||||
static void fmtClock0(brls::Label *l, uint32_t hz) {
|
||||
char b[32];
|
||||
std::snprintf(b, sizeof b, "%u MHz", hz / 1000000u);
|
||||
l->setText(b);
|
||||
}
|
||||
static void fmtVolt(brls::Label *l, uint32_t uv) {
|
||||
char b[32];
|
||||
std::snprintf(b, sizeof b, "%u mV", uv / 1000u);
|
||||
l->setText(b);
|
||||
}
|
||||
static void fmtTemp(brls::Label *l, int32_t mc) {
|
||||
char b[32];
|
||||
std::snprintf(b, sizeof b, "%d°C", mc / 1000);
|
||||
l->setText(b);
|
||||
}
|
||||
|
||||
class SysInfoTab : public brls::Box {
|
||||
public:
|
||||
SysInfoTab() {
|
||||
this->setAxis(brls::Axis::COLUMN);
|
||||
this->setGrow(1.0f);
|
||||
this->setPadding(40.0f, 60.0f, 40.0f, 60.0f);
|
||||
|
||||
auto *clk = new brls::Header();
|
||||
clk->setTitle("Clocks");
|
||||
this->addView(clk);
|
||||
cpuR = makeCompRow(this, "CPU");
|
||||
gpuR = makeCompRow(this, "GPU");
|
||||
ramR = makeCompRow(this, "RAM");
|
||||
|
||||
auto *sys = new brls::Header();
|
||||
sys->setTitle("System");
|
||||
this->addView(sys);
|
||||
mode = makeRow(this, "Mode");
|
||||
threads = makeRow(this, "Threads");
|
||||
|
||||
hocclk_init();
|
||||
refresh();
|
||||
}
|
||||
|
||||
void frame(brls::FrameContext *ctx) override {
|
||||
if (++tick >= 15) {
|
||||
tick = 0;
|
||||
refresh();
|
||||
}
|
||||
brls::Box::frame(ctx);
|
||||
}
|
||||
|
||||
private:
|
||||
static StatCells makeCompRow(brls::Box *parent, const char *name) {
|
||||
auto *row = new brls::Box(brls::Axis::ROW);
|
||||
row->setMarginBottom(10.0f);
|
||||
auto *n = new brls::Label();
|
||||
n->setText(name);
|
||||
n->setGrow(1.0f);
|
||||
row->addView(n);
|
||||
StatCells c;
|
||||
c.load = statCell(row, 0);
|
||||
statSep(row, 0);
|
||||
c.clock = statCell(row, 0);
|
||||
statSep(row, 0);
|
||||
c.volt = statCell(row, 0);
|
||||
statSep(row, 0);
|
||||
c.temp = statCell(row, 0);
|
||||
parent->addView(row);
|
||||
return c;
|
||||
}
|
||||
void setRow(StatCells &r, unsigned loadOrBw, bool isRam, uint32_t hz, uint32_t uv, int32_t mc) {
|
||||
fmtLoad(r.load, loadOrBw, isRam);
|
||||
fmtClock1(r.clock, hz);
|
||||
fmtVolt(r.volt, uv);
|
||||
fmtTemp(r.temp, mc);
|
||||
}
|
||||
void naRow(StatCells &r) {
|
||||
r.load->setText("N/A");
|
||||
r.clock->setText("-");
|
||||
r.volt->setText("-");
|
||||
r.temp->setText("-");
|
||||
}
|
||||
void refresh() {
|
||||
sysinfo_t s;
|
||||
bench_get_sysinfo(&s);
|
||||
mode->setText(s.is_4gb ? "Application" : "Applet");
|
||||
threads->setText(fstru("%llu", (unsigned long long)s.threads));
|
||||
|
||||
HocClkContext c;
|
||||
if (hocclk_get(&c)) {
|
||||
setRow(cpuR, c.stable.partLoad[3] / 10, false, c.stable.freqs[0], c.stable.voltages[2], c.stable.temps[5]);
|
||||
setRow(gpuR, c.stable.partLoad[2] / 10, false, c.stable.freqs[1], c.stable.voltages[3], c.stable.temps[6]);
|
||||
setRow(ramR, c.stable.partLoad[6], true, c.stable.freqs[2], c.stable.voltages[1], c.stable.temps[7]);
|
||||
} else {
|
||||
naRow(cpuR);
|
||||
naRow(gpuR);
|
||||
naRow(ramR);
|
||||
}
|
||||
}
|
||||
StatCells cpuR, gpuR, ramR;
|
||||
brls::Label *mode, *threads;
|
||||
int tick = 0;
|
||||
};
|
||||
|
||||
class BenchTab : public brls::Box {
|
||||
public:
|
||||
BenchTab() {
|
||||
this->setAxis(brls::Axis::COLUMN);
|
||||
this->setGrow(1.0f);
|
||||
this->setPadding(40.0f, 60.0f, 40.0f, 60.0f);
|
||||
|
||||
runBtn = new brls::Button();
|
||||
runBtn->setText("Run");
|
||||
runBtn->registerClickAction([this](brls::View *) {
|
||||
start();
|
||||
return true;
|
||||
});
|
||||
this->addView(runBtn);
|
||||
|
||||
status = new brls::Label();
|
||||
status->setText("Idle");
|
||||
status->setMarginTop(8.0f);
|
||||
status->setMarginBottom(6.0f);
|
||||
this->addView(status);
|
||||
|
||||
bar = new brls::Box(brls::Axis::ROW);
|
||||
bar->setHeight(18.0f);
|
||||
bar->setWidthPercentage(100.0f);
|
||||
bar->setMarginBottom(14.0f);
|
||||
barFill = new brls::Rectangle();
|
||||
barFill->setColor(nvgRGB(0, 193, 210));
|
||||
barFill->setWidthPercentage(0.0f);
|
||||
barTrack = new brls::Rectangle();
|
||||
barTrack->setColor(nvgRGB(48, 48, 54));
|
||||
barTrack->setGrow(1.0f);
|
||||
bar->addView(barFill);
|
||||
bar->addView(barTrack);
|
||||
this->addView(bar);
|
||||
|
||||
auto *h1 = new brls::Header();
|
||||
h1->setTitle("GPU bandwidth");
|
||||
this->addView(h1);
|
||||
gpuCopy = makeRow(this, "GPU Copy");
|
||||
gpuRead = makeRow(this, "GPU Read");
|
||||
gpuWrite = makeRow(this, "GPU Write");
|
||||
|
||||
auto *h2 = new brls::Header();
|
||||
h2->setTitle("CPU bandwidth");
|
||||
this->addView(h2);
|
||||
cpuCopy = makeRow(this, "CPU Copy");
|
||||
cpuRead = makeRow(this, "CPU Read");
|
||||
cpuWrite = makeRow(this, "CPU Write");
|
||||
|
||||
auto *h3 = new brls::Header();
|
||||
h3->setTitle("RAM latency");
|
||||
this->addView(h3);
|
||||
l2 = makeRow(this, "L2");
|
||||
ram = makeRow(this, "Full RAM");
|
||||
}
|
||||
|
||||
~BenchTab() override {
|
||||
if (ctx)
|
||||
bench_end(ctx);
|
||||
}
|
||||
|
||||
void frame(brls::FrameContext *fc) override {
|
||||
if (running) {
|
||||
if (primed) {
|
||||
|
||||
primed = false;
|
||||
} else {
|
||||
const char *label = "";
|
||||
float frac = 0.0f;
|
||||
bool more = bench_step(ctx, &res, &label, &frac);
|
||||
setProgress(frac);
|
||||
if (more) {
|
||||
status->setText(fstr("%.0f%%", frac * 100.0f) + " " + label);
|
||||
primed = true;
|
||||
} else {
|
||||
showResults();
|
||||
bench_end(ctx);
|
||||
ctx = nullptr;
|
||||
running = false;
|
||||
status->setText("Done");
|
||||
appletSetAutoSleepDisabled(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
brls::Box::frame(fc);
|
||||
}
|
||||
|
||||
private:
|
||||
void setProgress(float f) {
|
||||
barFill->setWidthPercentage(f * 100.0f);
|
||||
}
|
||||
|
||||
void showResults() {
|
||||
gpuCopy->setText(fstr("%.1f MB/s", res.gpu_copy));
|
||||
gpuRead->setText(fstr("%.1f MB/s", res.gpu_read));
|
||||
gpuWrite->setText(fstr("%.1f MB/s", res.gpu_write));
|
||||
cpuCopy->setText(fstr("%.1f MB/s", res.cpu_copy));
|
||||
cpuRead->setText(fstr("%.1f MB/s", res.cpu_read));
|
||||
cpuWrite->setText(fstr("%.1f MB/s", res.cpu_write));
|
||||
l2->setText(fstr("%.1f ns", res.l2_ns));
|
||||
ram->setText(fstr("%.1f ns", res.ram_ns));
|
||||
}
|
||||
|
||||
void start() {
|
||||
if (running)
|
||||
return;
|
||||
memset(&res, 0, sizeof(res));
|
||||
ctx = bench_begin();
|
||||
if (!ctx) {
|
||||
status->setText("Out of memory");
|
||||
return;
|
||||
}
|
||||
running = true;
|
||||
primed = true;
|
||||
setProgress(0.0f);
|
||||
status->setText("Running benchmark...");
|
||||
appletSetAutoSleepDisabled(true);
|
||||
}
|
||||
|
||||
bench_ctx *ctx = nullptr;
|
||||
bool running = false;
|
||||
bool primed = false;
|
||||
bench_results_t res{};
|
||||
brls::Button *runBtn;
|
||||
brls::Label *status;
|
||||
brls::Box *bar;
|
||||
brls::Rectangle *barFill, *barTrack;
|
||||
brls::Label *gpuCopy, *gpuRead, *gpuWrite, *cpuCopy, *cpuRead, *cpuWrite, *l2, *ram;
|
||||
};
|
||||
|
||||
struct StressShared {
|
||||
std::atomic<bool> running{ false }, stop{ false };
|
||||
std::atomic<double> gflops{ 0.0 };
|
||||
std::atomic<uint64_t> dispatches{ 0 }, mismatches{ 0 };
|
||||
std::thread worker;
|
||||
};
|
||||
|
||||
class StressTab : public brls::Box {
|
||||
public:
|
||||
StressTab() {
|
||||
this->setAxis(brls::Axis::COLUMN);
|
||||
this->setGrow(1.0f);
|
||||
this->setPadding(40.0f, 60.0f, 40.0f, 60.0f);
|
||||
|
||||
toggle = new brls::Button();
|
||||
toggle->setText("Start GPU stress");
|
||||
toggle->registerClickAction([this](brls::View *) {
|
||||
onToggle();
|
||||
return true;
|
||||
});
|
||||
this->addView(toggle);
|
||||
|
||||
statusL = new brls::Label();
|
||||
statusL->setText("Stopped");
|
||||
statusL->setMarginTop(8.0f);
|
||||
statusL->setMarginBottom(8.0f);
|
||||
this->addView(statusL);
|
||||
|
||||
auto *h = new brls::Header();
|
||||
h->setTitle("Live");
|
||||
this->addView(h);
|
||||
gflops = makeRow(this, "GFLOPS");
|
||||
dispatches = makeRow(this, "Dispatches");
|
||||
mismatches = makeRow(this, "Mismatches");
|
||||
|
||||
auto *info = new brls::Label();
|
||||
info->setText("GPU stress test, Mismatches > 0 indicate instability.");
|
||||
info->setMarginTop(16.0f);
|
||||
this->addView(info);
|
||||
}
|
||||
|
||||
~StressTab() override {
|
||||
stopWorker();
|
||||
}
|
||||
|
||||
void willDisappear(bool resetState = false) override {
|
||||
stopWorker();
|
||||
brls::Box::willDisappear(resetState);
|
||||
}
|
||||
|
||||
void frame(brls::FrameContext *ctx) override {
|
||||
if (sh.running.load()) {
|
||||
gflops->setText(fstr("%.1f", sh.gflops.load()));
|
||||
dispatches->setText(fstru("%llu", (unsigned long long)sh.dispatches.load()));
|
||||
mismatches->setText(fstru("%llu", (unsigned long long)sh.mismatches.load()));
|
||||
}
|
||||
brls::Box::frame(ctx);
|
||||
}
|
||||
|
||||
private:
|
||||
void onToggle() {
|
||||
if (sh.running.load())
|
||||
stopWorker();
|
||||
else
|
||||
startWorker();
|
||||
}
|
||||
void startWorker() {
|
||||
if (sh.worker.joinable())
|
||||
sh.worker.join();
|
||||
sh.stop.store(false);
|
||||
sh.running.store(true);
|
||||
toggle->setText("Stop GPU stress");
|
||||
statusL->setText("Running...");
|
||||
sh.worker = std::thread([this] {
|
||||
appletSetAutoSleepDisabled(true);
|
||||
uint64_t totD = 0, totM = 0;
|
||||
while (!sh.stop.load()) {
|
||||
double g = 0;
|
||||
uint64_t d = 0, m = 0;
|
||||
if (!gpu_stress_run(&g, &d, &m))
|
||||
break;
|
||||
totD += d;
|
||||
totM += m;
|
||||
sh.gflops.store(g);
|
||||
sh.dispatches.store(totD);
|
||||
sh.mismatches.store(totM);
|
||||
}
|
||||
appletSetAutoSleepDisabled(false);
|
||||
sh.running.store(false);
|
||||
});
|
||||
}
|
||||
void stopWorker() {
|
||||
sh.stop.store(true);
|
||||
if (sh.worker.joinable())
|
||||
sh.worker.join();
|
||||
gpu_stress_shutdown();
|
||||
sh.running.store(false);
|
||||
if (toggle)
|
||||
toggle->setText("Start GPU stress");
|
||||
if (statusL)
|
||||
statusL->setText("Stopped");
|
||||
}
|
||||
StressShared sh;
|
||||
brls::Button *toggle;
|
||||
brls::Label *statusL, *gflops, *dispatches, *mismatches;
|
||||
};
|
||||
|
||||
class FurmarkTab : public brls::Box {
|
||||
public:
|
||||
FurmarkTab(int which, const char *desc) : which(which) {
|
||||
this->setAxis(brls::Axis::COLUMN);
|
||||
this->setGrow(1.0f);
|
||||
this->setPadding(40.0f, 60.0f, 40.0f, 60.0f);
|
||||
|
||||
toggle = new brls::Button();
|
||||
toggle->setText("Start");
|
||||
toggle->registerClickAction([this](brls::View *) {
|
||||
onToggle();
|
||||
return true;
|
||||
});
|
||||
this->addView(toggle);
|
||||
|
||||
statusL = new brls::Label();
|
||||
statusL->setText("Stopped");
|
||||
statusL->setMarginTop(8.0f);
|
||||
statusL->setMarginBottom(12.0f);
|
||||
this->addView(statusL);
|
||||
|
||||
auto *info = new brls::Label();
|
||||
info->setText(desc);
|
||||
this->addView(info);
|
||||
}
|
||||
|
||||
~FurmarkTab() override {
|
||||
if (run_furmark_running())
|
||||
run_furmark_stop();
|
||||
}
|
||||
|
||||
void willDisappear(bool resetState = false) override {
|
||||
if (run_furmark_running())
|
||||
run_furmark_stop();
|
||||
brls::Box::willDisappear(resetState);
|
||||
}
|
||||
|
||||
void frame(brls::FrameContext *fc) override {
|
||||
bool r = run_furmark_running() != 0;
|
||||
if (r != shown) {
|
||||
shown = r;
|
||||
toggle->setText(r ? "Stop" : "Start");
|
||||
statusL->setText(r ? "Running..." : "Stopped");
|
||||
}
|
||||
brls::Box::frame(fc);
|
||||
}
|
||||
|
||||
private:
|
||||
void onToggle() {
|
||||
if (run_furmark_running())
|
||||
run_furmark_stop();
|
||||
else
|
||||
run_furmark_start(which);
|
||||
}
|
||||
int which;
|
||||
bool shown = false;
|
||||
brls::Button *toggle;
|
||||
brls::Label *statusL;
|
||||
};
|
||||
|
||||
class AppFrame : public brls::TabFrame {
|
||||
public:
|
||||
AppFrame() {
|
||||
box = dynamic_cast<brls::Box *>(this->getView("brls/applet_frame/header_stats"));
|
||||
if (box) {
|
||||
box->setJustifyContent(brls::JustifyContent::FLEX_END);
|
||||
box->setAlignItems(brls::AlignItems::CENTER);
|
||||
const float fs = 13.0f;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
grp[i].load = statCell(box, fs);
|
||||
if (i)
|
||||
grp[i].load->setMarginLeft(18.0f);
|
||||
statSep(box, fs);
|
||||
grp[i].clock = statCell(box, fs);
|
||||
statSep(box, fs);
|
||||
grp[i].temp = statCell(box, fs);
|
||||
}
|
||||
}
|
||||
hocclk_init();
|
||||
update();
|
||||
}
|
||||
|
||||
void frame(brls::FrameContext *ctx) override {
|
||||
if (box && ++tick >= 12) {
|
||||
tick = 0;
|
||||
update();
|
||||
}
|
||||
brls::TabFrame::frame(ctx);
|
||||
}
|
||||
|
||||
private:
|
||||
void setGrp(StatCells &g, const char *name, unsigned loadOrBw, bool isRam, uint32_t hz, int32_t mc) {
|
||||
char b[48];
|
||||
if (isRam)
|
||||
std::snprintf(b, sizeof b, "%s %u.%u GB/s", name, loadOrBw / 1000u, (loadOrBw % 1000u) / 100u);
|
||||
else
|
||||
std::snprintf(b, sizeof b, "%s %u%%", name, loadOrBw);
|
||||
g.load->setText(b);
|
||||
fmtClock0(g.clock, hz);
|
||||
fmtTemp(g.temp, mc);
|
||||
}
|
||||
void update() {
|
||||
if (!box)
|
||||
return;
|
||||
HocClkContext c;
|
||||
if (!hocclk_get(&c)) {
|
||||
grp[0].load->setText("hoc:clk N/A");
|
||||
grp[0].clock->setText("-");
|
||||
grp[0].temp->setText("-");
|
||||
grp[1].load->setText("-");
|
||||
grp[1].clock->setText("-");
|
||||
grp[1].temp->setText("-");
|
||||
grp[2].load->setText("-");
|
||||
grp[2].clock->setText("-");
|
||||
grp[2].temp->setText("-");
|
||||
return;
|
||||
}
|
||||
setGrp(grp[0], "CPU", c.stable.partLoad[3] / 10, false, c.stable.freqs[0], c.stable.temps[5]);
|
||||
setGrp(grp[1], "GPU", c.stable.partLoad[2] / 10, false, c.stable.freqs[1], c.stable.temps[6]);
|
||||
setGrp(grp[2], "RAM", c.stable.partLoad[6], true, c.stable.freqs[2], c.stable.temps[7]);
|
||||
}
|
||||
brls::Box *box = nullptr;
|
||||
StatCells grp[3];
|
||||
int tick = 0;
|
||||
};
|
||||
|
||||
class MainActivity : public brls::Activity {
|
||||
public:
|
||||
brls::View *createContentView() override {
|
||||
auto *tab = new AppFrame();
|
||||
tab->setTitle("Benchmark Toolbox");
|
||||
tab->setIconFromRes("img/logo.png");
|
||||
tab->addTab("System Info", [] { return new SysInfoTab(); });
|
||||
tab->addSeparator();
|
||||
tab->addTab("Membench", [] { return new BenchTab(); });
|
||||
tab->addTab("GPU Test", [] { return new StressTab(); });
|
||||
tab->addSeparator();
|
||||
tab->addTab("Furmark", [] { return new FurmarkTab(0, "FurMark for Switch (48 step)"); });
|
||||
tab->addTab("Furmark RAM", [] { return new FurmarkTab(1, "FurMark with extra ram stress"); });
|
||||
tab->addTab("GPU Path Trace", [] { return new FurmarkTab(2, "GPU Path Tracer"); });
|
||||
tab->addTab("Black Hole", [] { return new FurmarkTab(3, "CPU+GPU black-hole."); });
|
||||
tab->addTab("CPU Ray Trace", [] { return new FurmarkTab(4, "CPU Path Tracer"); });
|
||||
tab->addTab("CPU RAM", [] { return new FurmarkTab(5, "CPU RT with extra RAM stress"); });
|
||||
return tab;
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
brls::Logger::setLogLevel(brls::LogLevel::INFO);
|
||||
|
||||
if (!brls::Application::init()) {
|
||||
brls::Logger::error("Unable to init borealis application");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
brls::Application::createWindow("Benchmark Toolbox");
|
||||
brls::Application::setGlobalQuit(true);
|
||||
brls::Application::pushActivity(new MainActivity());
|
||||
|
||||
while (brls::Application::mainLoop())
|
||||
;
|
||||
|
||||
_exit(EXIT_SUCCESS);
|
||||
}
|
||||
Reference in New Issue
Block a user