X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?p=xscreensaver;a=blobdiff_plain;f=hacks%2Fglx%2Fglslideshow.c;h=d735e7bd5e3514010a9f41981587dfe45d4a3aa1;hp=d4ca5a4ca721e6c2b4aefc45e0640b63498483ed;hb=ffd8c0873576a9e3065696a624dce6b766b77062;hpb=9c9d475ff889ed8be02e8ce8c17da28b93278fca diff --git a/hacks/glx/glslideshow.c b/hacks/glx/glslideshow.c index d4ca5a4c..d735e7bd 100644 --- a/hacks/glx/glslideshow.c +++ b/hacks/glx/glslideshow.c @@ -15,6 +15,56 @@ * software for any purpose. It is provided "as is" without express or * implied warranty. * + * TODO: + * + * - Resizing the window makes everything go black forevermore. No idea why. + * + * + * - When a new image is loaded, there is a glitch: animation pauses during + * the period when we're loading the image-to-fade-in. On fast (2GHz) + * machines, this stutter is short but noticable (usually less than half a + * second.) On slower machines, it can be much more pronounced. + * + * In xscreensaver 4.17, I added the new functions fork_load_random_image() + * and fork_screen_to_ximage() to make it possible to do image loading in + * the background, in an attempt to solve this (the idea being to only swap + * in the new image once it has been loaded.) Using those routines, we + * continue animating while the file system is being searched for an image + * file; while that image data is read, parsed, and decompressed; while that + * data is placed on a Pixmap in the X server. + * + * However, two things still happen in the "parent" (glslideshow) process: + * converting that server-side Pixmap to a client-side XImage (XGetImage); + * and converting that XImage to an OpenGL texture (gluBuild2DMipmaps). + * It's possible that some new code would allow us to do the Pixmap-to-XImage + * conversion in the forked process (feed it back upstream through a pipe or + * SHM segment or something); however, it turns out that significant + * parent-process image-loading time is being spent in gluBuild2DMipmaps(). + * + * So, the next step would be to figure out some way to create a texture on + * the other end of the fork that would be usable by the parent process. Is + * that even possible? Is it possible to use a single GLX context in a + * multithreaded way like that? (Or use a second GLX context, but allow the + * two contexts to share data?) + * + * Another question remains: is the stalling happening in the GL/GLX + * libraries, or are we actually seeing a stall on the graphics pipeline? + * If the latter, then no amount of threading would help, because the + * bottleneck is pushing the bits from system memory to the graphics card. + * + * How does Apple do this with their MacOSX slideshow screen saver? Perhaps + * it's easier for them because their OpenGL libraries have thread support + * at a lower level? + * + * + * - Even if the glitch was solved, there's still a bug in the background + * loading of images: as soon as the image comes in, we slap it into place + * in the target quad. This can lead to an image being changed while it is + * still being drawn, if that quad happens to be visible already. Instead, + * when the callback goes off, we should make sure to load it into the + * invisible quad, or if both are visible, we should wait until one goes + * invisible and then load it there (in other words, wait for the next + * fade-out to end.) */ #include @@ -104,6 +154,8 @@ typedef struct { Bool low_fps_p; /* Whether we have compensated for a low frame rate. */ + Bool fork_p; /* threaded image loading; #### still buggy */ + XFontStruct *xfont; GLuint font_dlist; @@ -152,6 +204,23 @@ static argtype vars[] = { ModeSpecOpt slideshow_opts = {countof(opts), opts, countof(vars), vars, NULL}; +static const char * +blurb (void) +{ + static char buf[255]; + time_t now = time ((time_t *) 0); + char *ct = (char *) ctime (&now); + int n = strlen(progname); + if (n > 100) n = 99; + strncpy(buf, progname, n); + buf[n++] = ':'; + buf[n++] = ' '; + strncpy(buf+n, ct+11, 8); + strcpy(buf+n+9, ": "); + return buf; +} + + /* Returns the current time in seconds as a double. */ static double @@ -489,7 +558,7 @@ shrink_image (ModeInfo *mi, XImage *ximage) if (debug_p) fprintf (stderr, "%s: debug: shrinking image %dx%d -> %dx%d\n", - progname, ximage->width, ximage->height, w2, h2); + blurb(), ximage->width, ximage->height, w2, h2); ximage2 = XCreateImage (MI_DISPLAY (mi), mi->xgwa.visual, 32, ZPixmap, 0, 0, @@ -498,7 +567,7 @@ shrink_image (ModeInfo *mi, XImage *ximage) if (!ximage2->data) { fprintf (stderr, "%s: out of memory (scaling %dx%d image to %dx%d)\n", - progname, ximage->width, ximage->height, w2, h2); + blurb(), ximage->width, ximage->height, w2, h2); exit (1); } for (y = 0; y < h2; y++) @@ -514,16 +583,17 @@ shrink_image (ModeInfo *mi, XImage *ximage) /* Load a new image into a texture for the given quad. */ static void -load_quad (ModeInfo *mi, gls_quad *q) +load_quad_1 (ModeInfo *mi, gls_quad *q, XImage *ximage, + const char *filename, double start_time, double cvt_time) { slideshow_state *ss = &sss[MI_SCREEN(mi)]; - XImage *ximage; int status; int max_reduction = 7; int err_count = 0; int wire = MI_IS_WIREFRAME(mi); + double load_time=0, mipmap_time=0; /* for debugging messages */ - if (q->state != DEAD) abort(); + /* if (q->state != DEAD) abort(); */ /* Figure out which texid is currently in use, and pick the other one. */ @@ -545,16 +615,11 @@ load_quad (ModeInfo *mi, gls_quad *q) ss->current_texid = tid; } - if (debug_p) - fprintf (stderr, "%s: debug: loading image %d (%dx%d)\n", - progname, q->texid, mi->xgwa.width, mi->xgwa.height); - if (wire) goto DONE; if (q->title) free (q->title); - q->title = 0; - ximage = screen_to_ximage (mi->xgwa.screen, mi->window, &q->title); + q->title = (filename ? strdup (filename) : 0); if (q->title) /* strip filename to part after last /. */ { @@ -563,8 +628,11 @@ load_quad (ModeInfo *mi, gls_quad *q) } if (debug_p) - fprintf (stderr, "%s: debug: loaded image %d (%s)\n", - progname, q->texid, (q->title ? q->title : "(null)")); + { + fprintf (stderr, "%s: debug: loaded image %d: \"%s\"\n", + blurb(), q->texid, (q->title ? q->title : "(null)")); + load_time = double_time(); + } glBindTexture (GL_TEXTURE_2D, q->texid); glPixelStorei (GL_UNPACK_ALIGNMENT, 1); @@ -607,17 +675,17 @@ load_quad (ModeInfo *mi, gls_quad *q) "%s: GLU said: \"%s\".\n" "%s: probably this means " "\"your video card is worthless and weak\"?\n\n", - progname, MI_WIDTH(mi), MI_HEIGHT(mi), + blurb(), MI_WIDTH(mi), MI_HEIGHT(mi), ximage->width, ximage->height, - progname, s, - progname); + blurb(), s, + blurb()); exit (1); } else { if (debug_p) fprintf (stderr, "%s: debug: mipmap error (%dx%d): %s\n", - progname, ximage->width, ximage->height, s); + blurb(), ximage->width, ximage->height, s); shrink_image (mi, ximage); goto AGAIN; } @@ -630,6 +698,24 @@ load_quad (ModeInfo *mi, gls_quad *q) ximage->data = 0; XDestroyImage(ximage); + if (debug_p) + { + fprintf (stderr, "%s: debug: mipmapped image %d: %dx%d\n", + blurb(), q->texid, mi->xgwa.width, mi->xgwa.height); + mipmap_time = double_time(); + } + + if (cvt_time == 0) + cvt_time = load_time; + if (debug_p) + fprintf (stderr, + "%s: debug: load time elapsed: %.2f + %.2f + %.2f = %.2f sec\n", + blurb(), + cvt_time - start_time, + load_time - cvt_time, + mipmap_time - load_time, + mipmap_time - start_time); + DONE: /* Re-set "now" so that time spent loading the image file does not count @@ -644,6 +730,67 @@ load_quad (ModeInfo *mi, gls_quad *q) } +static void slideshow_load_cb (Screen *, Window, XImage *, + const char *filename, void *closure, + double cvt_time); + +typedef struct { + ModeInfo *mi; + gls_quad *q; + double start_time; +} img_load_closure; + + +/* Load a new image into a texture for the given quad. + */ +static void +load_quad (ModeInfo *mi, gls_quad *q) +{ + slideshow_state *ss = &sss[MI_SCREEN(mi)]; + img_load_closure *data; + + if (debug_p) + fprintf (stderr, "%s: debug: loading image %d: %dx%d\n", + blurb(), q->texid, mi->xgwa.width, mi->xgwa.height); + + if (q->state != DEAD) abort(); + if (q->title) free (q->title); + q->title = 0; + + if (MI_IS_WIREFRAME(mi)) + return; + + data = (img_load_closure *) calloc (1, sizeof(*data)); + data->mi = mi; + data->q = q; + data->start_time = double_time(); + + if (ss->fork_p) + { + fork_screen_to_ximage (mi->xgwa.screen, mi->window, + slideshow_load_cb, data); + } + else + { + char *title = 0; + XImage *ximage = screen_to_ximage (mi->xgwa.screen, mi->window, &title); + slideshow_load_cb (mi->xgwa.screen, mi->window, ximage, title, data, 0); + } +} + + +static void +slideshow_load_cb (Screen *screen, Window window, XImage *ximage, + const char *filename, void *closure, double cvt_time) +{ + img_load_closure *data = (img_load_closure *) closure; + load_quad_1 (data->mi, data->q, ximage, filename, + data->start_time, cvt_time); + memset (data, 0, sizeof (*data)); + free (data); +} + + void reshape_slideshow (ModeInfo *mi, int width, int height) { @@ -682,7 +829,7 @@ glslideshow_handle_event (ModeInfo *mi, XEvent *event) event->xany.type == VisibilityNotify) { if (debug_p) - fprintf (stderr, "%s: debug: exposure\n", progname); + fprintf (stderr, "%s: debug: exposure\n", blurb()); ss->redisplay_needed_p = True; return True; } @@ -719,6 +866,26 @@ sanity_check (ModeInfo *mi) } +/* Kludge to add "-v" to invocation of "xscreensaver-getimage" in -debug mode + */ +static void +hack_resources (void) +{ +#if 0 + char *res = "desktopGrabber"; + char *val = get_string_resource (res, "DesktopGrabber"); + char buf1[255]; + char buf2[255]; + XrmValue value; + sprintf (buf1, "%.100s.%.100s", progclass, res); + sprintf (buf2, "%.200s -v", val); + value.addr = buf2; + value.size = strlen(buf2); + XrmPutResource (&db, buf1, "String", &value); +#endif +} + + void init_slideshow (ModeInfo *mi) { @@ -740,11 +907,15 @@ init_slideshow (ModeInfo *mi) MI_CLEARWINDOW(mi); } + if (debug_p) + fprintf (stderr, "%s: debug: pan: %d; fade: %d; img: %d; zoom: %d%%\n", + blurb(), pan_seconds, fade_seconds, image_seconds, zoom); + sanity_check(mi); if (debug_p) fprintf (stderr, "%s: debug: pan: %d; fade: %d; img: %d; zoom: %d%%\n", - progname, pan_seconds, fade_seconds, image_seconds, zoom); + blurb(), pan_seconds, fade_seconds, image_seconds, zoom); if (! wire) { @@ -779,10 +950,16 @@ init_slideshow (ModeInfo *mi) q->state = DEAD; } + if (debug_p) + hack_resources(); + load_quad (mi, &ss->quads[0]); ss->quads[0].state = IN; ss->redisplay_needed_p = True; + + ss->fork_p = 0; /* #### buggy */ + } @@ -823,7 +1000,7 @@ ponder_state_change (ModeInfo *mi) if (debug_p) fprintf (stderr, "%s: debug: %s %3d frames %2d sec %4.1f fps\n", - progname, which, frames, secs, fps); + blurb(), which, frames, secs, fps); if (fps < fps_cutoff && !ss->low_fps_p) /* oops, this computer sucks! */ @@ -831,9 +1008,8 @@ ponder_state_change (ModeInfo *mi) int i; fprintf (stderr, - "%s: frame rate is only %.1f! " - "Turning off pan/fade to compensate...\n", - progname, fps); + "%s: only %.1f fps! Turning off pan/fade to compensate...\n", + blurb(), fps); zoom = 100; fade_seconds = 0; ss->low_fps_p = True; @@ -961,7 +1137,7 @@ draw_slideshow (ModeInfo *mi) if (!ss->redisplay_needed_p) return; else if (debug_p && zoom == 100) - fprintf (stderr, "%s: debug: drawing (%d)\n", progname, + fprintf (stderr, "%s: debug: drawing (%d)\n", blurb(), (int) (ss->now - ss->dawn_of_time)); draw_quads (mi);