summaryrefslogtreecommitdiffstats
path: root/src/tests/opengl_surfaceless.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/tests/opengl_surfaceless.c')
-rw-r--r--src/tests/opengl_surfaceless.c247
1 files changed, 247 insertions, 0 deletions
diff --git a/src/tests/opengl_surfaceless.c b/src/tests/opengl_surfaceless.c
new file mode 100644
index 0000000..2d12a08
--- /dev/null
+++ b/src/tests/opengl_surfaceless.c
@@ -0,0 +1,247 @@
+#include "gpu_tests.h"
+#include "opengl/utils.h"
+
+#include <libplacebo/opengl.h>
+
+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;
+}