summaryrefslogtreecommitdiff
path: root/tests/common/test_gl.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/common/test_gl.c')
-rw-r--r--tests/common/test_gl.c158
1 files changed, 158 insertions, 0 deletions
diff --git a/tests/common/test_gl.c b/tests/common/test_gl.c
new file mode 100644
index 0000000..cd51575
--- /dev/null
+++ b/tests/common/test_gl.c
@@ -0,0 +1,158 @@
+#define _POSIX_C_SOURCE 200809L
+#include <yasl/gl.h>
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+typedef const unsigned char *(*get_string_fn)(unsigned);
+typedef void (*clear_color_fn)(float, float, float, float);
+typedef void (*clear_fn)(unsigned);
+typedef void (*finish_fn)(void);
+typedef void (*read_pixels_fn)(int, int, int, int, unsigned, unsigned, void *);
+typedef void (*gen_textures_fn)(int, unsigned *);
+typedef void (*bind_texture_fn)(unsigned, unsigned);
+typedef unsigned char (*is_texture_fn)(unsigned);
+typedef void (*delete_textures_fn)(int, const unsigned *);
+
+enum { GL_VERSION_ = 0x1f02, GL_COLOR_BUFFER_BIT_ = 0x00004000,
+ GL_RGBA_ = 0x1908, GL_UNSIGNED_BYTE_ = 0x1401, GL_TEXTURE_2D_ = 0x0de1 };
+
+static yasl_surface *primary;
+static yasl_surface *shared;
+static yasl_surface *cpu;
+
+static void fail(const char *message, int line)
+{
+ const char *description = NULL;
+ enum yasl_error error = yasl_get_error(&description);
+ fprintf(stderr, "GL integration failure at line %d: %s (YASL error %d: %s, native error %d)\n",
+ line, message, error, description ? description : "none", yasl_get_native_error());
+ (void)yasl_gl_make_current(NULL);
+ if (cpu) yasl_surface_destroy(cpu);
+ if (shared) yasl_surface_destroy(shared);
+ if (primary) yasl_surface_destroy(primary);
+ yasl_shutdown();
+ exit(1);
+}
+
+#define REQUIRE(condition, message) do { if (!(condition)) fail((message), __LINE__); } while (0)
+
+#define LOAD_GL(function, type, symbol) do { \
+ union { yasl_gl_proc generic; type typed; } conversion; \
+ conversion.generic = yasl_gl_get_proc_address(symbol); \
+ REQUIRE(conversion.generic != NULL, "missing OpenGL function " symbol); \
+ function = conversion.typed; \
+} while (0)
+
+static double seconds_now(void)
+{
+ struct timespec ts;
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) return 0;
+ return (double)ts.tv_sec + (double)ts.tv_nsec / 1e9;
+}
+
+static void wait_for_size(yasl_surface *surface, int width, int height)
+{
+ double deadline = seconds_now() + 8.0;
+ int actual_width = 0, actual_height = 0;
+ while (seconds_now() < deadline) {
+ yasl_surface_get_size(surface, &actual_width, &actual_height);
+ if (actual_width == width && actual_height == height) return;
+ yasl_event event = {0};
+ int result = yasl_wait_event(&event, 50);
+ REQUIRE(result >= 0, "waiting for configure event");
+ }
+ char message[128];
+ snprintf(message, sizeof(message), "resize did not settle at %dx%d (got %dx%d)",
+ width, height, actual_width, actual_height);
+ fail(message, __LINE__);
+}
+
+int main(int argc, char **argv)
+{
+ REQUIRE(argc == 2, "expected backend argument: wayland or x11");
+ if (!strcmp(argv[1], "wayland")) REQUIRE(yasl_set_backend(YASL_BACKEND_WAYLAND), "select Wayland backend");
+ else if (!strcmp(argv[1], "x11")) REQUIRE(yasl_set_backend(YASL_BACKEND_X11), "select X11 backend");
+ else REQUIRE(false, "backend must be wayland or x11");
+
+ primary = yasl_gl_surface_create("YASL GL test", 320, 240, 0, NULL, NULL);
+ REQUIRE(primary != NULL, "create default OpenGL 3.2 surface");
+ REQUIRE(yasl_gl_make_current(primary), "make first GL context current");
+
+ get_string_fn get_string = NULL;
+ clear_color_fn clear_color = NULL;
+ clear_fn clear = NULL;
+ finish_fn finish = NULL;
+ read_pixels_fn read_pixels = NULL;
+ gen_textures_fn gen_textures = NULL;
+ bind_texture_fn bind_texture = NULL;
+ is_texture_fn is_texture = NULL;
+ delete_textures_fn delete_textures = NULL;
+ LOAD_GL(get_string, get_string_fn, "glGetString");
+ LOAD_GL(clear_color, clear_color_fn, "glClearColor");
+ LOAD_GL(clear, clear_fn, "glClear");
+ LOAD_GL(finish, finish_fn, "glFinish");
+ LOAD_GL(read_pixels, read_pixels_fn, "glReadPixels");
+ LOAD_GL(gen_textures, gen_textures_fn, "glGenTextures");
+ LOAD_GL(bind_texture, bind_texture_fn, "glBindTexture");
+ LOAD_GL(is_texture, is_texture_fn, "glIsTexture");
+ LOAD_GL(delete_textures, delete_textures_fn, "glDeleteTextures");
+ const unsigned char *version = get_string(GL_VERSION_);
+ REQUIRE(version && version[0], "query GL version string");
+
+ clear_color(0.2f, 0.4f, 0.8f, 1.0f);
+ clear(GL_COLOR_BUFFER_BIT_);
+ finish();
+ unsigned char pixel[4] = {0};
+ int pixel_width = 0, pixel_height = 0;
+ yasl_surface_get_framebuffer_size(primary, &pixel_width, &pixel_height);
+ REQUIRE(pixel_width > 0 && pixel_height > 0, "query initial framebuffer size");
+ read_pixels(pixel_width / 2, pixel_height / 2, 1, 1, GL_RGBA_, GL_UNSIGNED_BYTE_, pixel);
+ REQUIRE(abs((int)pixel[0] - 51) <= 3 && abs((int)pixel[1] - 102) <= 3 &&
+ abs((int)pixel[2] - 204) <= 3 && pixel[3] >= 250,
+ "read rendered pixel from the native GL framebuffer");
+ REQUIRE(yasl_gl_swap_interval(primary, 0), "disable swap interval");
+ REQUIRE(yasl_gl_swap_buffers(primary), "swap first GL surface");
+
+ unsigned texture = 0;
+ gen_textures(1, &texture);
+ bind_texture(GL_TEXTURE_2D_, texture);
+ REQUIRE(is_texture(texture), "create a shareable GL texture");
+
+ shared = yasl_gl_surface_create("YASL GL shared context", 160, 120, 0, NULL, primary);
+ REQUIRE(shared != NULL, "create shared GL context");
+ REQUIRE(yasl_gl_make_current(shared), "make shared context current");
+ REQUIRE(is_texture(texture), "verify context shares GL objects");
+ delete_textures(1, &texture);
+ REQUIRE(yasl_gl_make_current(primary), "restore primary GL context");
+
+ REQUIRE(yasl_surface_request_size(primary, 400, 300), "request surface resize");
+ wait_for_size(primary, 400, 300);
+ yasl_surface_get_framebuffer_size(primary, &pixel_width, &pixel_height);
+ double scale = yasl_surface_get_scale(primary);
+ REQUIRE(abs(pixel_width - (int)lround(400 * scale)) <= 1 &&
+ abs(pixel_height - (int)lround(300 * scale)) <= 1,
+ "query resized framebuffer dimensions");
+ clear_color(0.7f, 0.1f, 0.3f, 1.0f);
+ clear(GL_COLOR_BUFFER_BIT_);
+ REQUIRE(yasl_gl_swap_buffers(primary), "swap resized GL surface");
+
+ const int invalid_attributes[] = {0x7fff, 1, YASL_GL_END};
+ REQUIRE(yasl_gl_surface_create("invalid attrs", 100, 100, 0, invalid_attributes, NULL) == NULL,
+ "reject unknown GL context attributes");
+ REQUIRE(yasl_gl_surface_create("invalid CPU GL", 100, 100, YASL_SURFACE_CPU, NULL, NULL) == NULL,
+ "reject CPU flag on GL surface creation");
+ cpu = yasl_surface_create("CPU surface", 100, 100, YASL_SURFACE_CPU);
+ REQUIRE(cpu != NULL, "create CPU surface for incompatibility check");
+ REQUIRE(!yasl_gl_make_current(cpu), "reject CPU surface as a GL context");
+
+ REQUIRE(yasl_gl_make_current(NULL), "detach GL context before destruction");
+ yasl_surface_destroy(cpu); cpu = NULL;
+ yasl_surface_destroy(shared); shared = NULL;
+ yasl_surface_destroy(primary); primary = NULL;
+ yasl_shutdown();
+ return 0;
+}