Compare commits
	
		
			5 Commits
		
	
	
		
			3.17.4
			...
			wip/xtogls
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 3ac563307e | ||
|   | 24956fadcc | ||
|   | fc5de438b5 | ||
|   | 94cee3ef09 | ||
|   | 1e6b042778 | 
| @@ -324,6 +324,11 @@ fi | ||||
|  | ||||
| GTK_DOC_CHECK([1.15], [--flavour no-tmpl]) | ||||
|  | ||||
| AC_CHECK_DECL([GL_EXT_x11_sync_object], | ||||
|               [], | ||||
|               [AC_MSG_ERROR([GL_EXT_x11_sync_object definition not found, please update your GL headers])], | ||||
|               [#include <GL/glx.h>]) | ||||
|  | ||||
| #### Warnings (last since -Werror can disturb other tests) | ||||
|  | ||||
| # Stay command-line compatible with the gnome-common configure option. Here | ||||
|   | ||||
| @@ -118,6 +118,8 @@ libmutter_la_SOURCES =				\ | ||||
| 	compositor/meta-surface-actor-wayland.h	\ | ||||
| 	compositor/meta-stage.h			\ | ||||
| 	compositor/meta-stage.c			\ | ||||
| 	compositor/meta-sync-ring.c		\ | ||||
| 	compositor/meta-sync-ring.h		\ | ||||
| 	compositor/meta-texture-rectangle.c	\ | ||||
| 	compositor/meta-texture-rectangle.h	\ | ||||
| 	compositor/meta-texture-tower.c		\ | ||||
|   | ||||
| @@ -15,7 +15,8 @@ struct _MetaCompositor | ||||
| { | ||||
|   MetaDisplay    *display; | ||||
|  | ||||
|   guint           repaint_func_id; | ||||
|   guint           pre_paint_func_id; | ||||
|   guint           post_paint_func_id; | ||||
|  | ||||
|   gint64          server_time_query_time; | ||||
|   gint64          server_time_offset; | ||||
| @@ -38,6 +39,9 @@ struct _MetaCompositor | ||||
|   gint                   switch_workspace_in_progress; | ||||
|  | ||||
|   MetaPluginManager *plugin_mgr; | ||||
|  | ||||
|   gboolean frame_has_updated_xsurfaces; | ||||
|   gboolean have_x11_sync_object; | ||||
| }; | ||||
|  | ||||
| /* Wait 2ms after vblank before starting to draw next frame */ | ||||
|   | ||||
| @@ -79,6 +79,7 @@ | ||||
| #include "frame.h" | ||||
| #include <X11/extensions/shape.h> | ||||
| #include <X11/extensions/Xcomposite.h> | ||||
| #include "meta-sync-ring.h" | ||||
|  | ||||
| #include "backends/meta-backend.h" | ||||
| #include "backends/x11/meta-backend-x11.h" | ||||
| @@ -136,7 +137,11 @@ meta_switch_workspace_completed (MetaCompositor *compositor) | ||||
| void | ||||
| meta_compositor_destroy (MetaCompositor *compositor) | ||||
| { | ||||
|   clutter_threads_remove_repaint_func (compositor->repaint_func_id); | ||||
|   clutter_threads_remove_repaint_func (compositor->pre_paint_func_id); | ||||
|   clutter_threads_remove_repaint_func (compositor->post_paint_func_id); | ||||
|  | ||||
|   if (compositor->have_x11_sync_object) | ||||
|     meta_sync_ring_destroy (); | ||||
| } | ||||
|  | ||||
| static void | ||||
| @@ -146,6 +151,8 @@ process_damage (MetaCompositor     *compositor, | ||||
| { | ||||
|   MetaWindowActor *window_actor = META_WINDOW_ACTOR (meta_window_get_compositor_private (window)); | ||||
|   meta_window_actor_process_x11_damage (window_actor, event); | ||||
|  | ||||
|   compositor->frame_has_updated_xsurfaces = TRUE; | ||||
| } | ||||
|  | ||||
| static Window | ||||
| @@ -569,6 +576,8 @@ meta_compositor_manage (MetaCompositor *compositor) | ||||
|        * contents until we show the stage. | ||||
|        */ | ||||
|       XMapWindow (xdisplay, compositor->output); | ||||
|  | ||||
|       compositor->have_x11_sync_object = meta_sync_ring_init (display); | ||||
|     } | ||||
|  | ||||
|   redirect_windows (display->screen); | ||||
| @@ -780,6 +789,10 @@ meta_compositor_process_event (MetaCompositor *compositor, | ||||
|   if (!meta_is_wayland_compositor () && event->type == MapNotify) | ||||
|     clutter_x11_handle_event (event); | ||||
|  | ||||
|   if (compositor->have_x11_sync_object && | ||||
|       event->type == (compositor->display->xsync_event_base + XSyncAlarmNotify)) | ||||
|     meta_sync_ring_handle_event ((XSyncAlarmNotifyEvent *) event); | ||||
|  | ||||
|   /* The above handling is basically just "observing" the events, so we return | ||||
|    * FALSE to indicate that the event should not be filtered out; if we have | ||||
|    * GTK+ windows in the same process, GTK+ needs the ConfigureNotify event, for example. | ||||
| @@ -1125,11 +1138,12 @@ frame_callback (CoglOnscreen  *onscreen, | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void | ||||
| pre_paint_windows (MetaCompositor *compositor) | ||||
| static gboolean | ||||
| meta_pre_paint_func (gpointer data) | ||||
| { | ||||
|   GList *l; | ||||
|   MetaWindowActor *top_window; | ||||
|   MetaCompositor *compositor = data; | ||||
|  | ||||
|   if (compositor->onscreen == NULL) | ||||
|     { | ||||
| @@ -1141,7 +1155,7 @@ pre_paint_windows (MetaCompositor *compositor) | ||||
|     } | ||||
|  | ||||
|   if (compositor->windows == NULL) | ||||
|     return; | ||||
|     return TRUE; | ||||
|  | ||||
|   top_window = g_list_last (compositor->windows)->data; | ||||
|  | ||||
| @@ -1153,13 +1167,53 @@ pre_paint_windows (MetaCompositor *compositor) | ||||
|  | ||||
|   for (l = compositor->windows; l; l = l->next) | ||||
|     meta_window_actor_pre_paint (l->data); | ||||
|  | ||||
|   if (compositor->frame_has_updated_xsurfaces) | ||||
|     { | ||||
|       /* We need to make sure that any X drawing that happens before | ||||
|        * the XDamageSubtract() for each window above is visible to | ||||
|        * subsequent GL rendering; the standardized way to do this is | ||||
|        * GL_EXT_X11_sync_object. Since this isn't implemented yet in | ||||
|        * mesa, we also have a path that relies on the implementation | ||||
|        * of the open source drivers. | ||||
|        * | ||||
|        * Anything else, we just hope for the best. | ||||
|        * | ||||
|        * Xorg and open source driver specifics: | ||||
|        * | ||||
|        * The X server makes sure to flush drawing to the kernel before | ||||
|        * sending out damage events, but since we use | ||||
|        * DamageReportBoundingBox there may be drawing between the last | ||||
|        * damage event and the XDamageSubtract() that needs to be | ||||
|        * flushed as well. | ||||
|        * | ||||
|        * Xorg always makes sure that drawing is flushed to the kernel | ||||
|        * before writing events or responses to the client, so any | ||||
|        * round trip request at this point is sufficient to flush the | ||||
|        * GLX buffers. | ||||
|        */ | ||||
|       if (compositor->have_x11_sync_object) | ||||
|         compositor->have_x11_sync_object = meta_sync_ring_insert_wait (); | ||||
|       else | ||||
|         XSync (compositor->display->xdisplay, False); | ||||
|     } | ||||
|  | ||||
|   return TRUE; | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| meta_repaint_func (gpointer data) | ||||
| meta_post_paint_func (gpointer data) | ||||
| { | ||||
|   MetaCompositor *compositor = data; | ||||
|   pre_paint_windows (compositor); | ||||
|  | ||||
|   if (compositor->frame_has_updated_xsurfaces) | ||||
|     { | ||||
|       if (compositor->have_x11_sync_object) | ||||
|         compositor->have_x11_sync_object = meta_sync_ring_after_frame (); | ||||
|  | ||||
|       compositor->frame_has_updated_xsurfaces = FALSE; | ||||
|     } | ||||
|  | ||||
|   return TRUE; | ||||
| } | ||||
|  | ||||
| @@ -1198,10 +1252,16 @@ meta_compositor_new (MetaDisplay *display) | ||||
|                     G_CALLBACK (on_shadow_factory_changed), | ||||
|                     compositor); | ||||
|  | ||||
|   compositor->repaint_func_id = clutter_threads_add_repaint_func (meta_repaint_func, | ||||
|                                                                   compositor, | ||||
|                                                                   NULL); | ||||
|  | ||||
|   compositor->pre_paint_func_id = | ||||
|     clutter_threads_add_repaint_func_full (CLUTTER_REPAINT_FLAGS_PRE_PAINT, | ||||
|                                            meta_pre_paint_func, | ||||
|                                            compositor, | ||||
|                                            NULL); | ||||
|   compositor->post_paint_func_id = | ||||
|     clutter_threads_add_repaint_func_full (CLUTTER_REPAINT_FLAGS_POST_PAINT, | ||||
|                                            meta_post_paint_func, | ||||
|                                            compositor, | ||||
|                                            NULL); | ||||
|   return compositor; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -238,26 +238,6 @@ meta_surface_actor_x11_pre_paint (MetaSurfaceActor *actor) | ||||
|       XDamageSubtract (xdisplay, priv->damage, None, None); | ||||
|       meta_error_trap_pop (display); | ||||
|  | ||||
|       /* We need to make sure that any X drawing that happens before the | ||||
|        * XDamageSubtract() above is visible to subsequent GL rendering; | ||||
|        * the only standardized way to do this is EXT_x11_sync_object, | ||||
|        * which isn't yet widely available. For now, we count on details | ||||
|        * of Xorg and the open source drivers, and hope for the best | ||||
|        * otherwise. | ||||
|        * | ||||
|        * Xorg and open source driver specifics: | ||||
|        * | ||||
|        * The X server makes sure to flush drawing to the kernel before | ||||
|        * sending out damage events, but since we use DamageReportBoundingBox | ||||
|        * there may be drawing between the last damage event and the | ||||
|        * XDamageSubtract() that needs to be flushed as well. | ||||
|        * | ||||
|        * Xorg always makes sure that drawing is flushed to the kernel | ||||
|        * before writing events or responses to the client, so any round trip | ||||
|        * request at this point is sufficient to flush the GLX buffers. | ||||
|        */ | ||||
|       XSync (xdisplay, False); | ||||
|  | ||||
|       priv->received_damage = FALSE; | ||||
|     } | ||||
|  | ||||
|   | ||||
							
								
								
									
										574
									
								
								src/compositor/meta-sync-ring.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										574
									
								
								src/compositor/meta-sync-ring.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,574 @@ | ||||
