Add support for vk image sharing mode and handle queues xfers correctly.

Change-Id: I9dbe6020d67cc452c9cbbdeace68f1d01275b419
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/293559
Commit-Queue: Greg Daniel <egdaniel@google.com>
Reviewed-by: Jim Van Verth <jvanverth@google.com>
diff --git a/RELEASE_NOTES.txt b/RELEASE_NOTES.txt
index 8f2eb25..e170cad 100644
--- a/RELEASE_NOTES.txt
+++ b/RELEASE_NOTES.txt
@@ -9,6 +9,9 @@
 
   * <insert new release notes here>
 
+  * Add VkSharingMode field to GrVkImageInfo.
+    https://review.skia.org/293559
+
   * Move SkBitmapRegionDecoder into client_utils/android.
 
   * Remove SkSurface::MakeFromBackendTextureAsRenderTarget.
diff --git a/include/gpu/vk/GrVkTypes.h b/include/gpu/vk/GrVkTypes.h
index 784930b..e869238 100644
--- a/include/gpu/vk/GrVkTypes.h
+++ b/include/gpu/vk/GrVkTypes.h
@@ -149,6 +149,12 @@
     VkFormatFeatureFlags             fFormatFeatures;
 };
 
+/*
+ * When wrapping a GrBackendTexture or GrBackendRendenderTarget, the fCurrentQueueFamily should
+ * either be VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_EXTERNAL, or VK_QUEUE_FAMILY_FOREIGN_EXT. If
+ * fSharingMode is VK_SHARING_MODE_EXCLUSIVE then fCurrentQueueFamily can also be the graphics
+ * queue index passed into Skia.
+ */
 struct GrVkImageInfo {
     VkImage                  fImage;
     GrVkAlloc                fAlloc;
@@ -159,6 +165,7 @@
     uint32_t                 fCurrentQueueFamily;
     GrProtected              fProtected;
     GrVkYcbcrConversionInfo  fYcbcrConversionInfo;
+    VkSharingMode            fSharingMode;
 
     GrVkImageInfo()
             : fImage(VK_NULL_HANDLE)
@@ -169,7 +176,8 @@
             , fLevelCount(0)
             , fCurrentQueueFamily(VK_QUEUE_FAMILY_IGNORED)
             , fProtected(GrProtected::kNo)
-            , fYcbcrConversionInfo() {}
+            , fYcbcrConversionInfo()
+            , fSharingMode(VK_SHARING_MODE_EXCLUSIVE) {}
 
     GrVkImageInfo(VkImage image,
                   GrVkAlloc alloc,
@@ -179,7 +187,8 @@
                   uint32_t levelCount,
                   uint32_t currentQueueFamily = VK_QUEUE_FAMILY_IGNORED,
                   GrProtected isProtected = GrProtected::kNo,
-                  GrVkYcbcrConversionInfo ycbcrConversionInfo = GrVkYcbcrConversionInfo())
+                  GrVkYcbcrConversionInfo ycbcrConversionInfo = GrVkYcbcrConversionInfo(),
+                  VkSharingMode sharingMode = VK_SHARING_MODE_EXCLUSIVE)
             : fImage(image)
             , fAlloc(alloc)
             , fImageTiling(imageTiling)
@@ -188,7 +197,8 @@
             , fLevelCount(levelCount)
             , fCurrentQueueFamily(currentQueueFamily)
             , fProtected(isProtected)
-            , fYcbcrConversionInfo(ycbcrConversionInfo) {}
+            , fYcbcrConversionInfo(ycbcrConversionInfo)
+            , fSharingMode(sharingMode) {}
 
     GrVkImageInfo(const GrVkImageInfo& info, VkImageLayout layout, uint32_t familyQueueIndex)
             : fImage(info.fImage)
@@ -199,7 +209,8 @@
             , fLevelCount(info.fLevelCount)
             , fCurrentQueueFamily(familyQueueIndex)
             , fProtected(info.fProtected)
