#include #include #include #include #include #include #include #include #include #include "Common.h" #define FOURCC_I420 0x30323449U #define FOURCC_IYUV 0x56555949U using namespace IUI; using namespace std; static bool use_shared = true; static bool use_tfp = true; static bool use_xv = false; static bool want_opengl = true; void (*glXBindTexImageEXT)(Display *dpy, GLXDrawable drawable, int buffer, const int *attrib_list); void (*glXReleaseTexImageEXT)(Display *dpy, GLXDrawable drawable, int buffer); static struct s_x_shm_returns { int major, minor; Bool pixmaps; } x_shm_returns; void dump_xv_adaptor_infos(XvAdaptorInfo *infos, unsigned int ninfos) { for(int i = 0; i < ninfos; i++) { printf("Adaptor %02d: %s\n", i, (infos->name != NULL)?infos->name:""); printf("\tPort: %02d Type: %d\n", infos->base_id, infos->type); printf("\tSupported Formats: %d\n", infos->num_formats); for(int j = 0; j < infos->num_formats; j++) { printf("\t\tDepth: %d, Visual: %d\n", infos->formats[j].depth, infos->formats[j].visual_id); } break; infos--; } } void dump_xv_imgfmt_infos(XvImageFormatValues *imgfmts, unsigned int nimgfmts) { for(unsigned int i = 0; i < nimgfmts; i++) { printf("Format 0x%08x:\n", imgfmts[i].id); printf("\tType: %s\n", imgfmts[i].type == XvRGB ? "RGB" : (imgfmts[i].type == XvYUV)?"YUV":"Unknown"); } } int main(int argc, char **argv) { XvPortID a_port; GLXFBConfig *fbconfig; struct SDL_SysWMinfo wminfo; SDLmm::Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_NOPARACHUTE); for(int i = 1; i < argc; i++) { if(strncmp("-noshm", argv[i], 7) == 0) use_shared = false; if(strncmp("-notfp", argv[i], 7) == 0) use_tfp = false; if(strncmp("-xv", argv[i], 7) == 0) use_xv = true; if(strncmp("-nogl", argv[i], 7) == 0) want_opengl = false; } if(use_xv && !use_tfp) { printf("You cannot mix -notfp and -xv\n"); return 1; } if(want_opengl) { SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); } SetRes(1280, 800, want_opengl); SDL_VERSION(&wminfo.version); SDL_GetWMInfo(&wminfo); printf("Pointer to Display: %p\n", wminfo.info.x11.gfxdisplay); /* Query Xv extension to verify we have an Xv adaptor. If not, force Xv off. */ unsigned nadaptors; int nimgfmts; XvAdaptorInfo *adaptor_infos; XvImageFormatValues *f_values, *desired = NULL; XvQueryAdaptors(wminfo.info.x11.gfxdisplay, wminfo.info.x11.window, &nadaptors, &adaptor_infos); printf("Number of Xv Adaptors: %d\n", nadaptors); if(nadaptors < 1) { printf("Cannot use Xv!\n"); use_xv = false; } else { dump_xv_adaptor_infos(adaptor_infos, nadaptors); a_port = adaptor_infos->base_id; /* XXX: Just bind to the first adaptor port */ } XvFreeAdaptorInfo(adaptor_infos); adaptor_infos = NULL; /* Next, gather the supported image formats. */ f_values = XvListImageFormats(wminfo.info.x11.gfxdisplay, a_port, &nimgfmts); if(nimgfmts < 1) { printf("Cannot use Xv - No image formats!\n"); use_xv = false; } else { dump_xv_imgfmt_infos(f_values, nimgfmts); for(int i = 0; i < nimgfmts; i++) /* IYUV and I420 are identical. */ if((f_values[i].id == FOURCC_I420) || (f_values[i].id == FOURCC_IYUV)) { printf("Found I420 at %d\n", i); desired = &f_values[i]; break; } } if(desired == NULL) { printf("Cannot use Xv - Could not find I420 format!\n"); use_xv = false; } bzero(&x_shm_returns, sizeof(x_shm_returns)); XShmQueryVersion(wminfo.info.x11.gfxdisplay, &x_shm_returns.major, &x_shm_returns.minor, &x_shm_returns.pixmaps); printf("X SHM Info: %d.%d pixmaps? %s\n", x_shm_returns.major, x_shm_returns.minor, x_shm_returns.pixmaps == True ? "Y":"N"); if(want_opengl) { glEnable(GL_BLEND); glEnable(GL_DEPTH_TEST); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); glTranslatef(0.f, 0.f, -1.f); fbconfig = GetSuitableFBConfig(wminfo, !use_xv); printf("FBConfig pointer: %p\n", fbconfig); } /* Use a 1920x1080 surface. */ XShmSegmentInfo shm_seg_info; XImage *s_image = NULL; /* For normal texture_from_pixmap */ XvImage *s_xvimage = NULL; /* For Xv tests */ #define IMG_WIDTH 1920 #define IMG_HEIGHT 1080 bzero(&shm_seg_info, sizeof(shm_seg_info)); /* Allocate the SHM region. */ if(use_xv) { /* For YUV, allocate enough space for an 8-bit Y plane, and 2x2 subsampled U and V planes */ shm_seg_info.shmid = shmget(IPC_PRIVATE, 3 * (IMG_WIDTH*IMG_HEIGHT >> 1), IPC_CREAT|0777); } else { /* For RGBA, allocate a full RGBA surface. */ shm_seg_info.shmid = shmget(IPC_PRIVATE, 4*IMG_WIDTH*IMG_HEIGHT, IPC_CREAT|0777); } shm_seg_info.shmaddr = (char*)shmat(shm_seg_info.shmid, NULL, 0); shm_seg_info.readOnly = False; printf("SHM Address Pointer: %p\n", shm_seg_info.shmaddr); /* Build a SDL_Surface that targets the SHM region. */ SDL_Surface *sdl_s = NULL; if(!use_xv) sdl_s = SDL_CreateRGBSurfaceFrom(shm_seg_info.shmaddr, IMG_WIDTH, IMG_HEIGHT, 32, IMG_WIDTH*4, 0x00ff000000, 0x0000ff00, 0x000000ff, 0xff000000); /* Build a TSharedSurface that targets the SHM region, using the SDL_Surface that was created. */ TSharedSurface *s = NULL; char *p = NULL; if(!use_xv) { s = new TSharedSurface(sdl_s); p = (char*)s->pixels(); } else { p = shm_seg_info.shmaddr; } GC xgc; Pixmap s_pixmap; if(use_tfp) { /* Create an XImage object that targets the SHM region. X11 uses this as the source for Pixmap writes. */ if(use_shared) { if(!use_xv) { s_image = XShmCreateImage(wminfo.info.x11.gfxdisplay, glXGetVisualFromFBConfig( wminfo.info.x11.gfxdisplay, *fbconfig)->visual, 32, ZPixmap, NULL, &shm_seg_info, IMG_WIDTH, IMG_HEIGHT); s_image->data = shm_seg_info.shmaddr; printf("XImage Pointer: %p\n", s_image); } else { int r = XvGrabPort(wminfo.info.x11.gfxdisplay, a_port, CurrentTime); s_xvimage = XvShmCreateImage(wminfo.info.x11.gfxdisplay, a_port, desired->id, NULL, IMG_WIDTH, IMG_HEIGHT, &shm_seg_info); s_xvimage->data = shm_seg_info.shmaddr; printf("XvImage Pointer: %p (data: %p), Grab return: %d\n", s_xvimage, s_xvimage->data, r); } } else { if(!use_xv) { s_image = XCreateImage(wminfo.info.x11.gfxdisplay, glXGetVisualFromFBConfig( wminfo.info.x11.gfxdisplay, *fbconfig)->visual, 32, ZPixmap, 0, p, IMG_WIDTH, IMG_HEIGHT, 32, IMG_WIDTH*4); printf("XImage Pointer: %p\n", s_image); } else { int r = XvGrabPort(wminfo.info.x11.gfxdisplay, a_port, CurrentTime); s_xvimage = XvCreateImage(wminfo.info.x11.gfxdisplay, a_port, desired->id, p, IMG_WIDTH, IMG_HEIGHT); printf("XvImage Pointer: %p (data: %p), Grab return: %d\n", s_xvimage, s_xvimage->data, r); } } /* Allocate an off-screen Pixmap of 1920x1080 in the X11 server. */ s_pixmap = XCreatePixmap(wminfo.info.x11.gfxdisplay, wminfo.info.x11.window, IMG_WIDTH, IMG_HEIGHT, use_xv?24:32); printf("Pixmap Pointer: %p\n", s_pixmap); /* Create a NO-OP GC. */ xgc = XCreateGC(wminfo.info.x11.gfxdisplay, s_pixmap, 0, NULL); if(want_opengl) { /* Get the GLX function pointers for GLX_EXT_texture_from_pixmap extension. */ glXBindTexImageEXT = (void (*)(Display*, GLXDrawable, int, const int*))glXGetProcAddress((GLubyte*)"glXBindTexImageEXT"); glXReleaseTexImageEXT = (void (*)(Display*, GLXDrawable, int))glXGetProcAddress((GLubyte*)"glXReleaseTexImageEXT"); } } /* Texture setup. */ GLuint tex; GLXPixmap glxp; const int glx_pixmap_attrs[] = { GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT, GLX_TEXTURE_FORMAT_EXT, use_xv?GLX_TEXTURE_FORMAT_RGB_EXT:GLX_TEXTURE_FORMAT_RGBA_EXT, None }; if(want_opengl) { glEnable(GL_TEXTURE_2D); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glGenTextures(1, &tex); /* Create a GLX Pixmap handle to reference the X11 Pixmap in video memory for OpenGL. */ if(use_tfp) { glxp = glXCreatePixmap(wminfo.info.x11.gfxdisplay, *fbconfig, s_pixmap, glx_pixmap_attrs); } glBindTexture(GL_TEXTURE_2D, tex); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } if(use_tfp) { /* Bind the GLX Pixmap to a Texture, connecting this texture handle to the X11 Pixmap. */ if(want_opengl) glXBindTexImageEXT(wminfo.info.x11.gfxdisplay, glxp, GLX_FRONT_LEFT_EXT, NULL); if(use_shared) { /* Tell the X11 server to attach to the SHM region. */ XShmAttach(wminfo.info.x11.gfxdisplay, &shm_seg_info); /* Tell the shm region to clean up after close. */ shmctl(shm_seg_info.shmid, IPC_RMID, NULL); } } float color = 0.2f; unsigned int clr = 0xff000000; struct timespec ts_start, ts_stop; for(int c = 0; c != ~0U; c++) { int r; double timer_value; if(want_opengl) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); clock_gettime(CLOCK_MONOTONIC, &ts_start); /* Transfer the data from the XImage to the Pixmap (the texture buffer) */ printf("Putting..."); if(use_tfp) { if(!use_xv) { if(use_shared) { r = XShmPutImage(wminfo.info.x11.gfxdisplay, s_pixmap, xgc, s_image, 0, 0, 0, 0, IMG_WIDTH, IMG_HEIGHT, True); } else { r = XPutImage(wminfo.info.x11.gfxdisplay, s_pixmap, xgc, s_image, 0, 0, 0, 0, IMG_WIDTH, IMG_HEIGHT); } } else { XvStopVideo(wminfo.info.x11.gfxdisplay, a_port, (want_opengl)?s_pixmap:wminfo.info.x11.window); if(use_shared) { r = XvShmPutImage(wminfo.info.x11.gfxdisplay, a_port, (want_opengl)?s_pixmap:wminfo.info.x11.window, xgc, s_xvimage, 0, 0, IMG_WIDTH, IMG_HEIGHT, 0, 0, IMG_WIDTH, IMG_HEIGHT, True); } else { r = XvPutImage(wminfo.info.x11.gfxdisplay, a_port, (want_opengl)?s_pixmap:wminfo.info.x11.window, xgc, s_xvimage, 0, 0, IMG_WIDTH, IMG_HEIGHT, 0, 0, IMG_WIDTH, IMG_HEIGHT); } } printf("%d\n", r); /* Force X11 calls to complete. */ //XFlush(wminfo.info.x11.gfxdisplay); } else { if(want_opengl) glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, IMG_WIDTH, IMG_HEIGHT, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, s->pixels()); } printf("Blitting...%d\n", r); if(use_shared) { XEvent ev; while(XCheckTypedEvent(wminfo.info.x11.gfxdisplay, XShmGetEventBase(wminfo.info.x11.gfxdisplay), &ev) == False) { usleep(1000); } printf("Got XSHM Event\n"); } //usleep(500000); /* Blit a rectangle to the screen containing the pixmap texture. */ if(want_opengl) { glBegin(GL_QUADS); glColor3f(1.f, 1.f, 1.f); color += 0.02f; glTexCoord2d(0.,0.); glVertex2f(0.f, 0.f); glTexCoord2d(1.,0.); glVertex2f(0.f, 720.f); glTexCoord2d(1.,1.); glVertex2f(1280.f, 720.f); glTexCoord2d(0.,1.); glVertex2f(1280.f, 0.f); glEnd(); } clock_gettime(CLOCK_MONOTONIC, &ts_stop); /* Swap GL buffers. */ printf("Swapping...\n"); if(want_opengl) { glFlush(); SDL_GL_SwapBuffers(); } //usleep(500000); timer_value = static_cast(ts_stop.tv_sec) - static_cast(ts_start.tv_sec) + (static_cast(ts_stop.tv_nsec - ts_start.tv_nsec) / 1000000000.); printf("PutImage + Blit:\t%0.6f msec\n", timer_value * 1000.); clock_gettime(CLOCK_MONOTONIC, &ts_start); /* Write a new pattern into the surface. */ printf("Painting...\n"); if(use_xv) { Uint8 *y_plane = (Uint8*)shm_seg_info.shmaddr; Uint8 *u_plane = &(y_plane[IMG_HEIGHT*IMG_WIDTH]); Uint8 *v_plane = &(u_plane[IMG_HEIGHT*IMG_WIDTH >> 2]); for(int i = 0; i < IMG_HEIGHT; i++) { for(int j = 0; j < IMG_WIDTH; j++) { y_plane[(i * IMG_WIDTH) + j] = 0xff; if(((j % 2) == 0) && ((i % 2) == 0)) { u_plane[((i >> 1) * IMG_WIDTH) + (j >> 1)] = (clr & 0xff); v_plane[((i >> 1) * IMG_WIDTH) + (j >> 1)] = (clr++ & 0xff); } }; }; clr++; } else { p = (char*)shm_seg_info.shmaddr; for(int i = 0; i < IMG_HEIGHT; i++) { for(int j = 0; j < IMG_WIDTH; j++) { *(Uint32*)(&p[i*s->pitch() + (j << 2)]) = 0xff000000 | (clr++ & 0xffffff); }; }; }; clock_gettime(CLOCK_MONOTONIC, &ts_stop); timer_value = static_cast(ts_stop.tv_sec) - static_cast(ts_start.tv_sec) + (static_cast(ts_stop.tv_nsec - ts_start.tv_nsec) / 1000000000.); printf("Fill Img:\t\t%0.6f msec\n", timer_value * 1000.); } glBindTexture(GL_TEXTURE_2D, 0); if(use_tfp) { glXReleaseTexImageEXT(wminfo.info.x11.gfxdisplay, glxp, GLX_FRONT_LEFT_EXT); glXDestroyPixmap(wminfo.info.x11.gfxdisplay, glxp); XFreePixmap(wminfo.info.x11.gfxdisplay, s_pixmap); if(use_shared) XShmDetach(wminfo.info.x11.gfxdisplay, &shm_seg_info); } if(use_xv) XvUngrabPort(wminfo.info.x11.gfxdisplay, a_port, CurrentTime); if(!use_xv) { delete s; SDL_FreeSurface(sdl_s); } shmdt(shm_seg_info.shmaddr); SDLmm::Quit(); };