OSDN Git Service

Merge remote-tracking branch 'mesa/13.0' into nougat-x86
authorChih-Wei Huang <cwhuang@linux.org.tw>
Wed, 7 Dec 2016 05:01:00 +0000 (13:01 +0800)
committerChih-Wei Huang <cwhuang@linux.org.tw>
Wed, 7 Dec 2016 05:01:00 +0000 (13:01 +0800)
VERSION
docs/relnotes/13.0.2.html [new file with mode: 0644]
src/amd/common/ac_nir_to_llvm.c
src/amd/vulkan/radv_device.c
src/amd/vulkan/radv_image.c
src/amd/vulkan/radv_meta_clear.c
src/vulkan/wsi/Makefile.sources
src/vulkan/wsi/wsi_common.h
src/vulkan/wsi/wsi_common_queue.h [new file with mode: 0644]
src/vulkan/wsi/wsi_common_wayland.c
src/vulkan/wsi/wsi_common_x11.c

diff --git a/VERSION b/VERSION
index 5cb7d85..347caf3 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-13.0.1
+13.0.2
diff --git a/docs/relnotes/13.0.2.html b/docs/relnotes/13.0.2.html
new file mode 100644 (file)
index 0000000..2f50199
--- /dev/null
@@ -0,0 +1,189 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html lang="en">
+<head>
+  <meta http-equiv="content-type" content="text/html; charset=utf-8">
+  <title>Mesa Release Notes</title>
+  <link rel="stylesheet" type="text/css" href="../mesa.css">
+</head>
+<body>
+
+<div class="header">
+  <h1>The Mesa 3D Graphics Library</h1>
+</div>
+
+<iframe src="../contents.html"></iframe>
+<div class="content">
+
+<h1>Mesa 13.0.2 Release Notes / November 28, 2016</h1>
+
+<p>
+Mesa 13.0.2 is a bug fix release which fixes bugs found since the 13.0.1 release.
+</p>
+<p>
+Mesa 13.0.2 implements the OpenGL 4.4 API, but the version reported by
+glGetString(GL_VERSION) or glGetIntegerv(GL_MAJOR_VERSION) /
+glGetIntegerv(GL_MINOR_VERSION) depends on the particular driver being used.
+Some drivers don't support all the features required in OpenGL 4.4.  OpenGL
+4.4 is <strong>only</strong> available if requested at context creation
+because compatibility contexts are not supported.
+</p>
+
+
+<h2>SHA256 checksums</h2>
+<pre>
+6014233a5db6032ab8de4881384871bbe029de684502707794ce7b3e6beec308  mesa-13.0.2.tar.gz
+a6ed622645f4ed61da418bf65adde5bcc4bb79023c36ba7d6b45b389da4416d5  mesa-13.0.2.tar.xz
+</pre>
+
+
+<h2>New features</h2>
+<p>None</p>
+
+
+<h2>Bug fixes</h2>
+
+<ul>
+
+<li><a href="https://bugs.freedesktop.org/show_bug.cgi?id=97321">Bug 97321</a> - Query INFO_LOG_LENGTH for empty info log should return 0</li>
+
+<li><a href="https://bugs.freedesktop.org/show_bug.cgi?id=97420">Bug 97420</a> - &quot;#version 0&quot; crashes glsl_compiler</li>
+
+<li><a href="https://bugs.freedesktop.org/show_bug.cgi?id=98632">Bug 98632</a> - Fix build on Hurd without PATH_MAX</li>
+
+</ul>
+
+
+<h2>Changes</h2>
+
+<p>Ben Widawsky (3):</p>
+<ul>
+  <li>i965: Add some APL and KBL SKU strings</li>
+  <li>i965: Reorder PCI ID list to match release order</li>
+  <li>i965/glk: Add basic Geminilake support</li>
+</ul>
+
+<p>Dave Airlie (14):</p>
+<ul>
+  <li>radv: fix texturesamples to handle single sample case</li>
+  <li>wsi: fix VK_INCOMPLETE for vkGetSwapchainImagesKHR</li>
+  <li>radv: don't crash on null swapchain destroy.</li>
+  <li>ac/nir/llvm: fix channel in texture gather lowering code.</li>
+  <li>radv: make sure to flush input attachments correctly.</li>
+  <li>radv: fix image view creation for depth and stencil only</li>
+  <li>radv: spir-v allows texture size query with and without lod.</li>
+  <li>vulkan/wsi/x11: handle timeouts properly in next image acquire (v1.1)</li>
+  <li>vulkan/wsi: store present mode in swapchain base class</li>
+  <li>vulkan/wsi/x11: add support for IMMEDIATE present mode</li>
+  <li>radv: fix texel fetch offset with 2d arrays.</li>
+  <li>radv/si: fix optimal micro tile selection</li>
+  <li>radv/ac/llvm: shadow samplers only return one value.</li>
+  <li>radv: fix 3D clears with baseMiplevel</li>
+</ul>
+
+<p>Eduardo Lima Mitev (2):</p>
+<ul>
+  <li>vulkan/wsi/x11: Fix behavior of vkGetPhysicalDeviceSurfaceFormatsKHR</li>
+  <li>vulkan/wsi/x11: Fix behavior of vkGetPhysicalDeviceSurfacePresentModesKHR</li>
+</ul>
+
+<p>Emil Velikov (5):</p>
+<ul>
+  <li>docs: add sha256 checksums for 13.0.1</li>
+  <li>cherry-ignore: add reverted LLVM_LIBDIR patch</li>
+  <li>anv: fix enumeration of properties</li>
+  <li>radv: honour the number of properties available</li>
+  <li>Update version to 13.0.2</li>
+</ul>
+
+<p>Eric Anholt (3):</p>
+<ul>
+  <li>vc4: Don't abort when a shader compile fails.</li>
+  <li>vc4: Clamp the shadow comparison value.</li>
+  <li>vc4: Fix register class handling of DDX/DDY arguments.</li>
+</ul>
+
+<p>Gwan-gyeong Mun (2):</p>
+<ul>
+  <li>util/disk_cache: close a previously opened handle in disk_cache_put (v2)</li>
+  <li>anv: Fix unintentional integer overflow in anv_CreateDmaBufImageINTEL</li>
+</ul>
+
+<p>Iago Toral Quiroga (1):</p>
+<ul>
+  <li>anv/format: handle unsupported formats properly</li>
+</ul>
+
+<p>Ian Romanick (2):</p>
+<ul>
+  <li>glcpp: Handle '#version 0' and other invalid values</li>
+  <li>glsl: Parse 0 as a preprocessor INTCONSTANT</li>
+</ul>
+
+<p>Jason Ekstrand (15):</p>
+<ul>
+  <li>anv/gen8: Stall when needed in Cmd(Set|Reset)Event</li>
+  <li>anv/wsi: Set the fence to signaled in AcquireNextImageKHR</li>
+  <li>anv: Rework fences</li>
+  <li>vulkan/wsi/wayland: Include pthread.h</li>
+  <li>vulkan/wsi/wayland: Clean up some error handling paths</li>
+  <li>vulkan/wsi: Report the correct min/maxImageCount</li>
+  <li>i965/gs: Allow primitive id to be a system value</li>
+  <li>anv: Handle null in all destructors</li>
+  <li>anv/fence: Handle ANV_FENCE_CREATE_SIGNALED_BIT</li>
+  <li>nir/spirv: Fix handling of gl_PrimitiveId</li>
+  <li>anv/blorp: Ignore clears for attachments first used as resolve destinations</li>
+  <li>anv: Implement a depth stall restriction on gen7</li>
+  <li>anv/cmd_buffer: Handle running out of binding tables in compute shaders</li>
+  <li>anv/cmd_buffer: Emit a CS stall before setting a CS pipeline</li>
+  <li>vulkan/wsi/x11: Implement FIFO mode.</li>
+</ul>
+
+<p>Jordan Justen (2):</p>
+<ul>
+  <li>isl: Fix height calculation in isl_msaa_interleaved_scale_px_to_sa</li>
+  <li>i965/hsw: Set integer mode in sampling state for stencil texturing</li>
+</ul>
+
+<p>Kenneth Graunke (4):</p>
+<ul>
+  <li>intel: Set min_ds_entries on Broxton.</li>
+  <li>i965: Fix compute shader crash.</li>
+  <li>mesa: Drop PATH_MAX usage.</li>
+  <li>i965: Fix GS push inputs with enhanced layouts.</li>
+</ul>
+
+<p>Kevin Strasser (1):</p>
+<ul>
+  <li>vulkan/wsi: Add a thread-safe queue implementation</li>
+</ul>
+
+<p>Lionel Landwerlin (1):</p>
+<ul>
+  <li>anv: fix multi level clears with VK_REMAINING_MIP_LEVELS</li>
+</ul>
+
+<p>Lucas Stach (1):</p>
+<ul>
+  <li>gbm: request correct version of the DRI2_FENCE extension</li>
+</ul>
+
+<p>Nicolai Hähnle (2):</p>
+<ul>
+  <li>radeonsi: store group_size_variable in struct si_compute</li>
+  <li>glsl/lower_output_reads: fix geometry shader output handling with conditional emit</li>
+</ul>
+
+<p>Steinar H. Gunderson (1):</p>
+<ul>
+  <li>Fix races during _mesa_HashWalk().</li>
+</ul>
+
+<p>Tapani Pälli (1):</p>
+<ul>
+  <li>mesa: fix empty program log length</li>
+</ul>
+
+
+</div>
+</body>
+</html>
index 31d7b6e..0daef08 100644 (file)
@@ -3517,12 +3517,13 @@ static void visit_tex(struct nir_to_llvm_context *ctx, nir_tex_instr *instr)
        if (offsets && instr->op == nir_texop_txf) {
                nir_const_value *const_offset =
                        nir_src_as_const_value(instr->src[const_src].src);
-
+               int num_offsets = instr->src[const_src].src.ssa->num_components;
                assert(const_offset);
-               if (instr->coord_components > 2)
+               num_offsets = MIN2(num_offsets, instr->coord_components);
+               if (num_offsets > 2)
                        address[2] = LLVMBuildAdd(ctx->builder,
                                                  address[2], LLVMConstInt(ctx->i32, const_offset->i32[2], false), "");
-               if (instr->coord_components > 1)
+               if (num_offsets > 1)
                        address[1] = LLVMBuildAdd(ctx->builder,
                                                  address[1], LLVMConstInt(ctx->i32, const_offset->i32[1], false), "");
                address[0] = LLVMBuildAdd(ctx->builder,
@@ -3544,6 +3545,8 @@ static void visit_tex(struct nir_to_llvm_context *ctx, nir_tex_instr *instr)
 
        if (instr->op == nir_texop_query_levels)
                result = LLVMBuildExtractElement(ctx->builder, result, LLVMConstInt(ctx->i32, 3, false), "");
+       else if (instr->is_shadow && instr->op != nir_texop_txs && instr->op != nir_texop_lod)
+               result = LLVMBuildExtractElement(ctx->builder, result, ctx->i32zero, "");
        else if (instr->op == nir_texop_txs &&
                 instr->sampler_dim == GLSL_SAMPLER_DIM_CUBE &&
                 instr->is_array) {
index 4a924ea..94a2ef0 100644 (file)
@@ -659,17 +659,15 @@ VkResult radv_EnumerateInstanceExtensionProperties(
        uint32_t*                                   pPropertyCount,
        VkExtensionProperties*                      pProperties)
 {
-       unsigned i;
        if (pProperties == NULL) {
                *pPropertyCount = ARRAY_SIZE(global_extensions);
                return VK_SUCCESS;
        }
 
-       for (i = 0; i < *pPropertyCount; i++)
-               memcpy(&pProperties[i], &global_extensions[i], sizeof(VkExtensionProperties));
+       *pPropertyCount = MIN2(*pPropertyCount, ARRAY_SIZE(global_extensions));
+       typed_memcpy(pProperties, global_extensions, *pPropertyCount);
 
-       *pPropertyCount = i;
-       if (i < ARRAY_SIZE(global_extensions))
+       if (*pPropertyCount < ARRAY_SIZE(global_extensions))
                return VK_INCOMPLETE;
 
        return VK_SUCCESS;
@@ -681,19 +679,17 @@ VkResult radv_EnumerateDeviceExtensionProperties(
        uint32_t*                                   pPropertyCount,
        VkExtensionProperties*                      pProperties)
 {
-       unsigned i;
-
        if (pProperties == NULL) {
                *pPropertyCount = ARRAY_SIZE(device_extensions);
                return VK_SUCCESS;
        }
 
-       for (i = 0; i < *pPropertyCount; i++)
-               memcpy(&pProperties[i], &device_extensions[i], sizeof(VkExtensionProperties));
+       *pPropertyCount = MIN2(*pPropertyCount, ARRAY_SIZE(device_extensions));
+       typed_memcpy(pProperties, device_extensions, *pPropertyCount);
 
-       *pPropertyCount = i;
-       if (i < ARRAY_SIZE(device_extensions))
+       if (*pPropertyCount < ARRAY_SIZE(device_extensions))
                return VK_INCOMPLETE;
+
        return VK_SUCCESS;
 }
 
index 3099d83..9649158 100644 (file)
@@ -831,29 +831,29 @@ void radv_image_set_optimal_micro_tile_mode(struct radv_device *device,
                switch (micro_tile_mode) {
                case 0: /* displayable */
                        switch (image->surface.bpe) {
-                       case 8:
+                       case 1:
                             image->surface.tiling_index[0] = 10;
                             break;
-                       case 16:
+                       case 2:
                             image->surface.tiling_index[0] = 11;
                             break;
-                       default: /* 32, 64 */
+                       default: /* 4, 8 */
                             image->surface.tiling_index[0] = 12;
                             break;
                        }
                        break;
                case 1: /* thin */
                        switch (image->surface.bpe) {
-                       case 8:
+                       case 1:
                                 image->surface.tiling_index[0] = 14;
                                 break;
-                       case 16:
+                       case 2:
                                 image->surface.tiling_index[0] = 15;
                                 break;
-                       case 32:
+                       case 4:
                                 image->surface.tiling_index[0] = 16;
                                 break;
-                       default: /* 64, 128 */
+                       default: /* 8, 16 */
                                 image->surface.tiling_index[0] = 17;
                                 break;
                        }
index 7e3e5f4..a347703 100644 (file)
@@ -998,7 +998,7 @@ radv_cmd_clear_image(struct radv_cmd_buffer *cmd_buffer,
                const VkImageSubresourceRange *range = &ranges[r];
                for (uint32_t l = 0; l < radv_get_levelCount(image, range); ++l) {
                        const uint32_t layer_count = image->type == VK_IMAGE_TYPE_3D ?
-                               radv_minify(image->extent.depth, l) :
+                               radv_minify(image->extent.depth, range->baseMipLevel + l) :
                                radv_get_layerCount(image, range);
                        for (uint32_t s = 0; s < layer_count; ++s) {
                                struct radv_image_view iview;
index 3139e6d..50660f9 100644 (file)
@@ -1,6 +1,7 @@
 
 VULKAN_WSI_FILES := \
-       wsi_common.h
+       wsi_common.h \
+       wsi_common_queue.h
 
 VULKAN_WSI_WAYLAND_FILES := \
        wsi_common_wayland.c \
index ee67511..a1f5a40 100644 (file)
@@ -53,6 +53,7 @@ struct wsi_swapchain {
    VkAllocationCallbacks alloc;
    const struct wsi_image_fns *image_fns;
    VkFence fences[3];
+   VkPresentModeKHR present_mode;
 
    VkResult (*destroy)(struct wsi_swapchain *swapchain,
                        const VkAllocationCallbacks *pAllocator);
diff --git a/src/vulkan/wsi/wsi_common_queue.h b/src/vulkan/wsi/wsi_common_queue.h
new file mode 100644 (file)
index 0000000..0e72c8d
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * Copyright © 2016 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef VULKAN_WSI_COMMON_QUEUE_H
+#define VULKAN_WSI_COMMON_QUEUE_H
+
+#include <time.h>
+#include <pthread.h>
+#include "util/u_vector.h"
+
+struct wsi_queue {
+   struct u_vector vector;
+   pthread_mutex_t mutex;
+   pthread_cond_t cond;
+};
+
+static inline int
+wsi_queue_init(struct wsi_queue *queue, int length)
+{
+   int ret;
+
+   uint32_t length_pow2 = 4;
+   while (length_pow2 < length)
+      length_pow2 *= 2;
+
+   ret = u_vector_init(&queue->vector, sizeof(uint32_t),
+                       sizeof(uint32_t) * length_pow2);
+   if (!ret)
+      return ENOMEM;
+
+   pthread_condattr_t condattr;
+   ret = pthread_condattr_init(&condattr);
+   if (ret)
+      goto fail_vector;
+
+   ret = pthread_condattr_setclock(&condattr, CLOCK_MONOTONIC);
+   if (ret)
+      goto fail_condattr;
+
+   ret = pthread_cond_init(&queue->cond, &condattr);
+   if (ret)
+      goto fail_condattr;
+
+   ret = pthread_mutex_init(&queue->mutex, NULL);
+   if (ret)
+      goto fail_cond;
+
+   return 0;
+
+fail_cond:
+   pthread_cond_destroy(&queue->cond);
+fail_condattr:
+   pthread_condattr_destroy(&condattr);
+fail_vector:
+   u_vector_finish(&queue->vector);
+
+   return ret;
+}
+
+static inline void
+wsi_queue_destroy(struct wsi_queue *queue)
+{
+   u_vector_finish(&queue->vector);
+   pthread_mutex_destroy(&queue->mutex);
+   pthread_cond_destroy(&queue->cond);
+}
+
+static inline void
+wsi_queue_push(struct wsi_queue *queue, uint32_t index)
+{
+   uint32_t *elem;
+
+   pthread_mutex_lock(&queue->mutex);
+
+   if (u_vector_length(&queue->vector) == 0)
+      pthread_cond_signal(&queue->cond);
+
+   elem = u_vector_add(&queue->vector);
+   *elem = index;
+
+   pthread_mutex_unlock(&queue->mutex);
+}
+
+#define NSEC_PER_SEC 1000000000
+#define INT_TYPE_MAX(type) ((1ull << (sizeof(type) * 8 - 1)) - 1)
+
+static inline VkResult
+wsi_queue_pull(struct wsi_queue *queue, uint32_t *index, uint64_t timeout)
+{
+   VkResult result;
+   int32_t ret;
+
+   pthread_mutex_lock(&queue->mutex);
+
+   struct timespec now;
+   clock_gettime(CLOCK_MONOTONIC, &now);
+
+   uint32_t abs_nsec = now.tv_nsec + timeout % NSEC_PER_SEC;
+   uint64_t abs_sec = now.tv_sec + (abs_nsec / NSEC_PER_SEC) +
+                      (timeout / NSEC_PER_SEC);
+   abs_nsec %= NSEC_PER_SEC;
+
+   /* Avoid roll-over in tv_sec on 32-bit systems if the user provided timeout
+    * is UINT64_MAX
+    */
+   struct timespec abstime;
+   abstime.tv_nsec = abs_nsec;
+   abstime.tv_sec = MIN2(abs_sec, INT_TYPE_MAX(abstime.tv_sec));
+
+   while (u_vector_length(&queue->vector) == 0) {
+      ret = pthread_cond_timedwait(&queue->cond, &queue->mutex, &abstime);
+      if (ret == 0) {
+         continue;
+      } else if (ret == ETIMEDOUT) {
+         result = VK_TIMEOUT;
+         goto end;
+      } else {
+         /* Something went badly wrong */
+         result = VK_ERROR_OUT_OF_DATE_KHR;
+         goto end;
+      }
+   }
+
+   uint32_t *elem = u_vector_remove(&queue->vector);
+   *index = *elem;
+   result = VK_SUCCESS;
+
+end:
+   pthread_mutex_unlock(&queue->mutex);
+
+   return result;
+}
+
+#endif /* VULKAN_WSI_COMMON_QUEUE_H */
index a8130ce..f6c218b 100644 (file)
@@ -576,7 +576,7 @@ wsi_wl_swapchain_queue_present(struct wsi_swapchain *wsi_chain,
 {
    struct wsi_wl_swapchain *chain = (struct wsi_wl_swapchain *)wsi_chain;
 
-   if (chain->present_mode == VK_PRESENT_MODE_FIFO_KHR) {
+   if (chain->base.present_mode == VK_PRESENT_MODE_FIFO_KHR) {
       while (!chain->fifo_ready) {
          int ret = wl_display_dispatch_queue(chain->display->display,
                                              chain->queue);
@@ -589,7 +589,7 @@ wsi_wl_swapchain_queue_present(struct wsi_swapchain *wsi_chain,
    wl_surface_attach(chain->surface, chain->images[image_index].buffer, 0, 0);
    wl_surface_damage(chain->surface, 0, 0, INT32_MAX, INT32_MAX);
 
-   if (chain->present_mode == VK_PRESENT_MODE_FIFO_KHR) {
+   if (chain->base.present_mode == VK_PRESENT_MODE_FIFO_KHR) {
       struct wl_callback *frame = wl_surface_frame(chain->surface);
       wl_proxy_set_queue((struct wl_proxy *)frame, chain->queue);
       wl_callback_add_listener(frame, &frame_listener, chain);
@@ -717,12 +717,12 @@ wsi_wl_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,
    chain->base.acquire_next_image = wsi_wl_swapchain_acquire_next_image;
    chain->base.queue_present = wsi_wl_swapchain_queue_present;
    chain->base.image_fns = image_fns;
+   chain->base.present_mode = pCreateInfo->presentMode;
    chain->surface = surface->surface;
    chain->extent = pCreateInfo->imageExtent;
    chain->vk_format = pCreateInfo->imageFormat;
    chain->drm_format = wl_drm_format_for_vk_format(chain->vk_format, alpha);
 
-   chain->present_mode = pCreateInfo->presentMode;
    chain->fifo_ready = true;
 
    chain->image_count = num_images;
index 09718eb..8e0043f 100644 (file)
 #include <errno.h>
 #include <string.h>
 
+#include <poll.h>
 #include "util/hash_table.h"
 
 #include "wsi_common.h"
 #include "wsi_common_x11.h"
+#include "wsi_common_queue.h"
 
 #define typed_memcpy(dest, src, count) ({ \
    static_assert(sizeof(*src) == sizeof(*dest), ""); \
@@ -142,7 +144,9 @@ static const VkSurfaceFormatKHR formats[] = {
 };
 
 static const VkPresentModeKHR present_modes[] = {
+   VK_PRESENT_MODE_IMMEDIATE_KHR,
    VK_PRESENT_MODE_MAILBOX_KHR,
+   VK_PRESENT_MODE_FIFO_KHR,
 };
 
 static xcb_screen_t *
@@ -488,8 +492,15 @@ struct x11_swapchain {
    xcb_present_event_t                          event_id;
    xcb_special_event_t *                        special_event;
    uint64_t                                     send_sbc;
+   uint64_t                                     last_present_msc;
    uint32_t                                     stamp;
 
+   bool                                         threaded;
+   VkResult                                     status;
+   struct wsi_queue                             present_queue;
+   struct wsi_queue                             acquire_queue;
+   pthread_t                                    queue_manager;
+
    struct x11_image                             images[0];
 };
 
@@ -540,6 +551,8 @@ x11_handle_dri3_present_event(struct x11_swapchain *chain,
       for (unsigned i = 0; i < chain->image_count; i++) {
          if (chain->images[i].pixmap == idle->pixmap) {
             chain->images[i].busy = false;
+            if (chain->threaded)
+               wsi_queue_push(&chain->acquire_queue, i);
             break;
          }
       }
@@ -547,7 +560,13 @@ x11_handle_dri3_present_event(struct x11_swapchain *chain,
       break;
    }
 
-   case XCB_PRESENT_COMPLETE_NOTIFY:
+   case XCB_PRESENT_EVENT_COMPLETE_NOTIFY: {
+      xcb_present_complete_notify_event_t *complete = (void *) event;
+      if (complete->kind == XCB_PRESENT_COMPLETE_KIND_PIXMAP)
+         chain->last_present_msc = complete->msc;
+      break;
+   }
+
    default:
       break;
    }
@@ -555,14 +574,33 @@ x11_handle_dri3_present_event(struct x11_swapchain *chain,
    return VK_SUCCESS;
 }
 
-static VkResult
-x11_acquire_next_image(struct wsi_swapchain *anv_chain,
-                       uint64_t timeout,
-                       VkSemaphore semaphore,
-                       uint32_t *image_index)
+
+static uint64_t wsi_get_current_time(void)
 {
-   struct x11_swapchain *chain = (struct x11_swapchain *)anv_chain;
+   uint64_t current_time;
+   struct timespec tv;
+
+   clock_gettime(CLOCK_MONOTONIC, &tv);
+   current_time = tv.tv_nsec + tv.tv_sec*1000000000ull;
+   return current_time;
+}
+
+static uint64_t wsi_get_absolute_timeout(uint64_t timeout)
+{
+   uint64_t current_time = wsi_get_current_time();
+
+   timeout = MIN2(UINT64_MAX - current_time, timeout);
+
+   return current_time + timeout;
+}
 
+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;
    while (1) {
       for (uint32_t i = 0; i < chain->image_count; i++) {
          if (!chain->images[i].busy) {
@@ -575,10 +613,39 @@ x11_acquire_next_image(struct wsi_swapchain *anv_chain,
       }
 
       xcb_flush(chain->conn);
-      xcb_generic_event_t *event =
-         xcb_wait_for_special_event(chain->conn, chain->special_event);
-      if (!event)
-         return VK_ERROR_OUT_OF_DATE_KHR;
+
+      if (timeout == UINT64_MAX) {
+         event = xcb_wait_for_special_event(chain->conn, chain->special_event);
+         if (!event)
+            return 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 VK_NOT_READY;
+
+            atimeout = wsi_get_absolute_timeout(timeout);
+
+            pfds.fd = xcb_get_file_descriptor(chain->conn);
+            pfds.events = POLLIN;
+            ret = poll(&pfds, 1, timeout / 1000 / 1000);
+            if (ret == 0)
+               return VK_TIMEOUT;
+            if (ret == -1)
+               return VK_ERROR_OUT_OF_DATE_KHR;
+
+            /* If a non-special event happens, the fd will still
+             * poll. So recalculate the timeout now just in case.
+             */
+            uint64_t current_time = wsi_get_current_time();
+            if (atimeout > current_time)
+               timeout = atimeout - current_time;
+            else
+               timeout = 0;
+            continue;
+         }
+      }
 
       VkResult result = x11_handle_dri3_present_event(chain, (void *)event);
       free(event);
@@ -588,21 +655,43 @@ x11_acquire_next_image(struct wsi_swapchain *anv_chain,
 }
 
 static VkResult
-x11_queue_present(struct wsi_swapchain *anv_chain,
-                  uint32_t image_index)
+x11_acquire_next_image_from_queue(struct x11_swapchain *chain,
+                                  uint32_t *image_index_out, uint64_t timeout)
+{
+   assert(chain->threaded);
+
+   uint32_t image_index;
+   VkResult result = wsi_queue_pull(&chain->acquire_queue,
+                                    &image_index, timeout);
+   if (result != VK_SUCCESS) {
+      return result;
+   } else if (chain->status != VK_SUCCESS) {
+      return chain->status;
+   }
+
+   assert(image_index < chain->image_count);
+   xshmfence_await(chain->images[image_index].shm_fence);
+
+   *image_index_out = image_index;
+
+   return VK_SUCCESS;
+}
+
+static VkResult
+x11_present_to_x11(struct x11_swapchain *chain, uint32_t image_index,
+                   uint32_t target_msc)
 {
-   struct x11_swapchain *chain = (struct x11_swapchain *)anv_chain;
    struct x11_image *image = &chain->images[image_index];
 
    assert(image_index < chain->image_count);
 
    uint32_t options = XCB_PRESENT_OPTION_NONE;
 
-   int64_t target_msc = 0;
    int64_t divisor = 0;
    int64_t remainder = 0;
 
-   options |= XCB_PRESENT_OPTION_ASYNC;
+   if (chain->base.present_mode == VK_PRESENT_MODE_IMMEDIATE_KHR)
+      options |= XCB_PRESENT_OPTION_ASYNC;
 
    xshmfence_reset(image->shm_fence);
 
@@ -632,6 +721,82 @@ x11_queue_present(struct wsi_swapchain *anv_chain,
 }
 
 static VkResult
+x11_acquire_next_image(struct wsi_swapchain *anv_chain,
+                       uint64_t timeout,
+                       VkSemaphore semaphore,
+                       uint32_t *image_index)
+{
+   struct x11_swapchain *chain = (struct x11_swapchain *)anv_chain;
+
+   if (chain->threaded) {
+      return x11_acquire_next_image_from_queue(chain, image_index, timeout);
+   } else {
+      return x11_acquire_next_image_poll_x11(chain, image_index, timeout);
+   }
+}
+
+static VkResult
+x11_queue_present(struct wsi_swapchain *anv_chain,
+                  uint32_t image_index)
+{
+   struct x11_swapchain *chain = (struct x11_swapchain *)anv_chain;
+
+   if (chain->threaded) {
+      wsi_queue_push(&chain->present_queue, image_index);
+      return chain->status;
+   } else {
+      return x11_present_to_x11(chain, image_index, 0);
+   }
+}
+
+static void *
+x11_manage_fifo_queues(void *state)
+{
+   struct x11_swapchain *chain = state;
+   VkResult result;
+
+   assert(chain->base.present_mode == VK_PRESENT_MODE_FIFO_KHR);
+
+   while (chain->status == VK_SUCCESS) {
+      /* It should be safe to unconditionally block here.  Later in the loop
+       * we blocks until the previous present has landed on-screen.  At that
+       * point, we should have received IDLE_NOTIFY on all images presented
+       * before that point so the client should be able to acquire any image
+       * other than the currently presented one.
+       */
+      uint32_t image_index;
+      result = wsi_queue_pull(&chain->present_queue, &image_index, INT64_MAX);
+      if (result != VK_SUCCESS) {
+         goto fail;
+      } else if (chain->status != VK_SUCCESS) {
+         return NULL;
+      }
+
+      uint64_t target_msc = chain->last_present_msc + 1;
+      result = x11_present_to_x11(chain, image_index, target_msc);
+      if (result != VK_SUCCESS)
+         goto fail;
+
+      while (chain->last_present_msc < target_msc) {
+         xcb_generic_event_t *event =
+            xcb_wait_for_special_event(chain->conn, chain->special_event);
+         if (!event)
+            goto fail;
+
+         result = x11_handle_dri3_present_event(chain, (void *)event);
+         if (result != VK_SUCCESS)
+            goto fail;
+      }
+   }
+
+fail:
+   chain->status = result;
+   wsi_queue_push(&chain->acquire_queue, UINT32_MAX);
+
+   return NULL;
+}
+
+static VkResult
 x11_image_init(VkDevice device_h, struct x11_swapchain *chain,
                const VkSwapchainCreateInfoKHR *pCreateInfo,
                const VkAllocationCallbacks* pAllocator,
@@ -729,6 +894,15 @@ x11_swapchain_destroy(struct wsi_swapchain *anv_chain,
    for (uint32_t i = 0; i < chain->image_count; i++)
       x11_image_finish(chain, pAllocator, &chain->images[i]);
 
+   if (chain->threaded) {
+      chain->status = VK_ERROR_OUT_OF_DATE_KHR;
+      /* Push a UINT32_MAX to wake up the manager */
+      wsi_queue_push(&chain->present_queue, UINT32_MAX);
+      pthread_join(chain->queue_manager, NULL);
+      wsi_queue_destroy(&chain->acquire_queue);
+      wsi_queue_destroy(&chain->present_queue);
+   }
+
    xcb_unregister_for_special_event(chain->conn, chain->special_event);
 
    vk_free(pAllocator, chain);
@@ -773,12 +947,16 @@ x11_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,
    chain->base.acquire_next_image = x11_acquire_next_image;
    chain->base.queue_present = x11_queue_present;
    chain->base.image_fns = image_fns;
+   chain->base.present_mode = pCreateInfo->presentMode;
    chain->conn = conn;
    chain->window = window;
    chain->depth = geometry->depth;
    chain->extent = pCreateInfo->imageExtent;
    chain->image_count = num_images;
    chain->send_sbc = 0;
+   chain->last_present_msc = 0;
+   chain->threaded = false;
+   chain->status = VK_SUCCESS;
 
    free(geometry);
 
@@ -817,6 +995,37 @@ x11_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,
          goto fail_init_images;
    }
 
+   if (chain->base.present_mode == VK_PRESENT_MODE_FIFO_KHR) {
+      chain->threaded = true;
+
+      /* Initialize our queues.  We make them image_count + 1 because we will
+       * occasionally use UINT32_MAX to signal the other thread that an error
+       * has occurred and we don't want an overflow.
+       */
+      int ret;
+      ret = wsi_queue_init(&chain->acquire_queue, chain->image_count + 1);
+      if (ret) {
+         goto fail_init_images;
+      }
+
+      ret = wsi_queue_init(&chain->present_queue, chain->image_count + 1);
+      if (ret) {
+         wsi_queue_destroy(&chain->acquire_queue);
+         goto fail_init_images;
+      }
+
+      for (unsigned i = 0; i < chain->image_count; i++)
+         wsi_queue_push(&chain->acquire_queue, i);
+
+      ret = pthread_create(&chain->queue_manager, NULL,
+                           x11_manage_fifo_queues, chain);
+      if (ret) {
+         wsi_queue_destroy(&chain->present_queue);
+         wsi_queue_destroy(&chain->acquire_queue);
+         goto fail_init_images;
+      }
+   }
+
    *swapchain_out = &chain->base;
 
    return VK_SUCCESS;