From 25af671a3d7d790b31b085172952df3867337431 Mon Sep 17 00:00:00 2001 From: Greg Daniel Date: Wed, 25 Apr 2018 10:44:38 -0400 Subject: Add GrCap check for whether we can do a copy or not, and implement in Vk backend Today, we only know if we fail a copy during the flush so we have no way to cleanly handle a failed copy. This will allow us to know if we'll fail a copy during recording and allow us to do some appropriate fallback and/or dropping of the draw. Bug: skia: Change-Id: I38f209dbd4ebb4e762210b4609147d4b0a1b01b1 Reviewed-on: https://skia-review.googlesource.com/123560 Reviewed-by: Brian Salomon Reviewed-by: Robert Phillips Commit-Queue: Greg Daniel --- src/gpu/vk/GrVkCaps.cpp | 122 ++++++++++++++++++++++++++++++++++++++++++++++++ src/gpu/vk/GrVkCaps.h | 22 +++++++++ src/gpu/vk/GrVkGpu.cpp | 113 ++++++++++++++------------------------------ 3 files changed, 178 insertions(+), 79 deletions(-) (limited to 'src/gpu/vk') diff --git a/src/gpu/vk/GrVkCaps.cpp b/src/gpu/vk/GrVkCaps.cpp index ae1570cd71..1c36a1494c 100644 --- a/src/gpu/vk/GrVkCaps.cpp +++ b/src/gpu/vk/GrVkCaps.cpp @@ -74,6 +74,128 @@ bool GrVkCaps::initDescForDstCopy(const GrRenderTargetProxy* src, GrSurfaceDesc* return true; } +bool GrVkCaps::canCopyImage(GrPixelConfig dstConfig, int dstSampleCnt, GrSurfaceOrigin dstOrigin, + GrPixelConfig srcConfig, int srcSampleCnt, + GrSurfaceOrigin srcOrigin) const { + if ((dstSampleCnt > 1 || srcSampleCnt > 1) && dstSampleCnt != srcSampleCnt) { + return false; + } + + // We require that all vulkan GrSurfaces have been created with transfer_dst and transfer_src + // as image usage flags. + if (srcOrigin != dstOrigin || GrBytesPerPixel(srcConfig) != GrBytesPerPixel(dstConfig)) { + return false; + } + + if (this->shaderCaps()->configOutputSwizzle(srcConfig) != + this->shaderCaps()->configOutputSwizzle(dstConfig)) { + return false; + } + + return true; +} + +bool GrVkCaps::canCopyAsBlit(GrPixelConfig dstConfig, int dstSampleCnt, bool dstIsLinear, + GrPixelConfig srcConfig, int srcSampleCnt, bool srcIsLinear) const { + // We require that all vulkan GrSurfaces have been created with transfer_dst and transfer_src + // as image usage flags. + if (!this->configCanBeDstofBlit(dstConfig, dstIsLinear) || + !this->configCanBeSrcofBlit(srcConfig, srcIsLinear)) { + return false; + } + + if (this->shaderCaps()->configOutputSwizzle(srcConfig) != + this->shaderCaps()->configOutputSwizzle(dstConfig)) { + return false; + } + + // We cannot blit images that are multisampled. Will need to figure out if we can blit the + // resolved msaa though. + if (dstSampleCnt > 1 || srcSampleCnt > 1) { + return false; + } + + return true; +} + +bool GrVkCaps::canCopyAsResolve(GrPixelConfig dstConfig, int dstSampleCnt, + GrSurfaceOrigin dstOrigin, GrPixelConfig srcConfig, + int srcSampleCnt, GrSurfaceOrigin srcOrigin) const { + // The src surface must be multisampled. + if (srcSampleCnt <= 1) { + return false; + } + + // The dst must not be multisampled. + if (dstSampleCnt > 1) { + return false; + } + + // Surfaces must have the same format. + if (dstConfig != srcConfig) { + return false; + } + + // Surfaces must have the same origin. + if (srcOrigin != dstOrigin) { + return false; + } + + return true; +} + +bool GrVkCaps::canCopyAsDraw(GrPixelConfig dstConfig, bool dstIsRenderable, + GrPixelConfig srcConfig, bool srcIsTextureable) const { + // TODO: Make copySurfaceAsDraw handle the swizzle + if (this->shaderCaps()->configOutputSwizzle(srcConfig) != + this->shaderCaps()->configOutputSwizzle(dstConfig)) { + return false; + } + + // Make sure the dst is a render target and the src is a texture. + if (!dstIsRenderable || !srcIsTextureable) { + return false; + } + + return true; +} + +bool GrVkCaps::canCopySurface(const GrSurfaceProxy* dst, const GrSurfaceProxy* src, + const SkIRect& srcRect, const SkIPoint& dstPoint) const { + GrSurfaceOrigin dstOrigin = dst->origin(); + GrSurfaceOrigin srcOrigin = src->origin(); + + GrPixelConfig dstConfig = dst->config(); + GrPixelConfig srcConfig = src->config(); + + // TODO: Figure out a way to track if we've wrapped a linear texture in a proxy (e.g. + // PromiseImage which won't get instantiated right away. Does this need a similar thing like the + // tracking of external or rectangle textures in GL? For now we don't create linear textures + // internally, and I don't believe anyone is wrapping them. + bool srcIsLinear = false; + bool dstIsLinear = false; + + int dstSampleCnt = 0; + int srcSampleCnt = 0; + if (const GrRenderTargetProxy* rtProxy = dst->asRenderTargetProxy()) { + dstSampleCnt = rtProxy->numColorSamples(); + } + if (const GrRenderTargetProxy* rtProxy = src->asRenderTargetProxy()) { + srcSampleCnt = rtProxy->numColorSamples(); + } + SkASSERT((dstSampleCnt > 0) == SkToBool(dst->asRenderTargetProxy())); + SkASSERT((srcSampleCnt > 0) == SkToBool(src->asRenderTargetProxy())); + + return this->canCopyImage(dstConfig, dstSampleCnt, dstOrigin, + srcConfig, srcSampleCnt, srcOrigin) || + this->canCopyAsBlit(dstConfig, dstSampleCnt, dstIsLinear, + srcConfig, srcSampleCnt, srcIsLinear) || + this->canCopyAsResolve(dstConfig, dstSampleCnt, dstOrigin, + srcConfig, srcSampleCnt, srcOrigin) || + this->canCopyAsDraw(dstConfig, dstSampleCnt > 0, + srcConfig, SkToBool(src->asTextureProxy())); +} + void GrVkCaps::init(const GrContextOptions& contextOptions, const GrVkInterface* vkInterface, VkPhysicalDevice physDev, uint32_t featureFlags, uint32_t extensionFlags) { diff --git a/src/gpu/vk/GrVkCaps.h b/src/gpu/vk/GrVkCaps.h index 881aacc9dc..69298d7f5e 100644 --- a/src/gpu/vk/GrVkCaps.h +++ b/src/gpu/vk/GrVkCaps.h @@ -110,6 +110,28 @@ public: return fPreferedStencilFormat; } + /** + * Helpers used by canCopySurface. In all cases if the SampleCnt parameter is zero that means + * the surface is not a render target, otherwise it is the number of samples in the render + * target. + */ + bool canCopyImage(GrPixelConfig dstConfig, int dstSampleCnt, GrSurfaceOrigin dstOrigin, + GrPixelConfig srcConfig, int srcSamplecnt, GrSurfaceOrigin srcOrigin) const; + + bool canCopyAsBlit(GrPixelConfig dstConfig, int dstSampleCnt, bool dstIsLinear, + GrPixelConfig srcConfig, int srcSampleCnt, bool srcIsLinear) const; + + bool canCopyAsResolve(GrPixelConfig dstConfig, int dstSampleCnt, GrSurfaceOrigin dstOrigin, + GrPixelConfig srcConfig, int srcSamplecnt, + GrSurfaceOrigin srcOrigin) const; + + bool canCopyAsDraw(GrPixelConfig dstConfig, bool dstIsRenderable, + GrPixelConfig srcConfig, bool srcIsTextureable) const; + + bool canCopySurface(const GrSurfaceProxy* dst, const GrSurfaceProxy* src, + const SkIRect& srcRect, const SkIPoint& dstPoint) const override; + + bool initDescForDstCopy(const GrRenderTargetProxy* src, GrSurfaceDesc* desc, GrSurfaceOrigin*, bool* rectsMustMatch, bool* disallowSubrect) const override; diff --git a/src/gpu/vk/GrVkGpu.cpp b/src/gpu/vk/GrVkGpu.cpp index bc47ae6558..428a74bae7 100644 --- a/src/gpu/vk/GrVkGpu.cpp +++ b/src/gpu/vk/GrVkGpu.cpp @@ -503,6 +503,7 @@ void GrVkGpu::resolveImage(GrSurface* dst, GrVkRenderTarget* src, const SkIRect& SkASSERT(dst->asTexture()); dstImage = static_cast(dst->asTexture()); } + SkASSERT(1 == dstImage->mipLevels()); dstImage->setImageLayout(this, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT, @@ -1669,33 +1670,11 @@ void GrVkGpu::clearStencil(GrRenderTarget* target, int clearValue) { fCurrentCmdBuffer->clearDepthStencilImage(this, vkStencil, &vkStencilColor, 1, &subRange); } -inline bool can_copy_image(const GrSurface* dst, GrSurfaceOrigin dstOrigin, - const GrSurface* src, GrSurfaceOrigin srcOrigin, - const GrVkGpu* gpu) { - 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; - } +static int get_surface_sample_cnt(GrSurface* surf) { + if (const GrRenderTarget* rt = surf->asRenderTarget()) { + return rt->numColorSamples(); } - - // We require that all vulkan GrSurfaces have been created with transfer_dst and transfer_src - // as image usage flags. - if (srcOrigin == dstOrigin && - GrBytesPerPixel(src->config()) == GrBytesPerPixel(dst->config())) { - return true; - } - - return false; + return 0; } void GrVkGpu::copySurfaceAsCopyImage(GrSurface* dst, GrSurfaceOrigin dstOrigin, @@ -1704,7 +1683,13 @@ void GrVkGpu::copySurfaceAsCopyImage(GrSurface* dst, GrSurfaceOrigin dstOrigin, GrVkImage* srcImage, const SkIRect& srcRect, const SkIPoint& dstPoint) { - SkASSERT(can_copy_image(dst, dstOrigin, src, srcOrigin, this)); +#ifdef SK_DEBUG + int dstSampleCnt = get_surface_sample_cnt(dst); + int srcSampleCnt = get_surface_sample_cnt(src); + SkASSERT(this->vkCaps().canCopyImage(dst->config(), dstSampleCnt, dstOrigin, + src->config(), srcSampleCnt, srcOrigin)); + +#endif // These flags are for flushing/invalidating caches and for the dst image it doesn't matter if // the cache is flushed since it is only being written to. @@ -1752,37 +1737,19 @@ void GrVkGpu::copySurfaceAsCopyImage(GrSurface* dst, GrSurfaceOrigin dstOrigin, this->didWriteToSurface(dst, dstOrigin, &dstRect); } -inline bool can_copy_as_blit(const GrSurface* dst, - const GrSurface* src, - const GrVkImage* dstImage, - const GrVkImage* srcImage, - const GrVkGpu* gpu) { - // We require that all vulkan GrSurfaces have been created with transfer_dst and transfer_src - // as image usage flags. - const GrVkCaps& caps = gpu->vkCaps(); - if (!caps.configCanBeDstofBlit(dst->config(), dstImage->isLinearTiled()) || - !caps.configCanBeSrcofBlit(src->config(), srcImage->isLinearTiled())) { - return false; - } - - // We cannot blit images that are multisampled. Will need to figure out if we can blit the - // resolved msaa though. - if ((dst->asRenderTarget() && dst->asRenderTarget()->numColorSamples() > 1) || - (src->asRenderTarget() && src->asRenderTarget()->numColorSamples() > 1)) { - return false; - } - - return true; -} - void GrVkGpu::copySurfaceAsBlit(GrSurface* dst, GrSurfaceOrigin dstOrigin, GrSurface* src, GrSurfaceOrigin srcOrigin, GrVkImage* dstImage, GrVkImage* srcImage, const SkIRect& srcRect, const SkIPoint& dstPoint) { - SkASSERT(can_copy_as_blit(dst, src, dstImage, srcImage, this)); +#ifdef SK_DEBUG + int dstSampleCnt = get_surface_sample_cnt(dst); + int srcSampleCnt = get_surface_sample_cnt(src); + SkASSERT(this->vkCaps().canCopyAsBlit(dst->config(), dstSampleCnt, dstImage->isLinearTiled(), + src->config(), srcSampleCnt, srcImage->isLinearTiled())); +#endif dstImage->setImageLayout(this, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT, @@ -1843,29 +1810,6 @@ void GrVkGpu::copySurfaceAsBlit(GrSurface* dst, GrSurfaceOrigin dstOrigin, this->didWriteToSurface(dst, dstOrigin, &dstRect); } -inline bool can_copy_as_resolve(const GrSurface* dst, GrSurfaceOrigin dstOrigin, - const GrSurface* src, GrSurfaceOrigin srcOrigin, - const GrVkGpu* gpu) { - // Our src must be a multisampled render target - if (!src->asRenderTarget() || 1 == src->asRenderTarget()->numColorSamples()) { - return false; - } - - // The dst must not be a multisampled render target, expect in the case where the dst is the - // resolve texture connected to the msaa src. We check for this in case we are copying a part of - // a surface to a different region in the same surface. - if (dst->asRenderTarget() && dst->asRenderTarget()->numColorSamples() > 1 && dst != src) { - return false; - } - - // Surfaces must have the same origin. - if (srcOrigin != dstOrigin) { - return false; - } - - return true; -} - void GrVkGpu::copySurfaceAsResolve(GrSurface* dst, GrSurfaceOrigin dstOrigin, GrSurface* src, GrSurfaceOrigin srcOrigin, const SkIRect& origSrcRect, const SkIPoint& origDstPoint) { @@ -1885,7 +1829,14 @@ bool GrVkGpu::onCopySurface(GrSurface* dst, GrSurfaceOrigin dstOrigin, GrSurface* src, GrSurfaceOrigin srcOrigin, const SkIRect& srcRect, const SkIPoint& dstPoint, bool canDiscardOutsideDstRect) { - if (can_copy_as_resolve(dst, dstOrigin, src, srcOrigin, this)) { + GrPixelConfig dstConfig = dst->config(); + GrPixelConfig srcConfig = src->config(); + + int dstSampleCnt = get_surface_sample_cnt(dst); + int srcSampleCnt = get_surface_sample_cnt(src); + + if (this->vkCaps().canCopyAsResolve(dstConfig, dstSampleCnt, dstOrigin, + srcConfig, srcSampleCnt, srcOrigin)) { this->copySurfaceAsResolve(dst, dstOrigin, src, srcOrigin, srcRect, dstPoint); return true; } @@ -1894,8 +1845,10 @@ bool GrVkGpu::onCopySurface(GrSurface* dst, GrSurfaceOrigin dstOrigin, this->submitCommandBuffer(GrVkGpu::kSkip_SyncQueue); } - if (fCopyManager.copySurfaceAsDraw(this, dst, dstOrigin, src, srcOrigin, srcRect, dstPoint, - canDiscardOutsideDstRect)) { + if (this->vkCaps().canCopyAsDraw(dstConfig, SkToBool(dst->asRenderTarget()), + srcConfig, SkToBool(src->asTexture()))) { + SkAssertResult(fCopyManager.copySurfaceAsDraw(this, dst, dstOrigin, src, srcOrigin, srcRect, + dstPoint, canDiscardOutsideDstRect)); auto dstRect = srcRect.makeOffset(dstPoint.fX, dstPoint.fY); this->didWriteToSurface(dst, dstOrigin, &dstRect); return true; @@ -1920,13 +1873,15 @@ bool GrVkGpu::onCopySurface(GrSurface* dst, GrSurfaceOrigin dstOrigin, srcImage = static_cast(src->asTexture()); } - if (can_copy_image(dst, dstOrigin, src, srcOrigin, this)) { + if (this->vkCaps().canCopyImage(dstConfig, dstSampleCnt, dstOrigin, + srcConfig, srcSampleCnt, srcOrigin)) { this->copySurfaceAsCopyImage(dst, dstOrigin, src, srcOrigin, dstImage, srcImage, srcRect, dstPoint); return true; } - if (can_copy_as_blit(dst, src, dstImage, srcImage, this)) { + if (this->vkCaps().canCopyAsBlit(dstConfig, dstSampleCnt, dstImage->isLinearTiled(), + srcConfig, srcSampleCnt, srcImage->isLinearTiled())) { this->copySurfaceAsBlit(dst, dstOrigin, src, srcOrigin, dstImage, srcImage, srcRect, dstPoint); return true; -- cgit v1.2.3