Clone of mesa.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

glthreads.c 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  1. /*
  2. * Copyright (C) 2000 Brian Paul All Rights Reserved.
  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 shall be included
  12. * in all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  15. * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  17. * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
  18. * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  19. * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  20. */
  21. /*
  22. * This program tests GLX thread safety.
  23. * Command line options:
  24. * -p Open a display connection for each thread
  25. * -n <num threads> Number of threads to create (default is 2)
  26. * -display <display name> Specify X display (default is :0.0)
  27. *
  28. * Brian Paul 20 July 2000
  29. */
  30. #if defined(PTHREADS) /* defined by Mesa on Linux and other platforms */
  31. #include <assert.h>
  32. #include <GL/gl.h>
  33. #include <GL/glx.h>
  34. #include <stdio.h>
  35. #include <stdlib.h>
  36. #include <string.h>
  37. #include <unistd.h>
  38. #include <pthread.h>
  39. /*
  40. * Each window/thread/context:
  41. */
  42. struct winthread {
  43. Display *Dpy;
  44. int Index;
  45. pthread_t Thread;
  46. Window Win;
  47. GLXContext Context;
  48. float Angle;
  49. int WinWidth, WinHeight;
  50. GLboolean NewSize;
  51. };
  52. #define MAX_WINTHREADS 100
  53. static struct winthread WinThreads[MAX_WINTHREADS];
  54. static int NumWinThreads = 0;
  55. static volatile GLboolean ExitFlag = GL_FALSE;
  56. static GLboolean MultiDisplays = 0;
  57. static GLboolean Locking = 0;
  58. static pthread_mutex_t Mutex;
  59. static void
  60. Error(const char *msg)
  61. {
  62. fprintf(stderr, "Error: %s\n", msg);
  63. exit(1);
  64. }
  65. /* draw a colored cube */
  66. static void
  67. draw_object(void)
  68. {
  69. glPushMatrix();
  70. glScalef(0.75, 0.75, 0.75);
  71. glColor3f(1, 0, 0);
  72. glBegin(GL_POLYGON);
  73. glVertex3f(1, -1, -1);
  74. glVertex3f(1, 1, -1);
  75. glVertex3f(1, 1, 1);
  76. glVertex3f(1, -1, 1);
  77. glEnd();
  78. glColor3f(0, 1, 1);
  79. glBegin(GL_POLYGON);
  80. glVertex3f(-1, -1, -1);
  81. glVertex3f(-1, 1, -1);
  82. glVertex3f(-1, 1, 1);
  83. glVertex3f(-1, -1, 1);
  84. glEnd();
  85. glColor3f(0, 1, 0);
  86. glBegin(GL_POLYGON);
  87. glVertex3f(-1, 1, -1);
  88. glVertex3f( 1, 1, -1);
  89. glVertex3f( 1, 1, 1);
  90. glVertex3f(-1, 1, 1);
  91. glEnd();
  92. glColor3f(1, 0, 1);
  93. glBegin(GL_POLYGON);
  94. glVertex3f(-1, -1, -1);
  95. glVertex3f( 1, -1, -1);
  96. glVertex3f( 1, -1, 1);
  97. glVertex3f(-1, -1, 1);
  98. glEnd();
  99. glColor3f(0, 0, 1);
  100. glBegin(GL_POLYGON);
  101. glVertex3f(-1, -1, 1);
  102. glVertex3f( 1, -1, 1);
  103. glVertex3f( 1, 1, 1);
  104. glVertex3f(-1, 1, 1);
  105. glEnd();
  106. glColor3f(1, 1, 0);
  107. glBegin(GL_POLYGON);
  108. glVertex3f(-1, -1, -1);
  109. glVertex3f( 1, -1, -1);
  110. glVertex3f( 1, 1, -1);
  111. glVertex3f(-1, 1, -1);
  112. glEnd();
  113. glPopMatrix();
  114. }
  115. /* signal resize of given window */
  116. static void
  117. resize(struct winthread *wt, int w, int h)
  118. {
  119. wt->NewSize = GL_TRUE;
  120. wt->WinWidth = w;
  121. wt->WinHeight = h;
  122. }
  123. /*
  124. * We have an instance of this for each thread.
  125. */
  126. static void
  127. draw_loop(struct winthread *wt)
  128. {
  129. GLboolean firstIter = GL_TRUE;
  130. while (!ExitFlag) {
  131. if (Locking)
  132. pthread_mutex_lock(&Mutex);
  133. glXMakeCurrent(wt->Dpy, wt->Win, wt->Context);
  134. if (firstIter) {
  135. printf("glthreads: %d: GL_RENDERER = %s\n", wt->Index,
  136. (char *) glGetString(GL_RENDERER));
  137. firstIter = GL_FALSE;
  138. }
  139. if (Locking)
  140. pthread_mutex_unlock(&Mutex);
  141. glEnable(GL_DEPTH_TEST);
  142. if (wt->NewSize) {
  143. GLfloat w = (float) wt->WinWidth / (float) wt->WinHeight;
  144. glViewport(0, 0, wt->WinWidth, wt->WinHeight);
  145. glMatrixMode(GL_PROJECTION);
  146. glLoadIdentity();
  147. glFrustum(-w, w, -1.0, 1.0, 1.5, 10);
  148. glMatrixMode(GL_MODELVIEW);
  149. glLoadIdentity();
  150. glTranslatef(0, 0, -2.5);
  151. wt->NewSize = GL_FALSE;
  152. }
  153. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  154. glPushMatrix();
  155. glRotatef(wt->Angle, 0, 0, 1);
  156. glRotatef(wt->Angle, 1, 0, 0);
  157. glScalef(0.7, 0.7, 0.7);
  158. draw_object();
  159. glPopMatrix();
  160. if (Locking)
  161. pthread_mutex_lock(&Mutex);
  162. glXSwapBuffers(wt->Dpy, wt->Win);
  163. if (Locking)
  164. pthread_mutex_unlock(&Mutex);
  165. usleep(5000);
  166. wt->Angle += 1.0;
  167. }
  168. }
  169. /*
  170. * The main process thread runs this loop.
  171. * Single display connection for all threads.
  172. */
  173. static void
  174. event_loop(Display *dpy)
  175. {
  176. XEvent event;
  177. int i;
  178. assert(!MultiDisplays);
  179. while (!ExitFlag) {
  180. if (Locking) {
  181. while (1) {
  182. int k;
  183. pthread_mutex_lock(&Mutex);
  184. k = XPending(dpy);
  185. if (k) {
  186. XNextEvent(dpy, &event);
  187. pthread_mutex_unlock(&Mutex);
  188. break;
  189. }
  190. pthread_mutex_unlock(&Mutex);
  191. usleep(5000);
  192. }
  193. }
  194. else {
  195. XNextEvent(dpy, &event);
  196. }
  197. switch (event.type) {
  198. case ConfigureNotify:
  199. /* Find winthread for this event's window */
  200. for (i = 0; i < NumWinThreads; i++) {
  201. struct winthread *wt = &WinThreads[i];
  202. if (event.xconfigure.window == wt->Win) {
  203. resize(wt, event.xconfigure.width,
  204. event.xconfigure.height);
  205. break;
  206. }
  207. }
  208. break;
  209. case KeyPress:
  210. /* tell all threads to exit */
  211. ExitFlag = GL_TRUE;
  212. /*printf("exit draw_loop %d\n", wt->Index);*/
  213. return;
  214. default:
  215. /*no-op*/ ;
  216. }
  217. }
  218. }
  219. /*
  220. * Separate display connection for each thread.
  221. */
  222. static void
  223. event_loop_multi(void)
  224. {
  225. XEvent event;
  226. int w = 0;
  227. assert(MultiDisplays);
  228. while (!ExitFlag) {
  229. struct winthread *wt = &WinThreads[w];
  230. if (XPending(wt->Dpy)) {
  231. XNextEvent(wt->Dpy, &event);
  232. switch (event.type) {
  233. case ConfigureNotify:
  234. resize(wt, event.xconfigure.width, event.xconfigure.height);
  235. break;
  236. case KeyPress:
  237. /* tell all threads to exit */
  238. ExitFlag = GL_TRUE;
  239. /*printf("exit draw_loop %d\n", wt->Index);*/
  240. return;
  241. default:
  242. /*no-op*/ ;
  243. }
  244. }
  245. w = (w + 1) % NumWinThreads;
  246. usleep(5000);
  247. }
  248. }
  249. /*
  250. * we'll call this once for each thread, before the threads are created.
  251. */
  252. static void
  253. create_window(struct winthread *wt)
  254. {
  255. Window win;
  256. GLXContext ctx;
  257. int attrib[] = { GLX_RGBA,
  258. GLX_RED_SIZE, 1,
  259. GLX_GREEN_SIZE, 1,
  260. GLX_BLUE_SIZE, 1,
  261. GLX_DEPTH_SIZE, 1,
  262. GLX_DOUBLEBUFFER,
  263. None };
  264. int scrnum;
  265. XSetWindowAttributes attr;
  266. unsigned long mask;
  267. Window root;
  268. XVisualInfo *visinfo;
  269. int width = 80, height = 80;
  270. int xpos = (wt->Index % 10) * 90;
  271. int ypos = (wt->Index / 10) * 100;
  272. scrnum = DefaultScreen(wt->Dpy);
  273. root = RootWindow(wt->Dpy, scrnum);
  274. visinfo = glXChooseVisual(wt->Dpy, scrnum, attrib);
  275. if (!visinfo) {
  276. Error("Unable to find RGB, Z, double-buffered visual");
  277. }
  278. /* window attributes */
  279. attr.background_pixel = 0;
  280. attr.border_pixel = 0;
  281. attr.colormap = XCreateColormap(wt->Dpy, root, visinfo->visual, AllocNone);
  282. attr.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask;
  283. mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
  284. win = XCreateWindow(wt->Dpy, root, xpos, ypos, width, height,
  285. 0, visinfo->depth, InputOutput,
  286. visinfo->visual, mask, &attr);
  287. if (!win) {
  288. Error("Couldn't create window");
  289. }
  290. {
  291. XSizeHints sizehints;
  292. sizehints.x = xpos;
  293. sizehints.y = ypos;
  294. sizehints.width = width;
  295. sizehints.height = height;
  296. sizehints.flags = USSize | USPosition;
  297. XSetNormalHints(wt->Dpy, win, &sizehints);
  298. XSetStandardProperties(wt->Dpy, win, "glthreads", "glthreads",
  299. None, (char **)NULL, 0, &sizehints);
  300. }
  301. ctx = glXCreateContext(wt->Dpy, visinfo, NULL, True);
  302. if (!ctx) {
  303. Error("Couldn't create GLX context");
  304. }
  305. XMapWindow(wt->Dpy, win);
  306. XSync(wt->Dpy, 0);
  307. /* save the info for this window/context */
  308. wt->Win = win;
  309. wt->Context = ctx;
  310. wt->Angle = 0.0;
  311. wt->WinWidth = width;
  312. wt->WinHeight = height;
  313. wt->NewSize = GL_TRUE;
  314. }
  315. /*
  316. * Called by pthread_create()
  317. */
  318. static void *
  319. thread_function(void *p)
  320. {
  321. struct winthread *wt = (struct winthread *) p;
  322. draw_loop(wt);
  323. return NULL;
  324. }
  325. /*
  326. * called before exit to wait for all threads to finish
  327. */
  328. static void
  329. clean_up(void)
  330. {
  331. int i;
  332. /* wait for threads to finish */
  333. for (i = 0; i < NumWinThreads; i++) {
  334. pthread_join(WinThreads[i].Thread, NULL);
  335. }
  336. for (i = 0; i < NumWinThreads; i++) {
  337. glXDestroyContext(WinThreads[i].Dpy, WinThreads[i].Context);
  338. XDestroyWindow(WinThreads[i].Dpy, WinThreads[i].Win);
  339. }
  340. }
  341. int
  342. main(int argc, char *argv[])
  343. {
  344. char *displayName = ":0.0";
  345. int numThreads = 2;
  346. Display *dpy = NULL;
  347. int i;
  348. Status threadStat;
  349. if (argc == 1) {
  350. printf("glthreads: test of GL thread safety (any key = exit)\n");
  351. printf("Usage:\n");
  352. printf(" glthreads [-display dpyName] [-n numthreads]\n");
  353. }
  354. else {
  355. int i;
  356. for (i = 1; i < argc; i++) {
  357. if (strcmp(argv[i], "-display") == 0 && i + 1 < argc) {
  358. displayName = argv[i + 1];
  359. i++;
  360. }
  361. else if (strcmp(argv[i], "-p") == 0) {
  362. MultiDisplays = 1;
  363. }
  364. else if (strcmp(argv[i], "-l") == 0) {
  365. Locking = 1;
  366. }
  367. else if (strcmp(argv[i], "-n") == 0 && i + 1 < argc) {
  368. numThreads = atoi(argv[i + 1]);
  369. if (numThreads < 1)
  370. numThreads = 1;
  371. else if (numThreads > MAX_WINTHREADS)
  372. numThreads = MAX_WINTHREADS;
  373. i++;
  374. }
  375. else {
  376. fprintf(stderr, "glthreads: unexpected flag: %s\n", argv[i]);
  377. }
  378. }
  379. }
  380. if (Locking)
  381. printf("glthreads: Using explict locks around Xlib calls.\n");
  382. else
  383. printf("glthreads: No explict locking.\n");
  384. if (MultiDisplays)
  385. printf("glthreads: Per-thread display connections.\n");
  386. else
  387. printf("glthreads: Single display connection.\n");
  388. /*
  389. * VERY IMPORTANT: call XInitThreads() before any other Xlib functions.
  390. */
  391. if (!MultiDisplays) {
  392. if (!Locking) {
  393. threadStat = XInitThreads();
  394. if (threadStat) {
  395. printf("XInitThreads() returned %d (success)\n", (int) threadStat);
  396. }
  397. else {
  398. printf("XInitThreads() returned 0 (failure- this program may fail)\n");
  399. }
  400. }
  401. dpy = XOpenDisplay(displayName);
  402. if (!dpy) {
  403. fprintf(stderr, "Unable to open display %s\n", XDisplayName(displayName));
  404. return -1;
  405. }
  406. }
  407. if (Locking) {
  408. pthread_mutex_init(&Mutex, NULL);
  409. }
  410. printf("glthreads: creating windows\n");
  411. NumWinThreads = numThreads;
  412. /* Create the GLX windows and contexts */
  413. for (i = 0; i < numThreads; i++) {
  414. if (MultiDisplays) {
  415. WinThreads[i].Dpy = XOpenDisplay(displayName);
  416. assert(WinThreads[i].Dpy);
  417. }
  418. else {
  419. WinThreads[i].Dpy = dpy;
  420. }
  421. WinThreads[i].Index = i;
  422. create_window(&WinThreads[i]);
  423. }
  424. printf("glthreads: creating threads\n");
  425. /* Create the threads */
  426. for (i = 0; i < numThreads; i++) {
  427. pthread_create(&WinThreads[i].Thread, NULL, thread_function,
  428. (void*) &WinThreads[i]);
  429. printf("glthreads: Created thread %u\n", (unsigned int) WinThreads[i].Thread);
  430. }
  431. if (MultiDisplays)
  432. event_loop_multi();
  433. else
  434. event_loop(dpy);
  435. clean_up();
  436. if (MultiDisplays) {
  437. for (i = 0; i < numThreads; i++) {
  438. XCloseDisplay(WinThreads[i].Dpy);
  439. }
  440. }
  441. else {
  442. XCloseDisplay(dpy);
  443. }
  444. return 0;
  445. }
  446. #else /* PTHREADS */
  447. #include <stdio.h>
  448. int
  449. main(int argc, char *argv[])
  450. {
  451. printf("Sorry, this program wasn't compiled with PTHREADS defined.\n");
  452. return 0;
  453. }
  454. #endif /* PTHREADS */