static void clutter_backend_glx_dispose (GObject *gobject) { ClutterBackendGLX *backend_glx = CLUTTER_BACKEND_GLX (gobject); ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (gobject); /* Unrealize all shaders, since the GL context is going away */ _clutter_shader_release_all (); if (backend_glx->gl_context) { glXMakeContextCurrent (backend_x11->xdpy, None, None, NULL); glXDestroyContext (backend_x11->xdpy, backend_glx->gl_context); backend_glx->gl_context = None; } if (backend_glx->dummy_glxwin) { glXDestroyWindow (backend_x11->xdpy, backend_glx->dummy_glxwin); backend_glx->dummy_glxwin = None; } if (backend_glx->dummy_xwin) { XDestroyWindow (backend_x11->xdpy, backend_glx->dummy_xwin); backend_glx->dummy_xwin = None; } G_OBJECT_CLASS (clutter_backend_glx_parent_class)->dispose (gobject); }
static gboolean clutter_stage_glx_realize (ClutterStageWindow *stage_window) { ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window); ClutterStageGLX *stage_glx = CLUTTER_STAGE_GLX (stage_window); ClutterBackendX11 *backend_x11; ClutterBackendGLX *backend_glx; CLUTTER_NOTE (ACTOR, "Realizing stage '%s' [%p]", G_OBJECT_TYPE_NAME (stage_window), stage_window); if (!_clutter_stage_x11_create_window (stage_x11)) return FALSE; backend_x11 = stage_x11->backend; backend_glx = CLUTTER_BACKEND_GLX (backend_x11); if (stage_glx->glxwin == None) { int major; int minor; GLXFBConfig config; /* Try and create a GLXWindow to use with extensions dependent on * GLX versions >= 1.3 that don't accept regular X Windows as GLX * drawables. */ if (glXQueryVersion (backend_x11->xdpy, &major, &minor) && major == 1 && minor >= 3 && _clutter_backend_glx_get_fbconfig (backend_glx, &config)) { stage_glx->glxwin = glXCreateWindow (backend_x11->xdpy, config, stage_x11->xwin, NULL); } } #ifdef GLX_INTEL_swap_event if (clutter_feature_available (CLUTTER_FEATURE_SWAP_EVENTS)) { GLXDrawable drawable = stage_glx->glxwin ? stage_glx->glxwin : stage_x11->xwin; /* we unconditionally select this event because we rely on it to * advance the master clock, and drive redraw/relayout, animations * and event handling. */ glXSelectEvent (backend_x11->xdpy, drawable, GLX_BUFFER_SWAP_COMPLETE_INTEL_MASK); } #endif /* GLX_INTEL_swap_event */ /* chain up to the StageX11 implementation */ return clutter_stage_window_parent_iface->realize (stage_window); }
static XVisualInfo * clutter_backend_glx_get_visual_info (ClutterBackendX11 *backend_x11) { ClutterBackendGLX *backend_glx = CLUTTER_BACKEND_GLX (backend_x11); GLXFBConfig config; if (!_clutter_backend_glx_get_fbconfig (backend_glx, &config)) return NULL; return glXGetVisualFromFBConfig (backend_x11->xdpy, config); }
static gboolean clutter_backend_glx_create_context (ClutterBackend *backend, gboolean is_offscreen, GError **error) { ClutterBackendGLX *backend_glx = CLUTTER_BACKEND_GLX (backend); ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (backend); if (backend_glx->gl_context == None) { XVisualInfo *xvisinfo; xvisinfo = clutter_backend_x11_get_visual_info (backend_x11, is_offscreen); CLUTTER_NOTE (GL, "Creating GL Context (display: %p, %s)", backend_x11->xdpy, is_offscreen ? "offscreen" : "onscreen"); backend_glx->gl_context = glXCreateContext (backend_x11->xdpy, xvisinfo, 0, is_offscreen ? False : True); XFree (xvisinfo); if (backend_glx->gl_context == None) { g_set_error (error, CLUTTER_INIT_ERROR, CLUTTER_INIT_ERROR_BACKEND, "Unable to create suitable %s GL context", is_offscreen ? "offscreen" : "onscreen"); return FALSE; } if (!is_offscreen) { gboolean is_direct; is_direct = glXIsDirect (backend_x11->xdpy, backend_glx->gl_context); CLUTTER_NOTE (GL, "Setting %s context", is_direct ? "direct" : "indirect"); _cogl_set_indirect_context (!is_direct); } } return TRUE; }
static void clutter_backend_glx_redraw (ClutterBackend *backend, ClutterStage *stage) { ClutterStageGLX *stage_glx; ClutterStageX11 *stage_x11; ClutterStageWindow *impl; impl = _clutter_stage_get_window (stage); if (G_UNLIKELY (impl == NULL)) { CLUTTER_NOTE (BACKEND, "Stage [%p] has no implementation", stage); return; } g_assert (CLUTTER_IS_STAGE_GLX (impl)); stage_x11 = CLUTTER_STAGE_X11 (impl); stage_glx = CLUTTER_STAGE_GLX (impl); /* this will cause the stage implementation to be painted */ clutter_actor_paint (CLUTTER_ACTOR (stage)); cogl_flush (); if (stage_x11->xwin != None) { /* wait for the next vblank */ CLUTTER_NOTE (BACKEND, "Waiting for vblank"); glx_wait_for_vblank (CLUTTER_BACKEND_GLX (backend)); /* push on the screen */ CLUTTER_NOTE (BACKEND, "glXSwapBuffers (display: %p, window: 0x%lx)", stage_x11->xdpy, (unsigned long) stage_x11->xwin); glXSwapBuffers (stage_x11->xdpy, stage_x11->xwin); } else { /* offscreen */ glXWaitGL (); CLUTTER_GLERR (); } }
static ClutterTranslateReturn clutter_stage_glx_translate_event (ClutterEventTranslator *translator, gpointer native, ClutterEvent *event) { #ifdef GLX_INTEL_swap_event ClutterBackendGLX *backend_glx; XEvent *xevent = native; backend_glx = CLUTTER_BACKEND_GLX (clutter_get_default_backend ()); if (xevent->type == (backend_glx->event_base + GLX_BufferSwapComplete)) { ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (translator); ClutterStageGLX *stage_glx = CLUTTER_STAGE_GLX (translator); GLXBufferSwapComplete *swap_complete_event; swap_complete_event = (GLXBufferSwapComplete *) xevent; if (stage_x11->xwin == swap_complete_event->drawable) { /* Early versions of the swap_event implementation in Mesa * deliver BufferSwapComplete event when not selected for, * so if we get a swap event we aren't expecting, just ignore it. * * https://bugs.freedesktop.org/show_bug.cgi?id=27962 */ if (stage_glx->pending_swaps > 0) stage_glx->pending_swaps--; return CLUTTER_TRANSLATE_REMOVE; } } #endif /* chain up to the common X11 implementation */ return clutter_event_translator_parent_iface->translate_event (translator, native, event); }
static gboolean clutter_backend_glx_post_parse (ClutterBackend *backend, GError **error) { ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (backend); ClutterBackendGLX *backend_glx = CLUTTER_BACKEND_GLX (backend); ClutterBackendClass *backend_class = CLUTTER_BACKEND_CLASS (clutter_backend_glx_parent_class); int glx_major, glx_minor; if (!backend_class->post_parse (backend, error)) return FALSE; if (!glXQueryExtension (backend_x11->xdpy, &backend_glx->error_base, &backend_glx->event_base)) { g_set_error (error, CLUTTER_INIT_ERROR, CLUTTER_INIT_ERROR_BACKEND, "XServer appears to lack required GLX support"); return FALSE; } /* XXX: Technically we should require >= GLX 1.3 support but for a long * time Mesa has exported a hybrid GLX, exporting extensions specified * to require GLX 1.3, but still reporting 1.2 via glXQueryVersion. */ if (!glXQueryVersion (backend_x11->xdpy, &glx_major, &glx_minor) || !(glx_major == 1 && glx_minor >= 2)) { g_set_error (error, CLUTTER_INIT_ERROR, CLUTTER_INIT_ERROR_BACKEND, "XServer appears to lack required GLX 1.2 support"); return FALSE; } return TRUE; }
static GObject * clutter_backend_glx_constructor (GType gtype, guint n_params, GObjectConstructParam *params) { GObjectClass *parent_class; GObject *retval; if (!backend_singleton) { parent_class = G_OBJECT_CLASS (clutter_backend_glx_parent_class); retval = parent_class->constructor (gtype, n_params, params); backend_singleton = CLUTTER_BACKEND_GLX (retval); return retval; } g_warning ("Attempting to create a new backend object. This should " "never happen, so we return the singleton instance."); return g_object_ref (backend_singleton); }
/* TODO: remove this interface in favour of * _clutter_stage_window_make_current () */ static void clutter_backend_glx_ensure_context (ClutterBackend *backend, ClutterStage *stage) { ClutterStageWindow *impl; /* if there is no stage, the stage is being destroyed or it has no * implementation attached to it then we clear the GL context */ if (stage == NULL || (CLUTTER_PRIVATE_FLAGS (stage) & CLUTTER_ACTOR_IN_DESTRUCTION) || ((impl = _clutter_stage_get_window (stage)) == NULL)) { ClutterBackendX11 *backend_x11; backend_x11 = CLUTTER_BACKEND_X11 (backend); CLUTTER_NOTE (MULTISTAGE, "Clearing all context"); glXMakeContextCurrent (backend_x11->xdpy, None, None, NULL); } else { ClutterBackendGLX *backend_glx; ClutterBackendX11 *backend_x11; ClutterStageGLX *stage_glx; ClutterStageX11 *stage_x11; GLXDrawable drawable; g_assert (impl != NULL); stage_glx = CLUTTER_STAGE_GLX (impl); stage_x11 = CLUTTER_STAGE_X11 (impl); backend_glx = CLUTTER_BACKEND_GLX (backend); backend_x11 = CLUTTER_BACKEND_X11 (backend); drawable = stage_glx->glxwin ? stage_glx->glxwin : stage_x11->xwin; CLUTTER_NOTE (BACKEND, "Setting context for stage of type %s, window: 0x%x", G_OBJECT_TYPE_NAME (impl), (unsigned int) drawable); /* no GL context to set */ if (backend_glx->gl_context == None) return; clutter_x11_trap_x_errors (); /* we might get here inside the final dispose cycle, so we * need to handle this gracefully */ if (drawable == None) { GLXDrawable dummy_drawable; CLUTTER_NOTE (BACKEND, "Received a stale stage, clearing all context"); if (backend_glx->dummy_glxwin) dummy_drawable = backend_glx->dummy_glxwin; else dummy_drawable = backend_glx->dummy_xwin; if (dummy_drawable == None) glXMakeContextCurrent (backend_x11->xdpy, None, None, NULL); else { glXMakeContextCurrent (backend_x11->xdpy, dummy_drawable, dummy_drawable, backend_glx->gl_context); } } else { CLUTTER_NOTE (BACKEND, "MakeContextCurrent dpy: %p, window: 0x%x (%s), context: %p", backend_x11->xdpy, (unsigned int) drawable, stage_x11->is_foreign_xwin ? "foreign" : "native", backend_glx->gl_context); glXMakeContextCurrent (backend_x11->xdpy, drawable, drawable, backend_glx->gl_context); /* * In case we are using GLX_SGI_swap_control for vblank syncing we need call * glXSwapIntervalSGI here to make sure that it affects the current drawable. */ if (backend_glx->vblank_type == CLUTTER_VBLANK_GLX_SWAP && backend_glx->swap_interval != NULL) backend_glx->swap_interval (1); } if (clutter_x11_untrap_x_errors ()) g_critical ("Unable to make the stage window 0x%x the current " "GLX drawable", (unsigned int) drawable); } }
static gboolean clutter_backend_glx_create_context (ClutterBackend *backend, GError **error) { ClutterBackendGLX *backend_glx = CLUTTER_BACKEND_GLX (backend); ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (backend); GLXFBConfig config; gboolean is_direct; Window root_xwin; XSetWindowAttributes attrs; XVisualInfo *xvisinfo; Display *xdisplay; int major; int minor; GLXDrawable dummy_drawable; if (backend_glx->gl_context != None) return TRUE; xdisplay = clutter_x11_get_default_display (); root_xwin = clutter_x11_get_root_window (); if (!_clutter_backend_glx_get_fbconfig (backend_glx, &config)) { g_set_error (error, CLUTTER_INIT_ERROR, CLUTTER_INIT_ERROR_BACKEND, "Unable to find suitable fbconfig for the GLX context"); return FALSE; } CLUTTER_NOTE (BACKEND, "Creating GLX Context (display: %p)", xdisplay); backend_glx->gl_context = glXCreateNewContext (xdisplay, config, GLX_RGBA_TYPE, NULL, True); if (backend_glx->gl_context == None) { g_set_error (error, CLUTTER_INIT_ERROR, CLUTTER_INIT_ERROR_BACKEND, "Unable to create suitable GL context"); return FALSE; } is_direct = glXIsDirect (xdisplay, backend_glx->gl_context); CLUTTER_NOTE (GL, "Setting %s context", is_direct ? "direct" : "indirect"); _cogl_set_indirect_context (!is_direct); /* COGL assumes that there is always a GL context selected; in order * to make sure that a GLX context exists and is made current, we use * a dummy, offscreen override-redirect window to which we can always * fall back if no stage is available * * XXX - we need to do this dance because GLX does not allow creating * a context and querying it for basic information (even the function * pointers) unless it's made current to a real Drawable. it should be * possible to avoid this in future releases of Mesa and X11, but right * now this is the best solution available. */ xvisinfo = glXGetVisualFromFBConfig (xdisplay, config); if (xvisinfo == None) { g_set_error (error, CLUTTER_INIT_ERROR, CLUTTER_INIT_ERROR_BACKEND, "Unable to retrieve the X11 visual"); return FALSE; } clutter_x11_trap_x_errors (); attrs.override_redirect = True; attrs.colormap = XCreateColormap (xdisplay, root_xwin, xvisinfo->visual, AllocNone); attrs.border_pixel = 0; backend_glx->dummy_xwin = XCreateWindow (xdisplay, root_xwin, -100, -100, 1, 1, 0, xvisinfo->depth, CopyFromParent, xvisinfo->visual, CWOverrideRedirect | CWColormap | CWBorderPixel, &attrs); /* Try and create a GLXWindow to use with extensions dependent on * GLX versions >= 1.3 that don't accept regular X Windows as GLX * drawables. */ if (glXQueryVersion (backend_x11->xdpy, &major, &minor) && major == 1 && minor >= 3) { backend_glx->dummy_glxwin = glXCreateWindow (backend_x11->xdpy, config, backend_glx->dummy_xwin, NULL); } if (backend_glx->dummy_glxwin) dummy_drawable = backend_glx->dummy_glxwin; else dummy_drawable = backend_glx->dummy_xwin; CLUTTER_NOTE (BACKEND, "Selecting dummy 0x%x for the GLX context", (unsigned int) dummy_drawable); glXMakeContextCurrent (xdisplay, dummy_drawable, dummy_drawable, backend_glx->gl_context); XFree (xvisinfo); if (clutter_x11_untrap_x_errors ()) { g_set_error (error, CLUTTER_INIT_ERROR, CLUTTER_INIT_ERROR_BACKEND, "Unable to select the newly created GLX context"); return FALSE; } return TRUE; }
static ClutterFeatureFlags clutter_backend_glx_get_features (ClutterBackend *backend) { ClutterBackendGLX *backend_glx = CLUTTER_BACKEND_GLX (backend); const gchar *glx_extensions = NULL; const gchar *gl_extensions = NULL; ClutterFeatureFlags flags; gboolean use_dri = FALSE; flags = clutter_backend_x11_get_features (backend); flags |= CLUTTER_FEATURE_STAGE_MULTIPLE; /* this will make sure that the GL context exists */ g_assert (backend_glx->gl_context != None); g_assert (glXGetCurrentDrawable () != None); CLUTTER_NOTE (BACKEND, "Checking features\n" " GL_VENDOR: %s\n" " GL_RENDERER: %s\n" " GL_VERSION: %s\n" " GL_EXTENSIONS: %s", glGetString (GL_VENDOR), glGetString (GL_RENDERER), glGetString (GL_VERSION), glGetString (GL_EXTENSIONS)); glx_extensions = glXQueryExtensionsString (clutter_x11_get_default_display (), clutter_x11_get_default_screen ()); CLUTTER_NOTE (BACKEND, " GLX Extensions: %s", glx_extensions); gl_extensions = (const gchar *)glGetString (GL_EXTENSIONS); /* When using glBlitFramebuffer or glXCopySubBufferMESA for sub stage * redraws, we cannot rely on glXSwapIntervalSGI to throttle the blits * so we need to resort to manually synchronizing with the vblank so we * always check for the video_sync extension... */ if (_cogl_check_extension ("GLX_SGI_video_sync", glx_extensions) && /* Note: the GLX_SGI_video_sync spec explicitly states this extension * only works for direct contexts. */ glXIsDirect (clutter_x11_get_default_display (), backend_glx->gl_context)) { backend_glx->get_video_sync = (GetVideoSyncProc) cogl_get_proc_address ("glXGetVideoSyncSGI"); backend_glx->wait_video_sync = (WaitVideoSyncProc) cogl_get_proc_address ("glXWaitVideoSyncSGI"); } use_dri = check_vblank_env ("dri"); /* First check for explicit disabling or it set elsewhere (eg NVIDIA) */ if (check_vblank_env ("none")) { CLUTTER_NOTE (BACKEND, "vblank sync: disabled at user request"); goto vblank_setup_done; } if (g_getenv ("__GL_SYNC_TO_VBLANK") != NULL) { backend_glx->vblank_type = CLUTTER_VBLANK_GLX_SWAP; flags |= CLUTTER_FEATURE_SYNC_TO_VBLANK; CLUTTER_NOTE (BACKEND, "Using __GL_SYNC_TO_VBLANK hint"); goto vblank_setup_done; } /* We try two GL vblank syncing mechanisms. * glXSwapIntervalSGI is tried first, then glXGetVideoSyncSGI. * * glXSwapIntervalSGI is known to work with Mesa and in particular * the Intel drivers. glXGetVideoSyncSGI has serious problems with * Intel drivers causing terrible frame rate so it only tried as a * fallback. * * How well glXGetVideoSyncSGI works with other driver (ATI etc) needs * to be investigated. glXGetVideoSyncSGI on ATI at least seems to have * no effect. */ if (!use_dri && _cogl_check_extension ("GLX_SGI_swap_control", glx_extensions)) { backend_glx->swap_interval = (SwapIntervalProc) cogl_get_proc_address ("glXSwapIntervalSGI"); CLUTTER_NOTE (BACKEND, "attempting glXSwapIntervalSGI vblank setup"); if (backend_glx->swap_interval != NULL && backend_glx->swap_interval (1) == 0) { backend_glx->vblank_type = CLUTTER_VBLANK_GLX_SWAP; flags |= CLUTTER_FEATURE_SYNC_TO_VBLANK; CLUTTER_NOTE (BACKEND, "glXSwapIntervalSGI setup success"); #ifdef GLX_INTEL_swap_event /* GLX_INTEL_swap_event allows us to avoid blocking the CPU * while we wait for glXSwapBuffers to complete, and instead * we get an X event notifying us of completion... */ if (!(clutter_paint_debug_flags & CLUTTER_DEBUG_DISABLE_SWAP_EVENTS) && _cogl_check_extension ("GLX_INTEL_swap_event", glx_extensions)) { flags |= CLUTTER_FEATURE_SWAP_EVENTS; } #endif /* GLX_INTEL_swap_event */ goto vblank_setup_done; } CLUTTER_NOTE (BACKEND, "glXSwapIntervalSGI vblank setup failed"); } if (!use_dri && !(flags & CLUTTER_FEATURE_SYNC_TO_VBLANK) && _cogl_check_extension ("GLX_SGI_video_sync", glx_extensions)) { CLUTTER_NOTE (BACKEND, "attempting glXGetVideoSyncSGI vblank setup"); if ((backend_glx->get_video_sync != NULL) && (backend_glx->wait_video_sync != NULL)) { CLUTTER_NOTE (BACKEND, "glXGetVideoSyncSGI vblank setup success"); backend_glx->vblank_type = CLUTTER_VBLANK_GLX; flags |= CLUTTER_FEATURE_SYNC_TO_VBLANK; goto vblank_setup_done; } CLUTTER_NOTE (BACKEND, "glXGetVideoSyncSGI vblank setup failed"); } #ifdef __linux__ /* * DRI is really an extreme fallback -rumoured to work with Via chipsets */ if (!(flags & CLUTTER_FEATURE_SYNC_TO_VBLANK)) { CLUTTER_NOTE (BACKEND, "attempting DRI vblank setup"); backend_glx->dri_fd = open("/dev/dri/card0", O_RDWR); if (backend_glx->dri_fd >= 0) { CLUTTER_NOTE (BACKEND, "DRI vblank setup success"); backend_glx->vblank_type = CLUTTER_VBLANK_DRI; flags |= CLUTTER_FEATURE_SYNC_TO_VBLANK; goto vblank_setup_done; } CLUTTER_NOTE (BACKEND, "DRI vblank setup failed"); } #endif /* __linux__ */ CLUTTER_NOTE (BACKEND, "no use-able vblank mechanism found"); vblank_setup_done: if (_cogl_check_extension ("GLX_MESA_copy_sub_buffer", glx_extensions)) { backend_glx->copy_sub_buffer = (CopySubBufferProc) cogl_get_proc_address ("glXCopySubBufferMESA"); backend_glx->can_blit_sub_buffer = TRUE; backend_glx->blit_sub_buffer_is_synchronized = TRUE; } else if (_cogl_check_extension ("GL_EXT_framebuffer_blit", gl_extensions)) { CLUTTER_NOTE (BACKEND, "Using glBlitFramebuffer fallback for sub_buffer copies"); backend_glx->blit_framebuffer = (BlitFramebufferProc) cogl_get_proc_address ("glBlitFramebuffer"); backend_glx->can_blit_sub_buffer = TRUE; backend_glx->blit_sub_buffer_is_synchronized = FALSE; } CLUTTER_NOTE (BACKEND, "backend features checked"); return flags; }
static void clutter_stage_glx_redraw (ClutterStageWindow *stage_window) { ClutterBackendX11 *backend_x11; ClutterBackendGLX *backend_glx; ClutterStageX11 *stage_x11; ClutterStageGLX *stage_glx; GLXDrawable drawable; unsigned int video_sync_count; gboolean may_use_clipped_redraw; gboolean use_clipped_redraw; CLUTTER_STATIC_TIMER (painting_timer, "Redrawing", /* parent */ "Painting actors", "The time spent painting actors", 0 /* no application private data */); CLUTTER_STATIC_TIMER (swapbuffers_timer, "Redrawing", /* parent */ "glXSwapBuffers", "The time spent blocked by glXSwapBuffers", 0 /* no application private data */); CLUTTER_STATIC_TIMER (blit_sub_buffer_timer, "Redrawing", /* parent */ "glx_blit_sub_buffer", "The time spent in _glx_blit_sub_buffer", 0 /* no application private data */); stage_x11 = CLUTTER_STAGE_X11 (stage_window); if (stage_x11->xwin == None) return; stage_glx = CLUTTER_STAGE_GLX (stage_window); backend_x11 = stage_x11->backend; backend_glx = CLUTTER_BACKEND_GLX (backend_x11); CLUTTER_TIMER_START (_clutter_uprof_context, painting_timer); if (G_LIKELY (backend_glx->can_blit_sub_buffer) && /* NB: a zero width redraw clip == full stage redraw */ stage_glx->bounding_redraw_clip.width != 0 && /* some drivers struggle to get going and produce some junk * frames when starting up... */ G_LIKELY (stage_glx->frame_count > 3) && /* While resizing a window clipped redraws are disabled to avoid * artefacts. See clutter-event-x11.c:event_translate for a * detailed explanation */ G_LIKELY (stage_x11->clipped_redraws_cool_off == 0)) { may_use_clipped_redraw = TRUE; } else may_use_clipped_redraw = FALSE; if (may_use_clipped_redraw && G_LIKELY (!(clutter_paint_debug_flags & CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS))) use_clipped_redraw = TRUE; else use_clipped_redraw = FALSE; if (use_clipped_redraw) { CLUTTER_NOTE (CLIPPING, "Stage clip pushed: x=%d, y=%d, width=%d, height=%d\n", stage_glx->bounding_redraw_clip.x, stage_glx->bounding_redraw_clip.y, stage_glx->bounding_redraw_clip.width, stage_glx->bounding_redraw_clip.height); cogl_clip_push_window_rectangle (stage_glx->bounding_redraw_clip.x, stage_glx->bounding_redraw_clip.y, stage_glx->bounding_redraw_clip.width, stage_glx->bounding_redraw_clip.height); _clutter_stage_do_paint (stage_x11->wrapper, &stage_glx->bounding_redraw_clip); cogl_clip_pop (); } else { CLUTTER_NOTE (CLIPPING, "Unclipped stage paint\n"); _clutter_stage_do_paint (stage_x11->wrapper, NULL); } if (may_use_clipped_redraw && G_UNLIKELY ((clutter_paint_debug_flags & CLUTTER_DEBUG_REDRAWS))) { static CoglMaterial *outline = NULL; ClutterGeometry *clip = &stage_glx->bounding_redraw_clip; ClutterActor *actor = CLUTTER_ACTOR (stage_x11->wrapper); CoglHandle vbo; float x_1 = clip->x; float x_2 = clip->x + clip->width; float y_1 = clip->y; float y_2 = clip->y + clip->height; float quad[8] = { x_1, y_1, x_2, y_1, x_2, y_2, x_1, y_2 }; CoglMatrix modelview; if (outline == NULL) { outline = cogl_material_new (); cogl_material_set_color4ub (outline, 0xff, 0x00, 0x00, 0xff); } vbo = cogl_vertex_buffer_new (4); cogl_vertex_buffer_add (vbo, "gl_Vertex", 2, /* n_components */ COGL_ATTRIBUTE_TYPE_FLOAT, FALSE, /* normalized */ 0, /* stride */ quad); cogl_vertex_buffer_submit (vbo); cogl_push_matrix (); cogl_matrix_init_identity (&modelview); _clutter_actor_apply_modelview_transform (actor, &modelview); cogl_set_modelview_matrix (&modelview); cogl_set_source (outline); cogl_vertex_buffer_draw (vbo, COGL_VERTICES_MODE_LINE_LOOP, 0 , 4); cogl_pop_matrix (); cogl_object_unref (vbo); } cogl_flush (); CLUTTER_TIMER_STOP (_clutter_uprof_context, painting_timer); drawable = stage_glx->glxwin ? stage_glx->glxwin : stage_x11->xwin; /* If we might ever use _clutter_backend_glx_blit_sub_buffer then we * always need to keep track of the video_sync_count so that we can * throttle blits. * * Note: we get the count *before* we issue any glXCopySubBuffer or * blit_sub_buffer request in case the count would go up before * returning control to us. */ if (backend_glx->can_blit_sub_buffer && backend_glx->get_video_sync) backend_glx->get_video_sync (&video_sync_count); /* push on the screen */ if (use_clipped_redraw) { ClutterGeometry *clip = &stage_glx->bounding_redraw_clip; ClutterGeometry copy_area; ClutterActor *actor; CLUTTER_NOTE (BACKEND, "_glx_blit_sub_buffer (window: 0x%lx, " "x: %d, y: %d, " "width: %d, height: %d)", (unsigned long) drawable, stage_glx->bounding_redraw_clip.x, stage_glx->bounding_redraw_clip.y, stage_glx->bounding_redraw_clip.width, stage_glx->bounding_redraw_clip.height); /* XXX: It seems there will be a race here in that the stage * window may be resized before glXCopySubBufferMESA is handled * and so we may copy the wrong region. I can't really see how * we can handle this with the current state of X but at least * in this case a full redraw should be queued by the resize * anyway so it should only exhibit temporary artefacts. */ actor = CLUTTER_ACTOR (stage_x11->wrapper); copy_area.y = clutter_actor_get_height (actor) - clip->y - clip->height; copy_area.x = clip->x; copy_area.width = clip->width; copy_area.height = clip->height; /* glXCopySubBufferMESA and glBlitFramebuffer are not integrated * with the glXSwapIntervalSGI mechanism which we usually use to * throttle the Clutter framerate to the vertical refresh and so * we have to manually wait for the vblank period... */ /* Here 'is_synchronized' only means that the blit won't cause a * tear, ie it won't prevent multiple blits per retrace if they * can all be performed in the blanking period. If that's the * case then we still want to use the vblank sync menchanism but * we only need it to throttle redraws. */ if (!backend_glx->blit_sub_buffer_is_synchronized) { /* XXX: note that glXCopySubBuffer, at least for Intel, is * synchronized with the vblank but glBlitFramebuffer may * not be so we use the same scheme we do when calling * glXSwapBuffers without the swap_control extension and * call glFinish () before waiting for the vblank period. * * See where we call glXSwapBuffers for more details. */ glFinish (); wait_for_vblank (backend_glx); } else if (backend_glx->get_video_sync) { /* If we have the GLX_SGI_video_sync extension then we can * be a bit smarter about how we throttle blits by avoiding * any waits if we can see that the video sync count has * already progressed. */ if (backend_glx->last_video_sync_count == video_sync_count) wait_for_vblank (backend_glx); } else wait_for_vblank (backend_glx); CLUTTER_TIMER_START (_clutter_uprof_context, blit_sub_buffer_timer); _clutter_backend_glx_blit_sub_buffer (backend_glx, drawable, copy_area.x, copy_area.y, copy_area.width, copy_area.height); CLUTTER_TIMER_STOP (_clutter_uprof_context, blit_sub_buffer_timer); } else { CLUTTER_NOTE (BACKEND, "glXSwapBuffers (display: %p, window: 0x%lx)", backend_x11->xdpy, (unsigned long) drawable); /* If we have GLX swap buffer events then glXSwapBuffers will return * immediately and we need to track that there is a swap in * progress... */ if (clutter_feature_available (CLUTTER_FEATURE_SWAP_EVENTS)) stage_glx->pending_swaps++; if (backend_glx->vblank_type != CLUTTER_VBLANK_GLX_SWAP && backend_glx->vblank_type != CLUTTER_VBLANK_NONE) { /* If we are going to wait for VBLANK manually, we not only * need to flush out pending drawing to the GPU before we * sleep, we need to wait for it to finish. Otherwise, we * may end up with the situation: * * - We finish drawing - GPU drawing continues * - We go to sleep - GPU drawing continues * VBLANK - We call glXSwapBuffers - GPU drawing continues * - GPU drawing continues * - Swap buffers happens * * Producing a tear. Calling glFinish() first will cause us * to properly wait for the next VBLANK before we swap. This * obviously does not happen when we use _GLX_SWAP and let * the driver do the right thing */ glFinish (); wait_for_vblank (backend_glx); } CLUTTER_TIMER_START (_clutter_uprof_context, swapbuffers_timer); glXSwapBuffers (backend_x11->xdpy, drawable); CLUTTER_TIMER_STOP (_clutter_uprof_context, swapbuffers_timer); _cogl_swap_buffers_notify (); } backend_glx->last_video_sync_count = video_sync_count; /* reset the redraw clipping for the next paint... */ stage_glx->initialized_redraw_clip = FALSE; stage_glx->frame_count++; }
static gboolean clutter_stage_glx_realize (ClutterStageWindow *stage_window) { ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window); ClutterStageGLX *stage_glx = CLUTTER_STAGE_GLX (stage_window); ClutterBackend *backend; ClutterBackendGLX *backend_glx; ClutterBackendX11 *backend_x11; GError *error; CLUTTER_NOTE (ACTOR, "Realizing stage '%s' [%p]", G_OBJECT_TYPE_NAME (stage_window), stage_window); backend = clutter_get_default_backend (); backend_glx = CLUTTER_BACKEND_GLX (backend); backend_x11 = CLUTTER_BACKEND_X11 (backend); if (stage_x11->xwin == None) { XSetWindowAttributes xattr; unsigned long mask; XVisualInfo *xvisinfo; gfloat width, height; CLUTTER_NOTE (MISC, "Creating stage X window"); xvisinfo = clutter_backend_x11_get_visual_info (backend_x11); if (xvisinfo == NULL) { g_critical ("Unable to find suitable GL visual."); return FALSE; } /* window attributes */ xattr.background_pixel = WhitePixel (backend_x11->xdpy, backend_x11->xscreen_num); xattr.border_pixel = 0; xattr.colormap = XCreateColormap (backend_x11->xdpy, backend_x11->xwin_root, xvisinfo->visual, AllocNone); mask = CWBorderPixel | CWColormap; /* Call get_size - this will either get the geometry size (which * before we create the window is set to 640x480), or if a size * is set, it will get that. This lets you set a size on the * stage before it's realized. */ clutter_actor_get_size (CLUTTER_ACTOR (stage_x11->wrapper), &width, &height); stage_x11->xwin_width = (gint)width; stage_x11->xwin_height = (gint)height; stage_x11->xwin = XCreateWindow (backend_x11->xdpy, backend_x11->xwin_root, 0, 0, stage_x11->xwin_width, stage_x11->xwin_height, 0, xvisinfo->depth, InputOutput, xvisinfo->visual, mask, &xattr); CLUTTER_NOTE (BACKEND, "Stage [%p], window: 0x%x, size: %dx%d", stage_window, (unsigned int) stage_x11->xwin, stage_x11->xwin_width, stage_x11->xwin_height); XFree (xvisinfo); } if (stage_glx->glxwin == None) { int major; int minor; GLXFBConfig config; /* Try and create a GLXWindow to use with extensions dependent on * GLX versions >= 1.3 that don't accept regular X Windows as GLX * drawables. */ if (glXQueryVersion (backend_x11->xdpy, &major, &minor) && major == 1 && minor >= 3 && _clutter_backend_glx_get_fbconfig (backend_glx, &config)) { stage_glx->glxwin = glXCreateWindow (backend_x11->xdpy, config, stage_x11->xwin, NULL); } } if (clutter_x11_has_event_retrieval ()) { if (clutter_x11_has_xinput ()) { XSelectInput (backend_x11->xdpy, stage_x11->xwin, StructureNotifyMask | FocusChangeMask | ExposureMask | KeyPressMask | KeyReleaseMask | EnterWindowMask | LeaveWindowMask | PropertyChangeMask); #ifdef HAVE_XINPUT _clutter_x11_select_events (stage_x11->xwin); #endif } else XSelectInput (backend_x11->xdpy, stage_x11->xwin, StructureNotifyMask | FocusChangeMask | ExposureMask | PointerMotionMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask | PropertyChangeMask); #ifdef GLX_INTEL_swap_event if (clutter_feature_available (CLUTTER_FEATURE_SWAP_EVENTS)) { GLXDrawable drawable = stage_glx->glxwin ? stage_glx->glxwin : stage_x11->xwin; glXSelectEvent (backend_x11->xdpy, drawable, GLX_BUFFER_SWAP_COMPLETE_INTEL_MASK); } #endif /* GLX_INTEL_swap_event */ } /* no user resize.. */ clutter_stage_x11_fix_window_size (stage_x11, stage_x11->xwin_width, stage_x11->xwin_height); clutter_stage_x11_set_wm_protocols (stage_x11); /* ask for a context; a no-op, if a context already exists */ error = NULL; _clutter_backend_create_context (backend, &error); if (error) { g_critical ("Unable to realize stage: %s", error->message); g_error_free (error); return FALSE; } CLUTTER_NOTE (BACKEND, "Successfully realized stage"); /* chain up to the StageX11 implementation */ return clutter_stage_glx_parent_iface->realize (stage_window); }
static void clutter_glx_texture_pixmap_create_glx_pixmap (ClutterGLXTexturePixmap *texture) { ClutterGLXTexturePixmapPrivate *priv = texture->priv; GLXPixmap glx_pixmap = None; int attribs[7], i = 0, mipmap = 0; GLXFBConfig *fbconfig; Display *dpy; guint depth; Pixmap pixmap; guint pixmap_width, pixmap_height; ClutterBackendGLX *backend_glx; ClutterTextureQuality quality; CLUTTER_NOTE (TEXTURE, "Creating GLXPixmap"); backend_glx = CLUTTER_BACKEND_GLX(clutter_get_default_backend ()); dpy = clutter_x11_get_default_display (); if (priv->use_fallback == TRUE || !clutter_glx_texture_pixmap_using_extension (texture)) goto cleanup; priv->use_fallback = FALSE; g_object_get (texture, "pixmap-width", &pixmap_width, "pixmap-height", &pixmap_height, "pixmap-depth", &depth, "pixmap", &pixmap, NULL); if (!pixmap) { goto cleanup; } fbconfig = get_fbconfig_for_depth (depth); if (!fbconfig) { g_warning ("Could not find an FBConfig for selected pixmap"); goto cleanup; } attribs[i++] = GLX_TEXTURE_FORMAT_EXT; if (depth == 24) { attribs[i++] = GLX_TEXTURE_FORMAT_RGB_EXT; } else if (depth == 32) { attribs[i++] = GLX_TEXTURE_FORMAT_RGBA_EXT; } else { g_warning ("Pixmap with depth bellow 24 are not supported"); goto cleanup; } quality = clutter_texture_get_filter_quality (CLUTTER_TEXTURE (texture)); if (quality == CLUTTER_TEXTURE_QUALITY_HIGH) mipmap = 1; attribs[i++] = GLX_MIPMAP_TEXTURE_EXT; attribs[i++] = mipmap; attribs[i++] = GLX_TEXTURE_TARGET_EXT; attribs[i++] = GLX_TEXTURE_2D_EXT; attribs[i++] = None; clutter_x11_trap_x_errors (); glx_pixmap = glXCreatePixmap (dpy, *fbconfig, pixmap, attribs); XSync (dpy, FALSE); if (clutter_x11_untrap_x_errors ()) { CLUTTER_NOTE (TEXTURE, "Failed to create GLXPixmap"); /* Make sure we don't think the call actually succeeded */ glx_pixmap = None; } g_free (fbconfig); cleanup: if (priv->glx_pixmap) clutter_glx_texture_pixmap_free_glx_pixmap (texture); if (glx_pixmap != None) { priv->glx_pixmap = glx_pixmap; create_cogl_texture (CLUTTER_TEXTURE (texture), pixmap_width, pixmap_height); CLUTTER_NOTE (TEXTURE, "Created GLXPixmap"); return; } else { priv->use_fallback = TRUE; priv->glx_pixmap = None; /* Some fucky logic here - we've fallen back and need to make sure * we realize here.. */ clutter_actor_realize (CLUTTER_ACTOR (texture)); } }
static void clutter_backend_glx_ensure_context (ClutterBackend *backend, ClutterStage *stage) { ClutterStageWindow *impl; /* if there is no stage, the stage is being destroyed or it has no * implementation attached to it then we clear the GL context */ if (stage == NULL || (CLUTTER_PRIVATE_FLAGS (stage) & CLUTTER_ACTOR_IN_DESTRUCTION) || ((impl = _clutter_stage_get_window (stage)) == NULL)) { ClutterBackendX11 *backend_x11; backend_x11 = CLUTTER_BACKEND_X11 (backend); CLUTTER_NOTE (MULTISTAGE, "Clearing all context"); glXMakeCurrent (backend_x11->xdpy, None, NULL); } else { ClutterBackendGLX *backend_glx; ClutterStageGLX *stage_glx; ClutterStageX11 *stage_x11; g_assert (impl != NULL); CLUTTER_NOTE (MULTISTAGE, "Setting context for stage of type %s [%p]", g_type_name (G_OBJECT_TYPE (impl)), impl); stage_glx = CLUTTER_STAGE_GLX (impl); stage_x11 = CLUTTER_STAGE_X11 (impl); backend_glx = CLUTTER_BACKEND_GLX (backend); /* no GL context to set */ if (backend_glx->gl_context == None) return; clutter_x11_trap_x_errors (); /* we might get here inside the final dispose cycle, so we * need to handle this gracefully */ if (stage_x11->xwin == None) { ClutterBackendX11 *backend_x11; backend_x11 = CLUTTER_BACKEND_X11 (backend); CLUTTER_NOTE (MULTISTAGE, "Received a stale stage, clearing all context"); glXMakeCurrent (backend_x11->xdpy, None, NULL); } else { CLUTTER_NOTE (BACKEND, "MakeCurrent dpy: %p, window: 0x%x (%s), context: %p", stage_x11->xdpy, (int) stage_x11->xwin, stage_x11->is_foreign_xwin ? "foreign" : "native", backend_glx->gl_context); glXMakeCurrent (stage_x11->xdpy, stage_x11->xwin, backend_glx->gl_context); } if (clutter_x11_untrap_x_errors ()) g_critical ("Unable to make the stage window 0x%x the current " "GLX drawable", (int) stage_x11->xwin); } }
static ClutterFeatureFlags clutter_backend_glx_get_features (ClutterBackend *backend) { ClutterBackendGLX *backend_glx = CLUTTER_BACKEND_GLX (backend); const gchar *glx_extensions = NULL; ClutterFeatureFlags flags; flags = clutter_backend_x11_get_features (backend); flags |= CLUTTER_FEATURE_STAGE_MULTIPLE; /* this will make sure that the GL context exists and * it's bound to a drawable */ g_assert (backend_glx->gl_context != None); g_assert (glXGetCurrentDrawable () != None); CLUTTER_NOTE (BACKEND, "Checking features\n" "GL_VENDOR: %s\n" "GL_RENDERER: %s\n" "GL_VERSION: %s\n" "GL_EXTENSIONS: %s\n", glGetString (GL_VENDOR), glGetString (GL_RENDERER), glGetString (GL_VERSION), glGetString (GL_EXTENSIONS)); glx_extensions = glXQueryExtensionsString (clutter_x11_get_default_display (), clutter_x11_get_default_screen ()); CLUTTER_NOTE (BACKEND, "GLX Extensions: %s", glx_extensions); /* First check for explicit disabling or it set elsewhere (eg NVIDIA) */ if (getenv("__GL_SYNC_TO_VBLANK") || check_vblank_env ("none")) { CLUTTER_NOTE (BACKEND, "vblank sync: disabled at user request"); } else { /* We try two GL vblank syncing mechanisms. * glXSwapIntervalSGI is tried first, then glXGetVideoSyncSGI. * * glXSwapIntervalSGI is known to work with Mesa and in particular * the Intel drivers. glXGetVideoSyncSGI has serious problems with * Intel drivers causing terrible frame rate so it only tried as a * fallback. * * How well glXGetVideoSyncSGI works with other driver (ATI etc) needs * to be investigated. glXGetVideoSyncSGI on ATI at least seems to have * no effect. */ if (!check_vblank_env ("dri") && cogl_check_extension ("GLX_SGI_swap_control", glx_extensions)) { backend_glx->swap_interval = (SwapIntervalProc) cogl_get_proc_address ("glXSwapIntervalSGI"); CLUTTER_NOTE (BACKEND, "attempting glXSwapIntervalSGI vblank setup"); if (backend_glx->swap_interval != NULL) { if (backend_glx->swap_interval (1) == 0) { backend_glx->vblank_type = CLUTTER_VBLANK_GLX_SWAP; flags |= CLUTTER_FEATURE_SYNC_TO_VBLANK; CLUTTER_NOTE (BACKEND, "glXSwapIntervalSGI setup success"); } } if (!(flags & CLUTTER_FEATURE_SYNC_TO_VBLANK)) CLUTTER_NOTE (BACKEND, "glXSwapIntervalSGI vblank setup failed"); } if (!check_vblank_env ("dri") && !(flags & CLUTTER_FEATURE_SYNC_TO_VBLANK) && cogl_check_extension ("GLX_SGI_video_sync", glx_extensions)) { CLUTTER_NOTE (BACKEND, "attempting glXGetVideoSyncSGI vblank setup"); backend_glx->get_video_sync = (GetVideoSyncProc) cogl_get_proc_address ("glXGetVideoSyncSGI"); backend_glx->wait_video_sync = (WaitVideoSyncProc) cogl_get_proc_address ("glXWaitVideoSyncSGI"); if ((backend_glx->get_video_sync != NULL) && (backend_glx->wait_video_sync != NULL)) { CLUTTER_NOTE (BACKEND, "glXGetVideoSyncSGI vblank setup success"); backend_glx->vblank_type = CLUTTER_VBLANK_GLX; flags |= CLUTTER_FEATURE_SYNC_TO_VBLANK; } if (!(flags & CLUTTER_FEATURE_SYNC_TO_VBLANK)) CLUTTER_NOTE (BACKEND, "glXGetVideoSyncSGI vblank setup failed"); } #ifdef __linux__ /* * DRI is really an extreme fallback -rumoured to work with Via chipsets */ if (!(flags & CLUTTER_FEATURE_SYNC_TO_VBLANK)) { CLUTTER_NOTE (BACKEND, "attempting DRI vblank setup"); backend_glx->dri_fd = open("/dev/dri/card0", O_RDWR); if (backend_glx->dri_fd >= 0) { CLUTTER_NOTE (BACKEND, "DRI vblank setup success"); backend_glx->vblank_type = CLUTTER_VBLANK_DRI; flags |= CLUTTER_FEATURE_SYNC_TO_VBLANK; } if (!(flags & CLUTTER_FEATURE_SYNC_TO_VBLANK)) CLUTTER_NOTE (BACKEND, "DRI vblank setup failed"); } #endif if (!(flags & CLUTTER_FEATURE_SYNC_TO_VBLANK)) { CLUTTER_NOTE (BACKEND, "no use-able vblank mechanism found"); } } CLUTTER_NOTE (MISC, "backend features checked"); return flags; }