| /* | ||||
|  * This is based on an original C++ implementation for compiz that | ||||
|  * carries the following copyright notice: | ||||
|  * | ||||
|  * | ||||
|  * Copyright © 2011 NVIDIA Corporation | ||||
|  * | ||||
|  * Permission to use, copy, modify, distribute, and sell this software | ||||
|  * and its documentation for any purpose is hereby granted without | ||||
|  * fee, provided that the above copyright notice appear in all copies | ||||
|  * and that both that copyright notice and this permission notice | ||||
|  * appear in supporting documentation, and that the name of NVIDIA | ||||
|  * Corporation not be used in advertising or publicity pertaining to | ||||
|  * distribution of the software without specific, written prior | ||||
|  * permission.  NVIDIA Corporation makes no representations about the | ||||
|  * suitability of this software for any purpose. It is provided "as | ||||
|  * is" without express or implied warranty. | ||||
|  * | ||||
|  * NVIDIA CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS | ||||
|  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND | ||||
|  * FITNESS, IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY | ||||
|  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||
|  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN | ||||
|  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING | ||||
|  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS | ||||
|  * SOFTWARE. | ||||
|  * | ||||
|  * Authors: James Jones <jajones@nvidia.com> | ||||
|  */ | ||||
|  | ||||
| #include <string.h> | ||||
|  | ||||
| #include <GL/gl.h> | ||||
| #include <GL/glx.h> | ||||
|  | ||||
| #include <cogl/cogl.h> | ||||
|  | ||||
| #include <meta/display.h> | ||||
| #include <meta/util.h> | ||||
|  | ||||
| #include "meta-sync-ring.h" | ||||
|  | ||||
| /* Theory of operation: | ||||
|  * | ||||
|  * We use a ring of NUM_SYNCS fence objects. On each frame we advance | ||||
|  * to the next fence in the ring. For each fence we do: | ||||
|  * | ||||
|  * 1. fence is XSyncTriggerFence()'d and glWaitSync()'d | ||||
|  * 2. NUM_SYNCS / 2 frames later, fence should be triggered | ||||
|  * 3. fence is XSyncResetFence()'d | ||||
|  * 4. NUM_SYNCS / 2 frames later, fence should be reset | ||||
|  * 5. go back to 1 and re-use fence | ||||
|  * | ||||
|  * glClientWaitSync() and XAlarms are used in steps 2 and 4, | ||||
|  * respectively, to double-check the expectections. | ||||
|  */ | ||||
|  | ||||
| #define NUM_SYNCS 10 | ||||
| #define MAX_SYNC_WAIT_TIME (1 * 1000 * 1000 * 1000) /* one sec */ | ||||
| #define MAX_REBOOT_ATTEMPTS 2 | ||||
|  | ||||
| typedef enum | ||||
| { | ||||
|   META_SYNC_STATE_READY, | ||||
|   META_SYNC_STATE_WAITING, | ||||
|   META_SYNC_STATE_DONE, | ||||
|   META_SYNC_STATE_RESET_PENDING, | ||||
| } MetaSyncState; | ||||
|  | ||||
| typedef struct | ||||
| { | ||||
|   Display *xdisplay; | ||||
|  | ||||
|   XSyncFence xfence; | ||||
|   GLsync glsync; | ||||
|  | ||||
|   XSyncCounter xcounter; | ||||
|   XSyncAlarm xalarm; | ||||
|   XSyncValue next_counter_value; | ||||
|  | ||||
|   MetaSyncState state; | ||||
| } MetaSync; | ||||
|  | ||||
| typedef struct | ||||
| { | ||||
|   MetaDisplay *display; | ||||
|  | ||||
|   GHashTable *alarm_to_sync; | ||||
|  | ||||
|   MetaSync *syncs_array[NUM_SYNCS]; | ||||
|   guint current_sync_idx; | ||||
|   MetaSync *current_sync; | ||||
|   guint warmup_syncs; | ||||
|  | ||||
|   guint reboots; | ||||
| } MetaSyncRing; | ||||
|  | ||||
| static MetaSyncRing meta_sync_ring = { 0 }; | ||||
|  | ||||
| static XSyncValue SYNC_VALUE_ZERO; | ||||
| static XSyncValue SYNC_VALUE_ONE; | ||||
|  | ||||
| static void             (*meta_gl_get_integerv) (GLenum  pname, | ||||
|                                                  GLint  *params); | ||||
| static const char*      (*meta_gl_get_stringi) (GLenum name, | ||||
|                                                 GLuint index); | ||||
| static void             (*meta_gl_delete_sync) (GLsync sync); | ||||
| static GLenum           (*meta_gl_client_wait_sync) (GLsync sync, | ||||
|                                                      GLbitfield flags, | ||||
|                                                      GLuint64 timeout); | ||||
| static void             (*meta_gl_wait_sync) (GLsync sync, | ||||
|                                               GLbitfield flags, | ||||
|                                               GLuint64 timeout); | ||||
| static GLsync           (*meta_gl_import_sync) (GLenum external_sync_type, | ||||
|                                                 GLintptr external_sync, | ||||
|                                                 GLbitfield flags); | ||||
|  | ||||
| static GLenum           (*meta_gl_get_error) (void); | ||||
| static GLboolean        (*meta_gl_is_sync) (GLsync sync); | ||||
|  | ||||
| static MetaSyncRing * | ||||
| meta_sync_ring_get (void) | ||||
| { | ||||
|   if (meta_sync_ring.reboots > MAX_REBOOT_ATTEMPTS) | ||||
|     return NULL; | ||||
|  | ||||
|   return &meta_sync_ring; | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| load_gl_symbol (const char  *name, | ||||
|                 void       **func) | ||||
| { | ||||
|   *func = cogl_get_proc_address (name); | ||||
|   if (!*func) | ||||
|     { | ||||
|       meta_verbose ("MetaSyncRing: failed to resolve required GL symbol \"%s\"\n", name); | ||||
|       return FALSE; | ||||
|     } | ||||
|   return TRUE; | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| check_gl_extensions (void) | ||||
| { | ||||
|   int num_extensions, i; | ||||
|   gboolean sync = FALSE; | ||||
|   gboolean x11_sync_object = FALSE; | ||||
|  | ||||
|   meta_gl_get_integerv (GL_NUM_EXTENSIONS, &num_extensions); | ||||
|  | ||||
|   for (i = 0; i < num_extensions; ++i) | ||||
|     { | ||||
|       const char *ext = meta_gl_get_stringi (GL_EXTENSIONS, i); | ||||
|  | ||||
|       if (g_strcmp0 ("GL_ARB_sync", ext) == 0) | ||||
|         sync = TRUE; | ||||
|       else if (g_strcmp0 ("GL_EXT_x11_sync_object", ext) == 0) | ||||
|         x11_sync_object = TRUE; | ||||
|     } | ||||
|  | ||||
|   return sync && x11_sync_object; | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| load_required_symbols (void) | ||||
| { | ||||
|   static gboolean success = FALSE; | ||||
|  | ||||
|   if (success) | ||||
|     return TRUE; | ||||
|  | ||||
|   /* We don't link against libGL directly because cogl may want to | ||||
|    * use something else. This assumes that cogl has been initialized | ||||
|    * and dynamically loaded libGL at this point. | ||||
|    */ | ||||
|  | ||||
|   if (!load_gl_symbol ("glGetIntegerv", (void **) &meta_gl_get_integerv)) | ||||
|     goto out; | ||||
|   if (!load_gl_symbol ("glGetStringi", (void **) &meta_gl_get_stringi)) | ||||
|     goto out; | ||||
|  | ||||
|   if (!check_gl_extensions ()) | ||||
|     { | ||||
|       meta_verbose ("MetaSyncRing: couldn't find required GL extensions\n"); | ||||
|       goto out; | ||||
|     } | ||||
|  | ||||
|   if (!load_gl_symbol ("glDeleteSync", (void **) &meta_gl_delete_sync)) | ||||
|     goto out; | ||||
|   if (!load_gl_symbol ("glClientWaitSync", (void **) &meta_gl_client_wait_sync)) | ||||
|     goto out; | ||||
|   if (!load_gl_symbol ("glWaitSync", (void **) &meta_gl_wait_sync)) | ||||
|     goto out; | ||||
|   if (!load_gl_symbol ("glImportSyncEXT", (void **) &meta_gl_import_sync)) | ||||
|     goto out; | ||||
|  | ||||
|   if (!load_gl_symbol ("glGetError", (void **) &meta_gl_get_error)) | ||||
|     goto out; | ||||
|   if (!load_gl_symbol ("glIsSync", (void **) &meta_gl_is_sync)) | ||||
|     goto out; | ||||
|  | ||||
|   success = TRUE; | ||||
|  out: | ||||
|   return success; | ||||
| } | ||||
|  | ||||
| static void | ||||
| print_gl_error (void) | ||||
| { | ||||
|   GLenum e; | ||||
|   switch (e = meta_gl_get_error ()) | ||||
|     { | ||||
|     case GL_NO_ERROR: | ||||
|       meta_warning ("GL_NO_ERROR\n"); | ||||
|       break; | ||||
|     case GL_INVALID_ENUM: | ||||
|       meta_warning ("GL_INVALID_ENUM\n"); | ||||
|       break; | ||||
|     case GL_INVALID_VALUE: | ||||
|       meta_warning ("GL_INVALID_VALUE\n"); | ||||
|       break; | ||||
|     case GL_INVALID_OPERATION: | ||||
|       meta_warning ("GL_INVALID_OPERATION\n"); | ||||
|       break; | ||||
|     case GL_INVALID_FRAMEBUFFER_OPERATION: | ||||
|       meta_warning ("GL_INVALID_FRAMEBUFFER_OPERATION\n"); | ||||
|       break; | ||||
|     case GL_OUT_OF_MEMORY: | ||||
|       meta_warning ("GL_OUT_OF_MEMORY\n"); | ||||
|       break; | ||||
|     case GL_STACK_OVERFLOW: | ||||
|       meta_warning ("GL_STACK_OVERFLOW\n"); | ||||
|       break; | ||||
|     case GL_STACK_UNDERFLOW: | ||||
|       meta_warning ("GL_STACK_UNDERFLOW\n"); | ||||
|       break; | ||||
|     default: | ||||
|       meta_warning ("GL error 0x%x\n", e); | ||||
|       break; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void | ||||
| meta_sync_insert (MetaSync *self) | ||||
| { | ||||
|   g_return_if_fail (self->state == META_SYNC_STATE_READY); | ||||
|  | ||||
|   XSyncTriggerFence (self->xdisplay, self->xfence); | ||||
|   XFlush (self->xdisplay); | ||||
|  | ||||
|   meta_gl_wait_sync (self->glsync, 0, GL_TIMEOUT_IGNORED); | ||||
|  | ||||
|   self->state = META_SYNC_STATE_WAITING; | ||||
| } | ||||
|  | ||||
| static GLenum | ||||
| meta_sync_check_update_finished (MetaSync *self, | ||||
|                                  GLuint64  timeout) | ||||
| { | ||||
|   GLenum status = GL_WAIT_FAILED; | ||||
|  | ||||
|   switch (self->state) | ||||
|     { | ||||
|     case META_SYNC_STATE_DONE: | ||||
|       status = GL_ALREADY_SIGNALED; | ||||
|       break; | ||||
|     case META_SYNC_STATE_WAITING: | ||||
|       status = meta_gl_client_wait_sync (self->glsync, 0, timeout); | ||||
|       if (status == GL_ALREADY_SIGNALED || status == GL_CONDITION_SATISFIED) | ||||
|         self->state = META_SYNC_STATE_DONE; | ||||
|       break; | ||||
|     default: | ||||
|       break; | ||||
|     } | ||||
|  | ||||
|   g_warn_if_fail (status != GL_WAIT_FAILED); | ||||
|  | ||||
|   return status; | ||||
| } | ||||
|  | ||||
| static void | ||||
| meta_sync_reset (MetaSync *self) | ||||
| { | ||||
|   XSyncAlarmAttributes attrs; | ||||
|   int overflow; | ||||
|  | ||||
|   g_return_if_fail (self->state == META_SYNC_STATE_DONE); | ||||
|  | ||||
|   XSyncResetFence (self->xdisplay, self->xfence); | ||||
|  | ||||
|   attrs.trigger.wait_value = self->next_counter_value; | ||||
|  | ||||
|   XSyncChangeAlarm (self->xdisplay, self->xalarm, XSyncCAValue, &attrs); | ||||
|   XSyncSetCounter (self->xdisplay, self->xcounter, self->next_counter_value); | ||||
|  | ||||
|   XSyncValueAdd (&self->next_counter_value, | ||||
|                  self->next_counter_value, | ||||
|                  SYNC_VALUE_ONE, | ||||
|                  &overflow); | ||||
|  | ||||
|   self->state = META_SYNC_STATE_RESET_PENDING; | ||||
| } | ||||
|  | ||||
| static void | ||||
| meta_sync_handle_event (MetaSync              *self, | ||||
|                         XSyncAlarmNotifyEvent *event) | ||||
| { | ||||
|   g_return_if_fail (event->alarm == self->xalarm); | ||||
|   g_return_if_fail (self->state == META_SYNC_STATE_RESET_PENDING); | ||||
|  | ||||
|   self->state = META_SYNC_STATE_READY; | ||||
| } | ||||
|  | ||||
| static MetaSync * | ||||
| meta_sync_new (Display *xdisplay) | ||||
| { | ||||
|   MetaSync *self; | ||||
|   XSyncAlarmAttributes attrs; | ||||
|  | ||||
|   self = g_malloc0 (sizeof (MetaSync)); | ||||
|  | ||||
|   self->xdisplay = xdisplay; | ||||
|  | ||||
|   self->xfence = XSyncCreateFence (xdisplay, DefaultRootWindow (xdisplay), FALSE); | ||||
|   meta_warning ("created X fence 0x%x\n", self->xfence); | ||||
|   self->glsync = meta_gl_import_sync (GL_SYNC_X11_FENCE_EXT, self->xfence, 0); | ||||
|   print_gl_error (); | ||||
|  | ||||
|   self->xcounter = XSyncCreateCounter (xdisplay, SYNC_VALUE_ZERO); | ||||
|  | ||||
|   attrs.trigger.counter = self->xcounter; | ||||
|   attrs.trigger.value_type = XSyncAbsolute; | ||||
|   attrs.trigger.wait_value = SYNC_VALUE_ONE; | ||||
|   attrs.trigger.test_type = XSyncPositiveTransition; | ||||
|   attrs.events = TRUE; | ||||
|   self->xalarm = XSyncCreateAlarm (xdisplay, | ||||
|                                    XSyncCACounter | | ||||
|                                    XSyncCAValueType | | ||||
|                                    XSyncCAValue | | ||||
|                                    XSyncCATestType | | ||||
|                                    XSyncCAEvents, | ||||
|                                    &attrs); | ||||
|  | ||||
|   XSyncIntToValue (&self->next_counter_value, 1); | ||||
|  | ||||
|   self->state = META_SYNC_STATE_READY; | ||||
|  | ||||
|   return self; | ||||
| } | ||||
|  | ||||
| static Bool | ||||
| alarm_event_predicate (Display  *dpy, | ||||
|                        XEvent   *event, | ||||
|                        XPointer  data) | ||||
| { | ||||
|   int xsync_event_base; | ||||
|   MetaSyncRing *ring = meta_sync_ring_get (); | ||||
|  | ||||
|   if (!ring) | ||||
|     return False; | ||||
|  | ||||
|   xsync_event_base = meta_display_get_sync_event_base (ring->display); | ||||
|  | ||||
|   if (event->type == xsync_event_base + XSyncAlarmNotify) | ||||
|     { | ||||
|       if (((MetaSync *) data)->xalarm == ((XSyncAlarmNotifyEvent *) event)->alarm) | ||||
|         return True; | ||||
|     } | ||||
|   return False; | ||||
| } | ||||
|  | ||||
| static void | ||||
| meta_sync_free (MetaSync *self) | ||||
| { | ||||
|   /* When our assumptions don't hold, something has gone wrong but we | ||||
|    * don't know what, so we reboot the ring. While doing that, we | ||||
|    * trigger fences before deleting them to try to get ourselves out | ||||
|    * of a potentially stuck GPU state. | ||||
|    */ | ||||
|   switch (self->state) | ||||
|     { | ||||
|     case META_SYNC_STATE_WAITING: | ||||
|     case META_SYNC_STATE_DONE: | ||||
|       /* nothing to do */ | ||||
|       break; | ||||
|     case META_SYNC_STATE_RESET_PENDING: | ||||
|       { | ||||
|         XEvent event; | ||||
|         XIfEvent (self->xdisplay, &event, alarm_event_predicate, (XPointer) self); | ||||
|         meta_sync_handle_event (self, (XSyncAlarmNotifyEvent *) &event); | ||||
|       } | ||||
|       /* fall through */ | ||||
|     case META_SYNC_STATE_READY: | ||||
|       XSyncTriggerFence (self->xdisplay, self->xfence); | ||||
|       XFlush (self->xdisplay); | ||||
|       break; | ||||
|     default: | ||||
|       break; | ||||
|     } | ||||
|  | ||||
|   meta_gl_delete_sync (self->glsync); | ||||
|   XSyncDestroyFence (self->xdisplay, self->xfence); | ||||
|   XSyncDestroyCounter (self->xdisplay, self->xcounter); | ||||
|   XSyncDestroyAlarm (self->xdisplay, self->xalarm); | ||||
|  | ||||
|   g_free (self); | ||||
| } | ||||
|  | ||||
| gboolean | ||||
| meta_sync_ring_init (MetaDisplay *display) | ||||
| { | ||||
|   guint i; | ||||
|   MetaSyncRing *ring = meta_sync_ring_get (); | ||||
|  | ||||
|   if (!ring) | ||||
|     return FALSE; | ||||
|  | ||||
|   g_return_val_if_fail (display != NULL, FALSE); | ||||
|   g_return_val_if_fail (ring->display == NULL, FALSE); | ||||
|  | ||||
|   if (!load_required_symbols ()) | ||||
|     return FALSE; | ||||
|  | ||||
|   if (!meta_display_has_sync (display)) | ||||
|     return FALSE; | ||||
|  | ||||
|   print_gl_error (); | ||||
|  | ||||
|   XSyncIntToValue (&SYNC_VALUE_ZERO, 0); | ||||
|   XSyncIntToValue (&SYNC_VALUE_ONE, 1); | ||||
|  | ||||
|   ring->display = display; | ||||
|  | ||||
|   ring->alarm_to_sync = g_hash_table_new (NULL, NULL); | ||||
|  | ||||
|   for (i = 0; i < NUM_SYNCS; ++i) | ||||
|     { | ||||
|       MetaSync *sync = meta_sync_new (meta_display_get_xdisplay (display)); | ||||
|       ring->syncs_array[i] = sync; | ||||
|       g_hash_table_replace (ring->alarm_to_sync, (gpointer) sync->xalarm, sync); | ||||
|     } | ||||
|  | ||||
|   ring->current_sync_idx = 0; | ||||
|   ring->current_sync = ring->syncs_array[0]; | ||||
|   ring->warmup_syncs = 0; | ||||
|  | ||||
|   return TRUE; | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_sync_ring_destroy (void) | ||||
| { | ||||
|   guint i; | ||||
|   MetaSyncRing *ring = meta_sync_ring_get (); | ||||
|  | ||||
|   if (!ring) | ||||
|     return; | ||||
|  | ||||
|   g_return_if_fail (ring->display != NULL); | ||||
|  | ||||
|   ring->current_sync_idx = 0; | ||||
|   ring->current_sync = NULL; | ||||
|   ring->warmup_syncs = 0; | ||||
|  | ||||
|   for (i = 0; i < NUM_SYNCS; ++i) | ||||
|     meta_sync_free (ring->syncs_array[i]); | ||||
|  | ||||
|   g_hash_table_destroy (ring->alarm_to_sync); | ||||
|  | ||||
|   ring->display = NULL; | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| meta_sync_ring_reboot (MetaDisplay *display) | ||||
| { | ||||
|   MetaSyncRing *ring = meta_sync_ring_get (); | ||||
|  | ||||
|   if (!ring) | ||||
|     return FALSE; | ||||
|  | ||||
|   meta_sync_ring_destroy (); | ||||
|  | ||||
|   ring->reboots += 1; | ||||
|  | ||||
|   if (!meta_sync_ring_get ()) | ||||
|     { | ||||
|       meta_warning ("MetaSyncRing: Too many reboots -- disabling\n"); | ||||
|       return FALSE; | ||||
|     } | ||||
|  | ||||
|   return meta_sync_ring_init (display); | ||||
| } | ||||
|  | ||||
| gboolean | ||||
| meta_sync_ring_after_frame (void) | ||||
| { | ||||
|   MetaSyncRing *ring = meta_sync_ring_get (); | ||||
|  | ||||
|   if (!ring) | ||||
|     return FALSE; | ||||
|  | ||||
|   g_return_if_fail (ring->display != NULL); | ||||
|  | ||||
|   if (ring->warmup_syncs >= NUM_SYNCS / 2) | ||||
|     { | ||||
|       guint reset_sync_idx = (ring->current_sync_idx + NUM_SYNCS - (NUM_SYNCS / 2)) % NUM_SYNCS; | ||||
|       MetaSync *sync_to_reset = ring->syncs_array[reset_sync_idx]; | ||||
|  | ||||
|       GLenum status = meta_sync_check_update_finished (sync_to_reset, 0); | ||||
|       if (status == GL_TIMEOUT_EXPIRED) | ||||
|         { | ||||
|           meta_warning ("MetaSyncRing: We should never wait for a sync -- add more syncs?\n"); | ||||
|           status = meta_sync_check_update_finished (sync_to_reset, MAX_SYNC_WAIT_TIME); | ||||
|         } | ||||
|  | ||||
|       if (status != GL_ALREADY_SIGNALED && status != GL_CONDITION_SATISFIED) | ||||
|         { | ||||
|           meta_warning ("MetaSyncRing: Timed out waiting for sync object.\n"); | ||||
|           return meta_sync_ring_reboot (ring->display); | ||||
|         } | ||||
|  | ||||
|       meta_sync_reset (sync_to_reset); | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       ring->warmup_syncs += 1; | ||||
|     } | ||||
|  | ||||
|   ring->current_sync_idx += 1; | ||||
|   ring->current_sync_idx %= NUM_SYNCS; | ||||
|  | ||||
|   ring->current_sync = ring->syncs_array[ring->current_sync_idx]; | ||||
|  | ||||
|   return TRUE; | ||||
| } | ||||
|  | ||||
| gboolean | ||||
| meta_sync_ring_insert_wait (void) | ||||
| { | ||||
|   MetaSyncRing *ring = meta_sync_ring_get (); | ||||
|  | ||||
|   if (!ring) | ||||
|     return FALSE; | ||||
|  | ||||
|   g_return_if_fail (ring->display != NULL); | ||||
|  | ||||
|   if (ring->current_sync->state != META_SYNC_STATE_READY) | ||||
|     { | ||||
|       meta_warning ("MetaSyncRing: Sync object is not ready -- were events handled properly?\n"); | ||||
|       if (!meta_sync_ring_reboot (ring->display)) | ||||
|         return FALSE; | ||||
|     } | ||||
|  | ||||
|   meta_sync_insert (ring->current_sync); | ||||
|  | ||||
|   return TRUE; | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_sync_ring_handle_event (XSyncAlarmNotifyEvent *event) | ||||
| { | ||||
|   MetaSync *sync; | ||||
|   MetaSyncRing *ring = meta_sync_ring_get (); | ||||
|  | ||||
|   if (!ring) | ||||
|     return; | ||||
|  | ||||
|   g_return_if_fail (ring->display != NULL); | ||||
|  | ||||
|   sync = g_hash_table_lookup (ring->alarm_to_sync, (gpointer) event->alarm); | ||||
|   if (sync) | ||||
|     meta_sync_handle_event (sync, event); | ||||
| } | ||||
							
								
								
									
										17
									
								
								src/compositor/meta-sync-ring.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/compositor/meta-sync-ring.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| #ifndef _META_SYNC_RING_H_ | ||||
| #define _META_SYNC_RING_H_ | ||||
|  | ||||
| #include <glib.h> | ||||
|  | ||||
| #include <X11/Xlib.h> | ||||
| #include <X11/extensions/sync.h> | ||||
|  | ||||
| #include <meta/display.h> | ||||
|  | ||||
| gboolean meta_sync_ring_init (MetaDisplay *dpy); | ||||
| void meta_sync_ring_destroy (void); | ||||
| gboolean meta_sync_ring_after_frame (void); | ||||
| gboolean meta_sync_ring_insert_wait (void); | ||||
| void meta_sync_ring_handle_event (XSyncAlarmNotifyEvent *event); | ||||
|  | ||||
| #endif  /* _META_SYNC_RING_H_ */ | ||||
| @@ -3120,6 +3120,12 @@ meta_display_has_shape (MetaDisplay *display) | ||||
|   return META_DISPLAY_HAS_SHAPE (display); | ||||
| } | ||||
|  | ||||
| gboolean | ||||
| meta_display_has_sync (MetaDisplay *display) | ||||
| { | ||||
|   return META_DISPLAY_HAS_XSYNC (display); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * meta_display_get_focus_window: | ||||
|  * @display: a #MetaDisplay | ||||
| @@ -3148,6 +3154,12 @@ meta_display_get_shape_event_base (MetaDisplay *display) | ||||
|   return display->shape_event_base; | ||||
| } | ||||
|  | ||||
| int | ||||
| meta_display_get_sync_event_base (MetaDisplay *display) | ||||
| { | ||||
|   return display->xsync_event_base; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * meta_display_clear_mouse_mode: | ||||
|  * @display: a #MetaDisplay | ||||
|   | ||||
| @@ -75,6 +75,7 @@ Display *meta_display_get_xdisplay (MetaDisplay *display); | ||||
| MetaCompositor *meta_display_get_compositor (MetaDisplay *display); | ||||
|  | ||||
| gboolean meta_display_has_shape (MetaDisplay *display); | ||||
| gboolean meta_display_has_sync (MetaDisplay *display); | ||||
|  | ||||
| MetaWindow *meta_display_get_focus_window (MetaDisplay *display); | ||||
|  | ||||
| @@ -83,6 +84,7 @@ gboolean  meta_display_xwindow_is_a_no_focus_window (MetaDisplay *display, | ||||
|  | ||||
| int meta_display_get_damage_event_base (MetaDisplay *display); | ||||
| int meta_display_get_shape_event_base (MetaDisplay *display); | ||||
| int meta_display_get_sync_event_base (MetaDisplay *display); | ||||
|  | ||||
| gboolean meta_display_xserver_time_is_before (MetaDisplay *display, | ||||
|                                               guint32      time1, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user