Files
MobileGL/tools/cts/probe/mgprobe.c
T

360 lines
16 KiB
C

/* mgprobe - preflight gate for running a GL conformance suite against MobileGL
* from a bare adb-shell process (no APK, no Activity).
*
* Verifies, for one backend and one surface type, that MobileGL can hand out a
* GL 3.3 core context and that pixels read back correctly - both from the
* default framebuffer and from a user FBO. Run this before burning hours on a
* CTS run; it catches a broken device/library pairing in about a second.
*
* mgprobe --backend DirectGLES|DirectVulkan --surface pbuffer|imagereader
* [--lib /path/to/libMobileGL.so]
*
* Exit status: 0 if a context came up and FBO readback is correct, non-zero
* otherwise. Default-framebuffer readback is reported but does NOT gate, because
* DirectVulkan is known to return zeros there while FBO readback is sound.
*
* Build (NDK, arm64):
* $NDK/toolchains/llvm/prebuilt/<host>/bin/aarch64-linux-android26-clang \
* -O1 -o mgprobe mgprobe.c -ldl -llog -landroid -lmediandk
*/
#include <android/native_window.h>
#include <dlfcn.h>
#include <media/NdkImageReader.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
typedef void *EGLDisplay;
typedef void *EGLConfig;
typedef void *EGLSurface;
typedef void *EGLContext;
typedef int EGLint;
typedef unsigned int EGLBoolean;
typedef unsigned int EGLenum;
typedef void *EGLNativeDisplayType;
typedef void *EGLNativeWindowType;
#define EGL_DEFAULT_DISPLAY ((EGLNativeDisplayType)0)
#define EGL_NO_CONTEXT ((EGLContext)0)
#define EGL_NO_SURFACE ((EGLSurface)0)
#define EGL_NONE 0x3038
#define EGL_WIDTH 0x3057
#define EGL_HEIGHT 0x3056
#define EGL_RENDERABLE_TYPE 0x3040
#define EGL_SURFACE_TYPE 0x3033
#define EGL_WINDOW_BIT 0x0004
#define EGL_PBUFFER_BIT 0x0001
#define EGL_OPENGL_BIT 0x0008
#define EGL_OPENGL_API 0x30A2
#define EGL_RED_SIZE 0x3024
#define EGL_GREEN_SIZE 0x3023
#define EGL_BLUE_SIZE 0x3022
#define EGL_ALPHA_SIZE 0x3021
#define EGL_DEPTH_SIZE 0x3025
#define EGL_STENCIL_SIZE 0x3026
#define EGL_NATIVE_VISUAL_ID 0x302E
#define EGL_CONTEXT_MAJOR_VERSION 0x3098
#define EGL_CONTEXT_MINOR_VERSION 0x30FB
#define EGL_CONTEXT_OPENGL_PROFILE_MASK 0x30FD
#define EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT 0x00000001
#define GL_VENDOR 0x1F00
#define GL_RENDERER 0x1F01
#define GL_VERSION 0x1F02
#define GL_SHADING_LANGUAGE_VERSION 0x8B8C
#define GL_CONTEXT_PROFILE_MASK 0x9126
#define GL_MAJOR_VERSION 0x821B
#define GL_MINOR_VERSION 0x821C
#define GL_COLOR_BUFFER_BIT 0x00004000
#define GL_RGBA 0x1908
#define GL_RGBA8 0x8058
#define GL_UNSIGNED_BYTE 0x1401
#define GL_TEXTURE_2D 0x0DE1
#define GL_FRAMEBUFFER 0x8D40
#define GL_COLOR_ATTACHMENT0 0x8CE0
#define GL_FRAMEBUFFER_COMPLETE 0x8CD5
#define GL_TEXTURE_MIN_FILTER 0x2801
#define GL_TEXTURE_MAG_FILTER 0x2800
#define GL_NEAREST 0x2600
#define GL_RENDERBUFFER 0x8D41
typedef EGLDisplay (*P_getDisplay)(EGLNativeDisplayType);
typedef EGLBoolean (*P_initialize)(EGLDisplay, EGLint *, EGLint *);
typedef EGLBoolean (*P_bindAPI)(EGLenum);
typedef EGLBoolean (*P_chooseConfig)(EGLDisplay, const EGLint *, EGLConfig *, EGLint, EGLint *);
typedef EGLBoolean (*P_getConfigAttrib)(EGLDisplay, EGLConfig, EGLint, EGLint *);
typedef EGLSurface (*P_createWindowSurface)(EGLDisplay, EGLConfig, EGLNativeWindowType, const EGLint *);
typedef EGLSurface (*P_createPbufferSurface)(EGLDisplay, EGLConfig, const EGLint *);
typedef EGLContext (*P_createContext)(EGLDisplay, EGLConfig, EGLContext, const EGLint *);
typedef EGLBoolean (*P_makeCurrent)(EGLDisplay, EGLSurface, EGLSurface, EGLContext);
typedef EGLint (*P_getError)(void);
typedef const unsigned char *(*P_glGetString)(unsigned int);
typedef void (*P_glGetIntegerv)(unsigned int, int *);
typedef void (*P_glClearColor)(float, float, float, float);
typedef void (*P_glClear)(unsigned int);
typedef void (*P_glFinish)(void);
typedef void (*P_glReadPixels)(int, int, int, int, unsigned int, unsigned int, void *);
typedef unsigned int (*P_glGetError)(void);
typedef void (*P_glGenTextures)(int, unsigned int *);
typedef void (*P_glBindTexture)(unsigned int, unsigned int);
typedef void (*P_glTexImage2D)(unsigned int, int, int, int, int, int, unsigned int, unsigned int, const void *);
typedef void (*P_glTexParameteri)(unsigned int, unsigned int, int);
typedef void (*P_glGenFramebuffers)(int, unsigned int *);
typedef void (*P_glBindFramebuffer)(unsigned int, unsigned int);
typedef void (*P_glFramebufferTexture2D)(unsigned int, unsigned int, unsigned int, unsigned int, int);
typedef unsigned int (*P_glCheckFramebufferStatus)(unsigned int);
typedef void (*P_glViewport)(int, int, int, int);
typedef void (*P_glGenRenderbuffers)(int, unsigned int *);
typedef void (*P_glBindRenderbuffer)(unsigned int, unsigned int);
typedef void (*P_glRenderbufferStorage)(unsigned int, unsigned int, int, int);
typedef void (*P_glFramebufferRenderbuffer)(unsigned int, unsigned int, unsigned int, unsigned int);
static void *g_lib;
static void *S(const char *n) { return dlsym(g_lib, n); }
static void on_image(void *ctx, AImageReader *r) {
(void)ctx;
AImage *img = NULL;
/* Drain the queue, or the producer blocks once maxImages are in flight. */
if (AImageReader_acquireNextImage(r, &img) == AMEDIA_OK && img) AImage_delete(img);
}
#define DIM 256
static int near8(unsigned got, int want, int tol) {
int d = (int)got - want;
return d <= tol && d >= -tol;
}
int main(int argc, char **argv) {
const char *backend = "DirectGLES";
const char *surface = "pbuffer";
const char *libpath = "libMobileGL.so";
for (int i = 1; i < argc; ++i) {
if (!strcmp(argv[i], "--backend") && i + 1 < argc) backend = argv[++i];
else if (!strcmp(argv[i], "--surface") && i + 1 < argc) surface = argv[++i];
else if (!strcmp(argv[i], "--lib") && i + 1 < argc) libpath = argv[++i];
else {
fprintf(stderr, "usage: %s [--backend DirectGLES|DirectVulkan]"
" [--surface pbuffer|imagereader] [--lib path]\n", argv[0]);
return 2;
}
}
setvbuf(stdout, NULL, _IONBF, 0);
/* MobileGL parses its config from an ELF constructor, so the backend must be
* selected before the library is mapped. */
setenv("MOBILEGL_BACKEND_TYPE", backend, 1);
printf("mgprobe backend=%s surface=%s lib=%s\n", backend, surface, libpath);
int useWindow = !strcmp(surface, "imagereader");
ANativeWindow *win = NULL;
AImageReader *reader = NULL;
if (useWindow) {
if (AImageReader_newWithUsage(DIM, DIM, AIMAGE_FORMAT_RGBA_8888,
AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE |
AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT,
4, &reader) != AMEDIA_OK || !reader) {
printf("FAIL AImageReader_newWithUsage\n");
return 3;
}
AImageReader_ImageListener l = {NULL, on_image};
AImageReader_setImageListener(reader, &l);
if (AImageReader_getWindow(reader, &win) != AMEDIA_OK || !win) {
printf("FAIL AImageReader_getWindow\n");
return 3;
}
}
g_lib = dlopen(libpath, RTLD_NOW | RTLD_LOCAL);
if (!g_lib) {
printf("FAIL dlopen: %s\n", dlerror());
return 4;
}
P_getDisplay eglGetDisplay_ = (P_getDisplay)S("eglGetDisplay");
P_initialize eglInitialize_ = (P_initialize)S("eglInitialize");
P_bindAPI eglBindAPI_ = (P_bindAPI)S("eglBindAPI");
P_chooseConfig eglChooseConfig_ = (P_chooseConfig)S("eglChooseConfig");
P_getConfigAttrib eglGetConfigAttrib_ = (P_getConfigAttrib)S("eglGetConfigAttrib");
P_createWindowSurface eglCreateWindowSurface_ = (P_createWindowSurface)S("eglCreateWindowSurface");
P_createPbufferSurface eglCreatePbufferSurface_ = (P_createPbufferSurface)S("eglCreatePbufferSurface");
P_createContext eglCreateContext_ = (P_createContext)S("eglCreateContext");
P_makeCurrent eglMakeCurrent_ = (P_makeCurrent)S("eglMakeCurrent");
P_getError eglGetError_ = (P_getError)S("eglGetError");
if (!eglGetDisplay_ || !eglInitialize_ || !eglChooseConfig_ || !eglCreateContext_ || !eglMakeCurrent_) {
printf("FAIL missing core EGL exports\n");
return 5;
}
EGLDisplay dpy = eglGetDisplay_(EGL_DEFAULT_DISPLAY);
EGLint vmaj = 0, vmin = 0;
if (!eglInitialize_(dpy, &vmaj, &vmin)) {
printf("FAIL eglInitialize err=0x%x\n", eglGetError_ ? eglGetError_() : 0);
return 6;
}
if (eglBindAPI_ && !eglBindAPI_(EGL_OPENGL_API)) {
printf("FAIL eglBindAPI(EGL_OPENGL_API) err=0x%x\n", eglGetError_ ? eglGetError_() : 0);
return 7;
}
const EGLint cfgAttribs[] = {
EGL_SURFACE_TYPE, useWindow ? EGL_WINDOW_BIT : EGL_PBUFFER_BIT,
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 = 0;
EGLint ncfg = 0;
if (!eglChooseConfig_(dpy, cfgAttribs, &cfg, 1, &ncfg) || ncfg < 1) {
printf("FAIL eglChooseConfig n=%d err=0x%x\n", ncfg, eglGetError_ ? eglGetError_() : 0);
return 8;
}
EGLSurface surf;
if (useWindow) {
EGLint vis = 0;
if (eglGetConfigAttrib_ && eglGetConfigAttrib_(dpy, cfg, EGL_NATIVE_VISUAL_ID, &vis) && vis)
ANativeWindow_setBuffersGeometry(win, DIM, DIM, vis);
surf = eglCreateWindowSurface_(dpy, cfg, (EGLNativeWindowType)win, NULL);
} else {
const EGLint sa[] = {EGL_WIDTH, DIM, EGL_HEIGHT, DIM, EGL_NONE};
surf = eglCreatePbufferSurface_(dpy, cfg, sa);
}
if (surf == EGL_NO_SURFACE) {
printf("FAIL create%sSurface err=0x%x\n", useWindow ? "Window" : "Pbuffer",
eglGetError_ ? eglGetError_() : 0);
return 9;
}
const EGLint ctxAttribs[] = {
EGL_CONTEXT_MAJOR_VERSION, 3, EGL_CONTEXT_MINOR_VERSION, 3,
EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, EGL_NONE};
EGLContext ctx = eglCreateContext_(dpy, cfg, EGL_NO_CONTEXT, ctxAttribs);
if (ctx == EGL_NO_CONTEXT) {
printf("FAIL eglCreateContext(3.3 core) err=0x%x\n", eglGetError_ ? eglGetError_() : 0);
return 10;
}
/* MobileGL requires draw == read and rejects EGL_NO_SURFACE. */
if (!eglMakeCurrent_(dpy, surf, surf, ctx)) {
printf("FAIL eglMakeCurrent err=0x%x\n", eglGetError_ ? eglGetError_() : 0);
return 11;
}
P_glGetString glGetString_ = (P_glGetString)S("glGetString");
P_glGetIntegerv glGetIntegerv_ = (P_glGetIntegerv)S("glGetIntegerv");
P_glClearColor glClearColor_ = (P_glClearColor)S("glClearColor");
P_glClear glClear_ = (P_glClear)S("glClear");
P_glFinish glFinish_ = (P_glFinish)S("glFinish");
P_glReadPixels glReadPixels_ = (P_glReadPixels)S("glReadPixels");
P_glGetError glGetError_ = (P_glGetError)S("glGetError");
P_glGenTextures glGenTextures_ = (P_glGenTextures)S("glGenTextures");
P_glBindTexture glBindTexture_ = (P_glBindTexture)S("glBindTexture");
P_glTexImage2D glTexImage2D_ = (P_glTexImage2D)S("glTexImage2D");
P_glTexParameteri glTexParameteri_ = (P_glTexParameteri)S("glTexParameteri");
P_glGenFramebuffers glGenFramebuffers_ = (P_glGenFramebuffers)S("glGenFramebuffers");
P_glBindFramebuffer glBindFramebuffer_ = (P_glBindFramebuffer)S("glBindFramebuffer");
P_glFramebufferTexture2D glFramebufferTexture2D_ = (P_glFramebufferTexture2D)S("glFramebufferTexture2D");
P_glCheckFramebufferStatus glCheckFramebufferStatus_ = (P_glCheckFramebufferStatus)S("glCheckFramebufferStatus");
P_glViewport glViewport_ = (P_glViewport)S("glViewport");
P_glGenRenderbuffers glGenRenderbuffers_ = (P_glGenRenderbuffers)S("glGenRenderbuffers");
P_glBindRenderbuffer glBindRenderbuffer_ = (P_glBindRenderbuffer)S("glBindRenderbuffer");
P_glRenderbufferStorage glRenderbufferStorage_ = (P_glRenderbufferStorage)S("glRenderbufferStorage");
P_glFramebufferRenderbuffer glFramebufferRenderbuffer_ = (P_glFramebufferRenderbuffer)S("glFramebufferRenderbuffer");
int major = -1, minor = -1, profile = -1;
glGetIntegerv_(GL_MAJOR_VERSION, &major);
glGetIntegerv_(GL_MINOR_VERSION, &minor);
glGetIntegerv_(GL_CONTEXT_PROFILE_MASK, &profile);
printf(" GL_VENDOR %s\n", (const char *)glGetString_(GL_VENDOR));
printf(" GL_RENDERER %s\n", (const char *)glGetString_(GL_RENDERER));
printf(" GL_VERSION %s\n", (const char *)glGetString_(GL_VERSION));
printf(" GLSL %s\n", (const char *)glGetString_(GL_SHADING_LANGUAGE_VERSION));
printf(" version %d.%d profile_mask 0x%x %s\n", major, minor, profile,
(profile & 1) ? "(core)" : "(NOT CORE)");
unsigned char px[4];
/* Default framebuffer. */
glClearColor_(0.25f, 0.5f, 0.75f, 1.0f);
glClear_(GL_COLOR_BUFFER_BIT);
if (glFinish_) glFinish_();
memset(px, 0, sizeof px);
glReadPixels_(DIM / 2, DIM / 2, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, px);
int defOk = near8(px[0], 64, 10) && near8(px[1], 128, 10) && near8(px[2], 191, 10);
printf(" default-FB readback (%u,%u,%u,%u) %s\n", px[0], px[1], px[2], px[3],
defOk ? "ok" : "BROKEN");
/* User FBO - this is what dEQP uses with --deqp-surface-type=fbo. */
unsigned int tex = 0, fbo = 0;
glGenTextures_(1, &tex);
glBindTexture_(GL_TEXTURE_2D, tex);
glTexImage2D_(GL_TEXTURE_2D, 0, GL_RGBA8, DIM, DIM, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri_(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri_(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glGenFramebuffers_(1, &fbo);
glBindFramebuffer_(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0);
unsigned int fbst = glCheckFramebufferStatus_(GL_FRAMEBUFFER);
int fboOk = 0;
if (fbst == GL_FRAMEBUFFER_COMPLETE) {
glViewport_(0, 0, DIM, DIM);
glClearColor_(0.9f, 0.2f, 0.4f, 1.0f);
glClear_(GL_COLOR_BUFFER_BIT);
if (glFinish_) glFinish_();
memset(px, 0, sizeof px);
glReadPixels_(DIM / 2, DIM / 2, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, px);
fboOk = near8(px[0], 230, 10) && near8(px[1], 51, 10) && near8(px[2], 102, 10);
printf(" user-FBO readback (%u,%u,%u,%u) %s\n", px[0], px[1], px[2], px[3],
fboOk ? "ok" : "BROKEN");
} else {
printf(" user-FBO incomplete status=0x%x\n", fbst);
}
/* FBO with a RENDERBUFFER colour attachment. This is what dEQP's
* FboRenderContext allocates for --deqp-surface-type=fbo, so it is the path
* that actually decides a conformance run - a texture-attached FBO working
* says nothing about it. */
unsigned int rbo = 0, rfbo = 0;
int rboOk = 0;
if (glGenRenderbuffers_ && glBindRenderbuffer_ && glRenderbufferStorage_ && glFramebufferRenderbuffer_) {
glGenRenderbuffers_(1, &rbo);
glBindRenderbuffer_(GL_RENDERBUFFER, rbo);
glRenderbufferStorage_(GL_RENDERBUFFER, GL_RGBA8, DIM, DIM);
glBindRenderbuffer_(GL_RENDERBUFFER, 0);
glGenFramebuffers_(1, &rfbo);
glBindFramebuffer_(GL_FRAMEBUFFER, rfbo);
glFramebufferRenderbuffer_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo);
unsigned int rst = glCheckFramebufferStatus_(GL_FRAMEBUFFER);
if (rst == GL_FRAMEBUFFER_COMPLETE) {
glViewport_(0, 0, DIM, DIM);
glClearColor_(0.1f, 0.7f, 0.3f, 1.0f);
glClear_(GL_COLOR_BUFFER_BIT);
if (glFinish_) glFinish_();
memset(px, 0, sizeof px);
glReadPixels_(DIM / 2, DIM / 2, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, px);
rboOk = near8(px[0], 26, 10) && near8(px[1], 179, 10) && near8(px[2], 77, 10);
printf(" rbo-FBO readback (%u,%u,%u,%u) %s\n", px[0], px[1], px[2], px[3],
rboOk ? "ok" : "BROKEN");
} else {
printf(" rbo-FBO incomplete status=0x%x\n", rst);
}
} else {
printf(" rbo-FBO skipped (renderbuffer entry points unavailable)\n");
}
unsigned glerr = glGetError_ ? glGetError_() : 0;
int ok = fboOk && rboOk && (major > 3 || (major == 3 && minor >= 3)) && (profile & 1) && glerr == 0;
printf("%s backend=%s surface=%s default_fb=%s user_fbo=%s rbo_fbo=%s glerr=0x%x\n",
ok ? "PASS" : "FAIL", backend, surface, defOk ? "ok" : "broken",
fboOk ? "ok" : "broken", rboOk ? "ok" : "broken", glerr);
fflush(stdout);
/* MobileGL aborts in static teardown; leave before that runs. */
_exit(ok ? 0 : 1);
}