-            , fYcbcrConversionInfo(info.fYcbcrConversionInfo) {}
+            , fYcbcrConversionInfo(info.fYcbcrConversionInfo)
+            , fSharingMode(info.fSharingMode) {}
 
 #if GR_TEST_UTILS
     bool operator==(const GrVkImageInfo& that) const {
@@ -207,7 +218,8 @@
                fImageTiling == that.fImageTiling && fImageLayout == that.fImageLayout &&
                fFormat == that.fFormat && fLevelCount == that.fLevelCount &&
                fCurrentQueueFamily == that.fCurrentQueueFamily && fProtected == that.fProtected &&
-               fYcbcrConversionInfo == that.fYcbcrConversionInfo;
+               fYcbcrConversionInfo == that.fYcbcrConversionInfo &&
+               fSharingMode == that.fSharingMode;
     }
 #endif
 };
diff --git a/src/gpu/GrAHardwareBufferUtils.cpp b/src/gpu/GrAHardwareBufferUtils.cpp
index b701daf..badb8ee 100644
--- a/src/gpu/GrAHardwareBufferUtils.cpp
+++ b/src/gpu/GrAHardwareBufferUtils.cpp
@@ -486,6 +486,7 @@
     // "foreign" device we can leave them as external.
     imageInfo.fCurrentQueueFamily = VK_QUEUE_FAMILY_EXTERNAL;
     imageInfo.fYcbcrConversionInfo = *ycbcrConversion;
+    imageInfo.fSharingMode = imageCreateInfo.sharingMode;
 
     *deleteProc = delete_vk_image;
     *updateProc = update_vk_image;
diff --git a/src/gpu/vk/GrVkGpu.cpp b/src/gpu/vk/GrVkGpu.cpp
index 97d819f..c8ee2c7 100644
--- a/src/gpu/vk/GrVkGpu.cpp
+++ b/src/gpu/vk/GrVkGpu.cpp
@@ -1198,7 +1198,8 @@
 
 static bool check_image_info(const GrVkCaps& caps,
                              const GrVkImageInfo& info,
-                             bool needsAllocation) {
+                             bool needsAllocation,
+                             uint32_t graphicsQueueIndex) {
     if (VK_NULL_HANDLE == info.fImage) {
         return false;
     }
@@ -1211,6 +1212,18 @@
         return false;
     }
 
