libglfork.cpp 33.4 KB
Newer Older
1 2 3 4
#include <dlfcn.h>
#include <pthread.h>
#include <semaphore.h>
#include <time.h>
5
#include <unistd.h>
6 7 8 9 10 11 12 13
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cassert>
#include <map>
14
#include <string>
15
#include <X11/Xatom.h>
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
#pragma GCC visibility push(default)
#define GLX_GLXEXT_PROTOTYPES
#define GL_GLEXT_PROTOTYPES
#include <GL/glx.h>
#pragma GCC visibility pop

#define primus_print(c, ...) do { if (c) fprintf(stderr, "primus: " __VA_ARGS__); } while (0)

#define die_if(cond, ...)  do {if (cond) {primus_print(true, "fatal: " __VA_ARGS__); exit(1);} } while (0)
#define primus_warn(...) primus_print(primus.loglevel >= 1, "warning: " __VA_ARGS__)
#define primus_perf(...) primus_print(primus.loglevel >= 2, "profiling: " __VA_ARGS__)

// Try to load any of the colon-separated libraries
static void *mdlopen(const char *paths, int flag)
{
  char *p = strdupa(paths);
  char errors[1024], *errors_ptr = errors, *errors_end = errors + 1024;
  for (char *c = p; c; p = c + 1)
  {
    if ((c = strchr(p, ':')))
      *c = 0;
    die_if(p[0] != '/', "need absolute library path: %s\n", p);
    void *handle = dlopen(p, flag);
    if (handle)
      return handle;
    errors_ptr += snprintf(errors_ptr, errors_end - errors_ptr, "%s\n", dlerror());
  }
  die_if(true, "failed to load any of the libraries: %s\n%s", paths, errors);
}

46 47 48 49 50 51 52
static void *real_dlsym(void *handle, const char *symbol)
{
  typedef void* (*dlsym_fn)(void *, const char*);
  static dlsym_fn pdlsym = (dlsym_fn) dlsym(dlopen("libdl.so.2", RTLD_LAZY), "dlsym");
  return pdlsym(handle, symbol);
}

53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
// Pointers to implemented/forwarded GLX and OpenGL functions
struct CapturedFns {
  void *handle;
  // Declare functions as fields of the struct
#define DEF_GLX_PROTO(ret, name, args, ...) ret (*name) args;
#include "glx-reimpl.def"
#include "glx-dpyredir.def"
#include "glxext-reimpl.def"
#include "gl-passthru.def"
#include "gl-needed.def"
#undef DEF_GLX_PROTO
  CapturedFns(const char *lib)
  {
    handle = mdlopen(lib, RTLD_LAZY);
#define DEF_GLX_PROTO(ret, name, args, ...) name = (ret (*) args)real_dlsym(handle, #name);
#include "glx-reimpl.def"
#include "glx-dpyredir.def"
#undef DEF_GLX_PROTO
#define DEF_GLX_PROTO(ret, name, args, ...) name = (ret (*) args)this->glXGetProcAddress((GLubyte*)#name);
#include "glxext-reimpl.def"
#include "gl-passthru.def"
#include "gl-needed.def"
#undef DEF_GLX_PROTO
  }
  ~CapturedFns()
  {
    dlclose(handle);
  }
};

// Drawable tracking info
struct DrawableInfo {
  // Only XWindow is not explicitely created via GLX
  enum {XWindow, Window, Pixmap, Pbuffer} kind;
  GLXFBConfig fbconfig;
  GLXPbuffer  pbuffer;
  Drawable window;
  int width, height;
91
  enum ReinitTodo {NONE, RESIZE, SHUTDOWN} reinit;
92 93 94 95 96 97 98
  GLvoid *pixeldata;
  GLsync sync;
  GLXContext actx;

  struct {
    pthread_t worker;
    sem_t acqsem, relsem;
99
    ReinitTodo reinit;
100 101 102

    void spawn_worker(GLXDrawable draw, void* (*work)(void*))
    {
103
      reinit = RESIZE;
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
      sem_init(&acqsem, 0, 0);
      sem_init(&relsem, 0, 0);
      pthread_create(&worker, NULL, work, (void*)draw);
    }
    void reap_worker()
    {
      //pthread_cancel(worker);
      pthread_join(worker, NULL);
      sem_destroy(&relsem);
      sem_destroy(&acqsem);
      worker = 0;
    }
  } r, d;
  void reap_workers()
  {
    if (r.worker)
    {
121
      r.reinit = SHUTDOWN;
122 123 124 125 126 127
      sem_post(&r.acqsem);
      sem_wait(&r.relsem);
      r.reap_worker();
      d.reap_worker();
    }
  }
128
  void update_geometry(int width, int height)
129
  {
130 131
    if (this->width == width && this->height == height)
      return;
132
    this->width = width; this->height = height;
133 134
    __sync_synchronize();
    reinit = RESIZE;
135
  }
136
  ~DrawableInfo();
137 138 139 140 141 142 143 144 145
};

struct DrawablesInfo: public std::map<GLXDrawable, DrawableInfo> {
  bool known(GLXDrawable draw)
  {
    return this->find(draw) != this->end();
  }
};

146 147 148 149 150 151 152 153 154 155 156 157 158 159
struct ContextInfo {
  GLXFBConfig fbconfig;
  int sharegroup;
};

struct ContextsInfo: public std::map<GLXContext, ContextInfo> {
  void record(GLXContext ctx, GLXFBConfig config, GLXContext share)
  {
    static int nsharegroups;
    int sharegroup = share ? (*this)[share].sharegroup : nsharegroups++;
    (*this)[ctx] = (ContextInfo){config, sharegroup};
  }
};

160 161
// Shorthand for obtaining compile-time configurable value that can be
// overridden by environment
162
#define getconf(V) (getenv(#V) ? getenv(#V) : V)
163 164 165

