Add optional param to setBackendTextureState to return previous state.

Bug: skia:10742
Change-Id: I334e7896d0a1509eb666c46d5731d2573a5c1aba
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/318698
Reviewed-by: Austin Eng <enga@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Greg Daniel <egdaniel@google.com>
diff --git a/RELEASE_NOTES.txt b/RELEASE_NOTES.txt
index 7a5ccbf..2b64f0f 100644
--- a/RELEASE_NOTES.txt
+++ b/RELEASE_NOTES.txt
@@ -9,6 +9,10 @@
 
   * <insert new release notes here>
 
+  * Add new optional parameter to GrContext::setBackend[Texture/RenderTarget]State which can
+    be used to return the previous GrBackendSurfaceMutableState before the requested change.
+    https://review.skia.org/318698
+
   * New optimized clip stack for GPU backends. Enabled by default but old behavior based on
     SkClipStack can be restored by defining SK_DISABLE_NEW_GR_CLIP_STACK when building. It is not
     compatible with SK_SUPPORT_DEPRECATED_CLIPOPS and we are targeting the removal of support for
diff --git a/gn/gpu.gni b/gn/gpu.gni
index 0690900..2c91236 100644
--- a/gn/gpu.gni
+++ b/gn/gpu.gni
@@ -38,6 +38,7 @@
   "$_src/gpu/GrAutoLocaleSetter.h",
   "$_src/gpu/GrBackendSemaphore.cpp",
   "$_src/gpu/GrBackendSurface.cpp",
+  "$_src/gpu/GrBackendSurfaceMutableState.cpp",
   "$_src/gpu/GrBackendSurfaceMutableStateImpl.h",
   "$_src/gpu/GrBackendTextureImageGenerator.cpp",
   "$_src/gpu/GrBackendTextureImageGenerator.h",
diff --git a/include/gpu/GrBackendSurfaceMutableState.h b/include/gpu/GrBackendSurfaceMutableState.h
index 6425cb5..b3d3f79 100644
--- a/include/gpu/GrBackendSurfaceMutableState.h
+++ b/include/gpu/GrBackendSurfaceMutableState.h
@@ -24,35 +24,59 @@
  *
  * Vulkan: VkImageLayout and QueueFamilyIndex
  */
