Add ddl support to drawing to wrapped vulkan secondary command buffers.

Bug: skia:
Change-Id: I3ddb6b1e923b8c1733b6a0e219efffa36e665fa2
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/189282
Commit-Queue: Greg Daniel <egdaniel@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
diff --git a/include/core/SkSurfaceCharacterization.h b/include/core/SkSurfaceCharacterization.h
index 5e6066c..4d0aa2b 100644
--- a/include/core/SkSurfaceCharacterization.h
+++ b/include/core/SkSurfaceCharacterization.h
@@ -86,6 +86,7 @@
 
 private:
     friend class SkSurface_Gpu; // for 'set' & 'config'
+    friend class GrVkSecondaryCBDrawContext; // for 'set' & 'config'
     friend class GrContextThreadSafeProxy; // for private ctor
     friend class SkDeferredDisplayListRecorder; // for 'config'
     friend class SkSurface; // for 'config'
diff --git a/include/private/GrRenderTargetProxy.h b/include/private/GrRenderTargetProxy.h
index 3cedf74..4eb6be6 100644
--- a/include/private/GrRenderTargetProxy.h
+++ b/include/private/GrRenderTargetProxy.h
@@ -71,6 +71,8 @@
     GrRenderTargetProxy(const GrCaps&, const GrBackendFormat&, const GrSurfaceDesc&,
                         GrSurfaceOrigin, SkBackingFit, SkBudgeted, GrInternalSurfaceFlags);
 
+    enum class WrapsVkSecondaryCB : bool { kNo = false, kYes = true };
+
     // Lazy-callback version
     // There are two main use cases for lazily-instantiated proxies:
     //   basic knowledge - width, height, config, samples, origin are known
@@ -83,10 +85,10 @@
     // know the final size until flush time.
     GrRenderTargetProxy(LazyInstantiateCallback&&, LazyInstantiationType lazyType,
                         const GrBackendFormat&, const GrSurfaceDesc&, GrSurfaceOrigin,
-                        SkBackingFit, SkBudgeted, GrInternalSurfaceFlags);
+                        SkBackingFit, SkBudgeted, GrInternalSurfaceFlags,
+                        WrapsVkSecondaryCB wrapsVkSecondaryCB);
 
     // Wrapped version
-    enum class WrapsVkSecondaryCB : bool { kNo = false, kYes = true };
     GrRenderTargetProxy(sk_sp<GrSurface>, GrSurfaceOrigin,
                         WrapsVkSecondaryCB wrapsVkSecondaryCB = WrapsVkSecondaryCB::kNo);
 
diff --git a/src/core/SkDeferredDisplayListRecorder.cpp b/src/core/SkDeferredDisplayListRecorder.cpp
index 2422de7..8063349 100644
--- a/src/core/SkDeferredDisplayListRecorder.cpp
+++ b/src/core/SkDeferredDisplayListRecorder.cpp
@@ -115,6 +115,18 @@
         }
     }
 
+    if (fCharacterization.vulkanSecondaryCBCompatible()) {
+        // Because of the restrictive API allowed for a GrVkSecondaryCBDrawContext, we know ahead
+        // of time that we don't be able to support certain parameter combinations. Specifially we
+        // fail on usesGLFBO0 since we can't mix GL and Vulkan. We can't have a texturable object.
+        // And finally the GrVkSecondaryCBDrawContext always assumes a top left origin.
+        if (usesGLFBO0 ||
+            fCharacterization.isTextureable() ||
+            fCharacterization.origin() == kBottomLeft_GrSurfaceOrigin) {
+            return false;
+        }
+    }
+
     GrSurfaceDesc desc;
     desc.fFlags = kRenderTarget_GrSurfaceFlag;
     desc.fWidth = fCharacterization.width();
@@ -163,7 +175,8 @@
             surfaceFlags,
             optionalTextureInfo,
             SkBackingFit::kExact,