// Runs before all other initialization takes place
struct EarlyInitializer {
166
  EarlyInitializer(const char **adpy_strp, const char **libgla_strp)
167 168 169 170 171 172 173
  {
#ifdef BUMBLEBEE_SOCKET
    // Signal the Bumblebee daemon to bring up secondary X
    int sock = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
    struct sockaddr_un addr;
    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, getconf(BUMBLEBEE_SOCKET), sizeof(addr.sun_path));
174 175
    die_if(connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0,
           "failed to connect to Bumblebee daemon: %s\n", strerror(errno));
176 177 178 179 180 181 182
    static char c[256];
    if (!getenv("PRIMUS_DISPLAY"))
    {
      send(sock, "Q VirtualDisplay", strlen("Q VirtualDisplay") + 1, 0);
      recv(sock, &c, 255, 0);
      die_if(memcmp(c, "Value: ", strlen("Value: ")), "unexpected query response\n");
      *strchrnul(c, '\n') = 0;
183
      *adpy_strp = strdup(c + 7);
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
    }
    if (!getenv("PRIMUS_libGLa"))
    {
      send(sock, "Q LibraryPath", strlen("Q LibraryPath") + 1, 0);
      recv(sock, &c, 255, 0);
      die_if(memcmp(c, "Value: ", strlen("Value: ")), "unexpected query response\n");
      *strchrnul(c, '\n') = 0;
      int npaths = 0;
      for (char *p = c + 7; *p; npaths++, p = strchrnul(p + 1, ':'));
      if (npaths)
      {
	char *bblibs = new char[strlen(c + 7) + npaths * strlen("/libGL.so.1") + 1], *b = bblibs, *n, *p;
	for (p = c + 7; *p; p = n)
	{
	  n = strchrnul(p + 1, ':');
	  b += sprintf(b, "%.*s/libGL.so.1", (int)(n - p), p);
	}
201
	*libgla_strp = bblibs;
202 203 204
      }
    }
    send(sock, "C", 1, 0);
205 206 207
    recv(sock, &c, 255, 0);
    die_if(c[0] == 'N', "Bumblebee daemon reported: %s\n", c + 5);
    die_if(c[0] != 'Y', "failure contacting Bumblebee daemon\n");
208 209 210 211 212 213 214 215 216
    // the socket will be closed when the application quits, then bumblebee will shut down the secondary X
#else
#warning Building without Bumblebee daemon support
#endif
  }
};

// Process-wide data
static struct PrimusInfo {
217
  const char *adpy_str, *libgla_str;
218 219 220 221 222 223
  EarlyInitializer ei;
  // Readback-display synchronization method
  // 0: no sync, 1: D lags behind one frame, 2: fully synced
  int sync;
  // 0: only errors, 1: warnings, 2: profiling
  int loglevel;
224 225 226 227
  // 0: autodetect, 1: texture, 2: PBO glDrawPixels
  int dispmethod;
  // sleep ratio in readback thread, percent
  int autosleep;
228 229 230 231 232 233 234 235 236 237 238
  // The "accelerating" X display
  Display *adpy;
  // The "displaying" X display. The same as the application is using, but
  // primus opens its own connection.
  Display *ddpy;
  // An artifact: primus needs to make symbols from libglapi.so globally
  // visible before loading Mesa
  const void *needed_global;
  CapturedFns afns;
  CapturedFns dfns;
  // FIXME: there are race conditions in accesses to these
239 240
  DrawablesInfo drawables;
  ContextsInfo contexts;
241 242

  PrimusInfo():
243 244 245
    adpy_str(getconf(PRIMUS_DISPLAY)),
    libgla_str(getconf(PRIMUS_libGLa)),
    ei(&adpy_str, &libgla_str),
246 247
    sync(atoi(getconf(PRIMUS_SYNC))),
    loglevel(atoi(getconf(PRIMUS_VERBOSE))),
248 249
    dispmethod(atoi(getconf(PRIMUS_UPLOAD))),
    autosleep(atoi(getconf(PRIMUS_SLEEP))),
250
    adpy(XOpenDisplay(adpy_str)),
251 252
    ddpy(XOpenDisplay(NULL)),
    needed_global(dlopen(getconf(PRIMUS_LOAD_GLOBAL), RTLD_LAZY | RTLD_GLOBAL)),
253
    afns(libgla_str),
254 255 256
    dfns(getconf(PRIMUS_libGLd))
  {
    die_if(!adpy, "failed to open secondary X display\n");
257
    die_if(!ddpy, "failed to open main X display\n");
258 259 260 261 262
    die_if(!needed_global, "failed to load PRIMUS_LOAD_GLOBAL\n");
  }
} primus;

// Thread-specific data
263 264 265 266
static __thread struct {
  Display *dpy;
  GLXDrawable drawable, read_drawable;
  void make_current(Display *dpy, GLXDrawable draw, GLXDrawable read)
267
  {
268 269 270
    this->dpy = dpy;
    this->drawable = draw;
    this->read_drawable = read;
271
  }
272
} tsdata;
273 274

// Profiler
275
struct Profiler {
276 277 278
  const char *name;
  const char * const *state_names;

279 280 281 282
  double state_time[6], prev_timestamp, print_timestamp;
  int state, nframes, width, height;

  static double get_timestamp()
283 284 285
  {
    struct timespec tp;
    clock_gettime(CLOCK_MONOTONIC, &tp);
286
    return tp.tv_sec + 1e-9 * tp.tv_nsec;
287
  }
288 289 290 291 292

