1 /*
2 * Copyright 2013 Google Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24 /** @file glx-multithread-texture.c
25 *
26 * Test loading texture data from one thread and context while drawing with
27 * those textures from another thread and shared context. The threads are
28 * synchronized so they do not attempt to use the same texture at the same time.
29 */
30
31 #include <unistd.h>
32
33 #include "piglit-util-gl.h"
34 #include "piglit-glx-util.h"
35 #include "pthread.h"
36
37 int piglit_width = 50, piglit_height = 50;
38 static int tex_width = 512, tex_height = 512;
39 static unsigned num_test = 300;
40 static bool quit = false;
41
42 static Display *dpy;
43 static Window draw_win;
44 static GLXPixmap load_win;
45 static pthread_mutex_t mutex;
46 static XVisualInfo *visinfo;
47 static GLXContext draw_ctx, load_ctx;
48
49 /*
50 * list of textures
51 *
52 * user == DRAW means draw thread may draw with this texture
53 * user == LOAD means load thread may load data into this texture
54 * user == NONE means texture not in use and may be claimed by either thread
55 *
56 * Minimum of three items needed in this array for the code to work.
57 */
58 static struct texture {
59 GLuint id;
60 int color;
61 enum user { DRAW, LOAD, NONE } user;
62 } texture[5];
63
64 /*
65 * If texture at *pos is not in use claim it for 'user' and increment *pos.
66 * Return texture at *pos.
67 */
68 static struct texture *
advance(int * pos,enum user user)69 advance(int *pos, enum user user)
70 {
71 int cur = *pos % ARRAY_SIZE(texture);
72 int next = (cur + 1) % ARRAY_SIZE(texture);
73
74 pthread_mutex_lock(&mutex);
75 assert(texture[cur].user == user);
76 if (texture[next].user == NONE) {
77 texture[cur].user = NONE;
78 cur = next;
79 *pos += 1;
80 texture[cur].user = user;
81 }
82 pthread_mutex_unlock(&mutex);
83
84 /* helps avoid starvation */
85 usleep(1);
86
87 return texture + cur;
88 }
89
90 /*
91 * Texture writing thread: loads data into successive textures, taking note
92 * of what color was used so it can be checked later.
93 * Return NULL on failure, else non-NULL.
94 */
95 static void *
load_func(void * arg)96 load_func(void *arg)
97 {
98 int count = 1;
99 struct texture *tex = texture + count;
100 unsigned int tex_bytes = tex_width * tex_height * 4;
101 unsigned char *tex_data = malloc(tex_bytes);
102 int ret;
103
104 ret = glXMakeCurrent(dpy, load_win, load_ctx);
105 assert(ret);
106
107 glEnable(GL_TEXTURE_2D);
108
109 while (!quit && count <= num_test) {
110 int color = (3 * count) & 0xff;
111
112 assert(tex->user == LOAD);
113 if (tex->color != color) {
114 memset(tex_data, color, tex_bytes);
115 tex->color = color;
116 glBindTexture(GL_TEXTURE_2D, tex->id);
117 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex_width,
118 tex_height, 0, GL_RGBA, GL_UNSIGNED_BYTE,
119 tex_data);
120 glFlush();
121 }
122
123 tex = advance(&count, LOAD);
124 }
125
126 glFinish();
127 free(tex_data);
128
129 if (count <= num_test) {
130 quit = true;
131 return NULL;
132 }
133
134 return "";
135 }
136
137 /*
138 * Texture using thread: draws with successive textures and checks that the
139 * correct color is drawn. Return NULL on failure, else non-NULL.
140 */
141 static void *
draw_func(void * arg)142 draw_func(void *arg)
143 {
144 int count = 0;
145 int ret;
146
147 ret = glXMakeCurrent(dpy, draw_win, draw_ctx);
148 assert(ret);
149
150 piglit_ortho_projection(piglit_width, piglit_height, GL_FALSE);
151
152 glEnable(GL_TEXTURE_2D);
153 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
154 glReadBuffer(GL_FRONT);
155
156 while (!quit && count < num_test) {
157 struct texture *tex = advance(&count, DRAW);
158 float expect[] = {
159 tex->color / 255.,
160 tex->color / 255.,
161 tex->color / 255.,
162 };
163
164 assert(tex->user == DRAW);
165
166 glBindTexture(GL_TEXTURE_2D, tex->id);
167 piglit_draw_rect_tex(0, 0, piglit_width, piglit_height,
168 0, 0, 1, 1);
169 glXSwapBuffers(dpy, draw_win);
170
171 /* first texture not filled so don't check it */
172 if (count > 0 &&
173 !piglit_probe_rect_rgb(0, 0, piglit_width,
174 piglit_height, expect)) {
175 break;
176 }
177 }
178
179 if (count < num_test) {
180 quit = true;
181 return NULL;
182 }
183
184 return "";
185 }
186
187 enum piglit_result
draw(Display * dpy)188 draw(Display *dpy)
189 {
190 pthread_t draw_thread, load_thread;
191 void *draw_ret, *load_ret;
192 int ret, i;
193 GLXContext my_ctx;
194
195 my_ctx = piglit_get_glx_context_share(dpy, visinfo, NULL);
196 draw_ctx = piglit_get_glx_context_share(dpy, visinfo, my_ctx);
197 load_ctx = piglit_get_glx_context_share(dpy, visinfo, my_ctx);
198
199 ret = glXMakeCurrent(dpy, draw_win, my_ctx);
200 assert(ret);
201 piglit_dispatch_default_init(PIGLIT_DISPATCH_GL);
202
203 glEnable(GL_TEXTURE_2D);
204
205 for (i = 0; i < ARRAY_SIZE(texture); ++i) {
206 glGenTextures(1, &texture[i].id);
207 texture[i].color = -1;
208 texture[i].user = NONE;
209 glBindTexture(GL_TEXTURE_2D, texture[i].id);
210 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
211 GL_NEAREST);
212 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
213 GL_NEAREST);
214 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex_width, tex_height,
215 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
216 }
217 texture[0].user = DRAW;
218 texture[1].user = LOAD;
219
220 pthread_mutex_init(&mutex, NULL);
221
222 pthread_create(&draw_thread, NULL, draw_func, NULL);
223 pthread_create(&load_thread, NULL, load_func, NULL);
224
225 ret = pthread_join(draw_thread, &draw_ret);
226 assert(ret == 0);
227 ret = pthread_join(load_thread, &load_ret);
228 assert(ret == 0);
229
230 pthread_mutex_destroy(&mutex);
231
232 glXDestroyContext(dpy, load_ctx);
233 glXDestroyContext(dpy, draw_ctx);
234 glXDestroyContext(dpy, my_ctx);
235
236 return draw_ret && load_ret ? PIGLIT_PASS : PIGLIT_FAIL;
237 }
238
239 int
main(int argc,char ** argv)240 main(int argc, char **argv)
241 {
242 int i;
243 Pixmap pixmap;
244
245 for(i = 1; i < argc; ++i) {
246 if (!strcmp(argv[i], "-auto"))
247 piglit_automatic = 1;
248 else
249 fprintf(stderr, "Unknown option: %s\n", argv[i]);
250 }
251
252 XInitThreads();
253 dpy = XOpenDisplay(NULL);
254 if (dpy == NULL) {
255 fprintf(stderr, "couldn't open display\n");
256 piglit_report_result(PIGLIT_FAIL);
257 }
258 visinfo = piglit_get_glx_visual(dpy);
259 draw_win = piglit_get_glx_window(dpy, visinfo);
260 pixmap = XCreatePixmap(dpy, DefaultRootWindow(dpy),
261 piglit_width, piglit_height, visinfo->depth);
262 load_win = glXCreateGLXPixmap(dpy, visinfo, pixmap);
263
264 XMapWindow(dpy, draw_win);
265
266 piglit_glx_event_loop(dpy, draw);
267
268 return 0;
269 }
270