I'll try to clarify a few points below since I didn't do a good job the first time, sorry for that.

On 11-04-04 07:10 PM, DRC wrote:
On 4/4/11 12:15 PM, Nathan Kidd wrote:
b) M7 copies a GLXPixmap to only part of a destination window. This
gives wrong results; you'll get black pixels from the freshly
initialized pbuffer overwriting areas of the window not included in the
copy, because current XCopyArea only knows how to push a full window.
The PutImage path is also used to fix this case.

I don't understand why.  The glCopyPixels() command in XCopyArea()
should be copying only the range of pixels specified by src_x, src_y,
dst_x, dst_y, w, and h.

glXMakeCurrent creates a pbuffer the size of the destination window, glCopyPixels only writes to part of that pbuffer, but the glFinish pushes the whole pbuffer down to the window, including parts of the pbuffer that weren't touched by the glCopyPixels. You end up with black (uninitialized) pbuffer bits in the area of the window that shouldn't have been touched by the XCopyArea.

When run with VGL the attached glxpixmappartial.c demonstrates.

c) M7 does MakeCurrent(0,0) before calling XCopyArea, also not supported
by the current XCopyArea hook.  This is worked around by creating a new
temporary context if necessary.

This should really be implemented by using (possibly extending) the
tempctx class, IMO.

Yes, I looked at it briefly and IIRC it needs just a few modifications to fit. In fact the PutImage path is roughly mirroring what fbx does. However, I didn't try to integrate them because by the time I was ready to polish the proof-of-concept I was sold on pushing the GLXPixmap to 2D Xserver, the meaning of which I'll try to explain better below.

Since this is the middle of the X stream I can imagine that this change exposes us to potential problems with multi-threaded apps wanting to have a context current in a different thread. Of course there's always this risk when interposing, but when tmpctx is used currently it is directly from a GL call, not an X call.

d) M7 destroys the GLXPixmap before calling XCopyArea on the associated
> Pixmap; a questionable practice probably left over from the days when
> you could do 2D X drawing over a GLX drawable without problem. This is
> worked around by deferring GLXPixmap deletion till Pixmap deletion. (My
> NVIDIA driver + X.org gives a race when trying to do the XCopyArea, a
> race that M7 always seems to win when run on the console but my test app
> can occasionally loose, and VGL always seems to lose.)

You probably understand this better than me; do we risk "leaking" video memory with this approach? If the app calls glXDestroyPixmap but never calls XFreePixmap how will libGL know the pbuffer isn't needed any more? Of course we could just say "Bad app, don't do that", but I can imagine this case being common because the app really isn't that bad; the Pixmap will be implicitly freed when you close the X connection (but we can't hook that implicit free).

That's the context for the following patches.  However, I'm mostly
posting them for discussion.  I don't really like how ugly XCopyArea is
getting. I think it is better, cleaner, and at least as performant to
just push GLXPixmaps to the 2D X server. (In fact, I think it should
generally be faster, especially since PutImage will be slow for those
not using an X Proxy that compresses.) Also XCopyArea and XCopyPlane
will just work without intervention.  As it is we don't support
XCopyPlane at all.


Comments and discussion most welcome.  If there are no issues I've
missed, I plan to start down the push-glxpixmap-to-2d-xserver path soon.

Regardless of how ugly XCopyArea() may or may not be, I don't know of
another way to implement it and still support X servers that lack GLX.

Sorry, by "push GLXPixmaps to the 2D X server" I mean push the rendered pixels, just like we do for a GLX Window. In other words _doGLreadback won't just check winh.findpb(drawable, pbw) but also check if the current drawable is a GLXPixmap. This requires a few modifications to pbwin and to fbx which currently only know how to push a window to the desktop, but I believe the code change will be smaller and introduce less risk than the current bunch of patches.

My real question is what was the rationale behind the decision to not push GLXPixmaps to the 2D X server? Is there more than the idea "hey, they can only be seen by XCopyArea/XCopyPlane, if we hook those functions we don't need to bother doing readback on them?".