+    if (info.fCurrentQueueFamily != VK_QUEUE_FAMILY_IGNORED &&
+        info.fCurrentQueueFamily != VK_QUEUE_FAMILY_EXTERNAL &&
+        info.fCurrentQueueFamily != VK_QUEUE_FAMILY_FOREIGN_EXT) {
+        if (info.fSharingMode == VK_SHARING_MODE_EXCLUSIVE) {
+            if (info.fCurrentQueueFamily != graphicsQueueIndex) {
+                return false;
+            }
+        } else {
+            return false;
+        }
+    }
+
     if (info.fYcbcrConversionInfo.isValid()) {
         if (!caps.supportsYcbcrConversion()) {
             return false;
@@ -1256,7 +1269,8 @@
         return nullptr;
     }
 
-    if (!check_image_info(this->vkCaps(), imageInfo, kAdopt_GrWrapOwnership == ownership)) {
+    if (!check_image_info(this->vkCaps(), imageInfo, kAdopt_GrWrapOwnership == ownership,
+                          this->queueIndex())) {
         return nullptr;
     }
 
@@ -1282,7 +1296,8 @@
         return nullptr;
     }
 
-    if (!check_image_info(this->vkCaps(), imageInfo, kAdopt_GrWrapOwnership == ownership)) {
+    if (!check_image_info(this->vkCaps(), imageInfo, kAdopt_GrWrapOwnership == ownership,
+                          this->queueIndex())) {
         return nullptr;
     }
 
@@ -1309,7 +1324,8 @@
         return nullptr;
     }
 
-    if (!check_image_info(this->vkCaps(), imageInfo, kAdopt_GrWrapOwnership == ownership)) {
+    if (!check_image_info(this->vkCaps(), imageInfo, kAdopt_GrWrapOwnership == ownership,
+                          this->queueIndex())) {
         return nullptr;
     }
 
@@ -1349,7 +1365,7 @@
         return nullptr;
     }
 
-    if (!check_image_info(this->vkCaps(), info, false)) {
+    if (!check_image_info(this->vkCaps(), info, false, this->queueIndex())) {
         return nullptr;
     }
 
@@ -1382,7 +1398,7 @@
     if (!tex.getVkImageInfo(&imageInfo)) {
         return nullptr;
     }
-    if (!check_image_info(this->vkCaps(), imageInfo, false)) {
+    if (!check_image_info(this->vkCaps(), imageInfo, false, this->queueIndex())) {
         return nullptr;
     }
 
diff --git a/src/gpu/vk/GrVkImage.cpp b/src/gpu/vk/GrVkImage.cpp
index 9b50ca7..1270743 100644
--- a/src/gpu/vk/GrVkImage.cpp
+++ b/src/gpu/vk/GrVkImage.cpp
@@ -13,6 +13,43 @@
 
 #define VK_CALL(GPU, X) GR_VK_CALL(GPU->vkInterface(), X)
 
+GrVkImage::GrVkImage(const GrVkGpu* gpu,
+                     const GrVkImageInfo& info,
+                     sk_sp<GrBackendSurfaceMutableStateImpl> mutableState,
+                     GrBackendObjectOwnership ownership,
+                     bool forSecondaryCB)
+        : fInfo(info)
+        , fInitialQueueFamily(info.fCurrentQueueFamily)
+        , fMutableState(std::move(mutableState))
+        , fIsBorrowed(GrBackendObjectOwnership::kBorrowed == ownership) {
+    SkASSERT(fMutableState->getImageLayout() == fInfo.fImageLayout);
+    SkASSERT(fMutableState->getQueueFamilyIndex() == fInfo.fCurrentQueueFamily);
+#ifdef SK_DEBUG
+    // We can't transfer from the non graphics queue to the graphics queue since we can't
+    // release the image from the original queue without having that queue. This limits us in terms
+    // of the types of queue indices we can handle.
+    if (info.fCurrentQueueFamily != VK_QUEUE_FAMILY_IGNORED &&
+        info.fCurrentQueueFamily != VK_QUEUE_FAMILY_EXTERNAL &&
+        info.fCurrentQueueFamily != VK_QUEUE_FAMILY_FOREIGN_EXT) {
+        if (info.fSharingMode == VK_SHARING_MODE_EXCLUSIVE) {
+            if (info.fCurrentQueueFamily != gpu->queueIndex()) {
+                SkASSERT(false);
+            }
+        } else {
+            SkASSERT(false);
+        }
+    }
+#endif
+    if (forSecondaryCB) {
+        fResource = nullptr;
+    } else if (fIsBorrowed) {
+        fResource = new BorrowedResource(gpu, info.fImage, info.fAlloc, info.fImageTiling);
+    } else {
+        SkASSERT(VK_NULL_HANDLE != info.fAlloc.fMemory);
+        fResource = new Resource(gpu, info.fImage, info.fAlloc, info.fImageTiling);
+    }
+}
+
 VkPipelineStageFlags GrVkImage::LayoutToPipelineSrcStageFlags(const VkImageLayout layout) {
     if (VK_IMAGE_LAYOUT_GENERAL == layout) {
         return VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
@@ -116,15 +153,22 @@
     uint32_t srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
     uint32_t dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
     if (this->currentQueueFamilyIndex() != VK_QUEUE_FAMILY_IGNORED &&
-        gpu->queueIndex() != this->currentQueueFamilyIndex()) {
+        this->currentQueueFamilyIndex() != gpu->queueIndex()) {
         // The image still is owned by its original queue family and we need to transfer it into
         // ours.
         SkASSERT(!releaseFamilyQueue);
         SkASSERT(this->currentQueueFamilyIndex() == fInitialQueueFamily);
-
+        // We only support transferring from external or foreign queues here and not arbitrary
+        // ones.
+        SkASSERT(this->currentQueueFamilyIndex() == VK_QUEUE_FAMILY_EXTERNAL ||
+                 this->currentQueueFamilyIndex() == VK_QUEUE_FAMILY_FOREIGN_EXT);
         srcQueueFamilyIndex = this->currentQueueFamilyIndex();
-        dstQueueFamilyIndex = gpu->queueIndex();
-        this->setQueueFamilyIndex(gpu->queueIndex());
+        if (fInfo.fSharingMode == VK_SHARING_MODE_EXCLUSIVE) {
+            dstQueueFamilyIndex = gpu->queueIndex();
+        } else {
+            dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+        }
+        this->setQueueFamilyIndex(dstQueueFamilyIndex);
     } else if (releaseFamilyQueue) {
         // We are releasing the image so we must transfer the image back to its original queue
         // family.
@@ -217,6 +261,7 @@
     info->fCurrentQueueFamily = VK_QUEUE_FAMILY_IGNORED;
     info->fProtected =
             (createflags & VK_IMAGE_CREATE_PROTECTED_BIT) ? GrProtected::kYes : GrProtected::kNo;
+    info->fSharingMode = VK_SHARING_MODE_EXCLUSIVE;
     return true;
 }
 
diff --git a/src/gpu/vk/GrVkImage.h b/src/gpu/vk/GrVkImage.h
index 4ff3e7e..d7657dc 100644
--- a/src/gpu/vk/GrVkImage.h
+++ b/src/gpu/vk/GrVkImage.h
@@ -29,22 +29,8 @@
               const GrVkImageInfo& info,
               sk_sp<GrBackendSurfaceMutableStateImpl> mutableState,
               GrBackendObjectOwnership ownership,
-              bool forSecondaryCB = false)
-            : fInfo(info)
-            , fInitialQueueFamily(info.fCurrentQueueFamily)
-            , fMutableState(std::move(mutableState))
-            , fIsBorrowed(GrBackendObjectOwnership::kBorrowed == ownership) {
-        SkASSERT(fMutableState->getImageLayout() == fInfo.fImageLayout);
-        SkASSERT(fMutableState->getQueueFamilyIndex() == fInfo.fCurrentQueueFamily);
-        if (forSecondaryCB) {
-            fResource = nullptr;
-        } else if (fIsBorrowed) {
-            fResource = new BorrowedResource(gpu, info.fImage, info.fAlloc, info.fImageTiling);
-        } else {
-            SkASSERT(VK_NULL_HANDLE != info.fAlloc.fMemory);
-            fResource = new Resource(gpu, info.fImage, info.fAlloc, info.fImageTiling);
-        }
-    }
+              bool forSecondaryCB = false);
+
     virtual ~GrVkImage();
 
     VkImage image() const {
diff --git a/tools/sk_app/VulkanWindowContext.cpp b/tools/sk_app/VulkanWindowContext.cpp
index b65de09..593d60c 100644
--- a/tools/sk_app/VulkanWindowContext.cpp
+++ b/tools/sk_app/VulkanWindowContext.cpp
@@ -317,12 +317,14 @@
         fDestroySwapchainKHR(fDevice, swapchainCreateInfo.oldSwapchain, nullptr);
     }
 
-    this->createBuffers(swapchainCreateInfo.imageFormat, colorType);
+    this->createBuffers(swapchainCreateInfo.imageFormat, colorType,
+                        swapchainCreateInfo.imageSharingMode);
 
     return true;
 }
 
-void VulkanWindowContext::createBuffers(VkFormat format, SkColorType colorType) {
+void VulkanWindowContext::createBuffers(VkFormat format, SkColorType colorType,
+                                        VkSharingMode sharingMode) {
     fGetSwapchainImagesKHR(fDevice, fSwapchain, &fImageCount, nullptr);
     SkASSERT(fImageCount);
     fImages = new VkImage[fImageCount];
@@ -342,6 +344,7 @@
         info.fFormat = format;
         info.fLevelCount = 1;
         info.fCurrentQueueFamily = fPresentQueueIndex;
+        info.fSharingMode = sharingMode;
 
         if (fSampleCount == 1) {
             GrBackendRenderTarget backendRT(fWidth, fHeight, fSampleCount, info);
diff --git a/tools/sk_app/VulkanWindowContext.h b/tools/sk_app/VulkanWindowContext.h
index 2db9e79..5e245af 100644
--- a/tools/sk_app/VulkanWindowContext.h
+++ b/tools/sk_app/VulkanWindowContext.h
@@ -61,7 +61,7 @@
 
     BackbufferInfo* getAvailableBackbuffer();
     bool createSwapchain(int width, int height, const DisplayParams& params);
-    void createBuffers(VkFormat format, SkColorType colorType);
+    void createBuffers(VkFormat format, SkColorType colorType, VkSharingMode);
     void destroyBuffers();
 
     VkInstance fInstance = VK_NULL_HANDLE;