#include "gpu_tests.h" #include "opengl/utils.h" #include static void opengl_interop_tests(pl_gpu gpu) { pl_fmt fmt = pl_find_fmt(gpu, PL_FMT_UNORM, 1, 0, 0, PL_FMT_CAP_RENDERABLE | PL_FMT_CAP_LINEAR); if (!fmt) return; pl_tex export = pl_tex_create(gpu, pl_tex_params( .w = 32, .h = 32, .format = fmt, .sampleable = true, .renderable = true, .blit_dst = fmt->caps & PL_FMT_CAP_BLITTABLE, )); REQUIRE(export); struct pl_opengl_wrap_params wrap = { .width = export->params.w, .height = export->params.h, .depth = export->params.d, }; wrap.texture = pl_opengl_unwrap(gpu, export, &wrap.target, &wrap.iformat, NULL); REQUIRE(wrap.texture); pl_tex import = pl_opengl_wrap(gpu, &wrap); REQUIRE(import); REQUIRE(import->params.renderable); REQUIRE_CMP(import->params.blit_dst, ==, export->params.blit_dst, "d"); pl_tex_destroy(gpu, &import); pl_tex_destroy(gpu, &export); } #define PBUFFER_WIDTH 640 #define PBUFFER_HEIGHT 480 struct swapchain_priv { EGLDisplay display; EGLSurface surface; }; static void swap_buffers(void *priv) { struct swapchain_priv *p = priv; eglSwapBuffers(p->display, p->surface); } static void opengl_swapchain_tests(pl_opengl gl, EGLDisplay display, EGLSurface surface) { if (surface == EGL_NO_SURFACE) return; printf("testing opengl swapchain\n"); pl_gpu gpu = gl->gpu; pl_swapchain sw; sw = pl_opengl_create_swapchain(gl, pl_opengl_swapchain_params( .swap_buffers = swap_buffers, .priv = &(struct swapchain_priv) { display, surface }, )); REQUIRE(sw); int w = PBUFFER_WIDTH, h = PBUFFER_HEIGHT; REQUIRE(pl_swapchain_resize(sw, &w, &h)); for (int i = 0; i < 10; i++) { struct pl_swapchain_frame frame; REQUIRE(pl_swapchain_start_frame(sw, &frame)); if (frame.fbo->params.blit_dst) pl_tex_clear(gpu, frame.fbo, (float[4]){0}); // TODO: test this with an actual pl_renderer instance struct pl_frame target; pl_frame_from_swapchain(&target, &frame); REQUIRE(pl_swapchain_submit_frame(sw)); pl_swapchain_swap_buffers(sw); } pl_swapchain_destroy(&sw); } int main() { if (!gladLoaderLoadEGL(EGL_NO_DISPLAY)) return SKIP; const char *extstr = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); if (!extstr || !strstr(extstr, "EGL_MESA_platform_surfaceless")) return SKIP; // Create the OpenGL context EGLDisplay dpy = eglGetPlatformDisplayEXT(EGL_PLATFORM_SURFACELESS_MESA, (void *) EGL_DEFAULT_DISPLAY, NULL); if (dpy == EGL_NO_DISPLAY) return SKIP; EGLint major, minor; if (!eglInitialize(dpy, &major, &minor)) return SKIP; if (!gladLoaderLoadEGL(dpy)) return SKIP; printf("Initialized EGL v%d.%d\n", major, minor); int egl_ver = major * 10 + minor; struct { EGLenum api; EGLenum render; int major, minor; int glsl_ver; EGLenum profile; } egl_vers[] = { { EGL_OPENGL_API, EGL_OPENGL_BIT, 4, 6, 460, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT }, { EGL_OPENGL_API, EGL_OPENGL_BIT, 3, 3, 330, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT }, { EGL_OPENGL_API, EGL_OPENGL_BIT, 3, 0, 130, EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT, }, { EGL_OPENGL_ES_API, EGL_OPENGL_ES3_BIT, 3, 0, 300, }, }; struct pl_glsl_version last_glsl = {0}; struct pl_gpu_limits last_limits = {0}; pl_log log = pl_test_logger(); for (int i = 0; i < PL_ARRAY_SIZE(egl_vers); i++) { const int cfg_attribs[] = { EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, EGL_RENDERABLE_TYPE, egl_vers[i].render, EGL_NONE }; EGLConfig config = 0; EGLint num_configs = 0; bool ok = eglChooseConfig(dpy, cfg_attribs, &config, 1, &num_configs); if (!ok || !num_configs) goto error; if (!eglBindAPI(egl_vers[i].api)) goto error; EGLContext egl; if (egl_vers[i].api == EGL_OPENGL_ES_API) { // OpenGL ES const EGLint egl_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, egl_vers[i].major, (egl_ver >= 15) ? EGL_CONTEXT_OPENGL_DEBUG : EGL_NONE, EGL_TRUE, EGL_NONE }; printf("Attempting creation of OpenGL ES v%d context\n", egl_vers[i].major); egl = eglCreateContext(dpy, config, EGL_NO_CONTEXT, egl_attribs); } else { // Desktop OpenGL const int egl_attribs[] = { EGL_CONTEXT_MAJOR_VERSION, egl_vers[i].major, EGL_CONTEXT_MINOR_VERSION, egl_vers[i].minor, EGL_CONTEXT_OPENGL_PROFILE_MASK, egl_vers[i].profile, (egl_ver >= 15) ? EGL_CONTEXT_OPENGL_DEBUG : EGL_NONE, EGL_TRUE, EGL_NONE }; printf("Attempting creation of Desktop OpenGL v%d.%d context\n", egl_vers[i].major, egl_vers[i].minor); egl = eglCreateContext(dpy, config, EGL_NO_CONTEXT, egl_attribs); } if (!egl) goto error; const EGLint pbuffer_attribs[] = { EGL_WIDTH, PBUFFER_WIDTH, EGL_HEIGHT, PBUFFER_HEIGHT, EGL_NONE }; EGLSurface surf = eglCreatePbufferSurface(dpy, config, pbuffer_attribs); if (!eglMakeCurrent(dpy, surf, surf, egl)) goto error; pl_opengl gl = pl_opengl_create(log, pl_opengl_params( .get_proc_addr = (pl_voidfunc_t (*)(const char *)) eglGetProcAddress, .max_glsl_version = egl_vers[i].glsl_ver, .debug = true, .egl_display = dpy, .egl_context = egl, #ifdef CI_ALLOW_SW .allow_software = true, #endif )); if (!gl) goto next; // Skip repeat tests pl_gpu gpu = gl->gpu; if (memcmp(&last_glsl, &gpu->glsl, sizeof(last_glsl)) == 0 && memcmp(&last_limits, &gpu->limits, sizeof(last_limits)) == 0) { printf("Skipping tests due to duplicate capabilities/version\n"); goto next; } #ifdef CI_MAXGL if (last_glsl.version && last_glsl.gles == gpu->glsl.gles) goto next; #endif last_glsl = gpu->glsl; last_limits = gpu->limits; gpu_shader_tests(gpu); gpu_interop_tests(gpu); opengl_interop_tests(gpu); opengl_swapchain_tests(gl, dpy, surf); // Reduce log spam after first successful test pl_log_level_update(log, PL_LOG_INFO); next: pl_opengl_destroy(&gl); eglDestroySurface(dpy, surf); eglDestroyContext(dpy, egl); continue; error: ; EGLint error = eglGetError(); if (error != EGL_SUCCESS) fprintf(stderr, "EGL error: %s\n", egl_err_str(error)); } eglTerminate(dpy); gladLoaderUnloadEGL(); pl_log_destroy(&log); if (!last_glsl.version) return SKIP; }