  Profiler(const char *name, const char * const *state_names):
    name(name),
    state_names(state_names),
    state(0), nframes(0), width(0), height(0)
293
  {
294 295
    memset(state_time, 0, sizeof(state_time));
    prev_timestamp = print_timestamp = get_timestamp();
296
  }
297

298 299
  void tick(bool state_reset = false)
  {
300 301 302 303
    if (primus.loglevel < 2)
      return;
    double timestamp = get_timestamp();
    assert(state_reset || state_names[state]);
304 305
    if (state_reset)
      state = 0;
306 307
    assert(state * sizeof(state_time[0]) < sizeof(state_time));
    state_time[state++] += timestamp - prev_timestamp;
308
    prev_timestamp = timestamp;
309 310 311
    if (state_names[state])
      return;
    nframes++;
312 313
    // check if it's time to print again
    double period = timestamp - print_timestamp; // time since we printed
314
    if (period < 5)
315 316
      return;
    // construct output
317 318
    char buf[128], *cbuf = buf, *end = buf+128;
    for (int i = 0; i < state; i++)
319
      cbuf += snprintf(cbuf, end - cbuf, ", %.1f%% %s", 100 * state_time[i] / period, state_names[i]);
320
    primus_perf("%s: %dx%d, %.1f fps%s\n", name, width, height, nframes / period, buf);
321 322 323
    // start counting again
    print_timestamp = timestamp;
    nframes = 0;
324
    memset(state_time, 0, sizeof(state_time));
325 326 327
  }
};

328 329 330 331 332 333 334 335 336
// Find out the dimensions of the window
static void note_geometry(Display *dpy, Drawable draw, int *width, int *height)
{
  Window root;
  int x, y;
  unsigned bw, d;
  XGetGeometry(dpy, draw, &root, &x, &y, (unsigned *)width, (unsigned *)height, &bw, &d);
}

337 338 339 340 341 342 343 344 345 346
static GLXFBConfig* get_dconfigs(Display *dpy)
{
  static const int attrs[] = {GLX_DOUBLEBUFFER, GL_TRUE, None};
  int ncfg;
  GLXFBConfig *dconfigs = primus.dfns.glXChooseFBConfig(dpy, 0, attrs, &ncfg);
  die_if(!dconfigs, "broken GLX on main X display\n");
  return dconfigs;
}

static bool test_drawpixels_fast(Display *dpy, GLXContext ctx, GLXFBConfig dconfig)
347 348 349
{
  int width = 1920, height = 1080;
  int pbattrs[] = {GLX_PBUFFER_WIDTH, width, GLX_PBUFFER_HEIGHT, height, GLX_PRESERVED_CONTENTS, True, None};
350
  GLXPbuffer pbuffer = primus.dfns.glXCreatePbuffer(dpy, dconfig, pbattrs);
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375
  primus.dfns.glXMakeCurrent(dpy, pbuffer, ctx);
  GLuint pbo;
  primus.dfns.glGenBuffers(1, &pbo);
  primus.dfns.glBindBuffer(GL_PIXEL_UNPACK_BUFFER_EXT, pbo);
  primus.dfns.glBufferData(GL_PIXEL_UNPACK_BUFFER_EXT, width*height*4, NULL, GL_STREAM_DRAW);
  void *pixeldata = malloc(width*height*4);

  double end = 0.2 + Profiler::get_timestamp();
  int iters = 0;
  do {
    primus.dfns.glBufferSubData(GL_PIXEL_UNPACK_BUFFER_EXT, 0, width*height*4, pixeldata);
    primus.dfns.glDrawPixels(width, height, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
    primus.dfns.glXSwapBuffers(dpy, pbuffer);
    iters++;
  } while (end > Profiler::get_timestamp());

  free(pixeldata);
  primus.dfns.glDeleteBuffers(1, &pbo);
  primus.dfns.glXDestroyPbuffer(dpy, pbuffer);

  bool is_fast = iters >= 12;
  primus_perf("upload autodetection: will use %s path (%d iters)\n", is_fast ? "PBO" : "texture", iters);
  return is_fast;
}

376
static void* display_work(void *vd)
377
{
378 379
  GLXDrawable drawable = (GLXDrawable)vd;
  DrawableInfo &di = primus.drawables[drawable];
380 381
  int width, height;
  static const float quad_vertex_coords[]  = {-1, -1, -1, 1, 1, 1, 1, -1};
382 383
  static const float quad_texture_coords[] = { 0,  0,  0, 1, 1, 1, 1,  0};
  GLuint textures[2] = {0}, pbos[2] = {0};
384
  int ctex = 0;
385 386
  static const char *state_names[] = {"wait", "upload", "draw+swap", NULL};
  Profiler profiler("display", state_names);
387
  Display *ddpy = XOpenDisplay(NULL);
388 389 390 391 392
  if (!ddpy) // Chromium sandbox prevents opening new connections
  {
    ddpy = primus.ddpy;
    primus_warn("reusing initial X connection for display thread\n");
  }
393 394 395
  assert(di.kind == di.XWindow || di.kind == di.Window);
  XSelectInput(ddpy, di.window, StructureNotifyMask);
  note_geometry(ddpy, di.window, &width, &height);
396
  di.update_geometry(width, height);
397 398
  GLXFBConfig *dconfigs = get_dconfigs(ddpy);
  GLXContext context = primus.dfns.glXCreateNewContext(ddpy, *dconfigs, GLX_RGBA_TYPE, NULL, True);
399
  die_if(!primus.dfns.glXIsDirect(ddpy, context),
400
	 "failed to acquire direct rendering context for display thread\n");
401
  if (!primus.dispmethod)
402 403
    primus.dispmethod = test_drawpixels_fast(ddpy, context, *dconfigs) ? 2 : 1;
  XFree(dconfigs);
404
  primus.dfns.glXMakeCurrent(ddpy, di.window, context);
405 406 407 408 409 410 411 412 413 414 415 416
  bool use_textures = (primus.dispmethod == 1);
  if (use_textures)
  {
    primus.dfns.glVertexPointer  (2, GL_FLOAT, 0, quad_vertex_coords);
    primus.dfns.glTexCoordPointer(2, GL_FLOAT, 0, quad_texture_coords);
    primus.dfns.glEnableClientState(GL_VERTEX_ARRAY);
    primus.dfns.glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    primus.dfns.glGenTextures(2, textures);
    primus.dfns.glEnable(GL_TEXTURE_2D);
  }
  else
    primus.dfns.glGenBuffers(2, pbos);
417 418
  for (;;)
  {
419
    sem_wait(&di.d.acqsem);
420
    profiler.tick(true);
421
    if (di.d.reinit)
422
    {
423
      if (di.d.reinit == di.SHUTDOWN)
424
      {
425 426 427 428
	if (use_textures)
	  primus.dfns.glDeleteTextures(2, textures);
	else
	  primus.dfns.glDeleteBuffers(2, pbos);
429 430
	primus.dfns.glXMakeCurrent(ddpy, 0, NULL);
	primus.dfns.glXDestroyContext(ddpy, context);
431 432
	if (ddpy != primus.ddpy)
	  XCloseDisplay(ddpy);
433
	sem_post(&di.d.relsem);
434 435
	return NULL;
      }
436
      di.d.reinit = di.NONE;
437 438
      profiler.width = width = di.width;
      profiler.height = height = di.height;
439
      primus.dfns.glViewport(0, 0, width, height);
440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455
      if (use_textures)
      {
	primus.dfns.glBindTexture(GL_TEXTURE_2D, textures[ctex ^ 1]);
	primus.dfns.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	primus.dfns.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
	primus.dfns.glBindTexture(GL_TEXTURE_2D, textures[ctex]);
	primus.dfns.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	primus.dfns.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
      }
      else
      {
	primus.dfns.glBindBuffer(GL_PIXEL_UNPACK_BUFFER_EXT, pbos[ctex ^ 1]);
	primus.dfns.glBufferData(GL_PIXEL_UNPACK_BUFFER_EXT, width*height*4, NULL, GL_STREAM_DRAW);
	primus.dfns.glBindBuffer(GL_PIXEL_UNPACK_BUFFER_EXT, pbos[ctex]);
	primus.dfns.glBufferData(GL_PIXEL_UNPACK_BUFFER_EXT, width*height*4, NULL, GL_STREAM_DRAW);
      }
456
      sem_post(&di.d.relsem);
457 458
      continue;
    }
459 460 461 462
    if (use_textures)
      primus.dfns.glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_BGRA, GL_UNSIGNED_BYTE, di.pixeldata);
    else
      primus.dfns.glBufferSubData(GL_PIXEL_UNPACK_BUFFER_EXT, 0, width*height*4, di.pixeldata);
463
    if (!primus.sync)
464
      sem_post(&di.d.relsem); // Unlock as soon as possible
465
    profiler.tick();
466 467 468 469 470 471 472 473 474 475 476
    if (use_textures)
    {
      primus.dfns.glDrawArrays(GL_QUADS, 0, 4);
      primus.dfns.glBindTexture(GL_TEXTURE_2D, textures[ctex ^= 1]);
    }
    else
    {
      primus.dfns.glDrawPixels(width, height, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
      primus.dfns.glBindBuffer(GL_PIXEL_UNPACK_BUFFER_EXT, pbos[ctex ^= 1]);
    }
    primus.dfns.glXSwapBuffers(ddpy, di.window);
477 478 479 480
    for (int pending = XPending(ddpy); pending > 0; pending--)
    {
      XEvent event;
      XNextEvent(ddpy, &event);
481
      if (event.type == ConfigureNotify)
482
	di.update_geometry(event.xconfigure.width, event.xconfigure.height);
483
    }
484
    if (primus.sync)
485
      sem_post(&di.d.relsem); // Unlock only after drawing
486 487 488 489 490
    profiler.tick();
  }
  return NULL;
}

491
static void* readback_work(void *vd)
492
{
493 494 495
  GLXDrawable drawable = (GLXDrawable)vd;
  DrawableInfo &di = primus.drawables[drawable];
  int width, height;
496 497
  GLuint pbos[2] = {0};
  int cbuf = 0;
498 499
  unsigned sleep_usec = 0;
  static const char *state_names[] = {"app", "sleep", "map", "wait", NULL};
500 501 502
  Profiler profiler("readback", state_names);
  struct timespec tp;
  if (!primus.sync)
503 504 505 506 507 508
    sem_post(&di.d.relsem); // No PBO is mapped initially
  GLXContext context = primus.afns.glXCreateNewContext(primus.adpy, di.fbconfig, GLX_RGBA_TYPE, di.actx, True);
  die_if(!primus.afns.glXIsDirect(primus.adpy, context),
	 "failed to acquire direct rendering context for readback thread\n");
  primus.afns.glXMakeCurrent(primus.adpy, di.pbuffer, context);
  primus.afns.glGenBuffers(2, &pbos[0]);
509
  primus.afns.glReadBuffer(GL_FRONT);
510 511
  for (;;)
  {
512
    sem_wait(&di.r.acqsem);
513
    profiler.tick(true);
514
    if (di.r.reinit)
515 516 517 518
    {
      clock_gettime(CLOCK_REALTIME, &tp);
      tp.tv_sec  += 1;
      // Wait for D worker, if active
519
      if (!primus.sync && sem_timedwait(&di.d.relsem, &tp))
520
      {
521 522
	pthread_cancel(di.d.worker);
	sem_post(&di.d.relsem); // Pretend that D worker completed reinit
523
	primus_warn("timeout waiting for display worker\n");
524
	die_if(di.r.reinit != di.SHUTDOWN, "killed worker on resize\n");
525
      }
526
      di.d.reinit = di.r.reinit;
527 528 529 530
      sem_post(&di.d.acqsem); // Signal D worker to reinit
      sem_wait(&di.d.relsem); // Wait until reinit was completed
      if (!primus.sync)
	sem_post(&di.d.relsem); // Unlock as no PBO is currently mapped
531
      if (di.r.reinit == di.SHUTDOWN)
532 533 534 535
      {
	primus.afns.glBindBuffer(GL_PIXEL_PACK_BUFFER_EXT, pbos[cbuf ^ 1]);
	primus.afns.glUnmapBuffer(GL_PIXEL_PACK_BUFFER_EXT);
	primus.afns.glDeleteBuffers(2, &pbos[0]);
536 537 538
	primus.afns.glXMakeCurrent(primus.adpy, 0, NULL);
	primus.afns.glXDestroyContext(primus.adpy, context);
	sem_post(&di.r.relsem);
539 540
	return NULL;
      }
541
      di.r.reinit = di.NONE;
542 543
      profiler.width = width = di.width;
      profiler.height = height = di.height;
544
      primus.afns.glXMakeCurrent(primus.adpy, di.pbuffer, context);
545
      primus.afns.glBindBuffer(GL_PIXEL_PACK_BUFFER_EXT, pbos[cbuf ^ 1]);
546
      primus.afns.glBufferData(GL_PIXEL_PACK_BUFFER_EXT, width*height*4, NULL, GL_STREAM_READ);
547
      primus.afns.glBindBuffer(GL_PIXEL_PACK_BUFFER_EXT, pbos[cbuf]);
548
      primus.afns.glBufferData(GL_PIXEL_PACK_BUFFER_EXT, width*height*4, NULL, GL_STREAM_READ);
549
    }
550
    primus.afns.glWaitSync(di.sync, 0, GL_TIMEOUT_IGNORED);
551
    primus.afns.glReadPixels(0, 0, width, height, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
552
    if (!primus.sync)
553
      sem_post(&di.r.relsem); // Unblock main thread as soon as possible
554 555
    usleep(sleep_usec);
    profiler.tick();
556 557
    if (primus.sync == 1) // Get the previous framebuffer
      primus.afns.glBindBuffer(GL_PIXEL_PACK_BUFFER_EXT, pbos[cbuf ^ 1]);
558
    double map_time = Profiler::get_timestamp();
559
    GLvoid *pixeldata = primus.afns.glMapBuffer(GL_PIXEL_PACK_BUFFER_EXT, GL_READ_ONLY);
560 561
    map_time = Profiler::get_timestamp() - map_time;
    sleep_usec = (map_time * 1e6 + sleep_usec) * primus.autosleep / 100;
562 563 564
    profiler.tick();
    clock_gettime(CLOCK_REALTIME, &tp);
    tp.tv_sec  += 1;
565
    if (!primus.sync && sem_timedwait(&di.d.relsem, &tp))
566 567 568
      primus_warn("dropping a frame to avoid deadlock\n");
    else
    {
569 570
      di.pixeldata = pixeldata;
      sem_post(&di.d.acqsem);
571 572
      if (primus.sync)
      {
573 574
	sem_wait(&di.d.relsem);
	sem_post(&di.r.relsem); // Unblock main thread only after D::work has completed
575 576 577 578 579 580 581 582 583 584
      }
      cbuf ^= 1;
      primus.afns.glBindBuffer(GL_PIXEL_PACK_BUFFER_EXT, pbos[cbuf]);
    }
    primus.afns.glUnmapBuffer(GL_PIXEL_PACK_BUFFER_EXT);
    profiler.tick();
  }
  return NULL;
}

585
// Find appropriate FBConfigs on adpy for a given Visual on ddpy
586
static GLXFBConfig* match_fbconfig(Display *dpy, XVisualInfo *vis)
587 588 589 590 591 592 593
{
  int ncfg, attrs[] = {
    GLX_DOUBLEBUFFER, 0, GLX_STEREO, 0, GLX_AUX_BUFFERS, 0,
    GLX_RED_SIZE, 0, GLX_GREEN_SIZE, 0, GLX_BLUE_SIZE, 0,
    GLX_ALPHA_SIZE, 0, GLX_DEPTH_SIZE, 0, GLX_STENCIL_SIZE, 0,
    GLX_ACCUM_RED_SIZE, 0, GLX_ACCUM_GREEN_SIZE, 0, GLX_ACCUM_BLUE_SIZE, 0, GLX_ACCUM_ALPHA_SIZE, 0,
    GLX_SAMPLE_BUFFERS, 0, GLX_SAMPLES, 0, None
594
  };
595
  for (int i = 0; attrs[i] != None; i += 2)
596
    primus.dfns.glXGetConfig(dpy, vis, attrs[i], &attrs[i+1]);
597
  return primus.afns.glXChooseFBConfig(primus.adpy, 0, attrs, &ncfg);
598 599 600 601
}

GLXContext glXCreateContext(Display *dpy, XVisualInfo *vis, GLXContext shareList, Bool direct)
{
602
  GLXFBConfig *acfgs = match_fbconfig(dpy, vis);
603
  GLXContext actx = primus.afns.glXCreateNewContext(primus.adpy, *acfgs, GLX_RGBA_TYPE, shareList, direct);
604
  primus.contexts.record(actx, *acfgs, shareList);
605 606 607 608 609 610
  return actx;
}

GLXContext glXCreateNewContext(Display *dpy, GLXFBConfig config, int renderType, GLXContext shareList, Bool direct)
{
  GLXContext actx = primus.afns.glXCreateNewContext(primus.adpy, config, renderType, shareList, direct);
611
  primus.contexts.record(actx, config, shareList);
612 613 614
  return actx;
}

615 616 617 618 619 620 621
GLXContext glXCreateContextAttribsARB(Display *dpy, GLXFBConfig config, GLXContext shareList, Bool direct, const int *attrib_list)
{
  GLXContext actx = primus.afns.glXCreateContextAttribsARB(primus.adpy, config, shareList, direct, attrib_list);
  primus.contexts.record(actx, config, shareList);
  return actx;
}

622 623
void glXDestroyContext(Display *dpy, GLXContext ctx)
{
624
  primus.contexts.erase(ctx);
625 626
  // kludge: reap background tasks when deleting the last context
  // otherwise something will deadlock during unloading the library
627
  if (primus.contexts.empty())
628 629
    for (DrawablesInfo::iterator i = primus.drawables.begin(); i != primus.drawables.end(); i++)
      i->second.reap_workers();
630 631 632
  primus.afns.glXDestroyContext(primus.adpy, ctx);
}

633 634 635 636 637 638
static GLXPbuffer create_pbuffer(DrawableInfo &di)
{
  int pbattrs[] = {GLX_PBUFFER_WIDTH, di.width, GLX_PBUFFER_HEIGHT, di.height, GLX_PRESERVED_CONTENTS, True, None};
  return primus.afns.glXCreatePbuffer(primus.adpy, di.fbconfig, pbattrs);
}

639 640 641 642 643 644 645 646 647 648
// Create or recall backing Pbuffer for the drawable
static GLXPbuffer lookup_pbuffer(Display *dpy, GLXDrawable draw, GLXContext ctx)
{
  if (!draw)
    return 0;
  bool known = primus.drawables.known(draw);
  DrawableInfo &di = primus.drawables[draw];
  if (!known)
  {
    // Drawable is a plain X Window. Get the FBConfig from the context
649 650 651 652 653 654 655 656 657 658
    if (ctx)
      di.fbconfig = primus.contexts[ctx].fbconfig;
    else
    {
      XWindowAttributes attrs;
      die_if(!XGetWindowAttributes(dpy, draw, &attrs), "failed to query attributes");
      int nvis;
      XVisualInfo tmpl = {0}, *vis;
      tmpl.visualid = XVisualIDFromVisual(attrs.visual);
      die_if(!(vis = XGetVisualInfo(dpy, VisualIDMask, &tmpl, &nvis)), "no visuals");
659
      di.fbconfig = *match_fbconfig(dpy, vis);
660 661
      XFree(vis);
    }
662 663 664 665
    di.kind = di.XWindow;
    di.window = draw;
    note_geometry(dpy, draw, &di.width, &di.height);
  }
666
  else if (ctx && di.fbconfig != primus.contexts[ctx].fbconfig)
667
  {
668 669 670 671 672 673 674 675
    if (di.pbuffer)
    {
      primus_warn("recreating incompatible pbuffer\n");
      di.reap_workers();
      primus.afns.glXDestroyPbuffer(primus.adpy, di.pbuffer);
      di.pbuffer = 0;
    }
    di.fbconfig = primus.contexts[ctx].fbconfig;
676
  }
677 678 679
  if (!di.pbuffer)
    di.pbuffer = create_pbuffer(di);
  return di.pbuffer;
680 681 682 683 684
}

Bool glXMakeCurrent(Display *dpy, GLXDrawable drawable, GLXContext ctx)
{
  GLXPbuffer pbuffer = lookup_pbuffer(dpy, drawable, ctx);
685
  tsdata.make_current(dpy, drawable, drawable);
686 687 688 689 690 691 692 693 694
  return primus.afns.glXMakeCurrent(primus.adpy, pbuffer, ctx);
}

Bool glXMakeContextCurrent(Display *dpy, GLXDrawable draw, GLXDrawable read, GLXContext ctx)
{
  if (draw == read)
    return glXMakeCurrent(dpy, draw, ctx);
  GLXPbuffer pbuffer = lookup_pbuffer(dpy, draw, ctx);
  GLXPbuffer pb_read = lookup_pbuffer(dpy, read, ctx);
695
  tsdata.make_current(dpy, draw, read);
696 697 698 699 700
  return primus.afns.glXMakeContextCurrent(primus.adpy, pbuffer, pb_read, ctx);
}

void glXSwapBuffers(Display *dpy, GLXDrawable drawable)
{
701
  XFlush(dpy);
702 703
  assert(primus.drawables.known(drawable));
  DrawableInfo &di = primus.drawables[drawable];
704
  primus.afns.glXSwapBuffers(primus.adpy, di.pbuffer);
705
  if (di.kind == di.Pbuffer || di.kind == di.Pixmap)
706
    return;
707 708 709
  GLXContext ctx = glXGetCurrentContext();
  if (!ctx)
    primus_warn("glXSwapBuffers: no current context\n");
710 711 712
  else if (drawable != tsdata.drawable)
    primus_warn("glXSwapBuffers: drawable not current\n");
  if (di.r.worker && ctx && (!di.actx || primus.contexts[di.actx].sharegroup != primus.contexts[ctx].sharegroup))
713 714 715 716 717 718 719
  {
    primus_warn("glXSwapBuffers: respawning threads after context change\n");
    di.reap_workers();
  }
  if (!di.r.worker)
  {
    // Need to create a sharing context to use GL sync objects
720
    di.actx = ctx;
721 722 723
    di.d.spawn_worker(drawable, display_work);
    di.r.spawn_worker(drawable, readback_work);
  }
724
  // Readback thread needs a sync object to avoid reading an incomplete frame
725 726 727 728
  di.sync = primus.afns.glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
  sem_post(&di.r.acqsem); // Signal the readback worker thread
  sem_wait(&di.r.relsem); // Wait until it has issued glReadBuffer
  primus.afns.glDeleteSync(di.sync);
729 730
  if (di.reinit == di.RESIZE)
  {
731
    __sync_synchronize();
732 733 734 735 736 737 738
    primus.afns.glXDestroyPbuffer(primus.adpy, di.pbuffer);
    di.pbuffer = create_pbuffer(di);
    if (ctx) // FIXME: drawable can be current in other threads
      glXMakeContextCurrent(dpy, tsdata.drawable, tsdata.read_drawable, ctx);
    di.r.reinit = di.reinit;
    di.reinit = di.NONE;
  }
739 740 741 742
}

GLXWindow glXCreateWindow(Display *dpy, GLXFBConfig config, Window win, const int *attribList)
{
743 744 745
  GLXFBConfig *dconfigs = get_dconfigs(dpy);
  GLXWindow glxwin = primus.dfns.glXCreateWindow(dpy, *dconfigs, win, attribList);
  XFree(dconfigs);
746 747 748 749 750 751 752 753
  DrawableInfo &di = primus.drawables[glxwin];
  di.kind = di.Window;
  di.fbconfig = config;
  di.window = win;
  note_geometry(dpy, win, &di.width, &di.height);
  return glxwin;
}

754 755 756 757 758 759 760
DrawableInfo::~DrawableInfo()
{
  reap_workers();
  if (pbuffer)
    primus.afns.glXDestroyPbuffer(primus.adpy, pbuffer);
}

761 762
void glXDestroyWindow(Display *dpy, GLXWindow window)
{
763
  assert(primus.drawables.known(window));
764
  primus.drawables.erase(window);
765
  primus.dfns.glXDestroyWindow(dpy, window);
766 767 768 769
}

GLXPbuffer glXCreatePbuffer(Display *dpy, GLXFBConfig config, const int *attribList)
{
770 771 772
  GLXFBConfig *dconfigs = get_dconfigs(dpy);
  GLXPbuffer pbuffer = primus.dfns.glXCreatePbuffer(dpy, *dconfigs, attribList);
  XFree(dconfigs);
773 774 775
  DrawableInfo &di = primus.drawables[pbuffer];
  di.kind = di.Pbuffer;
  di.fbconfig = config;
776 777 778 779 780
  for (int i = 0; attribList[i] != None; i++)
    if (attribList[i] == GLX_PBUFFER_WIDTH)
      di.width = attribList[i+1];
    else if (attribList[i] == GLX_PBUFFER_HEIGHT)
      di.height = attribList[i+1];
781 782 783 784 785
  return pbuffer;
}

void glXDestroyPbuffer(Display *dpy, GLXPbuffer pbuf)
{
786
  assert(primus.drawables.known(pbuf));
787
  primus.drawables.erase(pbuf);
788
  primus.dfns.glXDestroyPbuffer(dpy, pbuf);
789 790 791 792
}

GLXPixmap glXCreatePixmap(Display *dpy, GLXFBConfig config, Pixmap pixmap, const int *attribList)
{
793 794 795
  GLXFBConfig *dconfigs = get_dconfigs(dpy);
  GLXPixmap glxpix = primus.dfns.glXCreatePixmap(dpy, *dconfigs, pixmap, attribList);
  XFree(dconfigs);
796 797 798 799 800 801 802 803 804
  DrawableInfo &di = primus.drawables[glxpix];
  di.kind = di.Pixmap;
  di.fbconfig = config;
  note_geometry(dpy, pixmap, &di.width, &di.height);
  return glxpix;
}

void glXDestroyPixmap(Display *dpy, GLXPixmap pixmap)
{
805
  assert(primus.drawables.known(pixmap));
806
  primus.drawables.erase(pixmap);
807
  primus.dfns.glXDestroyPixmap(dpy, pixmap);
808 809 810 811
}

GLXPixmap glXCreateGLXPixmap(Display *dpy, XVisualInfo *visual, Pixmap pixmap)
{
812
  GLXPixmap glxpix = primus.dfns.glXCreateGLXPixmap(dpy, visual, pixmap);
813 814 815
  DrawableInfo &di = primus.drawables[glxpix];
  di.kind = di.Pixmap;
  note_geometry(dpy, pixmap, &di.width, &di.height);
816
  GLXFBConfig *acfgs = match_fbconfig(dpy, visual);
817 818 819 820 821 822
  di.fbconfig = *acfgs;
  return glxpix;
}

void glXDestroyGLXPixmap(Display *dpy, GLXPixmap pixmap)
{
823
  glXDestroyPixmap(dpy, pixmap);
824 825
}

826
static XVisualInfo *match_visual(Display *dpy, int attrs[])
827
{
828
  XVisualInfo *vis = glXChooseVisual(dpy, 0, attrs);
829 830 831
  for (int i = 2; attrs[i] != None && vis; i += 2)
  {
    int tmp = attrs[i+1];
832
    primus.dfns.glXGetConfig(dpy, vis, attrs[i], &attrs[i+1]);
833 834 835 836 837 838
    if (tmp != attrs[i+1])
      vis = NULL;
  }
  return vis;
}

839 840
XVisualInfo *glXGetVisualFromFBConfig(Display *dpy, GLXFBConfig config)
{
841 842 843 844 845 846 847 848 849 850 851 852 853
  if (!primus.afns.glXGetVisualFromFBConfig(primus.adpy, config))
    return NULL;
  int i, attrs[] = {
    GLX_RGBA, GLX_DOUBLEBUFFER,
    GLX_RED_SIZE, 0, GLX_GREEN_SIZE, 0, GLX_BLUE_SIZE, 0,
    GLX_ALPHA_SIZE, 0, GLX_DEPTH_SIZE, 0, GLX_STENCIL_SIZE, 0,
    GLX_SAMPLE_BUFFERS, 0, GLX_SAMPLES, 0, None
  };
  for (i = 2; attrs[i] != None; i += 2)
    primus.afns.glXGetFBConfigAttrib(primus.adpy, config, attrs[i], &attrs[i+1]);
  XVisualInfo *vis = NULL;
  for (i -= 2; i >= 0 && !vis; i -= 2)
  {
854
    vis = match_visual(dpy, attrs);
855 856 857
    attrs[i] = None;
  }
  return vis;
858 859
}

860 861
int glXGetFBConfigAttrib(Display *dpy, GLXFBConfig config, int attribute, int *value)
{
862 863
  int r = primus.afns.glXGetFBConfigAttrib(primus.adpy, config, attribute, value);
  if (attribute == GLX_VISUAL_ID && *value)
864
    return primus.dfns.glXGetConfig(dpy, glXGetVisualFromFBConfig(dpy, config), attribute, value);
865
  return r;
866 867
}

868 869
void glXQueryDrawable(Display *dpy, GLXDrawable draw, int attribute, unsigned int *value)
{
870
  primus.afns.glXQueryDrawable(primus.adpy, lookup_pbuffer(dpy, draw, NULL), attribute, value);
871 872 873 874
}

void glXUseXFont(Font font, int first, int count, int list)
{
875
  unsigned long prop;
876 877
  Display *dpy = glXGetCurrentDisplay();
  XFontStruct *fs = XQueryFont(dpy, font);
878
  XGetFontProperty(fs, XA_FONT, &prop);
879
  char *xlfd = XGetAtomName(dpy, prop);
880 881 882 883 884
  Font afont = XLoadFont(primus.adpy, xlfd);
  primus.afns.glXUseXFont(afont, first, count, list);
  XUnloadFont(primus.adpy, afont);
  XFree(xlfd);
  XFreeFontInfo(NULL, fs, 1);
885 886 887 888 889 890 891 892 893
}

GLXContext glXGetCurrentContext(void)
{
  return primus.afns.glXGetCurrentContext();
}

GLXDrawable glXGetCurrentDrawable(void)
{
894
  return tsdata.drawable;
895 896 897 898 899 900 901 902 903 904 905 906
}

void glXWaitGL(void)
{
}

void glXWaitX(void)
{
}

Display *glXGetCurrentDisplay(void)
{
907
  return tsdata.dpy;
908 909 910 911
}

GLXDrawable glXGetCurrentReadDrawable(void)
{
912
  return tsdata.read_drawable;
913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934
}

// Application sees ddpy-side Visuals, but adpy-side FBConfigs and Contexts
XVisualInfo* glXChooseVisual(Display *dpy, int screen, int *attribList)
{
  return primus.dfns.glXChooseVisual(dpy, screen, attribList);
}

int glXGetConfig(Display *dpy, XVisualInfo *visual, int attrib, int *value)
{
  return primus.dfns.glXGetConfig(dpy, visual, attrib, value);
}

// GLX forwarders that reroute to adpy
#define DEF_GLX_PROTO(ret, name, par, ...) \
ret name par \
{ return primus.afns.name(primus.adpy, __VA_ARGS__); }
#include "glx-dpyredir.def"
#undef DEF_GLX_PROTO

// OpenGL forwarders
#define DEF_GLX_PROTO(ret, name, par, ...) \
935 936
void ifunc_##name(void) asm(#name) __attribute__((visibility("default"),ifunc("i" #name))); \
extern "C" { \
937 938
static ret l##name par \
{ return primus.afns.name(__VA_ARGS__); } \
939 940
static void *i##name(void) \
{ return primus.afns.handle ? real_dlsym(primus.afns.handle, #name) : (void*)l##name; } }
941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982
#include "gl-passthru.def"
#undef DEF_GLX_PROTO

// GLX extensions

int glXSwapIntervalSGI(int interval)
{
  return 1; // Indicate failure to set swapinterval
}

__GLXextFuncPtr glXGetProcAddress(const GLubyte *procName)
{
  static const char * const redefined_names[] = {
#define DEF_GLX_PROTO(ret, name, args, ...) #name,
#include "glx-reimpl.def"
#include "glxext-reimpl.def"
#include "glx-dpyredir.def"
#undef  DEF_GLX_PROTO
  };
  static const __GLXextFuncPtr redefined_fns[] = {
#define DEF_GLX_PROTO(ret, name, args, ...) (__GLXextFuncPtr)name,
#include "glx-reimpl.def"
#include "glxext-reimpl.def"
#include "glx-dpyredir.def"
#undef  DEF_GLX_PROTO
  };
  enum {n_redefined = sizeof(redefined_fns) / sizeof(redefined_fns[0])};
  // Non-GLX functions are forwarded to the accelerating libGL
  if (memcmp(procName, "glX", 3))
    return primus.afns.glXGetProcAddress(procName);
  // All GLX functions are either implemented in primus or not available
  for (int i = 0; i < n_redefined; i++)
    if (!strcmp((const char *)procName, redefined_names[i]))
      return redefined_fns[i];
  return NULL;
}

__GLXextFuncPtr glXGetProcAddressARB(const GLubyte *procName)
{
  return glXGetProcAddress(procName);
}

983 984 985 986
static const char glxext_clientside[] = "GLX_ARB_get_proc_address ";
static const char glxext_adpy[] = "GLX_ARB_create_context GLX_ARB_create_context_profile ";
static const char glxext_ddpy[] = "";

987 988
const char *glXGetClientString(Display *dpy, int name)
{
989
  static std::string exts(std::string(glxext_clientside) + glxext_adpy + glxext_ddpy);
990 991 992 993
  switch (name)
  {
    case GLX_VENDOR: return "primus";
    case GLX_VERSION: return "1.4";
994
    case GLX_EXTENSIONS: return exts.c_str();
995 996 997 998
    default: return NULL;
  }
}

999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010
static std::string intersect_exts(const char *set1, const char *set2)
{
  std::string r;
  for (const char *p; *set1; set1 = p + 1)
  {
    p = strchr(set1, ' ');
    if (memmem(set2, strlen(set2), set1, p - set1))
      r.append(set1, p - set1 + 1);
  }
  return r;
}

1011 1012
const char *glXQueryExtensionsString(Display *dpy, int screen)
{
1013 1014 1015
  static std::string exts
    (std::string(glxext_clientside)
     + intersect_exts(glxext_adpy, primus.afns.glXQueryExtensionsString(primus.adpy, 0))
1016
     + intersect_exts(glxext_ddpy, primus.dfns.glXQueryExtensionsString(dpy, 0)));
1017
  return exts.c_str();
1018 1019 1020 1021 1022 1023 1024
}

// OpenGL ABI specifies that anything above OpenGL 1.2 + ARB_multitexture must
// be obtained via glXGetProcAddress, but some applications link against
// extension functions, and Mesa and vendor libraries let them
#ifndef PRIMUS_STRICT
#warning Enabled workarounds for applications demanding more than promised by the OpenGL ABI
1025 1026 1027

// OpenGL extension forwarders
#define P(name) \
1028 1029 1030
void ifunc_##name(void) asm(#name) __attribute__((visibility("default"),ifunc("i" #name))); \
extern "C" { static void *i##name(void) \
{ return primus.afns.handle ? real_dlsym(primus.afns.handle, #name) : NULL; } }
1031 1032
#include "glext-passthru.def"
#undef P
1033
#endif