-            SkBudgeted::kYes);
+            SkBudgeted::kYes,
+            fCharacterization.vulkanSecondaryCBCompatible());
 
     sk_sp<GrSurfaceContext> c = fContext->priv().makeWrappedSurfaceContext(
                                                                  std::move(proxy),
diff --git a/src/gpu/GrProxyProvider.cpp b/src/gpu/GrProxyProvider.cpp
index d65320a..d4f5555 100644
--- a/src/gpu/GrProxyProvider.cpp
+++ b/src/gpu/GrProxyProvider.cpp
@@ -691,7 +691,8 @@
 sk_sp<GrRenderTargetProxy> GrProxyProvider::createLazyRenderTargetProxy(
         LazyInstantiateCallback&& callback, const GrBackendFormat& format,
         const GrSurfaceDesc& desc, GrSurfaceOrigin origin, GrInternalSurfaceFlags surfaceFlags,
-        const TextureInfo* textureInfo, SkBackingFit fit, SkBudgeted budgeted) {
+        const TextureInfo* textureInfo, SkBackingFit fit, SkBudgeted budgeted,
+        bool wrapsVkSecondaryCB) {
     SkASSERT((desc.fWidth <= 0 && desc.fHeight <= 0) ||
              (desc.fWidth > 0 && desc.fHeight > 0));
 
@@ -714,13 +715,21 @@
                                                                : LazyInstantiationType::kMultipleUse;
 
     if (textureInfo) {
+        // Wrapped vulkan secondary command buffers don't support texturing since we won't have an
+        // actual VkImage to texture from.
+        SkASSERT(!wrapsVkSecondaryCB);
         return sk_sp<GrRenderTargetProxy>(new GrTextureRenderTargetProxy(
                 std::move(callback), lazyType, format, desc, origin, textureInfo->fMipMapped,
                 fit, budgeted, surfaceFlags));
     }
 
+    GrRenderTargetProxy::WrapsVkSecondaryCB vkSCB =
+            wrapsVkSecondaryCB ? GrRenderTargetProxy::WrapsVkSecondaryCB::kYes
+                               : GrRenderTargetProxy::WrapsVkSecondaryCB::kNo;
+
     return sk_sp<GrRenderTargetProxy>(new GrRenderTargetProxy(
-            std::move(callback), lazyType, format, desc, origin, fit, budgeted, surfaceFlags));
+            std::move(callback), lazyType, format, desc, origin, fit, budgeted, surfaceFlags,
+            vkSCB));
 }
 
 sk_sp<GrTextureProxy> GrProxyProvider::MakeFullyLazyProxy(LazyInstantiateCallback&& callback,
diff --git a/src/gpu/GrProxyProvider.h b/src/gpu/GrProxyProvider.h
index 8f3007d..40f2e62 100644
--- a/src/gpu/GrProxyProvider.h
+++ b/src/gpu/GrProxyProvider.h
@@ -180,7 +180,8 @@
                                                            GrInternalSurfaceFlags,
                                                            const TextureInfo*,
                                                            SkBackingFit,
-                                                           SkBudgeted);
+                                                           SkBudgeted,
+                                                           bool wrapsVkSecondaryCB);
 
     /**
      * Fully lazy proxies have unspecified width and height. Methods that rely on those values
diff --git a/src/gpu/GrRenderTargetProxy.cpp b/src/gpu/GrRenderTargetProxy.cpp
index 3f7b7e0..a9f23ff 100644
--- a/src/gpu/GrRenderTargetProxy.cpp
+++ b/src/gpu/GrRenderTargetProxy.cpp
@@ -39,12 +39,13 @@
                                          LazyInstantiationType lazyType,
                                          const GrBackendFormat& format, const GrSurfaceDesc& desc,
                                          GrSurfaceOrigin origin,  SkBackingFit fit,
-                                         SkBudgeted budgeted, GrInternalSurfaceFlags surfaceFlags)
+                                         SkBudgeted budgeted, GrInternalSurfaceFlags surfaceFlags,
+                                         WrapsVkSecondaryCB wrapsVkSecondaryCB)
         : INHERITED(std::move(callback), lazyType, format, desc, origin, fit, budgeted,
                     surfaceFlags)
         , fSampleCnt(desc.fSampleCnt)
         , fNeedsStencil(false)
-        , fWrapsVkSecondaryCB(WrapsVkSecondaryCB::kNo) {
+        , fWrapsVkSecondaryCB(wrapsVkSecondaryCB) {
     SkASSERT(SkToBool(kRenderTarget_GrSurfaceFlag & desc.fFlags));
 }
 
diff --git a/src/gpu/GrTextureRenderTargetProxy.cpp b/src/gpu/GrTextureRenderTargetProxy.cpp
index 3e52b2c..d217876 100644
--- a/src/gpu/GrTextureRenderTargetProxy.cpp
+++ b/src/gpu/GrTextureRenderTargetProxy.cpp
@@ -46,7 +46,7 @@
         // Since we have virtual inheritance, we initialize GrSurfaceProxy directly. Send null
         // callbacks to the texture and RT proxies simply to route to the appropriate constructors.
         , GrRenderTargetProxy(LazyInstantiateCallback(), lazyType, format, desc, origin, fit,
-                              budgeted, surfaceFlags)
+                              budgeted, surfaceFlags, WrapsVkSecondaryCB::kNo)
         , GrTextureProxy(LazyInstantiateCallback(), lazyType, format, desc, origin, mipMapped,
                          fit, budgeted, surfaceFlags) {}
 
diff --git a/src/gpu/vk/GrVkSecondaryCBDrawContext.cpp b/src/gpu/vk/GrVkSecondaryCBDrawContext.cpp
index 7618141..8083f57 100644
--- a/src/gpu/vk/GrVkSecondaryCBDrawContext.cpp
+++ b/src/gpu/vk/GrVkSecondaryCBDrawContext.cpp
@@ -9,10 +9,13 @@
 
 #include "GrContext.h"
 #include "GrContextPriv.h"
+#include "GrContextThreadSafeProxyPriv.h"
 #include "GrRenderTargetContext.h"
+#include "SkDeferredDisplayList.h"
 #include "SkGpuDevice.h"
 #include "SkImageInfo.h"
-#include "SkSurfaceProps.h"
+#include "SkSurfaceCharacterization.h"
+#include "SkSurfacePriv.h"
 #include "vk/GrVkTypes.h"
 
 sk_sp<GrVkSecondaryCBDrawContext> GrVkSecondaryCBDrawContext::Make(GrContext* ctx,
@@ -39,11 +42,14 @@
         return nullptr;
     }
 
-    return sk_sp<GrVkSecondaryCBDrawContext>(new GrVkSecondaryCBDrawContext(std::move(device)));
+    return sk_sp<GrVkSecondaryCBDrawContext>(new GrVkSecondaryCBDrawContext(std::move(device),
+                                                                            props));
 }
 
-GrVkSecondaryCBDrawContext::GrVkSecondaryCBDrawContext(sk_sp<SkGpuDevice> device)
-    : fDevice(device) {}
+GrVkSecondaryCBDrawContext::GrVkSecondaryCBDrawContext(sk_sp<SkGpuDevice> device,
+                                                       const SkSurfaceProps* props)
+    : fDevice(device)
+    , fProps(SkSurfacePropsCopyOrDefault(props)) {}
 
 GrVkSecondaryCBDrawContext::~GrVkSecondaryCBDrawContext() {
     SkASSERT(!fDevice);
@@ -71,3 +77,96 @@
     fDevice.reset();
 }
 
+bool GrVkSecondaryCBDrawContext::characterize(SkSurfaceCharacterization* characterization) const {
+    GrRenderTargetContext* rtc = fDevice->accessRenderTargetContext();
+    GrContext* ctx = fDevice->context();
+
+    int maxResourceCount;
+    size_t maxResourceBytes;
+    ctx->getResourceCacheLimits(&maxResourceCount, &maxResourceBytes);
+
+    // We current don't support textured GrVkSecondaryCBDrawContexts.
+    SkASSERT(!rtc->asTextureProxy());
+
+    // TODO: the addition of colorType to the surfaceContext should remove this calculation
+    SkColorType ct;
+    if (!GrPixelConfigToColorType(rtc->colorSpaceInfo().config(), &ct)) {
+        return false;
+    }
+
+    SkImageInfo ii = SkImageInfo::Make(rtc->width(), rtc->height(), ct, kPremul_SkAlphaType,
+                                       rtc->colorSpaceInfo().refColorSpace());
+
+    characterization->set(ctx->threadSafeProxy(), maxResourceBytes, ii, rtc->origin(),
+                          rtc->colorSpaceInfo().config(), rtc->fsaaType(), rtc->numStencilSamples(),
+                          SkSurfaceCharacterization::Textureable(false),
+                          SkSurfaceCharacterization::MipMapped(false),
+                          SkSurfaceCharacterization::UsesGLFBO0(false),
+                          SkSurfaceCharacterization::VulkanSecondaryCBCompatible(true),
+                          this->props());
+
+    return true;
+}
+
+bool GrVkSecondaryCBDrawContext::isCompatible(
+        const SkSurfaceCharacterization& characterization) const {
+    GrRenderTargetContext* rtc = fDevice->accessRenderTargetContext();
+    GrContext* ctx = fDevice->context();
+
+    if (!characterization.isValid()) {
+        return false;
+    }
+
+    if (!characterization.vulkanSecondaryCBCompatible()) {
+        return false;
+    }
+
+    // As long as the current state in the context allows for greater or equal resources,
+    // we allow the DDL to be replayed.
+    // DDL TODO: should we just remove the resource check and ignore the cache limits on playback?
+    int maxResourceCount;
+    size_t maxResourceBytes;
+    ctx->getResourceCacheLimits(&maxResourceCount, &maxResourceBytes);
+
+    if (characterization.isTextureable()) {
+        // We don't support textureable DDL when rendering to a GrVkSecondaryCBDrawContext.
+        return false;
+    }
+
+    if (characterization.usesGLFBO0()) {
+        return false;
+    }
+
+    // TODO: the addition of colorType to the surfaceContext should remove this calculation
+    SkColorType rtcColorType;
+    if (!GrPixelConfigToColorType(rtc->colorSpaceInfo().config(), &rtcColorType)) {
+        return false;
+    }
+
+    return characterization.contextInfo() && characterization.contextInfo()->priv().matches(ctx) &&
+           characterization.cacheMaxResourceBytes() <= maxResourceBytes &&
+           characterization.origin() == rtc->origin() &&
+           characterization.config() == rtc->colorSpaceInfo().config() &&
+           characterization.width() == rtc->width() &&
+           characterization.height() == rtc->height() &&
+           characterization.colorType() == rtcColorType &&
+           characterization.fsaaType() == rtc->fsaaType() &&
+           characterization.stencilCount() == rtc->numStencilSamples() &&
+           SkColorSpace::Equals(characterization.colorSpace(),
+                                rtc->colorSpaceInfo().colorSpace()) &&
+           characterization.surfaceProps() == rtc->surfaceProps();
+}
+
+bool GrVkSecondaryCBDrawContext::draw(SkDeferredDisplayList* ddl) {
+    if (!ddl || !this->isCompatible(ddl->characterization())) {
+        return false;
+    }
+
+    GrRenderTargetContext* rtc = fDevice->accessRenderTargetContext();
+    GrContext* ctx = fDevice->context();
+
+    ctx->priv().copyOpListsFromDDL(ddl, rtc->asRenderTargetProxy());
+    return true;
+}
+
+
diff --git a/src/gpu/vk/GrVkSecondaryCBDrawContext.h b/src/gpu/vk/GrVkSecondaryCBDrawContext.h
index c6ea9f9..23d6903 100644
--- a/src/gpu/vk/GrVkSecondaryCBDrawContext.h
+++ b/src/gpu/vk/GrVkSecondaryCBDrawContext.h
@@ -10,6 +10,7 @@
 
 #include "SkTypes.h"
 #include "SkRefCnt.h"
+#include "SkSurfaceProps.h"
 
 class GrBackendSemaphore;
 class GrContext;
@@ -91,15 +92,20 @@
     // are still in use by the GPU.
     void releaseResources();
 
+    const SkSurfaceProps& props() const { return fProps; }
+
     // TODO: Fill out these calls to support DDL
     bool characterize(SkSurfaceCharacterization* characterization) const;
     bool draw(SkDeferredDisplayList* deferredDisplayList);
 
 private:
-    explicit GrVkSecondaryCBDrawContext(sk_sp<SkGpuDevice>);
+    explicit GrVkSecondaryCBDrawContext(sk_sp<SkGpuDevice>, const SkSurfaceProps*);
+
+    bool isCompatible(const SkSurfaceCharacterization& characterization) const;
 
     sk_sp<SkGpuDevice>        fDevice;
     std::unique_ptr<SkCanvas> fCachedCanvas;
+    const SkSurfaceProps      fProps;
 
     typedef SkRefCnt INHERITED;
 };
diff --git a/src/image/SkSurface_Gpu.cpp b/src/image/SkSurface_Gpu.cpp
index 2df1e9d..aa83d1f 100644
--- a/src/image/SkSurface_Gpu.cpp
+++ b/src/image/SkSurface_Gpu.cpp
@@ -212,6 +212,10 @@
         return false;
     }
 
+    if (characterization.vulkanSecondaryCBCompatible()) {
+        return false;
+    }
+
     // As long as the current state if the context allows for greater or equal resources,
     // we allow the DDL to be replayed.
     // DDL TODO: should we just remove the resource check and ignore the cache limits on playback?