void zl_glx_vsync(zl_glx *x, bool sync) { #ifdef TARGET_WIN32 if (sync) { if (GLEE_WGL_EXT_swap_control) wglSwapIntervalEXT (1); } else { if (GLEE_WGL_EXT_swap_control) wglSwapIntervalEXT (0); } #endif #ifdef TARGET_OSX long sync = sync ? 1 : 0; CGLSetParameter (CGLGetCurrentContext(), kCGLCPSwapInterval, &sync); #endif #ifdef TARGET_LINUX int interval = sync ? 2 : 0; glXSwapIntervalSGIFunc glXSwapIntervalSGI = 0; glXSwapIntervalMESAFunc glXSwapIntervalMESA = 0; if (GLXExtensionSupported(x->xdpy->dpy, "GLX_MESA_swap_control")) { glXSwapIntervalMESA = (glXSwapIntervalMESAFunc) glXGetProcAddressARB((const GLubyte *)"glXSwapIntervalMESA"); if (glXSwapIntervalMESA) { ZL_LOG("glXSwapIntervalMESA(%d)", interval); glXSwapIntervalMESA (interval); } else { ZL_LOG("Could not get glXSwapIntervalMESA()\n"); } } else if (GLXExtensionSupported(x->xdpy->dpy, "GLX_SGI_swap_control")) { glXSwapIntervalSGI = (glXSwapIntervalSGIFunc) glXGetProcAddressARB((const GLubyte *)"glXSwapIntervalSGI"); if (glXSwapIntervalSGI) { ZL_LOG("glXSwapIntervalSGI(%d)", interval, glXSwapIntervalSGI); glXSwapIntervalSGI (interval); } else { ZL_LOG("Could not get glXSwapIntervalSGI()\n"); } } else { ZL_LOG("can't change vblank settings"); } #endif }
int main(int argc, char *argv[]) { Display *disp; XVisualInfo *pvi; XSetWindowAttributes swa; int attrib[14]; GLint last_val = -1, count = 0; Window winGL; GLXContext context; int dummy; Atom wmDelete; enum sync_type waitforsync = none; int width = 500, height = 500, verbose = 0, countonly = 0; int c, i = 1; opterr = 0; while ((c = getopt(argc, argv, optstr)) != -1) { switch (c) { case 'w': width = atoi(optarg); break; case 'h': height = atoi(optarg); break; case 's': switch (optarg[0]) { case 'n': waitforsync = none; break; case 's': waitforsync = sgi_video_sync; break; case 'b': waitforsync = buffer_swap; break; default: usage(argv[0]); break; } break; case 'v': verbose = 1; break; default: usage(argv[0]); break; } } disp = XOpenDisplay(NULL); if (!disp) { fprintf(stderr, "failed to open display\n"); return -1; } if (!glXQueryExtension(disp, &dummy, &dummy)) { fprintf(stderr, "glXQueryExtension failed\n"); return -1; } if (!GLXExtensionSupported(disp, "GLX_SGI_video_sync")) { fprintf(stderr, "GLX_SGI_video_sync not supported, exiting\n"); return -1; } attrib[0] = GLX_RGBA; attrib[1] = 1; attrib[2] = GLX_RED_SIZE; attrib[3] = 1; attrib[4] = GLX_GREEN_SIZE; attrib[5] = 1; attrib[6] = GLX_BLUE_SIZE; attrib[7] = 1; if (waitforsync != buffer_swap) attrib[8] = None; else { attrib[8] = GLX_DOUBLEBUFFER; attrib[9] = 1; attrib[10] = None; } pvi = glXChooseVisual(disp, DefaultScreen(disp), attrib); if (!pvi) { fprintf(stderr, "failed to choose visual, exiting\n"); return -1; } context = glXCreateContext(disp, pvi, None, GL_TRUE); if (!context) { fprintf(stderr, "failed to create glx context\n"); return -1; } pvi->screen = DefaultScreen(disp); swa.colormap = XCreateColormap(disp, RootWindow(disp, pvi->screen), pvi->visual, AllocNone); swa.border_pixel = 0; swa.event_mask = ExposureMask | KeyPressMask | ButtonPressMask | StructureNotifyMask; winGL = XCreateWindow(disp, RootWindow(disp, pvi->screen), 0, 0, width, height, 0, pvi->depth, InputOutput, pvi->visual, CWBorderPixel | CWColormap | CWEventMask, &swa); if (!winGL) { fprintf(stderr, "window creation failed\n"); return -1; } wmDelete = XInternAtom(disp, "WM_DELETE_WINDOW", True); XSetWMProtocols(disp, winGL, &wmDelete, 1); XSetStandardProperties(disp, winGL, "glsync test", "glsync text", None, NULL, 0, NULL); XMapRaised(disp, winGL); glXMakeCurrent(disp, winGL, context); video_sync_get = glXGetProcAddress((unsigned char *)"glXGetVideoSyncSGI"); video_sync = glXGetProcAddress((unsigned char *)"glXWaitVideoSyncSGI"); if (!video_sync_get || !video_sync) { fprintf(stderr, "failed to get sync functions\n"); return -1; } video_sync_get(&count); count++; while (i++) { /* Wait for vsync */ if (waitforsync == sgi_video_sync) { if (verbose) fprintf(stderr, "waiting on count %d\n", count); video_sync(2, (count + 1) % 2, &count); if (count < last_val) fprintf(stderr, "error: vblank count went backwards: %d -> %d\n", last_val, count); if (count == last_val) fprintf(stderr, "error: count didn't change: %d\n", count); last_val = count; } else if (waitforsync == buffer_swap) { glXSwapBuffers(disp, winGL); } if (countonly) { video_sync(2, 1, &count); fprintf(stderr, "current count: %d\n", count); sleep(1); continue; } /* Alternate colors to make tearing obvious */ if (i & 1) glClearColor(1.0f, 1.0f, 1.0f, 1.0f); else glClearColor(1.0f, 0.0f, 0.0f, 0.0f); glClear(GL_COLOR_BUFFER_BIT); glFlush(); } XDestroyWindow(disp, winGL); glXDestroyContext(disp, context); XCloseDisplay(disp); return 0; }
static int glw_x11_init(glw_x11_t *gx11) { int attribs[10]; int na = 0; XInitThreads(); int use_locales = XSupportsLocale() && XSetLocaleModifiers("") != NULL; if((gx11->display = XOpenDisplay(gx11->displayname_real)) == NULL) { TRACE(TRACE_ERROR, "GLW", "Unable to open X display \"%s\"\n", gx11->displayname_real); return 1; } if(!glXQueryExtension(gx11->display, NULL, NULL)) { TRACE(TRACE_ERROR, "GLW", "OpenGL GLX extension not supported by display \"%s\"\n", gx11->displayname_real); return 1; } gx11->screen = DefaultScreen(gx11->display); gx11->screen_width = DisplayWidth(gx11->display, gx11->screen); gx11->screen_height = DisplayHeight(gx11->display, gx11->screen); gx11->root = RootWindow(gx11->display, gx11->screen); attribs[na++] = GLX_RGBA; attribs[na++] = GLX_RED_SIZE; attribs[na++] = 1; attribs[na++] = GLX_GREEN_SIZE; attribs[na++] = 1; attribs[na++] = GLX_BLUE_SIZE; attribs[na++] = 1; attribs[na++] = GLX_DOUBLEBUFFER; attribs[na++] = None; gx11->xvi = glXChooseVisual(gx11->display, gx11->screen, attribs); if(gx11->xvi == NULL) { TRACE(TRACE_ERROR, "GLW", "Unable to find an adequate Visual on \"%s\"\n", gx11->displayname_real); return 1; } if(GLXExtensionSupported(gx11->display, "GLX_SGI_swap_control")) { TRACE(TRACE_DEBUG, "GLW", "GLX_SGI_swap_control extension is present"); gx11->glXSwapIntervalSGI = (PFNGLXSWAPINTERVALSGIPROC) glXGetProcAddress((const GLubyte*)"glXSwapIntervalSGI"); } build_blank_cursor(gx11); if(use_locales) gx11->im = XOpenIM(gx11->display, NULL, NULL, NULL); gx11->atom_deletewindow = XInternAtom(gx11->display, "WM_DELETE_WINDOW", 0); #if ENABLE_VDPAU if(GLXExtensionSupported(gx11->display, "GLX_EXT_texture_from_pixmap")) { gx11->gr.gr_be.gbr_bind_tex_image = (PFNGLXBINDTEXIMAGEEXTPROC) glXGetProcAddress((const GLubyte*)"glXBindTexImageEXT"); gx11->gr.gr_be.gbr_release_tex_image = (PFNGLXRELEASETEXIMAGEEXTPROC) glXGetProcAddress((const GLubyte*)"glXReleaseTexImageEXT"); gx11->gr.gr_be.gbr_vdpau_dev = vdpau_init_x11(gx11->display, gx11->screen, vdpau_preempted, gx11); } else { TRACE(TRACE_DEBUG, "VDPAU", "GLX_EXT_texture_from_pixmap extension not present, disabling VDPAU"); } #endif probe_wm(gx11); gx11->is_fullscreen = gx11->want_fullscreen; int fs = 0; if(gx11->wm_flags == 0) { fs = 1; // No window manager, open in fullscreen mode } else { /* If window manager cannot do fullscreen, ask window to open in fullscreen mode */ fs = gx11->want_fullscreen && !(gx11->wm_flags & GX11_WM_CAN_FULLSCREEN); } if(window_open(gx11, fs)) return -1; // Fullscreen via window manager if(gx11->want_fullscreen && !fs) wm_set_fullscreen(gx11, 1); return 0; }
static int glw_x11_init(glw_x11_t *gx11) { int attribs[10]; int na = 0; int use_locales = XSupportsLocale() && XSetLocaleModifiers("@im=none") != NULL; if((gx11->display = XOpenDisplay(gx11->displayname_real)) == NULL) { TRACE(TRACE_ERROR, "GLW", "Unable to open X display \"%s\"\n", gx11->displayname_real); return 1; } if(!glXQueryExtension(gx11->display, NULL, NULL)) { TRACE(TRACE_ERROR, "GLW", "OpenGL GLX extension not supported by display \"%s\"\n", gx11->displayname_real); return 1; } gx11->screen = DefaultScreen(gx11->display); gx11->screen_width = DisplayWidth(gx11->display, gx11->screen); gx11->screen_height = DisplayHeight(gx11->display, gx11->screen); gx11->root = RootWindow(gx11->display, gx11->screen); attribs[na++] = GLX_RGBA; attribs[na++] = GLX_RED_SIZE; attribs[na++] = 1; attribs[na++] = GLX_GREEN_SIZE; attribs[na++] = 1; attribs[na++] = GLX_BLUE_SIZE; attribs[na++] = 1; attribs[na++] = GLX_DOUBLEBUFFER; attribs[na++] = None; gx11->xvi = glXChooseVisual(gx11->display, gx11->screen, attribs); if(gx11->xvi == NULL) { TRACE(TRACE_ERROR, "GLW", "Unable to find an adequate Visual on \"%s\"\n", gx11->displayname_real); return 1; } if(GLXExtensionSupported(gx11->display, "GLX_SGI_swap_control")) { TRACE(TRACE_DEBUG, "GLW", "GLX_SGI_swap_control extension is present"); gx11->glXSwapIntervalSGI = (PFNGLXSWAPINTERVALSGIPROC) glXGetProcAddress((const GLubyte*)"glXSwapIntervalSGI"); } build_blank_cursor(gx11); if(use_locales) gx11->im = XOpenIM(gx11->display, NULL, NULL, NULL); gx11->atom_deletewindow = XInternAtom(gx11->display, "WM_DELETE_WINDOW", 0); #if ENABLE_VDPAU gx11->gr.gr_be.gbr_glVDPAUInitNV = (PFNGLVDPAUINITNVPROC) glXGetProcAddress((const GLubyte*)"glVDPAUInitNV"); if(gx11->gr.gr_be.gbr_glVDPAUInitNV != NULL) { vdpau_dev_t *vd; vd = vdpau_init_x11(gx11->display, gx11->screen, vdpau_preempted, gx11); gx11->gr.gr_be.gbr_vdpau_dev = vd; gx11->gr.gr_be.gbr_glVDPAUUnregisterSurfaceNV = (PFNGLVDPAUUNREGISTERSURFACENVPROC) glXGetProcAddress((const GLubyte*)"glVDPAUUnregisterSurfaceNV"); gx11->gr.gr_be.gbr_glVDPAUUnmapSurfacesNV = (PFNGLVDPAUUNMAPSURFACESNVPROC) glXGetProcAddress((const GLubyte*)"glVDPAUUnmapSurfacesNV"); gx11->gr.gr_be.gbr_glVDPAURegisterOutputSurfaceNV = (PFNGLVDPAUREGISTEROUTPUTSURFACENVPROC) glXGetProcAddress((const GLubyte*)"glVDPAURegisterOutputSurfaceNV"); gx11->gr.gr_be.gbr_glVDPAUMapSurfacesNV = (PFNGLVDPAUMAPSURFACESNVPROC) glXGetProcAddress((const GLubyte*)"glVDPAUMapSurfacesNV"); } #endif probe_wm(gx11); gx11->is_fullscreen = gx11->want_fullscreen; int fs = 0; if(gx11->wm_flags == 0) { fs = 1; // No window manager, open in fullscreen mode } else { /* If window manager cannot do fullscreen, ask window to open in fullscreen mode */ fs = gx11->want_fullscreen && !(gx11->wm_flags & GX11_WM_CAN_FULLSCREEN); } if(window_open(gx11, fs)) return -1; // Fullscreen via window manager if(gx11->want_fullscreen && !fs) wm_set_fullscreen(gx11, 1); return 0; }
static void *gfx_ctx_x_init(video_frame_info_t *video_info, void *data) { int nelements = 0; int major = 0; int minor = 0; #ifdef HAVE_OPENGL static const int visual_attribs[] = { GLX_X_RENDERABLE , True, GLX_DRAWABLE_TYPE , GLX_WINDOW_BIT, GLX_RENDER_TYPE , GLX_RGBA_BIT, GLX_DOUBLEBUFFER , True, GLX_RED_SIZE , 8, GLX_GREEN_SIZE , 8, GLX_BLUE_SIZE , 8, GLX_ALPHA_SIZE , 8, GLX_DEPTH_SIZE , 0, GLX_STENCIL_SIZE , 0, GLX_SAMPLE_BUFFERS , 0, GLX_SAMPLES , 0, None }; GLXFBConfig *fbcs = NULL; #endif gfx_ctx_x_data_t *x = (gfx_ctx_x_data_t*) calloc(1, sizeof(gfx_ctx_x_data_t)); #ifndef GL_DEBUG struct retro_hw_render_callback *hwr = video_driver_get_hw_context(); #endif if (!x) return NULL; current_context_data = x; XInitThreads(); if (!x11_connect()) goto error; switch (x_api) { case GFX_CTX_OPENGL_API: case GFX_CTX_OPENGL_ES_API: #if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES) glXQueryVersion(g_x11_dpy, &major, &minor); /* GLX 1.3+ minimum required. */ if ((major * 1000 + minor) < 1003) goto error; glx_create_context_attribs = (PFNGLXCREATECONTEXTATTRIBSARBPROC) glXGetProcAddress((const GLubyte*)"glXCreateContextAttribsARB"); #ifdef GL_DEBUG x->g_debug = true; #else x->g_debug = hwr->debug_context; #endif /* Have to use ContextAttribs */ #ifdef HAVE_OPENGLES2 x->g_core_es = true; x->g_core_es_core = true; #else x->g_core_es = (g_major * 1000 + g_minor) >= 3001; x->g_core_es_core = (g_major * 1000 + g_minor) >= 3002; #endif if ((x->g_core_es || x->g_debug) && !glx_create_context_attribs) goto error; fbcs = glXChooseFBConfig(g_x11_dpy, DefaultScreen(g_x11_dpy), visual_attribs, &nelements); if (!fbcs) goto error; if (!nelements) { XFree(fbcs); goto error; } x->g_fbc = fbcs[0]; XFree(fbcs); #endif break; case GFX_CTX_VULKAN_API: #ifdef HAVE_VULKAN /* Use XCB WSI since it's the most supported WSI over legacy Xlib. */ if (!vulkan_context_init(&x->vk, VULKAN_WSI_XCB)) goto error; #endif break; case GFX_CTX_NONE: default: break; } switch (x_api) { case GFX_CTX_OPENGL_API: #ifdef HAVE_OPENGL if (GLXExtensionSupported(g_x11_dpy, "GLX_EXT_swap_control_tear")) { RARCH_LOG("[GLX]: GLX_EXT_swap_control_tear supported.\n"); x_adaptive_vsync = true; } if (GLXExtensionSupported(g_x11_dpy, "GLX_OML_sync_control") && GLXExtensionSupported(g_x11_dpy, "GLX_MESA_swap_control") ) { RARCH_LOG("[GLX]: GLX_OML_sync_control and GLX_MESA_swap_control supported, using better swap control method...\n"); x->swap_mode = 1; glXGetSyncValuesOML = (GLXGETSYNCVALUESOMLPROC)glXGetProcAddress((unsigned char *)"glXGetSyncValuesOML"); glXGetMscRateOML = (GLXGETMSCRATEOMLPROC)glXGetProcAddress((unsigned char *)"glXGetMscRateOML"); glXSwapBuffersMscOML = (GLXSWAPBUFFERSMSCOMLPROC)glXGetProcAddress((unsigned char *)"glXSwapBuffersMscOML"); glXWaitForMscOML = (GLXWAITFORMSCOMLPROC)glXGetProcAddress((unsigned char *)"glXWaitForMscOML"); glXWaitForSbcOML = (GLXWAITFORSBCOMLPROC)glXGetProcAddress((unsigned char *)"glXWaitForSbcOML"); glXGetSyncValuesOML(g_x11_dpy, g_x11_win, &x->ust, &x->msc, &x->sbc); #if 0 RARCH_LOG("[GLX]: UST: %d, MSC: %d, SBC: %d\n", x->ust, x->msc, x->sbc); #endif } #endif break; default: break; } return x; error: if (x) { gfx_ctx_x_destroy_resources(x); free(x); } g_x11_screen = 0; return NULL; }