#include #include #include #include #include "gpu_bw.h" #include #include #include 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; }