-class GrBackendSurfaceMutableState {
+class SK_API GrBackendSurfaceMutableState {
 public:
+    GrBackendSurfaceMutableState() {}
+
 #ifdef SK_VULKAN
     GrBackendSurfaceMutableState(VkImageLayout layout, uint32_t queueFamilyIndex)
             : fVkState(layout, queueFamilyIndex)
-            , fBackend(GrBackend::kVulkan) {}
+            , fBackend(GrBackend::kVulkan)
+            , fIsValid(true) {}
 #endif
 
-    GrBackendSurfaceMutableState& operator=(const GrBackendSurfaceMutableState& that) {
-        switch (fBackend) {
-            case GrBackend::kVulkan:
+    GrBackendSurfaceMutableState(const GrBackendSurfaceMutableState& that);
+    GrBackendSurfaceMutableState& operator=(const GrBackendSurfaceMutableState& that);
+
 #ifdef SK_VULKAN
-                SkASSERT(that.fBackend == GrBackend::kVulkan);
-                fVkState = that.fVkState;
-#endif
-                break;
-
-            default:
-                (void)that;
-                SkUNREACHABLE;
+    // If this class is not Vulkan backed it will return value of VK_IMAGE_LAYOUT_UNDEFINED.
+    // Otherwise it will return the VkImageLayout.
+    VkImageLayout getVkImageLayout() const {
+        if (this->isValid() && fBackend != GrBackendApi::kVulkan) {
+            return VK_IMAGE_LAYOUT_UNDEFINED;
         }
-        fBackend = that.fBackend;
-        return *this;
+        return fVkState.getImageLayout();
     }
 
+    // If this class is not Vulkan backed it will return value of VK_QUEUE_FAMILY_IGNORED.
+    // Otherwise it will return the VkImageLayout.
+    uint32_t getQueueFamilyIndex() const {
+        if (this->isValid() && fBackend != GrBackendApi::kVulkan) {
+            return VK_QUEUE_FAMILY_IGNORED;
+        }
+        return fVkState.getQueueFamilyIndex();
+    }
+#endif
+
+    // Returns true if the backend mutable state has been initialized.
+    bool isValid() const { return fIsValid; }
+
+    GrBackendApi backend() const { return fBackend; }
+
 private:
     friend class GrBackendSurfaceMutableStateImpl;
     friend class GrVkGpu;
 
+#ifdef SK_VULKAN
+    void setVulkanState(VkImageLayout layout, uint32_t queueFamilyIndex) {
+        SkASSERT(!this->isValid() || fBackend == GrBackendApi::kVulkan);
+        fVkState.setImageLayout(layout);
+        fVkState.setQueueFamilyIndex(queueFamilyIndex);
+        fBackend = GrBackendApi::kVulkan;
+        fIsValid = true;
+    }
+#endif
+
     union {
         char fDummy;
 #ifdef SK_VULKAN
@@ -60,7 +84,8 @@
 #endif
     };
 
-    GrBackend fBackend;
+    GrBackend fBackend = GrBackendApi::kMock;
+    bool fIsValid = false;
 };
 
 #endif
diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h
index 5f4b2a8..0a96abc 100644
--- a/include/gpu/GrContext.h
+++ b/include/gpu/GrContext.h
@@ -687,18 +687,23 @@
      * called (e.g updateBackendTexture and flush). If finishedProc is not null then it will be
      * called with finishedContext after the state transition is known to have occurred on the GPU.
      *
+     * See GrBackendSurfaceMutableState to see what state can be set via this call.
+     *
      * If the backend API is Vulkan, the caller can set the GrBackendSurfaceMutableState's
      * VkImageLayout to VK_IMAGE_LAYOUT_UNDEFINED or queueFamilyIndex to VK_QUEUE_FAMILY_IGNORED to
      * tell Skia to not change those respective states.
      *
-     * See GrBackendSurfaceMutableState to see what state can be set via this call.
+     * If previousState is not null and this returns true, then Skia will have filled in
+     * previousState to have the values of the state before this call.
      */
     bool setBackendTextureState(const GrBackendTexture&,
                                 const GrBackendSurfaceMutableState&,
+                                GrBackendSurfaceMutableState* previousState = nullptr,
                                 GrGpuFinishedProc finishedProc = nullptr,
                                 GrGpuFinishedContext finishedContext = nullptr);
     bool setBackendRenderTargetState(const GrBackendRenderTarget&,
                                      const GrBackendSurfaceMutableState&,
+                                     GrBackendSurfaceMutableState* previousState = nullptr,
                                      GrGpuFinishedProc finishedProc = nullptr,
                                      GrGpuFinishedContext finishedContext = nullptr);
 
diff --git a/src/gpu/GrBackendSurfaceMutableState.cpp b/src/gpu/GrBackendSurfaceMutableState.cpp
new file mode 100644
index 0000000..dec31d7
--- /dev/null
+++ b/src/gpu/GrBackendSurfaceMutableState.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2020 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "include/gpu/GrBackendSurfaceMutableState.h"
+
+#include <new>
+
+GrBackendSurfaceMutableState::GrBackendSurfaceMutableState(const GrBackendSurfaceMutableState& that)
+        : fBackend(that.fBackend), fIsValid(that.fIsValid) {
+    if (!fIsValid) {
+        return;
+    }
+    switch (fBackend) {
+        case GrBackend::kVulkan:
+#ifdef SK_VULKAN
+            SkASSERT(that.fBackend == GrBackend::kVulkan);
+            fVkState = that.fVkState;
+#endif
+            break;
+        default:
+            (void)that;
+            SkUNREACHABLE;
+    }
+}
+
+GrBackendSurfaceMutableState& GrBackendSurfaceMutableState::operator=(
+        const GrBackendSurfaceMutableState& that) {
+    if (this != &that) {
+        this->~GrBackendSurfaceMutableState();
+        new (this) GrBackendSurfaceMutableState(that);
+    }
+    return *this;
+}
+
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index e6100c8..96edd6f 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -754,6 +754,7 @@
 
 bool GrContext::setBackendTextureState(const GrBackendTexture& backendTexture,
                                        const GrBackendSurfaceMutableState& state,
+                                       GrBackendSurfaceMutableState* previousState,
                                        GrGpuFinishedProc finishedProc,
                                        GrGpuFinishedContext finishedContext) {
     sk_sp<GrRefCntedCallback> callback;
@@ -769,7 +770,7 @@
         return false;
     }
 
-    return fGpu->setBackendTextureState(backendTexture, state, std::move(callback));
+    return fGpu->setBackendTextureState(backendTexture, state, previousState, std::move(callback));
 }
 
 bool GrContext::updateCompressedBackendTexture(const GrBackendTexture& backendTexture,
@@ -824,6 +825,7 @@
 
 bool GrContext::setBackendRenderTargetState(const GrBackendRenderTarget& backendRenderTarget,
                                             const GrBackendSurfaceMutableState& state,
+                                            GrBackendSurfaceMutableState* previousState,
                                             GrGpuFinishedProc finishedProc,
                                             GrGpuFinishedContext finishedContext) {
     sk_sp<GrRefCntedCallback> callback;
@@ -839,7 +841,8 @@
         return false;
     }
 
-    return fGpu->setBackendRenderTargetState(backendRenderTarget, state, std::move(callback));
+    return fGpu->setBackendRenderTargetState(backendRenderTarget, state, previousState,
+                                             std::move(callback));
 }
 
 void GrContext::deleteBackendTexture(GrBackendTexture backendTex) {
diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h
index 5cacf8a..a5f5e8c 100644
--- a/src/gpu/GrGpu.h
+++ b/src/gpu/GrGpu.h
@@ -635,12 +635,14 @@
 
     virtual bool setBackendTextureState(const GrBackendTexture&,
                                         const GrBackendSurfaceMutableState&,
+                                        GrBackendSurfaceMutableState* previousState,
                                         sk_sp<GrRefCntedCallback> finishedCallback) {
         return false;
     }
 
     virtual bool setBackendRenderTargetState(const GrBackendRenderTarget&,
                                              const GrBackendSurfaceMutableState&,
+                                             GrBackendSurfaceMutableState* previousState,
                                              sk_sp<GrRefCntedCallback> finishedCallback) {
         return false;
     }
diff --git a/src/gpu/vk/GrVkGpu.cpp b/src/gpu/vk/GrVkGpu.cpp
index 701fd8a..307210ca 100644
--- a/src/gpu/vk/GrVkGpu.cpp
+++ b/src/gpu/vk/GrVkGpu.cpp
@@ -1816,7 +1816,8 @@
 bool GrVkGpu::setBackendSurfaceState(GrVkImageInfo info,
                                      sk_sp<GrBackendSurfaceMutableStateImpl> currentState,
                                      SkISize dimensions,
-                                     const GrVkSharedImageInfo& newInfo) {
+                                     const GrVkSharedImageInfo& newInfo,
+                                     GrBackendSurfaceMutableState* previousState) {
     sk_sp<GrVkTexture> texture = GrVkTexture::MakeWrappedTexture(
             this, dimensions, kBorrow_GrWrapOwnership, GrWrapCacheable::kNo, kRW_GrIOType, info,
             std::move(currentState));
@@ -1824,32 +1825,39 @@
     if (!texture) {
         return false;
     }
+    if (previousState) {
+        previousState->setVulkanState(texture->currentLayout(),
+                                      texture->currentQueueFamilyIndex());
+    }
     set_layout_and_queue_from_mutable_state(this, texture.get(), newInfo);
     return true;
 }
 
 bool GrVkGpu::setBackendTextureState(const GrBackendTexture& backendTeture,
                                      const GrBackendSurfaceMutableState& newState,
+                                     GrBackendSurfaceMutableState* previousState,
                                      sk_sp<GrRefCntedCallback> finishedCallback) {
     GrVkImageInfo info;
     SkAssertResult(backendTeture.getVkImageInfo(&info));
     sk_sp<GrBackendSurfaceMutableStateImpl> currentState = backendTeture.getMutableState();
     SkASSERT(currentState);
-    SkASSERT(newState.fBackend == GrBackend::kVulkan);
+    SkASSERT(newState.isValid() && newState.fBackend == GrBackend::kVulkan);
     return this->setBackendSurfaceState(info, std::move(currentState), backendTeture.dimensions(),
-                                        newState.fVkState);
+                                        newState.fVkState, previousState);
 }
 
 bool GrVkGpu::setBackendRenderTargetState(const GrBackendRenderTarget& backendRenderTarget,
                                           const GrBackendSurfaceMutableState& newState,
-                                         sk_sp<GrRefCntedCallback> finishedCallback) {
+                                          GrBackendSurfaceMutableState* previousState,
+                                          sk_sp<GrRefCntedCallback> finishedCallback) {
     GrVkImageInfo info;
     SkAssertResult(backendRenderTarget.getVkImageInfo(&info));
     sk_sp<GrBackendSurfaceMutableStateImpl> currentState = backendRenderTarget.getMutableState();
     SkASSERT(currentState);
     SkASSERT(newState.fBackend == GrBackend::kVulkan);
     return this->setBackendSurfaceState(info, std::move(currentState),
-                                        backendRenderTarget.dimensions(), newState.fVkState);
+                                        backendRenderTarget.dimensions(), newState.fVkState,
+                                        previousState);
 }
 
 void GrVkGpu::querySampleLocations(GrRenderTarget* renderTarget,
diff --git a/src/gpu/vk/GrVkGpu.h b/src/gpu/vk/GrVkGpu.h
index bd27d93..63abc61 100644
--- a/src/gpu/vk/GrVkGpu.h
+++ b/src/gpu/vk/GrVkGpu.h
@@ -79,10 +79,12 @@
 
     bool setBackendTextureState(const GrBackendTexture&,
                                 const GrBackendSurfaceMutableState&,
+                                GrBackendSurfaceMutableState* previousState,
                                 sk_sp<GrRefCntedCallback> finishedCallback) override;
 
     bool setBackendRenderTargetState(const GrBackendRenderTarget&,
                                      const GrBackendSurfaceMutableState&,
+                                     GrBackendSurfaceMutableState* previousState,
                                      sk_sp<GrRefCntedCallback> finishedCallback) override;
 
     void deleteBackendTexture(const GrBackendTexture&) override;
@@ -215,7 +217,8 @@
     bool setBackendSurfaceState(GrVkImageInfo info,
                                 sk_sp<GrBackendSurfaceMutableStateImpl> currentState,
                                 SkISize dimensions,
-                                const GrVkSharedImageInfo& newInfo);
+                                const GrVkSharedImageInfo& newInfo,
+                                GrBackendSurfaceMutableState* previousState);
 
     sk_sp<GrTexture> onCreateTexture(SkISize,
                                      const GrBackendFormat&,
diff --git a/tests/BackendSurfaceMutableStateTest.cpp b/tests/BackendSurfaceMutableStateTest.cpp
index df8f1496..8e822e2 100644
--- a/tests/BackendSurfaceMutableStateTest.cpp
+++ b/tests/BackendSurfaceMutableStateTest.cpp
@@ -107,41 +107,65 @@
     // real transitions to the image so we need to be careful about doing actual valid transitions.
     GrVkGpu* gpu = static_cast<GrVkGpu*>(dContext->priv().getGpu());
 
-    dContext->setBackendTextureState(backendTex, newState);
+    GrBackendSurfaceMutableState previousState;
+
+    dContext->setBackendTextureState(backendTex, newState, &previousState);
 
     REPORTER_ASSERT(reporter, backendTex.getVkImageInfo(&info));
     REPORTER_ASSERT(reporter, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL == info.fImageLayout);
     REPORTER_ASSERT(reporter, gpu->queueIndex() == info.fCurrentQueueFamily);
 
+    REPORTER_ASSERT(reporter, previousState.isValid());
+    REPORTER_ASSERT(reporter, previousState.backend() == GrBackendApi::kVulkan);
+    REPORTER_ASSERT(reporter, previousState.getVkImageLayout() == initLayout);
+    REPORTER_ASSERT(reporter, previousState.getQueueFamilyIndex() == initQueue);
+
     // Make sure passing in VK_IMAGE_LAYOUT_UNDEFINED does not change the layout
     GrBackendSurfaceMutableState noopState(VK_IMAGE_LAYOUT_UNDEFINED, VK_QUEUE_FAMILY_IGNORED);
-    dContext->setBackendTextureState(backendTex, noopState);
+    dContext->setBackendTextureState(backendTex, noopState, &previousState);
     REPORTER_ASSERT(reporter, backendTex.getVkImageInfo(&info));
     REPORTER_ASSERT(reporter, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL == info.fImageLayout);
     REPORTER_ASSERT(reporter, gpu->queueIndex() == info.fCurrentQueueFamily);
 
+    REPORTER_ASSERT(reporter, previousState.isValid());
+    REPORTER_ASSERT(reporter, previousState.backend() == GrBackendApi::kVulkan);
+    REPORTER_ASSERT(reporter,
+                    previousState.getVkImageLayout() == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
+    REPORTER_ASSERT(reporter, previousState.getQueueFamilyIndex() == gpu->queueIndex());
+
     // To test queue transitions, we don't have any other valid queue available so instead we try
     // to transition to external queue.
     if (gpu->vkCaps().supportsExternalMemory()) {
         GrBackendSurfaceMutableState externalState(VK_IMAGE_LAYOUT_GENERAL,
                                                    VK_QUEUE_FAMILY_EXTERNAL);
 
-        dContext->setBackendTextureState(backendTex, externalState);
+        dContext->setBackendTextureState(backendTex, externalState, &previousState);
 
         REPORTER_ASSERT(reporter, backendTex.getVkImageInfo(&info));
         REPORTER_ASSERT(reporter, VK_IMAGE_LAYOUT_GENERAL == info.fImageLayout);
         REPORTER_ASSERT(reporter, VK_QUEUE_FAMILY_EXTERNAL == info.fCurrentQueueFamily);
 
+        REPORTER_ASSERT(reporter, previousState.isValid());
+        REPORTER_ASSERT(reporter, previousState.backend() == GrBackendApi::kVulkan);
+        REPORTER_ASSERT(reporter,
+                previousState.getVkImageLayout() == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
+        REPORTER_ASSERT(reporter, previousState.getQueueFamilyIndex() == gpu->queueIndex());
+
         dContext->submit();
 
         // Go back to the initial queue. Also we should stay in VK_IMAGE_LAYOUT_GENERAL since we
         // are passing in VK_IMAGE_LAYOUT_UNDEFINED
         GrBackendSurfaceMutableState externalState2(VK_IMAGE_LAYOUT_UNDEFINED, initQueue);
-        dContext->setBackendTextureState(backendTex, externalState2);
+        dContext->setBackendTextureState(backendTex, externalState2, &previousState);
 
         REPORTER_ASSERT(reporter, backendTex.getVkImageInfo(&info));
         REPORTER_ASSERT(reporter, VK_IMAGE_LAYOUT_GENERAL == info.fImageLayout);
         REPORTER_ASSERT(reporter, gpu->queueIndex() == info.fCurrentQueueFamily);
+
+        REPORTER_ASSERT(reporter, previousState.isValid());
+        REPORTER_ASSERT(reporter, previousState.backend() == GrBackendApi::kVulkan);
+        REPORTER_ASSERT(reporter, previousState.getVkImageLayout() == VK_IMAGE_LAYOUT_GENERAL);
+        REPORTER_ASSERT(reporter, previousState.getQueueFamilyIndex() == VK_QUEUE_FAMILY_EXTERNAL);
     }
 
     // We must submit this work before we try to delete the backend texture.