From eb85117c05471e1a55ce387cbc38279f857a4584 Mon Sep 17 00:00:00 2001 From: "bsalomon@google.com" Date: Mon, 15 Apr 2013 13:51:00 +0000 Subject: [PATCH] Add support for using glCopyTexSubImage2D when possible to copy surfaces. Review URL: https://codereview.chromium.org/13915011 git-svn-id: http://skia.googlecode.com/svn/trunk@8675 2bbb7eff-a529-9590-31e7-b0007b416f81 --- src/gpu/GrDrawTarget.cpp | 13 ++- src/gpu/GrDrawTarget.h | 8 ++ src/gpu/GrInOrderDrawBuffer.cpp | 4 + src/gpu/GrInOrderDrawBuffer.h | 3 + src/gpu/gl/GrGpuGL.cpp | 207 ++++++++++++++++++++++++++++------------ src/gpu/gl/GrGpuGL.h | 3 +- 6 files changed, 171 insertions(+), 67 deletions(-) diff --git a/src/gpu/GrDrawTarget.cpp b/src/gpu/GrDrawTarget.cpp index 9036a57..e875f0f 100644 --- a/src/gpu/GrDrawTarget.cpp +++ b/src/gpu/GrDrawTarget.cpp @@ -432,11 +432,9 @@ bool GrDrawTarget::setupDstReadIfNecessary(DrawInfo* info) { // 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. GrTextureDesc desc; - desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit; + this->initCopySurfaceDstDesc(rt, &desc); desc.fWidth = copyRect.width(); desc.fHeight = copyRect.height(); - desc.fSampleCnt = 0; - desc.fConfig = rt->config(); GrAutoScratchTexture ast(fContext, desc, GrContext::kApprox_ScratchTexMatch); @@ -447,7 +445,7 @@ bool GrDrawTarget::setupDstReadIfNecessary(DrawInfo* info) { SkIPoint dstPoint = {0, 0}; if (this->copySurface(ast.texture(), rt, copyRect, dstPoint)) { info->fDstCopy.setTexture(ast.texture()); - info->fDstCopy.setOffset(copyRect.fLeft, copyRect.fTop); + info->fDstCopy.setOffset(copyRect.fLeft, copyRect.fTop); return true; } else { return false; @@ -881,6 +879,13 @@ bool GrDrawTarget::onCopySurface(GrSurface* dst, return true; } +void GrDrawTarget::initCopySurfaceDstDesc(const GrSurface* src, GrTextureDesc* desc) { + // Make the dst of the copy be a render target because the default copySurface draws to the dst. + desc->fOrigin = kDefault_GrSurfaceOrigin; + desc->fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit; + desc->fConfig = src->config(); +} + /////////////////////////////////////////////////////////////////////////////// SK_DEFINE_INST_COUNT(GrDrawTargetCaps) diff --git a/src/gpu/GrDrawTarget.h b/src/gpu/GrDrawTarget.h index 13abee9..8f5f09f 100644 --- a/src/gpu/GrDrawTarget.h +++ b/src/gpu/GrDrawTarget.h @@ -432,6 +432,14 @@ public: const SkIPoint& dstPoint); /** + * This is can be called before allocating a texture to be a dst for copySurface. It will + * populate the origin, config, and flags fields of the desc such that copySurface is more + * likely to succeed and be efficient. + */ + virtual void initCopySurfaceDstDesc(const GrSurface* src, GrTextureDesc* desc); + + + /** * Release any resources that are cached but not currently in use. This * is intended to give an application some recourse when resources are low. */ diff --git a/src/gpu/GrInOrderDrawBuffer.cpp b/src/gpu/GrInOrderDrawBuffer.cpp index 481a395..7bd3f5d 100644 --- a/src/gpu/GrInOrderDrawBuffer.cpp +++ b/src/gpu/GrInOrderDrawBuffer.cpp @@ -540,6 +540,10 @@ bool GrInOrderDrawBuffer::onCanCopySurface(GrSurface* dst, return fDstGpu->canCopySurface(dst, src, srcRect, dstPoint); } +void GrInOrderDrawBuffer::initCopySurfaceDstDesc(const GrSurface* src, GrTextureDesc* desc) { + fDstGpu->initCopySurfaceDstDesc(src, desc); +} + void GrInOrderDrawBuffer::willReserveVertexAndIndexSpace( int vertexCount, int indexCount) { diff --git a/src/gpu/GrInOrderDrawBuffer.h b/src/gpu/GrInOrderDrawBuffer.h index c365501..6090235 100644 --- a/src/gpu/GrInOrderDrawBuffer.h +++ b/src/gpu/GrInOrderDrawBuffer.h @@ -77,6 +77,9 @@ public: GrColor color, GrRenderTarget* renderTarget = NULL) SK_OVERRIDE; + virtual void initCopySurfaceDstDesc(const GrSurface* src, GrTextureDesc* desc) SK_OVERRIDE; + + protected: virtual void clipWillBeSet(const GrClipData* newClip) SK_OVERRIDE; diff --git a/src/gpu/gl/GrGpuGL.cpp b/src/gpu/gl/GrGpuGL.cpp index 8719377..0fba75d 100644 --- a/src/gpu/gl/GrGpuGL.cpp +++ b/src/gpu/gl/GrGpuGL.cpp @@ -2229,23 +2229,142 @@ void GrGpuGL::setSpareTextureUnit() { namespace { // Determines whether glBlitFramebuffer could be used between src and dst. -inline bool can_blit_framebuffer(const GrSurface* dst, const GrSurface* src, const GrGpuGL* gpu) { - return gpu->isConfigRenderable(dst->config()) && gpu->isConfigRenderable(src->config()) && - (GrGLCaps::kDesktopEXT_MSFBOType == gpu->glCaps().msFBOType() || - GrGLCaps::kDesktopARB_MSFBOType == gpu->glCaps().msFBOType()); +inline bool can_blit_framebuffer(const GrSurface* dst, + const GrSurface* src, + const GrGpuGL* gpu, + bool* wouldNeedTempFBO = NULL) { + if (gpu->isConfigRenderable(dst->config()) && gpu->isConfigRenderable(src->config()) && + (GrGLCaps::kDesktopEXT_MSFBOType == gpu->glCaps().msFBOType() || + GrGLCaps::kDesktopARB_MSFBOType == gpu->glCaps().msFBOType())) { + if (NULL != wouldNeedTempFBO) { + *wouldNeedTempFBO = NULL == dst->asRenderTarget() || NULL == src->asRenderTarget(); + } + return true; + } else { + return false; + } } + +inline bool can_copy_texsubimage(const GrSurface* dst, + const GrSurface* src, + const GrGpuGL* gpu, + bool* wouldNeedTempFBO = NULL) { + // Table 3.9 of the ES2 spec indicates the supported formats with CopyTexSubImage + // and BGRA isn't in the spec. There doesn't appear to be any extension that adds it. Perhaps + // many drivers would allow it to work, but ANGLE does not. + if (kES2_GrGLBinding == gpu->glBinding() && gpu->glCaps().bgraIsInternalFormat() && + (kBGRA_8888_GrPixelConfig == dst->config() || kBGRA_8888_GrPixelConfig == src->config())) { + return false; + } + const GrGLRenderTarget* dstRT = static_cast(dst->asRenderTarget()); + // If dst is multisampled (and uses an extension where there is a separate MSAA renderbuffer) + // then we don't want to copy to the texture but to the MSAA buffer. + if (NULL != dstRT && dstRT->renderFBOID() != dstRT->textureFBOID()) { + return false; + } + if (gpu->isConfigRenderable(src->config()) && NULL != dst->asTexture() && + dst->origin() == src->origin() && kIndex_8_GrPixelConfig != src->config()) { + if (NULL != wouldNeedTempFBO) { + *wouldNeedTempFBO = NULL == src->asRenderTarget(); + } + return true; + } else { + return false; + } +} + +// If a temporary FBO was created, its non-zero ID is returned. The viewport that the copy rect is +// relative to is output. +inline GrGLuint bind_surface_as_fbo(const GrGLInterface* gl, + GrSurface* surface, + GrGLenum fboTarget, + GrGLIRect* viewport) { + GrGLRenderTarget* rt = static_cast(surface->asRenderTarget()); + GrGLuint tempFBOID; + if (NULL == rt) { + GrAssert(NULL != surface->asTexture()); + GrGLuint texID = static_cast(surface->asTexture())->textureID(); + GR_GL_CALL(gl, GenFramebuffers(1, &tempFBOID)); + GR_GL_CALL(gl, BindFramebuffer(fboTarget, tempFBOID)); + GR_GL_CALL(gl, FramebufferTexture2D(fboTarget, + GR_GL_COLOR_ATTACHMENT0, + GR_GL_TEXTURE_2D, + texID, + 0)); + viewport->fLeft = 0; + viewport->fBottom = 0; + viewport->fWidth = surface->width(); + viewport->fHeight = surface->height(); + } else { + tempFBOID = 0; + GR_GL_CALL(gl, BindFramebuffer(fboTarget, rt->renderFBOID())); + *viewport = rt->getViewport(); + } + return tempFBOID; +} + +} + +void GrGpuGL::initCopySurfaceDstDesc(const GrSurface* src, GrTextureDesc* desc) { + // Check for format issues with glCopyTexSubImage2D + if (kES2_GrGLBinding == this->glBinding() && this->glCaps().bgraIsInternalFormat() && + kBGRA_8888_GrPixelConfig == src->config()) { + // glCopyTexSubImage2D doesn't work with this config. We'll want to make it a render target + // to in order to call glBlitFramebuffer or to copy to it by rendering. + INHERITED::initCopySurfaceDstDesc(src, desc); + } else if (NULL == src->asRenderTarget()) { + // We don't want to have to create an FBO just to use glCopyTexSubImage2D. Let the base + // class handle it by rendering. + INHERITED::initCopySurfaceDstDesc(src, desc); + } else { + desc->fConfig = src->config(); + desc->fOrigin = src->origin(); + desc->fFlags = kNone_GrTextureFlags; + } } bool GrGpuGL::onCopySurface(GrSurface* dst, GrSurface* src, const SkIRect& srcRect, const SkIPoint& dstPoint) { - // TODO: Add support for glCopyTexSubImage for cases when src is an FBO and dst is not - // renderable or we don't have glBlitFramebuffer. + bool inheritedCouldCopy = INHERITED::onCanCopySurface(dst, src, srcRect, dstPoint); bool copied = false; - // Check whether both src and dst could be attached to an FBO and we're on a GL that supports - // glBlitFramebuffer. - if (can_blit_framebuffer(dst, src, this)) { + bool wouldNeedTempFBO = false; + if (can_copy_texsubimage(dst, src, this, &wouldNeedTempFBO) && + (!wouldNeedTempFBO || !inheritedCouldCopy)) { + GrGLuint srcFBO; + GrGLIRect srcVP; + srcFBO = bind_surface_as_fbo(this->glInterface(), src, GR_GL_FRAMEBUFFER, &srcVP); + GrGLTexture* dstTex = static_cast(dst->asTexture()); + GrAssert(NULL != dstTex); + // We modified the bound FBO + fHWBoundRenderTarget = NULL; + GrGLIRect srcGLRect; + srcGLRect.setRelativeTo(srcVP, + srcRect.fLeft, + srcRect.fTop, + srcRect.width(), + srcRect.height(), + src->origin()); + + this->setSpareTextureUnit(); + GL_CALL(BindTexture(GR_GL_TEXTURE_2D, dstTex->textureID())); + GrGLint dstY; + if (kBottomLeft_GrSurfaceOrigin == dst->origin()) { + dstY = dst->height() - (dstPoint.fY + srcGLRect.fHeight); + } else { + dstY = dstPoint.fY; + } + GL_CALL(CopyTexSubImage2D(GR_GL_TEXTURE_2D, 0, + dstPoint.fX, dstY, + srcGLRect.fLeft, srcGLRect.fBottom, + srcGLRect.fWidth, srcGLRect.fHeight)); + copied = true; + if (srcFBO) { + GL_CALL(DeleteFramebuffers(1, &srcFBO)); + } + } else if (can_blit_framebuffer(dst, src, this, &wouldNeedTempFBO) && + (!wouldNeedTempFBO || !inheritedCouldCopy)) { SkIRect dstRect = SkIRect::MakeXYWH(dstPoint.fX, dstPoint.fY, srcRect.width(), srcRect.height()); bool selfOverlap = false; @@ -2254,50 +2373,13 @@ bool GrGpuGL::onCopySurface(GrSurface* dst, } if (!selfOverlap) { - GrGLuint dstFBO = 0; - GrGLuint srcFBO = 0; + GrGLuint dstFBO; + GrGLuint srcFBO; GrGLIRect dstVP; GrGLIRect srcVP; - GrGLRenderTarget* dstRT = static_cast(dst->asRenderTarget()); - GrGLRenderTarget* srcRT = static_cast(src->asRenderTarget()); - if (NULL == dstRT) { - GrAssert(NULL != dst->asTexture()); - GrGLuint texID = static_cast(dst->asTexture())->textureID(); - GL_CALL(GenFramebuffers(1, &dstFBO)); - GL_CALL(BindFramebuffer(GR_GL_DRAW_FRAMEBUFFER, dstFBO)); - GL_CALL(FramebufferTexture2D(GR_GL_DRAW_FRAMEBUFFER, - GR_GL_COLOR_ATTACHMENT0, - GR_GL_TEXTURE_2D, - texID, - 0)); - dstVP.fLeft = 0; - dstVP.fBottom = 0; - dstVP.fWidth = dst->width(); - dstVP.fHeight = dst->height(); - } else { - GL_CALL(BindFramebuffer(GR_GL_DRAW_FRAMEBUFFER, dstRT->renderFBOID())); - dstVP = dstRT->getViewport(); - } - if (NULL == srcRT) { - GrAssert(NULL != src->asTexture()); - GrGLuint texID = static_cast(src->asTexture())->textureID(); - GL_CALL(GenFramebuffers(1, &srcFBO)); - GL_CALL(BindFramebuffer(GR_GL_READ_FRAMEBUFFER, srcFBO)); - GL_CALL(FramebufferTexture2D(GR_GL_READ_FRAMEBUFFER, - GR_GL_COLOR_ATTACHMENT0, - GR_GL_TEXTURE_2D, - texID, - 0)); - srcVP.fLeft = 0; - srcVP.fBottom = 0; - srcVP.fWidth = src->width(); - srcVP.fHeight = src->height(); - } else { - GL_CALL(BindFramebuffer(GR_GL_READ_FRAMEBUFFER, srcRT->renderFBOID())); - srcVP = srcRT->getViewport(); - } - - // We modified the bound FB + dstFBO = bind_surface_as_fbo(this->glInterface(), dst, GR_GL_DRAW_FRAMEBUFFER, &dstVP); + srcFBO = bind_surface_as_fbo(this->glInterface(), src, GR_GL_READ_FRAMEBUFFER, &srcVP); + // We modified the bound FBO fHWBoundRenderTarget = NULL; GrGLIRect srcGLRect; GrGLIRect dstGLRect; @@ -2349,8 +2431,9 @@ bool GrGpuGL::onCopySurface(GrSurface* dst, copied = true; } } - if (!copied) { + if (!copied && inheritedCouldCopy) { copied = INHERITED::onCopySurface(dst, src, srcRect, dstPoint); + GrAssert(copied); } return copied; } @@ -2360,21 +2443,21 @@ bool GrGpuGL::onCanCopySurface(GrSurface* dst, const SkIRect& srcRect, const SkIPoint& dstPoint) { // This mirrors the logic in onCopySurface. - bool canBlitFramebuffer = false; + if (can_copy_texsubimage(dst, src, this)) { + return true; + } if (can_blit_framebuffer(dst, src, this)) { - SkIRect dstRect = SkIRect::MakeXYWH(dstPoint.fX, dstPoint.fY, - srcRect.width(), srcRect.height()); if (dst->isSameAs(src)) { - canBlitFramebuffer = !SkIRect::IntersectsNoEmptyCheck(dstRect, srcRect); + SkIRect dstRect = SkIRect::MakeXYWH(dstPoint.fX, dstPoint.fY, + srcRect.width(), srcRect.height()); + if(!SkIRect::IntersectsNoEmptyCheck(dstRect, srcRect)) { + return true; + } } else { - canBlitFramebuffer = true; + return true; } } - if (canBlitFramebuffer) { - return true; - } else { - return INHERITED::onCanCopySurface(dst, src, srcRect, dstPoint); - } + return INHERITED::onCanCopySurface(dst, src, srcRect, dstPoint); } diff --git a/src/gpu/gl/GrGpuGL.h b/src/gpu/gl/GrGpuGL.h index 4ea4e12..0f75280 100644 --- a/src/gpu/gl/GrGpuGL.h +++ b/src/gpu/gl/GrGpuGL.h @@ -55,6 +55,8 @@ public: size_t rowBytes) const SK_OVERRIDE; virtual bool fullReadPixelsIsFasterThanPartial() const SK_OVERRIDE; + virtual void initCopySurfaceDstDesc(const GrSurface* src, GrTextureDesc* desc) SK_OVERRIDE; + virtual void abandonResources() SK_OVERRIDE; const GrGLCaps& glCaps() const { return *fGLContext.info().caps(); } @@ -96,7 +98,6 @@ protected: const SkIRect& srcRect, const SkIPoint& dstPoint) SK_OVERRIDE; - private: // GrGpu overrides virtual void onResetContext() SK_OVERRIDE; -- 2.7.4