Add some copy support for vulkan msaa
authoregdaniel <egdaniel@google.com>
Wed, 31 Aug 2016 14:37:31 +0000 (07:37 -0700)
committerCommit bot <commit-bot@chromium.org>
Wed, 31 Aug 2016 14:37:31 +0000 (07:37 -0700)
This allows us to do copies from:
    msaa->msaa with same sample count
    msaa->no-msaa with a resolve

Still missing support for no-msaa to msaa which will require a copyAsDraw
which is currently stalled and fixing possible driver bugs.

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2294533002

Review-Url: https://codereview.chromium.org/2294533002

src/gpu/GrDrawTarget.cpp
src/gpu/GrGpu.h
src/gpu/gl/GrGLGpu.cpp
src/gpu/gl/GrGLGpu.h
src/gpu/vk/GrVkGpu.cpp
src/gpu/vk/GrVkGpu.h
src/gpu/vk/GrVkRenderTarget.cpp
src/gpu/vk/GrVkTextureRenderTarget.cpp
tools/gpu/GrTest.cpp

index dc2fd30..d8b44b8 100644 (file)
@@ -179,7 +179,7 @@ bool GrDrawTarget::setupDstReadIfNecessary(const GrPipelineBuilder& pipelineBuil
     // MSAA consideration: When there is support for reading MSAA samples in the shader we could
     // have per-sample dst values by making the copy multisampled.
     GrSurfaceDesc desc;
-    if (!fGpu->initCopySurfaceDstDesc(rt, &desc)) {
+    if (!fGpu->initDescForDstCopy(rt, &desc)) {
         desc.fOrigin = kDefault_GrSurfaceOrigin;
         desc.fFlags = kRenderTarget_GrSurfaceFlag;
         desc.fConfig = rt->config();
index 393df1a..3e3a4cd 100644 (file)
@@ -308,12 +308,12 @@ public:
                         size_t offset, size_t rowBytes);
 
     /**
-     * This is can be called before allocating a texture to be a dst for copySurface. It will
+     * This is can be called before allocating a texture to be a dst for copySurface. This is only
+     * used for doing dst copies needed in blends, thus the src is always a GrRenderTarget. It will
      * populate the origin, config, and flags fields of the desc such that copySurface can
-     * efficiently succeed. It should only succeed if it can allow copySurface to perform a copy
-     * that would be more effecient than drawing the src to a dst render target.
+     * efficiently succeed.
      */
-    virtual bool initCopySurfaceDstDesc(const GrSurface* src, GrSurfaceDesc* desc) const = 0;
+    virtual bool initDescForDstCopy(const GrRenderTarget* src, GrSurfaceDesc* desc) const = 0;
 
     // After the client interacts directly with the 3D context state the GrGpu
     // must resync its internal state and assumptions about 3D context state.
index 9981b66..1370bf9 100644 (file)
@@ -3586,7 +3586,7 @@ void GrGLGpu::unbindTextureFBOForCopy(GrGLenum fboTarget, GrSurface* surface) {
     }
 }
 
-bool GrGLGpu::initCopySurfaceDstDesc(const GrSurface* src, GrSurfaceDesc* desc) const {
+bool GrGLGpu::initDescForDstCopy(const GrRenderTarget* src, GrSurfaceDesc* desc) const {
     // If the src is a texture, we can implement the blit as a draw assuming the config is
     // renderable.
     if (src->asTexture() && this->caps()->isConfigRenderable(src->config(), false)) {
@@ -3625,13 +3625,10 @@ bool GrGLGpu::initCopySurfaceDstDesc(const GrSurface* src, GrSurfaceDesc* desc)
             return true;
         }
         return false;
-    } else if (nullptr == src->asRenderTarget()) {
-        // CopyTexSubImage2D or fbo blit would require creating a temp fbo for the src.
-        return false;
     }
 
-    const GrGLRenderTarget* srcRT = static_cast<const GrGLRenderTarget*>(src->asRenderTarget());
-    if (srcRT && srcRT->renderFBOID() != srcRT->textureFBOID()) {
+    const GrGLRenderTarget* srcRT = static_cast<const GrGLRenderTarget*>(src);
+    if (srcRT->renderFBOID() != srcRT->textureFBOID()) {
         // It's illegal to call CopyTexSubImage2D on a MSAA renderbuffer. Set up for FBO blit or
         // fail.
         if (this->caps()->isConfigRenderable(src->config(), false)) {
index 26c9588..448c292 100644 (file)
@@ -71,7 +71,7 @@ public:
                               GrPixelConfig srcConfig, DrawPreference*,
                               WritePixelTempDrawInfo*) override;
 
-    bool initCopySurfaceDstDesc(const GrSurface* src, GrSurfaceDesc* desc) const override;
+    bool initDescForDstCopy(const GrRenderTarget* src, GrSurfaceDesc* desc) const override;
 
     // These functions should be used to bind GL objects. They track the GL state and skip redundant
     // bindings. Making the equivalent glBind calls directly will confuse the state tracking.
index 91da82d..9a8a6e4 100644 (file)
@@ -366,42 +366,55 @@ bool GrVkGpu::onWritePixels(GrSurface* surface,
     return success;
 }
 
+void GrVkGpu::resolveImage(GrVkRenderTarget* dst, GrVkRenderTarget* src, const SkIRect& srcRect,
+                           const SkIPoint& dstPoint) {
+    SkASSERT(dst);
+    SkASSERT(src && src->numColorSamples() > 1 && src->msaaImage());
+
+    // Flip rect if necessary
+    SkIRect srcVkRect = srcRect;
+    int32_t dstY = dstPoint.fY;
+
+    if (kBottomLeft_GrSurfaceOrigin == src->origin()) {
+        SkASSERT(kBottomLeft_GrSurfaceOrigin == dst->origin());
+        srcVkRect.fTop = src->height() - srcRect.fBottom;
+        srcVkRect.fBottom = src->height() - srcRect.fTop;
+        dstY = dst->height() - dstPoint.fY - srcVkRect.height();
+    }
+
+    VkImageResolve resolveInfo;
+    resolveInfo.srcSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 };
+    resolveInfo.srcOffset = { srcVkRect.fLeft, srcVkRect.fTop, 0 };
+    resolveInfo.dstSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 };
+    resolveInfo.dstOffset = { dstPoint.fX, dstY, 0 };
+    // By the spec the depth of the extent should be ignored for 2D images, but certain devices
+    // (e.g. nexus 5x) currently fail if it is not 1
+    resolveInfo.extent = { (uint32_t)srcVkRect.width(), (uint32_t)srcVkRect.height(), 1 };
+
+    dst->setImageLayout(this,
+                        VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+                        VK_ACCESS_TRANSFER_WRITE_BIT,
+                        VK_PIPELINE_STAGE_TRANSFER_BIT,
+                        false);
+
+    src->msaaImage()->setImageLayout(this,
+                                     VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+                                     VK_ACCESS_TRANSFER_READ_BIT,
+                                     VK_PIPELINE_STAGE_TRANSFER_BIT,
+                                     false);
+
+    fCurrentCmdBuffer->resolveImage(this, *src->msaaImage(), *dst, 1, &resolveInfo);
+}
+
 void GrVkGpu::onResolveRenderTarget(GrRenderTarget* target) {
     if (target->needsResolve()) {
         SkASSERT(target->numColorSamples() > 1);
         GrVkRenderTarget* rt = static_cast<GrVkRenderTarget*>(target);
         SkASSERT(rt->msaaImage());
+        
+        const SkIRect& srcRect = rt->getResolveRect();
 
-        // Flip rect if necessary
-        SkIRect srcVkRect = rt->getResolveRect();
-
-        if (kBottomLeft_GrSurfaceOrigin == rt->origin()) {
-            srcVkRect.fTop = rt->height() - rt->getResolveRect().fBottom;
-            srcVkRect.fBottom =  rt->height() - rt->getResolveRect().fTop;
-        }
-
-        VkImageResolve resolveInfo;
-        resolveInfo.srcSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 };
-        resolveInfo.srcOffset = { srcVkRect.fLeft, srcVkRect.fTop, 0 };
-        resolveInfo.dstSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 };
-        resolveInfo.dstOffset = { srcVkRect.fLeft, srcVkRect.fTop, 0 };
-        // By the spec the depth of the extent should be ignored for 2D images, but certain devices
-        // (e.g. nexus 5x) currently fail if it is not 1
-        resolveInfo.extent = { (uint32_t)srcVkRect.width(), (uint32_t)srcVkRect.height(), 1 };
-
-        rt->setImageLayout(this,
-                           VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
-                           VK_ACCESS_TRANSFER_WRITE_BIT,
-                           VK_PIPELINE_STAGE_TRANSFER_BIT,
-                           false);
-
-        rt->msaaImage()->setImageLayout(this,
-                                        VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
-                                        VK_ACCESS_TRANSFER_READ_BIT,
-                                        VK_PIPELINE_STAGE_TRANSFER_BIT,
-                                        false);
-
-        fCurrentCmdBuffer->resolveImage(this, *rt->msaaImage(), *rt, 1, &resolveInfo);
+        this->resolveImage(rt, rt, srcRect, SkIPoint::Make(srcRect.fLeft, srcRect.fTop));
 
         rt->flagAsResolved();
     }
@@ -1333,10 +1346,20 @@ void GrVkGpu::clearStencil(GrRenderTarget* target) {
 inline bool can_copy_image(const GrSurface* dst,
                            const GrSurface* src,
                            const GrVkGpu* gpu) {
-    // Currently we don't support msaa
-    if ((dst->asRenderTarget() && dst->asRenderTarget()->numColorSamples() > 1) ||
-        (src->asRenderTarget() && src->asRenderTarget()->numColorSamples() > 1)) {
-        return false;
+    const GrRenderTarget* dstRT = dst->asRenderTarget();
+    const GrRenderTarget* srcRT = src->asRenderTarget();
+    if (dstRT && srcRT) {
+        if (srcRT->numColorSamples() != dstRT->numColorSamples()) {
+            return false;
+        }
+    } else if (dstRT) {
+        if (dstRT->numColorSamples() > 1) {
+            return false;
+        }
+    } else if (srcRT) {
+        if (srcRT->numColorSamples() > 1) {
+            return false;
+        }
     }
 
     // We require that all vulkan GrSurfaces have been created with transfer_dst and transfer_src 
@@ -1346,9 +1369,6 @@ inline bool can_copy_image(const GrSurface* dst,
         return true;
     }
 
-    // How does msaa play into this? If a VkTexture is multisampled, are we copying the multisampled
-    // or the resolved image here? Im multisampled, Vulkan requires sample counts to be the same.
-
     return false;
 }
 
@@ -1500,6 +1520,37 @@ void GrVkGpu::copySurfaceAsBlit(GrSurface* dst,
     this->didWriteToSurface(dst, &dstRect);
 }
 
+inline bool can_copy_as_resolve(const GrSurface* dst,
+                                const GrSurface* src,
+                                const GrVkGpu* gpu) {
+    // Our src must be a multisampled render target
+    if (!src->asRenderTarget() || src->asRenderTarget()->numColorSamples() <= 1) {
+        return false;
+    }
+
+    // The dst must be a render target but not multisampled
+    if (!dst->asRenderTarget() || dst->asRenderTarget()->numColorSamples() > 1) {
+        return false;
+    }
+
+    // Surfaces must have the same origin.
+    if (src->origin() != dst->origin()) {
+        return false;
+    }
+
+    return true;
+}
+
+void GrVkGpu::copySurfaceAsResolve(GrSurface* dst,
+                                   GrSurface* src,
+                                   const SkIRect& srcRect,
+                                   const SkIPoint& dstPoint) {
+    GrVkRenderTarget* dstRT = static_cast<GrVkRenderTarget*>(dst->asRenderTarget());
+    GrVkRenderTarget* srcRT = static_cast<GrVkRenderTarget*>(src->asRenderTarget());
+    SkASSERT(dstRT && dstRT->numColorSamples() <= 1);
+    this->resolveImage(dstRT, srcRT, srcRect, dstPoint);
+}
+
 inline bool can_copy_as_draw(const GrSurface* dst,
                              const GrSurface* src,
                              const GrVkGpu* gpu) {
@@ -1517,19 +1568,27 @@ bool GrVkGpu::onCopySurface(GrSurface* dst,
                             GrSurface* src,
                             const SkIRect& srcRect,
                             const SkIPoint& dstPoint) {
+    if (can_copy_as_resolve(dst, src, this)) {
+        this->copySurfaceAsResolve(dst, src, srcRect, dstPoint);
+    }
+
     GrVkImage* dstImage;
     GrVkImage* srcImage;
-    if (dst->asTexture()) {
-        dstImage = static_cast<GrVkTexture*>(dst->asTexture());
+    GrRenderTarget* dstRT = dst->asRenderTarget();
+    if (dstRT) {
+        GrVkRenderTarget* vkRT = static_cast<GrVkRenderTarget*>(dstRT);
+        dstImage = vkRT->numColorSamples() > 1 ? vkRT->msaaImage() : vkRT;
     } else {
-        SkASSERT(dst->asRenderTarget());
-        dstImage = static_cast<GrVkRenderTarget*>(dst->asRenderTarget());
+        SkASSERT(dst->asTexture());
+        dstImage = static_cast<GrVkTexture*>(dst->asTexture());
     }
-    if (src->asTexture()) {
-        srcImage = static_cast<GrVkTexture*>(src->asTexture());
+    GrRenderTarget* srcRT = src->asRenderTarget();
+    if (srcRT) {
+        GrVkRenderTarget* vkRT = static_cast<GrVkRenderTarget*>(srcRT);
+        srcImage = vkRT->numColorSamples() > 1 ? vkRT->msaaImage() : vkRT;
     } else {
-        SkASSERT(src->asRenderTarget());
-        srcImage = static_cast<GrVkRenderTarget*>(src->asRenderTarget());
+        SkASSERT(src->asTexture());
+        srcImage = static_cast<GrVkTexture*>(src->asTexture());
     }
 
     if (can_copy_image(dst, src, this)) {
@@ -1550,17 +1609,13 @@ bool GrVkGpu::onCopySurface(GrSurface* dst,
     return false;
 }
 
-bool GrVkGpu::initCopySurfaceDstDesc(const GrSurface* src, GrSurfaceDesc* desc) const {
-    // Currently we don't support msaa
-    if (src->asRenderTarget() && src->asRenderTarget()->numColorSamples() > 1) {
-        return false;
-    }
-
-    // This will support copying the dst as CopyImage since all of our surfaces require transferSrc
-    // and transferDst usage flags in Vulkan.
+bool GrVkGpu::initDescForDstCopy(const GrRenderTarget* src, GrSurfaceDesc* desc) const {
+    // We can always succeed here with either a CopyImage (none msaa src) or ResolveImage (msaa).
+    // For CopyImage we can make a simple texture, for ResolveImage we require the dst to be a
+    // render target as well.
     desc->fOrigin = src->origin();
     desc->fConfig = src->config();
-    desc->fFlags = kNone_GrSurfaceFlags;
+    desc->fFlags = src->numColorSamples() > 1 ? kRenderTarget_GrSurfaceFlag : kNone_GrSurfaceFlags;
     return true;
 }
 
index ff9c3c7..8df6e6c 100644 (file)
@@ -82,7 +82,7 @@ public:
     void onGetMultisampleSpecs(GrRenderTarget* rt, const GrStencilSettings&,
                                int* effectiveSampleCnt, SamplePattern*) override;
 
-    bool initCopySurfaceDstDesc(const GrSurface* src, GrSurfaceDesc* desc) const override;
+    bool initDescForDstCopy(const GrRenderTarget* src, GrSurfaceDesc* desc) const override;
 
     void xferBarrier(GrRenderTarget*, GrXferBarrierType) override {}
 
@@ -220,6 +220,11 @@ private:
                            const SkIRect& srcRect,
                            const SkIPoint& dstPoint);
 
+    void copySurfaceAsResolve(GrSurface* dst,
+                              GrSurface* src,
+                              const SkIRect& srcRect,
+                              const SkIPoint& dstPoint);
+
     void copySurfaceAsDraw(GrSurface* dst,
                            GrSurface* src,
                            const SkIRect& srcRect,
@@ -236,6 +241,11 @@ private:
                               GrPixelConfig dataConfig,
                               const SkTArray<GrMipLevel>&);
 
+    void resolveImage(GrVkRenderTarget* dst,
+                      GrVkRenderTarget* src,
+                      const SkIRect& srcRect,
+                      const SkIPoint& dstPoint);
+
     SkAutoTUnref<const GrVkBackendContext> fBackendContext;
     SkAutoTUnref<GrVkCaps>                 fVkCaps;
 
index 59fc0b9..6d95ba0 100644 (file)
@@ -135,7 +135,9 @@ GrVkRenderTarget::Create(GrVkGpu* gpu,
         msImageDesc.fLevels = 1;
         msImageDesc.fSamples = desc.fSampleCnt;
         msImageDesc.fImageTiling = VK_IMAGE_TILING_OPTIMAL;
-        msImageDesc.fUsageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
+        msImageDesc.fUsageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
+                                  VK_IMAGE_USAGE_TRANSFER_DST_BIT |
+                                  VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
         msImageDesc.fMemProps = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
 
         if (!GrVkImage::InitImageInfo(gpu, msImageDesc, &msInfo)) {
index 532d0a2..37b68af 100644 (file)
@@ -49,7 +49,9 @@ GrVkTextureRenderTarget* GrVkTextureRenderTarget::Create(GrVkGpu* gpu,
         msImageDesc.fLevels = 1;
         msImageDesc.fSamples = desc.fSampleCnt;
         msImageDesc.fImageTiling = VK_IMAGE_TILING_OPTIMAL;
-        msImageDesc.fUsageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
+        msImageDesc.fUsageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
+                                  VK_IMAGE_USAGE_TRANSFER_DST_BIT |
+                                  VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
         msImageDesc.fMemProps = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
 
         if (!GrVkImage::InitImageInfo(gpu, msImageDesc, &msInfo)) {
index d2c7295..8a64ebc 100644 (file)
@@ -306,7 +306,7 @@ public:
         *effectiveSampleCnt = rt->desc().fSampleCnt;
     }
 
-    bool initCopySurfaceDstDesc(const GrSurface* src, GrSurfaceDesc* desc) const override {
+    bool initDescForDstCopy(const GrRenderTarget* src, GrSurfaceDesc* desc) const override {
         return false;
     }