diff --git a/include/vulkan/vulkan_core.h b/include/vulkan/vulkan_core.h
index 6b3ab8476a2d823875d43b536f52dc0d5b09d198..666c9b83c0e3c1c6f8570300ef29643e613b6097 100644
--- a/include/vulkan/vulkan_core.h
+++ b/include/vulkan/vulkan_core.h
@@ -560,6 +560,7 @@ typedef enum VkStructureType {
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES_EXT = 1000276000,
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_FEATURES_EXT = 1000281000,
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_PROPERTIES_EXT = 1000281001,
+    VK_STRUCTURE_TYPE_PRESENT_PERIOD_MESA = 1000329000,
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES,
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETER_FEATURES = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETERS_FEATURES,
     VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT,
@@ -10715,6 +10716,18 @@ typedef struct VkPhysicalDeviceTexelBufferAlignmentPropertiesEXT {
 #define VK_GOOGLE_USER_TYPE_SPEC_VERSION  1
 #define VK_GOOGLE_USER_TYPE_EXTENSION_NAME "VK_GOOGLE_user_type"
 
+
+#define VK_MESA_present_period 1
+#define VK_MESA_PRESENT_PERIOD_SPEC_VERSION 1
+#define VK_MESA_PRESENT_PERIOD_EXTENSION_NAME "VK_MESA_present_period"
+typedef struct VkPresentPeriodMESA {
+    VkStructureType    sType;
+    const void*        pNext;
+    uint32_t           swapchainCount;
+    const int64_t*     pPresentPeriods;
+} VkPresentPeriodMESA;
+
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/amd/vulkan/radv_extensions.py b/src/amd/vulkan/radv_extensions.py
index c255b49437a62c5ac047fd816a5001b09e17e19a..3048560dceac1990a802f41292b93df2f7c59bbe 100644
--- a/src/amd/vulkan/radv_extensions.py
+++ b/src/amd/vulkan/radv_extensions.py
@@ -167,6 +167,7 @@ EXTENSIONS = [
     Extension('VK_GOOGLE_decorate_string',                1, True),
     Extension('VK_GOOGLE_hlsl_functionality1',            1, True),
     Extension('VK_GOOGLE_display_timing',                 1, True),
+    Extension('VK_MESA_present_period',                   1, True),
     Extension('VK_NV_compute_shader_derivatives',         1, 'device->rad_info.chip_class >= GFX8'),
 ]
 
diff --git a/src/intel/vulkan/anv_extensions.py b/src/intel/vulkan/anv_extensions.py
index 256814a8584aa03cf360a21e77932b5d57caa038..7e744d67e6f2f56c40d768b6ee1aed1cc684b05c 100644
--- a/src/intel/vulkan/anv_extensions.py
+++ b/src/intel/vulkan/anv_extensions.py
@@ -174,6 +174,7 @@ EXTENSIONS = [
     Extension('VK_GOOGLE_hlsl_functionality1',            1, True),
     Extension('VK_INTEL_performance_query',               1, 'device->perf'),
     Extension('VK_INTEL_shader_integer_functions2',       1, 'device->info.gen >= 8'),
+    Extension('VK_MESA_present_period',                   1, True),
     Extension('VK_NV_compute_shader_derivatives',         1, True),
 ]
 
diff --git a/src/vulkan/registry/vk.xml b/src/vulkan/registry/vk.xml
index 88d421aca0feb256799827ad230202992ac19f7d..4d482786fc41b63fa8bdf57fd9db037df31849b9 100644
--- a/src/vulkan/registry/vk.xml
+++ b/src/vulkan/registry/vk.xml
@@ -2546,6 +2546,12 @@ typedef void <name>CAMetalLayer</name>;
             <member><type>float</type>   <name>x</name></member>
             <member><type>float</type>   <name>y</name></member>
         </type>
+        <type category="struct" name="VkPresentPeriodMESA" structextends="VkPresentInfoKHR">
+            <member values="VK_STRUCTURE_TYPE_PRESENT_PERIOD_MESA"><type>VkStructureType</type> <name>sType</name></member>
+            <member>const <type>void</type>*                      <name>pNext</name></member>
+            <member><type>uint32_t</type>                         <name>swapchainCount</name><comment>Copy of VkPresentInfoKHR::swapchainCount</comment></member>
+            <member len="swapchainCount" optional="true">const <type>int64_t</type>* <name>pPresentPeriods</name><comment>Present period values for each swapchain</comment></member>
+        </type>
         <type category="struct" name="VkHdrMetadataEXT">
                 <comment>Display primary in chromaticity coordinates</comment>
             <member values="VK_STRUCTURE_TYPE_HDR_METADATA_EXT"><type>VkStructureType</type> <name>sType</name></member>
@@ -12453,5 +12459,13 @@ typedef void <name>CAMetalLayer</name>;
                 <enum bitpos="2"  extends="VkMemoryHeapFlagBits"            name="VK_MEMORY_HEAP_RESERVED_2_BIT_KHR"/>
             </require>
         </extension>
+	<extension name="VK_MESA_present_period" number="330" type="device" requires="VK_KHR_swapchain" author="MESA" contact="Keith Packard @keithp" supported="vulkan">
+	    <require>
+		<enum value="1"                                             name="VK_MESA_PRESENT_PERIOD_SPEC_VERSION"/>
+		<enum value="&quot;VK_MESA_present_period&quot;"            name="VK_MESA_PRESENT_PERIOD_EXTENSION_NAME"/>
+		<enum offset="0" extends="VkStructureType"                  name="VK_STRUCTURE_TYPE_PRESENT_PERIOD_MESA"/>
+		<type name="VkPresentPeriodMESA"/>
+	    </require>
+	</extension>
     </extensions>
 </registry>
diff --git a/src/vulkan/wsi/wsi_common.c b/src/vulkan/wsi/wsi_common.c
index 7f5199878b6c9de33362735de81cc0f2c4531014..deff16d75c83e96f0df7614b68a94777b45f2e9a 100644
--- a/src/vulkan/wsi/wsi_common.c
+++ b/src/vulkan/wsi/wsi_common.c
@@ -1244,6 +1244,20 @@ wsi_next_timing(struct wsi_swapchain *chain, int image_index)
    return timing;
 }
 
+double
+mytime(void);
+
+double
+mytime(void)
+{
+   static uint64_t first;
+
+   uint64_t now = wsi_common_get_current_time();
+   if (first == 0)
+      first = now;
+   return (double)(now - first) / 1.0e9;
+}
+
 void
 wsi_present_complete(struct wsi_swapchain *swapchain,
                      struct wsi_image *image,
@@ -1251,91 +1265,111 @@ wsi_present_complete(struct wsi_swapchain *swapchain,
                      uint64_t msc)
 {
    const struct wsi_device *wsi = swapchain->wsi;
-   struct wsi_timing *timing = image->timing;
 
-   if (!timing)
-      return;
+//   printf("%9.4f present complete %d %ld\n", mytime(), image->image_index, msc);
 
-   uint64_t render_timestamp;
+   if (image == swapchain->pending) {
+      struct wsi_image *pending = image->pending;
+      uint64_t target_msc = image->pending_msc + msc;
 
-   VkResult result = wsi->GetQueryPoolResults(
-      swapchain->device, image->query_pool,
-      0, 1, sizeof(render_timestamp), &render_timestamp,
-      sizeof (uint64_t),
-      VK_QUERY_RESULT_64_BIT|VK_QUERY_RESULT_WAIT_BIT);
-   if (result != VK_SUCCESS)
-      return;
+      while ((swapchain->pending = pending) != NULL) {
 
-   static const VkCalibratedTimestampInfoEXT    timestampInfo[2] = {
-      {
-         .sType = VK_STRUCTURE_TYPE_CALIBRATED_TIMESTAMP_INFO_EXT,
-         .pNext = NULL,
-         .timeDomain = VK_TIME_DOMAIN_DEVICE_EXT,
-      },
-      {
-         .sType = VK_STRUCTURE_TYPE_CALIBRATED_TIMESTAMP_INFO_EXT,
-         .pNext = NULL,
-         .timeDomain = VK_TIME_DOMAIN_CLOCK_MONOTONIC_EXT,
-      },
-   };
+         if (target_msc > pending->timing->target_msc)
+            pending->timing->target_msc = target_msc;
+//         printf("delayed queuing index %d for %ld\n", pending->image_index, pending->timing->target_msc);
+         (void) swapchain->queue_present(swapchain, pending->image_index, image->region);
+         if (pending->pending_msc != 0)
+            break;
+         pending = pending->pending;
+      }
+   }
 
-   uint64_t     timestamps[2];
-   uint64_t     maxDeviation;
+   struct wsi_timing *timing = image->timing;
 
-   result = wsi->GetCalibratedTimestampsEXT(swapchain->device,
-                                            2,
-                                            timestampInfo,
-                                            timestamps,
-                                            &maxDeviation);
-   if (result != VK_SUCCESS)
-      return;
+   if (timing != NULL && timing->timing.presentID) {
 
-   uint64_t current_gpu_timestamp = timestamps[0];
-   uint64_t current_time = timestamps[1];
+      uint64_t render_timestamp;
 
-   VkRefreshCycleDurationGOOGLE display_timings;
-   swapchain->get_refresh_cycle_duration(swapchain, &display_timings);
+      VkResult result = wsi->GetQueryPoolResults(
+         swapchain->device, image->query_pool,
+         0, 1, sizeof(render_timestamp), &render_timestamp,
+         sizeof (uint64_t),
+         VK_QUERY_RESULT_64_BIT|VK_QUERY_RESULT_WAIT_BIT);
+      if (result != VK_SUCCESS)
+         return;
+
+      static const VkCalibratedTimestampInfoEXT    timestampInfo[2] = {
+         {
+            .sType = VK_STRUCTURE_TYPE_CALIBRATED_TIMESTAMP_INFO_EXT,
+            .pNext = NULL,
+            .timeDomain = VK_TIME_DOMAIN_DEVICE_EXT,
+         },
+         {
+            .sType = VK_STRUCTURE_TYPE_CALIBRATED_TIMESTAMP_INFO_EXT,
+            .pNext = NULL,
+            .timeDomain = VK_TIME_DOMAIN_CLOCK_MONOTONIC_EXT,
+         },
+      };
 
-   uint64_t refresh_duration = display_timings.refreshDuration;
+      uint64_t     timestamps[2];
+      uint64_t     maxDeviation;
 
-   /* When did drawing complete (in nsec) */
+      result = wsi->GetCalibratedTimestampsEXT(swapchain->device,
+                                               2,
+                                               timestampInfo,
+                                               timestamps,
+                                               &maxDeviation);
+      if (result != VK_SUCCESS)
+         return;
 
-   int64_t since_render = (int64_t) floor ((double) (current_gpu_timestamp - render_timestamp) *
-                                           (double) wsi->timestamp_period + 0.5);
-   uint64_t render_time = current_time - since_render;
+      uint64_t current_gpu_timestamp = timestamps[0];
+      uint64_t current_time = timestamps[1];
 
-   if (render_time > ust)
-      render_time = ust;
+      VkRefreshCycleDurationGOOGLE display_timings;
+      swapchain->get_refresh_cycle_duration(swapchain, &display_timings);
 
-   uint64_t render_frames = (ust - render_time) / refresh_duration;
+      uint64_t refresh_duration = display_timings.refreshDuration;
 
-   uint64_t earliest_time = ust - render_frames * refresh_duration;
+      /* When did drawing complete (in nsec) */
 
-   /* Use the presentation mode to figure out when the image could have been
-    * displayed. It couldn't have been displayed before the previous image, so
-    * use that as a lower bound. If we're in FIFO mode, then it couldn't have
-    * been displayed before one frame *after* the previous image
-    */
-   uint64_t possible_frame = swapchain->frame_ust;
+      int64_t since_render = (int64_t) floor ((double) (current_gpu_timestamp - render_timestamp) *
+                                              (double) wsi->timestamp_period + 0.5);
+      uint64_t render_time = current_time - since_render;
 
-   switch (swapchain->present_mode) {
-   case VK_PRESENT_MODE_FIFO_KHR:
-   case VK_PRESENT_MODE_FIFO_RELAXED_KHR:
-      possible_frame += refresh_duration;
-      break;
-   default:
-      break;
-   }
-   if (earliest_time < possible_frame)
-      earliest_time = possible_frame;
+      if (render_time > ust)
+         render_time = ust;
 
-   if (earliest_time > ust)
-      earliest_time = ust;
+      uint64_t render_frames = (ust - render_time) / refresh_duration;
 
-   timing->timing.actualPresentTime = ust;
-   timing->timing.earliestPresentTime = earliest_time;
-   timing->timing.presentMargin = earliest_time - render_time;
-   timing->complete = true;
+      uint64_t earliest_time = ust - render_frames * refresh_duration;
+
+      /* Use the presentation mode to figure out when the image could have been
+       * displayed. It couldn't have been displayed before the previous image, so
+       * use that as a lower bound. If we're in FIFO mode, then it couldn't have
+       * been displayed before one frame *after* the previous image
+       */
+      uint64_t possible_frame = swapchain->frame_ust;
+
+      switch (swapchain->present_mode) {
+      case VK_PRESENT_MODE_FIFO_KHR:
+      case VK_PRESENT_MODE_FIFO_RELAXED_KHR:
+         possible_frame += refresh_duration;
+         break;
+      default:
+         break;
+      }
+      if (earliest_time < possible_frame)
+         earliest_time = possible_frame;
+
+      if (earliest_time > ust)
+         earliest_time = ust;
+
+      timing->timing.actualPresentTime = ust;
+      timing->timing.earliestPresentTime = earliest_time;
+      timing->timing.presentMargin = earliest_time - render_time;
+   }
+   if (timing)
+      timing->complete = true;
 
    swapchain->frame_msc = msc;
    swapchain->frame_ust = ust;
@@ -1354,6 +1388,8 @@ wsi_common_queue_present(const struct wsi_device *wsi,
       vk_find_struct_const(pPresentInfo->pNext, PRESENT_REGIONS_KHR);
    const VkPresentTimesInfoGOOGLE *present_times_info =
       vk_find_struct_const(pPresentInfo->pNext, PRESENT_TIMES_INFO_GOOGLE);
+   const VkPresentPeriodMESA *present_periods =
+      vk_find_struct_const(pPresentInfo->pNext, PRESENT_PERIOD_MESA);
 
    for (uint32_t i = 0; i < pPresentInfo->swapchainCount; i++) {
       WSI_FROM_HANDLE(wsi_swapchain, swapchain, pPresentInfo->pSwapchains[i]);
@@ -1387,6 +1423,7 @@ wsi_common_queue_present(const struct wsi_device *wsi,
 
       struct wsi_image *image =
          swapchain->get_wsi_image(swapchain, image_index);
+      image->image_index = image_index;
 
       struct wsi_memory_signal_submit_info mem_signal = {
          .sType = VK_STRUCTURE_TYPE_WSI_MEMORY_SIGNAL_SUBMIT_INFO_MESA,
@@ -1435,6 +1472,51 @@ wsi_common_queue_present(const struct wsi_device *wsi,
             image->prime.blit_cmd_buffers[queue_family_index];
       }
 
+      image->pending_msc = 0;
+      image->pending = NULL;
+
+      int64_t present_period = 0;
+      if (present_periods && present_periods->pPresentPeriods) {
+         present_period = present_periods->pPresentPeriods[i];
+         if (present_period) {
+            if (present_period < 0)
+               present_period = -present_period;
+            else {
+               VkRefreshCycleDurationGOOGLE refresh_timing;
+
+               swapchain->get_refresh_cycle_duration(swapchain,
+                                                     &refresh_timing);
+
+               /* Round to nearest MSC */
+               present_period = (present_period + refresh_timing.refreshDuration/2) / refresh_timing.refreshDuration;
+            }
+            image->pending_msc = present_period;
+//            printf("%9.4f index %d pending_msc %ld\n", mytime(), image_index, present_period);
+         }
+      }
+
+      const VkPresentRegionKHR *region = NULL;
+      image->region = NULL;
+      if (regions && regions->pRegions) {
+         region = &regions->pRegions[i];
+         image->region = region;
+      }
+
+      if (swapchain->pending) {
+         if (timing == NULL) {
+            timing = wsi_next_timing(swapchain, pPresentInfo->pImageIndices[i]);
+            timing->timing.presentID = 0;
+            image->timing = timing;
+         }
+      }
+
+      if (swapchain->pending || image->pending_msc) {
+         struct wsi_image **prev = &swapchain->pending;
+         while (*prev)
+            prev = &(*prev)->pending;
+         *prev = image;
+      }
+
       /* Set up GOOGLE_display_timing bits */
       if (present_times_info &&
           present_times_info->pTimes != NULL &&
@@ -1443,11 +1525,13 @@ wsi_common_queue_present(const struct wsi_device *wsi,
          const VkPresentTimeGOOGLE *present_time =
             &present_times_info->pTimes[i];
 
-         timing = wsi_next_timing(swapchain, pPresentInfo->pImageIndices[i]);
+         if (timing == NULL) {
+            timing = wsi_next_timing(swapchain, pPresentInfo->pImageIndices[i]);
+            image->timing = timing;
+         }
          timing->timing.presentID = present_time->presentID;
          timing->timing.desiredPresentTime = present_time->desiredPresentTime;
          timing->target_msc = 0;
-         image->timing = timing;
 
          if (present_time->desiredPresentTime != 0)
          {
@@ -1469,8 +1553,10 @@ wsi_common_queue_present(const struct wsi_device *wsi,
             }
          }
 
-         submit_buffers[submit_info.commandBufferCount++] =
-            image->timestamp_buffer;
+         if (present_time->presentID) {
+            submit_buffers[submit_info.commandBufferCount++] =
+               image->timestamp_buffer;
+         }
       }
 
       result = wsi->QueueSubmit(queue, 1, &submit_info, swapchain->fences[image_index]);
@@ -1478,13 +1564,11 @@ wsi_common_queue_present(const struct wsi_device *wsi,
       if (result != VK_SUCCESS)
          goto fail_present;
 
-      const VkPresentRegionKHR *region = NULL;
-      if (regions && regions->pRegions)
-         region = &regions->pRegions[i];
-
-      result = swapchain->queue_present(swapchain, image_index, region);
-      if (result != VK_SUCCESS)
-         goto fail_present;
+      if (!swapchain->pending || swapchain->pending == image) {
+         result = swapchain->queue_present(swapchain, image_index, region);
+         if (result != VK_SUCCESS)
+            goto fail_present;
+      }
 
    fail_present:
       if (pPresentInfo->pResults != NULL)
diff --git a/src/vulkan/wsi/wsi_common_private.h b/src/vulkan/wsi/wsi_common_private.h
index 0fbda6f7d36ed9cfbd2f0b471112064c811176cc..d244f567c5d79909f031e13481ecde5e029c91bc 100644
--- a/src/vulkan/wsi/wsi_common_private.h
+++ b/src/vulkan/wsi/wsi_common_private.h
@@ -54,6 +54,11 @@ struct wsi_image {
    VkCommandBuffer timestamp_buffer;
 
    struct wsi_timing *timing;
+   struct wsi_image *pending;
+   uint64_t pending_msc;
+
+   uint32_t image_index;
+   const VkPresentRegionKHR *region;
 };
 
 #define WSI_TIMING_HISTORY      16
@@ -79,6 +84,8 @@ struct wsi_swapchain {
 
    float timestamp_period;
 
+   struct wsi_image *pending;
+
    /* Command pools, one per queue family */
    VkCommandPool *cmd_pools;
 
diff --git a/src/vulkan/wsi/wsi_common_x11.c b/src/vulkan/wsi/wsi_common_x11.c
index 2f283f530506b4228f2ce6879d5c8847133a36a7..c4030d5393438045f4f3fe9f979f7985262a2272 100644
--- a/src/vulkan/wsi/wsi_common_x11.c
+++ b/src/vulkan/wsi/wsi_common_x11.c
@@ -45,6 +45,8 @@
 #include "wsi_common_x11.h"
 #include "wsi_common_queue.h"
 
+double mytime(void);
+
 #define typed_memcpy(dest, src, count) ({ \
    STATIC_ASSERT(sizeof(*src) == sizeof(*dest)); \
    memcpy((dest), (src), (count) * sizeof(*(src))); \
@@ -778,6 +780,10 @@ struct x11_swapchain {
    struct wsi_queue                             acquire_queue;
    pthread_t                                    queue_manager;
 
+   pthread_t                                    event_thread;
+   pthread_mutex_t                              event_mutex;
+   pthread_cond_t                               event_cond;
+
    struct x11_image                             images[0];
 };
 WSI_DEFINE_NONDISP_HANDLE_CASTS(x11_swapchain, VkSwapchainKHR)
@@ -940,62 +946,50 @@ static VkResult
 x11_acquire_next_image_poll_x11(struct x11_swapchain *chain,
                                 uint32_t *image_index, uint64_t timeout)
 {
-   xcb_generic_event_t *event;
-   struct pollfd pfds;
-   uint64_t atimeout;
+   struct timespec abs_timeout = { 0, 0 };
+
    while (1) {
+      pthread_mutex_lock(&chain->event_mutex);
       for (uint32_t i = 0; i < chain->base.image_count; i++) {
          if (!chain->images[i].busy) {
+            pthread_mutex_unlock(&chain->event_mutex);
             /* We found a non-busy image */
             xshmfence_await(chain->images[i].shm_fence);
             *image_index = i;
             chain->images[i].busy = true;
+//            printf("%9.4f image %d ready\n", mytime(), i);
             return x11_swapchain_result(chain, VK_SUCCESS);
          }
       }
 
       xcb_flush(chain->conn);
 
-      if (timeout == UINT64_MAX) {
-         event = xcb_wait_for_special_event(chain->conn, chain->special_event);
-         if (!event)
-            return x11_swapchain_result(chain, VK_ERROR_OUT_OF_DATE_KHR);
-      } else {
-         event = xcb_poll_for_special_event(chain->conn, chain->special_event);
-         if (!event) {
-            int ret;
-            if (timeout == 0)
-               return x11_swapchain_result(chain, VK_NOT_READY);
+      if (timeout == 0) {
+         pthread_mutex_unlock(&chain->event_mutex);
+         return x11_swapchain_result(chain, VK_NOT_READY);
+      }
 
+      if (!abs_timeout.tv_sec) {
+         uint64_t atimeout;
+         if (timeout == UINT64_MAX)
+            atimeout = UINT64_MAX;
+         else
             atimeout = wsi_get_absolute_timeout(timeout);
+         abs_timeout.tv_sec = atimeout / 1000000000ULL;
+         abs_timeout.tv_nsec = atimeout % 1000000000ULL;
+      }
 
-            pfds.fd = xcb_get_file_descriptor(chain->conn);
-            pfds.events = POLLIN;
-            ret = poll(&pfds, 1, timeout / 1000 / 1000);
-            if (ret == 0)
-               return x11_swapchain_result(chain, VK_TIMEOUT);
-            if (ret == -1)
-               return x11_swapchain_result(chain, VK_ERROR_OUT_OF_DATE_KHR);
+      int ret = pthread_cond_timedwait(&chain->event_cond, &chain->event_mutex,
+                                       &abs_timeout);
 
-            /* If a non-special event happens, the fd will still
-             * poll. So recalculate the timeout now just in case.
-             */
-            uint64_t current_time = wsi_common_get_current_time();
-            if (atimeout > current_time)
-               timeout = atimeout - current_time;
-            else
-               timeout = 0;
-            continue;
-         }
-      }
+      pthread_mutex_unlock(&chain->event_mutex);
+//      printf("%9.4f check for image ready\n", mytime());
 
-      /* Update the swapchain status here. We may catch non-fatal errors here,
-       * in which case we need to update the status and continue.
-       */
-      VkResult result = x11_handle_dri3_present_event(chain, (void *)event);
-      free(event);
-      if (result < 0)
-         return x11_swapchain_result(chain, result);
+      if (ret) {
+         if (ret == ETIMEDOUT)
+            return x11_swapchain_result(chain, VK_NOT_READY);
+         return x11_swapchain_result(chain, VK_ERROR_SURFACE_LOST_KHR);
+      }
    }
 }
 
@@ -1119,6 +1113,7 @@ x11_queue_present(struct wsi_swapchain *anv_chain,
 
    chain->images[image_index].busy = true;
    if (chain->has_present_queue) {
+//      printf("%9.4f queue push %d\n", mytime(), image_index);
       wsi_queue_push(&chain->present_queue, image_index);
       return chain->status;
    } else {
@@ -1162,7 +1157,9 @@ x11_manage_fifo_queues(void *state)
        */
       uint32_t image_index = 0;
       struct x11_image *image;
+//      printf("%9.4f wait for fifo queue\n", mytime());
       result = wsi_queue_pull(&chain->present_queue, &image_index, INT64_MAX);
+//      printf("%9.4f fifo queue %d\n", mytime(), image_index);
       assert(result != VK_TIMEOUT);
       if (result < 0) {
          goto fail;
@@ -1193,25 +1190,26 @@ x11_manage_fifo_queues(void *state)
       if (timing && timing->target_msc != 0 && timing->target_msc > target_msc)
          target_msc = timing->target_msc;
 
+      printf("%9.4f present to x11 %d %ld\n", mytime(), image_index, target_msc);
       result = x11_present_to_x11(chain, image_index, target_msc);
       if (result < 0)
          goto fail;
 
+      xcb_flush(chain->conn);
+#if 1
       if (chain->has_acquire_queue) {
+         pthread_mutex_lock(&chain->event_mutex);
          while (chain->last_present_msc < target_msc) {
-            xcb_generic_event_t *event =
-               xcb_wait_for_special_event(chain->conn, chain->special_event);
-            if (!event) {
+            int ret = pthread_cond_wait(&chain->event_cond, &chain->event_mutex);
+            if (ret) {
+               pthread_mutex_unlock(&chain->event_mutex);
                result = VK_ERROR_OUT_OF_DATE_KHR;
                goto fail;
             }
-
-            result = x11_handle_dri3_present_event(chain, (void *)event);
-            free(event);
-            if (result < 0)
-               goto fail;
          }
+         pthread_mutex_unlock(&chain->event_mutex);
       }
+#endif
    }
 
 fail:
@@ -1447,6 +1445,10 @@ x11_swapchain_destroy(struct wsi_swapchain *anv_chain,
                                              XCB_PRESENT_EVENT_MASK_NO_EVENT);
    xcb_discard_reply(chain->conn, cookie.sequence);
 
+   pthread_cancel(chain->event_thread);
+   pthread_join(chain->event_thread, NULL);
+   pthread_cond_destroy(&chain->event_cond);
+   pthread_mutex_destroy(&chain->event_mutex);
    wsi_swapchain_finish(&chain->base);
 
    vk_free(pAllocator, chain);
@@ -1480,6 +1482,36 @@ wsi_x11_set_adaptive_sync_property(xcb_connection_t *conn,
    free(reply);
 }
 
+static void*
+x11_event_thread(void *data)
+{
+   struct x11_swapchain *chain = data;
+
+   pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
+   for (;;) {
+      xcb_generic_event_t *event;
+
+      xcb_flush(chain->conn);
+
+//      printf("%9.4f wait event\n", mytime());
+
+      event = xcb_wait_for_special_event(chain->conn, chain->special_event);
+      if (!event)
+         break;
+
+//      printf("%9.4f got event\n", mytime());
+
+      /* Update the swapchain status here. We may catch non-fatal errors here,
+       * in which case we need to update the status and continue.
+       */
+      pthread_mutex_lock(&chain->event_mutex);
+      (void) x11_handle_dri3_present_event(chain, (void *)event);
+      pthread_mutex_unlock(&chain->event_mutex);
+      pthread_cond_broadcast(&chain->event_cond);
+      free(event);
+   }
+   return NULL;
+}
 
 static VkResult
 x11_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,
@@ -1575,6 +1607,17 @@ x11_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,
       xcb_register_for_special_xge(chain->conn, &xcb_present_id,
                                    chain->event_id, NULL);
 
+   if (pthread_mutex_init(&chain->event_mutex, NULL))
+       goto fail_mutex;
+
+   if (!wsi_init_pthread_cond_monotonic(&chain->event_cond))
+      goto fail_cond;
+
+   int ret = pthread_create(&chain->event_thread, NULL,
+                            x11_event_thread, chain);
+   if (ret)
+      goto fail_thread;
+
    chain->gc = xcb_generate_id(chain->conn);
    if (!chain->gc) {
       /* FINISHME: Choose a better error. */
@@ -1671,6 +1714,11 @@ fail_init_images:
 fail_register:
    xcb_unregister_for_special_event(chain->conn, chain->special_event);
 
+fail_thread:
+   pthread_cond_destroy(&chain->event_cond);
+fail_cond:
+   pthread_mutex_destroy(&chain->event_mutex);
+fail_mutex:
    wsi_swapchain_finish(&chain->base);
 
 fail_alloc: