Add GrExternalTextureData and SkCrossContextImageData

GrExternalTextureData is an API for exporting the backend-specific
information about a texture in a type-safe way, and without pointing
into the GrTexture. The new detachBackendTexture API lets us release
ownership of a texture to the client.

SkCrossContextImageData is the public API that lets clients upload
textures on one thread/GrContext, then safely transfer ownership to
another thread and GrContext for rendering.

Only GL is implemented/supported right now. Vulkan support requires
that we add thread-safe memory pools, or otherwise transfer the
actual memory block containing the texture to the new context.

Re-land of https://skia-review.googlesource.com/c/8529/

BUG=skia:

Change-Id: I48ebd57d1ea0cfd3a1db10c475f2903afb821966
Reviewed-on: https://skia-review.googlesource.com/8960
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
diff --git a/src/gpu/GrCaps.cpp b/src/gpu/GrCaps.cpp
index 1efea17..ade64d6 100644
--- a/src/gpu/GrCaps.cpp
+++ b/src/gpu/GrCaps.cpp
@@ -53,6 +53,7 @@
     fMustClearUploadedBufferData = false;
     fSampleShadingSupport = false;
     fFenceSyncSupport = false;
+    fCrossContextTextureSupport = false;
 
     fUseDrawInsteadOfClear = false;
 
@@ -141,6 +142,7 @@
     r.appendf("Must clear buffer memory           : %s\n", gNY[fMustClearUploadedBufferData]);
     r.appendf("Sample shading support             : %s\n", gNY[fSampleShadingSupport]);
     r.appendf("Fence sync support                 : %s\n", gNY[fFenceSyncSupport]);
+    r.appendf("Cross context texture support      : %s\n", gNY[fCrossContextTextureSupport]);
 
     r.appendf("Draw Instead of Clear [workaround] : %s\n", gNY[fUseDrawInsteadOfClear]);
     r.appendf("Draw Instead of TexSubImage [workaround] : %s\n",
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index 4b1324e..e816687 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -545,6 +545,13 @@
     fDrawingManager->prepareSurfaceForExternalIO(surface);
 }
 
+GrFence GrContext::prepareSurfaceForExternalIOAndFlush(GrSurface* surface) {
+    this->prepareSurfaceForExternalIO(surface);
+    GrFence fence = fGpu->insertFence();
+    fGpu->flush();
+    return fence;
+}
+
 void GrContext::flushSurfaceWrites(GrSurface* surface) {
     ASSERT_SINGLE_OWNER
     RETURN_IF_ABANDONED
diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h
index 0aed3d2..f710810 100644
--- a/src/gpu/GrGpu.h
+++ b/src/gpu/GrGpu.h
@@ -382,6 +382,10 @@
     virtual bool waitFence(GrFence, uint64_t timeout = 1000) const = 0;
     virtual void deleteFence(GrFence) const = 0;
 
+    // Ensures that all queued up driver-level commands have been sent to the GPU. For example, on
+    // OpenGL, this calls glFlush.
+    virtual void flush() = 0;
+
     ///////////////////////////////////////////////////////////////////////////
     // Debugging and Stats
 
diff --git a/src/gpu/GrGpuResource.cpp b/src/gpu/GrGpuResource.cpp
index c3a9556..dcc4e62 100644
--- a/src/gpu/GrGpuResource.cpp
+++ b/src/gpu/GrGpuResource.cpp
@@ -43,6 +43,17 @@
     get_resource_cache(fGpu)->resourceAccess().insertResource(this);
 }
 
+void GrGpuResource::detachFromCache() {
+    if (this->wasDestroyed()) {
+        return;
+    }
+    if (fUniqueKey.isValid()) {
+        this->removeUniqueKey();
+    }
+    this->removeScratchKey();
+    this->makeUnbudgeted();
+}
+
 GrGpuResource::~GrGpuResource() {
     // The cache should have released or destroyed this resource.
     SkASSERT(this->wasDestroyed());
diff --git a/src/gpu/GrTexturePriv.h b/src/gpu/GrTexturePriv.h
index 0420611..cc0a05e 100644
--- a/src/gpu/GrTexturePriv.h
+++ b/src/gpu/GrTexturePriv.h
@@ -8,6 +8,7 @@
 #ifndef GrTexturePriv_DEFINED
 #define GrTexturePriv_DEFINED
 
+#include "GrExternalTextureData.h"
 #include "GrTexture.h"
 
 /** Class that adds methods to GrTexture that are only intended for use internal to Skia.
@@ -67,6 +68,15 @@
     }
     SkDestinationSurfaceColorMode mipColorMode() const { return fTexture->fMipColorMode; }
 
+    /**
+     *  Return the native bookkeeping data for this texture, and detach the backend object from
+     *  this GrTexture. It's lifetime will no longer be managed by Ganesh, and this GrTexture will
+     *  no longer refer to it. Leaves this GrTexture in an orphan state.
+     */
+    std::unique_ptr<GrExternalTextureData> detachBackendTexture() {
+        return fTexture->detachBackendTexture();
+    }
+
     static void ComputeScratchKey(const GrSurfaceDesc&, GrScratchKey*);
 
 private:
diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp
index ddf2252..0bcb7e8 100644
--- a/src/gpu/gl/GrGLCaps.cpp
+++ b/src/gpu/gl/GrGLCaps.cpp
@@ -603,6 +603,15 @@
         fFenceSyncSupport = true;
     }
 
+    // Safely moving textures between contexts requires fences. The Windows Intel driver has a
+    // bug with deleting and reusing texture IDs across contexts, so disallow this feature.
+    fCrossContextTextureSupport = fFenceSyncSupport;
+#ifdef SK_BUILD_FOR_WIN
+    if (kIntel_GrGLVendor == ctxInfo.vendor()) {
+        fCrossContextTextureSupport = false;
+    }
+#endif
+
     // We support manual mip-map generation (via iterative downsampling draw calls). This fixes
     // bugs on some cards/drivers that produce incorrect mip-maps for sRGB textures when using
     // glGenerateMipmap. Our implementation requires mip-level sampling control. Additionally,
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index ad00b0a..5dce7d1 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -4741,3 +4741,7 @@
 void GrGLGpu::deleteFence(GrFence fence) const {
     GL_CALL(DeleteSync((GrGLsync)fence));
 }
+
+void GrGLGpu::flush() {
+    GL_CALL(Flush());
+}
diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h
index fdc2ebb..b6ca4f6 100644
--- a/src/gpu/gl/GrGLGpu.h
+++ b/src/gpu/gl/GrGLGpu.h
@@ -150,6 +150,8 @@
     bool waitFence(GrFence, uint64_t timeout) const override;
     void deleteFence(GrFence) const override;
 
+    void flush() override;
+
 private:
     GrGLGpu(GrGLContext* ctx, GrContext* context);
 
diff --git a/src/gpu/gl/GrGLTexture.cpp b/src/gpu/gl/GrGLTexture.cpp
index edce7b1..a560988 100644
--- a/src/gpu/gl/GrGLTexture.cpp
+++ b/src/gpu/gl/GrGLTexture.cpp
@@ -5,9 +5,11 @@
  * found in the LICENSE file.
  */
 
+#include "GrContext.h"
 #include "GrGLTexture.h"
 #include "GrGLGpu.h"
 #include "GrShaderCaps.h"
+#include "SkMakeUnique.h"
 #include "SkTraceMemoryDump.h"
 
 #define GPUGL static_cast<GrGLGpu*>(this->getGpu())
@@ -111,6 +113,23 @@
     return reinterpret_cast<GrBackendObject>(&fInfo);
 }
 
+std::unique_ptr<GrExternalTextureData> GrGLTexture::detachBackendTexture() {
+    // Flush any pending writes to this texture, as well GL itself
+    GrFence fence = this->getContext()->prepareSurfaceForExternalIOAndFlush(this);
+
+    // Make a copy of our GL-specific information
+    auto data = skstd::make_unique<GrGLExternalTextureData>(fInfo, fence);
+
+    // Ensure the cache can't reach this texture anymore
+    this->detachFromCache();
+
+    // Detach from the GL object, so we don't use it (or try to delete it when we're freed)
+    fInfo.fTarget = 0;
+    fInfo.fID = 0;
+
+    return std::move(data);
+}
+
 void GrGLTexture::setMemoryBacking(SkTraceMemoryDump* traceMemoryDump,
                                    const SkString& dumpName) const {
     SkString texture_id;
diff --git a/src/gpu/gl/GrGLTexture.h b/src/gpu/gl/GrGLTexture.h
index 029fd87..16b47f1 100644
--- a/src/gpu/gl/GrGLTexture.h
+++ b/src/gpu/gl/GrGLTexture.h
@@ -71,6 +71,7 @@
     void onRelease() override;
     void setMemoryBacking(SkTraceMemoryDump* traceMemoryDump,
                           const SkString& dumpName) const override;
+    std::unique_ptr<GrExternalTextureData> detachBackendTexture() override;
 
 private:
     TexParams                       fTexParams;
diff --git a/src/gpu/vk/GrVkCaps.cpp b/src/gpu/vk/GrVkCaps.cpp
index 94c1a50..fd454ad 100644
--- a/src/gpu/vk/GrVkCaps.cpp
+++ b/src/gpu/vk/GrVkCaps.cpp
@@ -37,6 +37,7 @@
 
     fUseDrawInsteadOfClear = false;
     fFenceSyncSupport = true;   // always available in Vulkan
+    fCrossContextTextureSupport = false; // TODO: Add thread-safe memory pools so we can enable this
 
     fMapBufferFlags = kNone_MapFlags; //TODO: figure this out
     fBufferMapThreshold = SK_MaxS32;  //TODO: figure this out
diff --git a/src/gpu/vk/GrVkGpu.cpp b/src/gpu/vk/GrVkGpu.cpp
index 915ac97..8569c93 100644
--- a/src/gpu/vk/GrVkGpu.cpp
+++ b/src/gpu/vk/GrVkGpu.cpp
@@ -1860,3 +1860,6 @@
     GR_VK_CALL(this->vkInterface(), DestroyFence(this->device(), (VkFence)fence, nullptr));
 }
 
+void GrVkGpu::flush() {
+    // We submit the command buffer to the queue whenever Ganesh is flushed, so nothing is needed
+}
diff --git a/src/gpu/vk/GrVkGpu.h b/src/gpu/vk/GrVkGpu.h
index 18059b9..c935945 100644
--- a/src/gpu/vk/GrVkGpu.h
+++ b/src/gpu/vk/GrVkGpu.h
@@ -133,6 +133,8 @@
     bool waitFence(GrFence, uint64_t timeout) const override;
     void deleteFence(GrFence) const override;
 
+    void flush() override;
+
     void generateMipmap(GrVkTexture* tex);
 
     bool updateBuffer(GrVkBuffer* buffer, const void* src, VkDeviceSize offset, VkDeviceSize size);
diff --git a/src/gpu/vk/GrVkTexture.cpp b/src/gpu/vk/GrVkTexture.cpp
index 7f0cf8d..a8f1bf0 100644
--- a/src/gpu/vk/GrVkTexture.cpp
+++ b/src/gpu/vk/GrVkTexture.cpp
@@ -144,6 +144,12 @@
     return (GrBackendObject)&fInfo;
 }
 
+std::unique_ptr<GrExternalTextureData> GrVkTexture::detachBackendTexture() {
+    // Not supported on Vulkan yet
+    // TODO: Add thread-safe memory pools, and implement this.
+    return nullptr;
+}
+
 GrVkGpu* GrVkTexture::getVkGpu() const {
     SkASSERT(!this->wasDestroyed());
     return static_cast<GrVkGpu*>(this->getGpu());
diff --git a/src/gpu/vk/GrVkTexture.h b/src/gpu/vk/GrVkTexture.h
index 70db548..db7124e 100644
--- a/src/gpu/vk/GrVkTexture.h
+++ b/src/gpu/vk/GrVkTexture.h
@@ -42,6 +42,7 @@
 
     void onAbandon() override;
     void onRelease() override;
+    std::unique_ptr<GrExternalTextureData> detachBackendTexture() override;
 
 private:
     enum Wrapped { kWrapped };