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