Files
souldbminersmwc 4a5e889c40 benchmark-toolbox: add benchmark-toolbox
a unified benchmarking tool
2026-06-07 21:10:53 -04:00

252 lines
8.1 KiB
C

#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;
}