What I mean about "it should be generally faster" is if GLXPixmap pixels get pushed via the pbwin readback path then the pixels will be pushed efficiently for every transport type. A direct PutImage is only efficient for X Proxies that compress PutImage anyway (e.g. VGL transport will be slower with PutImage).

I'm not thinking about push-glxpixmap-pixels-to-2d-xserver from a "fix the general case for all apps" standpoint (I *did* read your earlier mails :) but rather from a "fix this specific app with least risk to other apps" perspective.

And yes, as you said, in this day and age apps have no business using GLXPixmaps for rendering; yet here we are in 2011 and the latest version of a popular app is doing just that. *shrug*

I realize that your company doesn't care about TurboVNC, but that's
still the most popular X server on which VirtualGL runs, so I cannot
accept any solution that removes functionality from that environment.

I certainly hope OpenText never looses sight of the fact that is it not just politic but also the right thing to work for what's good for everyone.

As far as rrfakerut, it is what it is.  I don't think it's fair to call
it "half baked".  It does require a bit of expertise to use, but it is
the only way I currently have to try to guarantee that a new patch
doesn't break something else.

Sorry, I meant *my changes* to rrfakerut were half-baked. Mathematica 7 is available as an eval if you ask, but tied to a specific machine. I'll try to finish up unit tests for each of these issues.

-Nathan

/*
 * A demonstration of what happens when you XCopyArea to less than the
 * full destination window.
 *
 * [email protected] April 2011
 */


#include <GL/gl.h>
#define GLX_GLXEXT_PROTOTYPES
#include <GL/glx.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


static GLXContext ctx;
static XVisualInfo *visinfo;
static GC gc;



static Window make_rgb_window( Display *dpy,
				  unsigned int width, unsigned int height )
{
   const int sbAttrib[] = { GLX_RGBA,
                            GLX_RED_SIZE, 1,
                            GLX_GREEN_SIZE, 1,
                            GLX_BLUE_SIZE, 1,
                            None };
   const int dbAttrib[] = { GLX_RGBA,
                            GLX_RED_SIZE, 1,
                            GLX_GREEN_SIZE, 1,
                            GLX_BLUE_SIZE, 1,
                            GLX_DOUBLEBUFFER,
                            None };
   int scrnum;
   XSetWindowAttributes attr;
   unsigned long mask;
   Window root;
   Window win;

   scrnum = DefaultScreen( dpy );
   root = RootWindow( dpy, scrnum );

   visinfo = glXChooseVisual( dpy, scrnum, (int *) sbAttrib );
   if (!visinfo) {
      visinfo = glXChooseVisual( dpy, scrnum, (int *) dbAttrib );
      if (!visinfo) {
         printf("Error: couldn't get an RGB visual\n");
         exit(1);
      }
   }

   /* window attributes */
   attr.background_pixel = WhitePixel(dpy,scrnum);
   attr.border_pixel = WhitePixel(dpy,scrnum);
   /* TODO: share root colormap if possible */
   attr.colormap = XCreateColormap( dpy, root, visinfo->visual, AllocNone);
   attr.event_mask = StructureNotifyMask | ExposureMask;
   mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;

   win = XCreateWindow( dpy, root, 0, 0, width, height,
		        0, visinfo->depth, InputOutput,
		        visinfo->visual, mask, &attr );

   /* make an X GC so we can do XCopyArea later */
   gc = XCreateGC( dpy, win, 0, NULL );

   /* need indirect context */
   ctx = glXCreateContext( dpy, visinfo, NULL, False );
   if (!ctx) {
      printf("Error: glXCreateContext failed\n");
      exit(-1);
   }

   printf("Direct rendering: %s\n", glXIsDirect(dpy, ctx) ? "Yes" : "No");

   return win;
}

static GLXPixmap make_pixmap( Display *dpy, Window win,
			       unsigned int width, unsigned int height,
                               Pixmap *pixmap)
{
   Pixmap pm, pm2;
   GLXPixmap glxpm;
   XWindowAttributes attr;

   pm = XCreatePixmap( dpy, win, width, height, visinfo->depth );
   pm2 = XCreatePixmap( dpy, win, width, height, visinfo->depth );
   if (!pm | !pm2) {
      printf("Error: XCreatePixmap failed\n");
      exit(-1);
   }

   XGetWindowAttributes( dpy, win, &attr );

   /*
    * IMPORTANT:
    *   Use the glXCreateGLXPixmapMESA funtion when using Mesa because
    *   Mesa needs to know the colormap associated with a pixmap in order
    *   to render correctly.  This is because Mesa allows RGB rendering
    *   into any kind of visual, not just TrueColor or DirectColor.
    */
#ifdef GLX_MESA_pixmap_colormap
   if (strstr(glXQueryExtensionsString(dpy, 0), "GLX_MESA_pixmap_colormap")) {
      /* stand-alone Mesa, specify the colormap */
      PFNGLXCREATEGLXPIXMAPMESAPROC glXCreateGLXPixmapMESA_func =
         (PFNGLXCREATEGLXPIXMAPMESAPROC)
         glXGetProcAddressARB((GLubyte *) "glXCreateGLXPixmapMESA");
      glxpm = glXCreateGLXPixmapMESA_func( dpy, visinfo, pm, attr.colormap );
   }
   else {
      glxpm = glXCreateGLXPixmap( dpy, visinfo, pm );
   }
#else
   /* This will work with Mesa too if the visual is TrueColor or DirectColor */
   glxpm = glXCreateGLXPixmap( dpy, visinfo, pm );
#endif

   if (!glxpm) {
      printf("Error: GLXCreateGLXPixmap failed\n");
      exit(-1);
   }

   pixmap[0] = pm;
   pixmap[1] = pm2;

   return glxpm;
}



int main( int argc, char *argv[] )
{
   Display *dpy;
   Window win;
   Pixmap pm[2];
   GLXPixmap glxpm;
   XTextProperty windowname;
   char *windowtext = "Dest Window";
   XStringListToTextProperty(&windowtext, 1, &windowname);

   dpy = XOpenDisplay(NULL);

   win = make_rgb_window( dpy, 300, 300 );
   glxpm = make_pixmap( dpy, win, 300, 300, &pm );

   XSync(dpy, False);
   XMapWindow( dpy, win );
   XSetWMName(dpy, win, &windowname);
 

   glXMakeCurrent( dpy, glxpm, ctx );
   printf("GL_RENDERER = %s\n", (char *) glGetString(GL_RENDERER));

   /* Render an image into the pixmap */
   glShadeModel( GL_FLAT );
   glClearColor( 0.5, 0.5, 0.5, 1.0 );
   glClear( GL_COLOR_BUFFER_BIT );
   glViewport( 0, 0, 300, 300 );
   glOrtho( -1.0, 1.0, -1.0, 1.0, -1.0, 1.0 );
   glColor3f( 0.0, 1.0, 1.0 );
   glRectf( -0.75, -0.75, 0.75, 0.75 );
   glFlush();
   glXWaitGL();

   XSync(dpy, False);

printf("we see plain white window\n");
sleep(5);
   XCopyArea( dpy, pm[0], win, gc, 0, 0, 200, 200, 50, 50 );
printf("parts that shouldn't be affected are now black\n");
   XSync(dpy, False);
   printf("done\n");
   fflush(stdout);
   while (1) { sleep(1);}

   return 0;
}
------------------------------------------------------------------------------
Xperia(TM) PLAY
It's a major breakthrough. An authentic gaming
smartphone on the nation's most reliable network.
And it wants your games.
http://p.sf.net/sfu/verizon-sfdev
_______________________________________________
VirtualGL-Users mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/virtualgl-users

Reply via email to