bool has_client_visible_address:1;
 };
 
+static inline struct anv_bo *
+anv_bo_ref(struct anv_bo *bo)
+{
+   p_atomic_inc(&bo->refcount);
+   return bo;
+}
+
 static inline struct anv_bo *
 anv_bo_unwrap(struct anv_bo *bo)
 {
 enum anv_fence_type {
    ANV_FENCE_TYPE_NONE = 0,
    ANV_FENCE_TYPE_BO,
+   ANV_FENCE_TYPE_WSI_BO,
    ANV_FENCE_TYPE_SYNCOBJ,
    ANV_FENCE_TYPE_WSI,
 };
    ANV_SEMAPHORE_TYPE_NONE = 0,
    ANV_SEMAPHORE_TYPE_DUMMY,
    ANV_SEMAPHORE_TYPE_BO,
+   ANV_SEMAPHORE_TYPE_WSI_BO,
    ANV_SEMAPHORE_TYPE_SYNC_FILE,
    ANV_SEMAPHORE_TYPE_DRM_SYNCOBJ,
    ANV_SEMAPHORE_TYPE_TIMELINE,
    enum anv_semaphore_type type;
 
    union {
-      /* A BO representing this semaphore when type == ANV_SEMAPHORE_TYPE_BO.
-       * This BO will be added to the object list on any execbuf2 calls for
-       * which this semaphore is used as a wait or signal fence.  When used as
-       * a signal fence, the EXEC_OBJECT_WRITE flag will be set.
+      /* A BO representing this semaphore when type == ANV_SEMAPHORE_TYPE_BO
+       * or type == ANV_SEMAPHORE_TYPE_WSI_BO.  This BO will be added to the
+       * object list on any execbuf2 calls for which this semaphore is used as
+       * a wait or signal fence.  When used as a signal fence or when type ==
+       * ANV_SEMAPHORE_TYPE_WSI_BO, the EXEC_OBJECT_WRITE flag will be set.
        */
       struct anv_bo *bo;
 
 
             goto error;
          break;
 
+      case ANV_SEMAPHORE_TYPE_WSI_BO:
+         /* When using a window-system buffer as a semaphore, always enable
+          * EXEC_OBJECT_WRITE.  This gives us a WaR hazard with the display or
+          * compositor's read of the buffer and enforces that we don't start
+          * rendering until they are finished.  This is exactly the
+          * synchronization we want with vkAcquireNextImage.
+          */
+         result = anv_queue_submit_add_fence_bo(submit, impl->bo, true /* signal */);
+         if (result != VK_SUCCESS)
+            goto error;
+         break;
+
       case ANV_SEMAPHORE_TYPE_SYNC_FILE:
          assert(!pdevice->has_syncobj);
          if (submit->in_fence == -1) {
       anv_bo_pool_free(&device->batch_bo_pool, impl->bo.bo);
       break;
 
+   case ANV_FENCE_TYPE_WSI_BO:
+      anv_device_release_bo(device, impl->bo.bo);
+      break;
+
    case ANV_FENCE_TYPE_SYNCOBJ:
       anv_gem_syncobj_destroy(device, impl->syncobj);
       break;
 
    switch (impl->type) {
    case ANV_FENCE_TYPE_BO:
+   case ANV_FENCE_TYPE_WSI_BO:
       /* BO fences don't support import/export */
       assert(fence->temporary.type == ANV_FENCE_TYPE_NONE);
       switch (impl->bo.state) {
       for (uint32_t i = 0; i < fenceCount; i++) {
          ANV_FROM_HANDLE(anv_fence, fence, pFences[i]);
 
-         /* This function assumes that all fences are BO fences and that they
-          * have no temporary state.  Since BO fences will never be exported,
-          * this should be a safe assumption.
-          */
-         assert(fence->permanent.type == ANV_FENCE_TYPE_BO);
-         assert(fence->temporary.type == ANV_FENCE_TYPE_NONE);
-         struct anv_fence_impl *impl = &fence->permanent;
+         struct anv_fence_impl *impl =
+            fence->temporary.type != ANV_FENCE_TYPE_NONE ?
+            &fence->temporary : &fence->permanent;
+         assert(impl->type == ANV_FENCE_TYPE_BO ||
+                impl->type == ANV_FENCE_TYPE_WSI_BO);
 
          switch (impl->bo.state) {
          case ANV_BO_FENCE_STATE_RESET:
          ANV_FROM_HANDLE(anv_fence, fence, pFences[i]);
          switch (fence->permanent.type) {
          case ANV_FENCE_TYPE_BO:
+         case ANV_FENCE_TYPE_WSI_BO:
             result = anv_wait_for_bo_fences(device, 1, &pFences[i],
                                             true, abs_timeout);
             break;
       break;
 
    case ANV_SEMAPHORE_TYPE_BO:
+   case ANV_SEMAPHORE_TYPE_WSI_BO:
       anv_device_release_bo(device, impl->bo);
       break;
 
 
    return image->drm_format_mod;
 }
 
+static void
+anv_wsi_signal_semaphore_for_memory(VkDevice _device,
+                                    VkSemaphore _semaphore,
+                                    VkDeviceMemory _memory)
+{
+   ANV_FROM_HANDLE(anv_device, device, _device);
+   ANV_FROM_HANDLE(anv_semaphore, semaphore, _semaphore);
+   ANV_FROM_HANDLE(anv_device_memory, memory, _memory);
+
+   /* Put a BO semaphore with the image BO in the temporary.  For BO binary
+    * semaphores, we always set EXEC_OBJECT_WRITE so this creates a WaR
+    * hazard with the display engine's read to ensure that no one writes to
+    * the image before the read is complete.
+    */
+   anv_semaphore_reset_temporary(device, semaphore);
+
+   struct anv_semaphore_impl *impl = &semaphore->temporary;
+   impl->type = ANV_SEMAPHORE_TYPE_WSI_BO;
+   impl->bo = anv_bo_ref(memory->bo);
+}
+
+static void
+anv_wsi_signal_fence_for_memory(VkDevice _device,
+                                VkFence _fence,
+                                VkDeviceMemory _memory)
+{
+   ANV_FROM_HANDLE(anv_device, device, _device);
+   ANV_FROM_HANDLE(anv_fence, fence, _fence);
+   ANV_FROM_HANDLE(anv_device_memory, memory, _memory);
+
+   /* Put a BO fence with the image BO in the temporary.  For BO fences, we
+    * always just wait until the BO isn't busy and reads from the BO should
+    * count as busy.
+    */
+   anv_fence_reset_temporary(device, fence);
+
+   struct anv_fence_impl *impl = &fence->temporary;
+   impl->type = ANV_FENCE_TYPE_WSI_BO;
+   impl->bo.bo = anv_bo_ref(memory->bo);
+   impl->bo.state = ANV_BO_FENCE_STATE_SUBMITTED;
+}
+
 VkResult
 anv_init_wsi(struct anv_physical_device *physical_device)
 {
 
    physical_device->wsi_device.supports_modifiers = true;
    physical_device->wsi_device.image_get_modifier = anv_wsi_image_get_modifier;
+   physical_device->wsi_device.signal_semaphore_for_memory =
+      anv_wsi_signal_semaphore_for_memory;
+   physical_device->wsi_device.signal_fence_for_memory =
+      anv_wsi_signal_fence_for_memory;
 
    return VK_SUCCESS;
 }
    ANV_FROM_HANDLE(anv_device, device, _device);
    struct anv_physical_device *pdevice = &device->instance->physicalDevice;
 
-   VkResult result = wsi_common_acquire_next_image2(&pdevice->wsi_device,
-                                                    _device,
-                                                    pAcquireInfo,
-                                                    pImageIndex);
-
-   /* Thanks to implicit sync, the image is ready immediately. However, we
-    * should wait for the current GPU state to finish. Regardless of the
-    * result of the presentation, we need to signal the semaphore & fence.
-    */
-
-   if (pAcquireInfo->semaphore != VK_NULL_HANDLE) {
-      /* Put a dummy semaphore in temporary, this is the fastest way to avoid
-       * any kind of work yet still provide some kind of synchronization. This
-       * only works because the Mesa WSI code always returns an image
-       * immediately if available.
-       */
-      ANV_FROM_HANDLE(anv_semaphore, semaphore, pAcquireInfo->semaphore);
-      anv_semaphore_reset_temporary(device, semaphore);
-
-      struct anv_semaphore_impl *impl = &semaphore->temporary;
-
-      impl->type = ANV_SEMAPHORE_TYPE_DUMMY;
-   }
-
-   if (pAcquireInfo->fence != VK_NULL_HANDLE) {
-      result = anv_QueueSubmit(anv_queue_to_handle(&device->queue),
-                               0, NULL, pAcquireInfo->fence);
-   }
-
-   return result;
+   return wsi_common_acquire_next_image2(&pdevice->wsi_device, _device,
+                                         pAcquireInfo, pImageIndex);
 }
 
 VkResult anv_QueuePresentKHR(