/* * Copyright 2013 Google Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ /** @file glx-multithread-texture.c * * Test loading texture data from one thread and context while drawing with * those textures from another thread and shared context. The threads are * synchronized so they do not attempt to use the same texture at the same time. */ #include #include "piglit-util-gl.h" #include "piglit-glx-util.h" #include "pthread.h" int piglit_width = 50, piglit_height = 50; static int tex_width = 512, tex_height = 512; static unsigned num_test = 300; static bool quit = false; static Display *dpy; static Window draw_win; static GLXPixmap load_win; static pthread_mutex_t mutex; static XVisualInfo *visinfo; static GLXContext draw_ctx, load_ctx; /* * list of textures * * user == DRAW means draw thread may draw with this texture * user == LOAD means load thread may load data into this texture * user == NONE means texture not in use and may be claimed by either thread * * Minimum of three items needed in this array for the code to work. */ static struct texture { GLuint id; int color; enum user { DRAW, LOAD, NONE } user; } texture[5]; /* * If texture at *pos is not in use claim it for 'user' and increment *pos. * Return texture at *pos. */ static struct texture * advance(int *pos, enum user user) { int cur = *pos % ARRAY_SIZE(texture); int next = (cur + 1) % ARRAY_SIZE(texture); pthread_mutex_lock(&mutex); assert(texture[cur].user == user); if (texture[next].user == NONE) { texture[cur].user = NONE; cur = next; *pos += 1; texture[cur].user = user; } pthread_mutex_unlock(&mutex); /* helps avoid starvation */ usleep(1); return texture + cur; } /* * Texture writing thread: loads data into successive textures, taking note * of what color was used so it can be checked later. * Return NULL on failure, else non-NULL. */ static void * load_func(void *arg) { int count = 1; struct texture *tex = texture + count; unsigned int tex_bytes = tex_width * tex_height * 4; unsigned char *tex_data = malloc(tex_bytes); int ret; ret = glXMakeCurrent(dpy, load_win, load_ctx); assert(ret); glEnable(GL_TEXTURE_2D); while (!quit && count <= num_test) { int color = (3 * count) & 0xff; assert(tex->user == LOAD); if (tex->color != color) { memset(tex_data, color, tex_bytes); tex->color = color; glBindTexture(GL_TEXTURE_2D, tex->id); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex_width, tex_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, tex_data); glFlush(); } tex = advance(&count, LOAD); } glFinish(); free(tex_data); if (count <= num_test) { quit = true; return NULL; } return ""; } /* * Texture using thread: draws with successive textures and checks that the * correct color is drawn. Return NULL on failure, else non-NULL. */ static void * draw_func(void *arg) { int count = 0; int ret; ret = glXMakeCurrent(dpy, draw_win, draw_ctx); assert(ret); piglit_ortho_projection(piglit_width, piglit_height, GL_FALSE); glEnable(GL_TEXTURE_2D); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glReadBuffer(GL_FRONT); while (!quit && count < num_test) { struct texture *tex = advance(&count, DRAW); float expect[] = { tex->color / 255., tex->color / 255., tex->color / 255., }; assert(tex->user == DRAW); glBindTexture(GL_TEXTURE_2D, tex->id); piglit_draw_rect_tex(0, 0, piglit_width, piglit_height, 0, 0, 1, 1); glXSwapBuffers(dpy, draw_win); /* first texture not filled so don't check it */ if (count > 0 && !piglit_probe_rect_rgb(0, 0, piglit_width, piglit_height, expect)) { break; } } if (count < num_test) { quit = true; return NULL; } return ""; } enum piglit_result draw(Display *dpy) { pthread_t draw_thread, load_thread; void *draw_ret, *load_ret; int ret, i; GLXContext my_ctx; my_ctx = piglit_get_glx_context_share(dpy, visinfo, NULL); draw_ctx = piglit_get_glx_context_share(dpy, visinfo, my_ctx); load_ctx = piglit_get_glx_context_share(dpy, visinfo, my_ctx); ret = glXMakeCurrent(dpy, draw_win, my_ctx); assert(ret); piglit_dispatch_default_init(PIGLIT_DISPATCH_GL); glEnable(GL_TEXTURE_2D); for (i = 0; i < ARRAY_SIZE(texture); ++i) { glGenTextures(1, &texture[i].id); texture[i].color = -1; texture[i].user = NONE; glBindTexture(GL_TEXTURE_2D, texture[i].id); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex_width, tex_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); } texture[0].user = DRAW; texture[1].user = LOAD; pthread_mutex_init(&mutex, NULL); pthread_create(&draw_thread, NULL, draw_func, NULL); pthread_create(&load_thread, NULL, load_func, NULL); ret = pthread_join(draw_thread, &draw_ret); assert(ret == 0); ret = pthread_join(load_thread, &load_ret); assert(ret == 0); pthread_mutex_destroy(&mutex); glXDestroyContext(dpy, load_ctx); glXDestroyContext(dpy, draw_ctx); glXDestroyContext(dpy, my_ctx); return draw_ret && load_ret ? PIGLIT_PASS : PIGLIT_FAIL; } int main(int argc, char **argv) { int i; Pixmap pixmap; for(i = 1; i < argc; ++i) { if (!strcmp(argv[i], "-auto")) piglit_automatic = 1; else fprintf(stderr, "Unknown option: %s\n", argv[i]); } XInitThreads(); dpy = XOpenDisplay(NULL); if (dpy == NULL) { fprintf(stderr, "couldn't open display\n"); piglit_report_result(PIGLIT_FAIL); } visinfo = piglit_get_glx_visual(dpy); draw_win = piglit_get_glx_window(dpy, visinfo); pixmap = XCreatePixmap(dpy, DefaultRootWindow(dpy), piglit_width, piglit_height, visinfo->depth); load_win = glXCreateGLXPixmap(dpy, visinfo, pixmap); XMapWindow(dpy, draw_win); piglit_glx_event_loop(dpy, draw); return 0; }