diff --git a/gm/mixedxfermodes.cpp b/gm/mixedxfermodes.cpp index beb5d5410e..c0045f84ae 100644 --- a/gm/mixedxfermodes.cpp +++ b/gm/mixedxfermodes.cpp @@ -45,12 +45,6 @@ protected: } virtual void onDraw(SkCanvas* canvas) { - // FIXME: Currently necessary for GPU to be able to make dst-copy in SampleApp because - // main layer is not a texture. - SkPaint layerPaint; - layerPaint.setXfermodeMode(SkXfermode::kSrc_Mode); - canvas->saveLayer(NULL, &layerPaint); - SkPaint bgPaint; bgPaint.setShader(fBG.get()); canvas->drawPaint(bgPaint); @@ -86,8 +80,6 @@ protected: areaSqrt/50, SkIntToScalar(size.fHeight / 2), txtPaint); - - canvas->restore(); } private: diff --git a/src/gpu/GrDrawTarget.cpp b/src/gpu/GrDrawTarget.cpp index d07d625865..9036a57796 100644 --- a/src/gpu/GrDrawTarget.cpp +++ b/src/gpu/GrDrawTarget.cpp @@ -411,12 +411,6 @@ bool GrDrawTarget::setupDstReadIfNecessary(DrawInfo* info) { return true; } GrRenderTarget* rt = this->drawState()->getRenderTarget(); - // If the dst is not a texture then we don't currently have a way of copying the - // texture. TODO: API-specific impl of onCopySurface that can handle more cases. - if (NULL == rt->asTexture()) { - GrPrintf("Reading Dst of non-texture render target is not currently supported.\n"); - return false; - } const GrClipData* clip = this->getClip(); GrIRect copyRect; @@ -435,11 +429,8 @@ bool GrDrawTarget::setupDstReadIfNecessary(DrawInfo* info) { #endif } - // The draw will resolve dst if it has MSAA. Two things to consider in the future: - // 1) to make the dst values be pre-resolve we'd need to be able to copy to MSAA - // texture and sample it correctly in the shader. 2) If 1 isn't available then we - // should just resolve and use the resolved texture directly rather than making a - // copy of it. + // 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; desc.fWidth = copyRect.width(); diff --git a/src/gpu/gl/GrGLIRect.h b/src/gpu/gl/GrGLIRect.h index cbc4cb89f6..f171cf1374 100644 --- a/src/gpu/gl/GrGLIRect.h +++ b/src/gpu/gl/GrGLIRect.h @@ -36,7 +36,7 @@ struct GrGLIRect { GR_GL_GetIntegerv(gl, GR_GL_VIEWPORT, (GrGLint*) this); } - // sometimes we have a GrIRect from the client that we + // sometimes we have a SkIRect from the client that we // want to simultaneously make relative to GL's viewport // and (optionally) convert from top-down to bottom-up. void setRelativeTo(const GrGLIRect& glRect, diff --git a/src/gpu/gl/GrGpuGL.cpp b/src/gpu/gl/GrGpuGL.cpp index d2996564f3..871937728e 100644 --- a/src/gpu/gl/GrGpuGL.cpp +++ b/src/gpu/gl/GrGpuGL.cpp @@ -2227,6 +2227,157 @@ 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()); +} +} + +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 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)) { + SkIRect dstRect = SkIRect::MakeXYWH(dstPoint.fX, dstPoint.fY, + srcRect.width(), srcRect.height()); + bool selfOverlap = false; + if (dst->isSameAs(src)) { + selfOverlap = SkIRect::IntersectsNoEmptyCheck(dstRect, srcRect); + } + + if (!selfOverlap) { + GrGLuint dstFBO = 0; + GrGLuint srcFBO = 0; + 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 + fHWBoundRenderTarget = NULL; + GrGLIRect srcGLRect; + GrGLIRect dstGLRect; + srcGLRect.setRelativeTo(srcVP, + srcRect.fLeft, + srcRect.fTop, + srcRect.width(), + srcRect.height(), + src->origin()); + dstGLRect.setRelativeTo(dstVP, + dstRect.fLeft, + dstRect.fTop, + dstRect.width(), + dstRect.height(), + dst->origin()); + + GrAutoTRestore asr; + if (GrGLCaps::kDesktopEXT_MSFBOType == this->glCaps().msFBOType()) { + // The EXT version applies the scissor during the blit, so disable it. + asr.reset(&fScissorState); + fScissorState.fEnabled = false; + this->flushScissor(); + } + GrGLint srcY0; + GrGLint srcY1; + // Does the blit need to y-mirror or not? + if (src->origin() == dst->origin()) { + srcY0 = srcGLRect.fBottom; + srcY1 = srcGLRect.fBottom + srcGLRect.fHeight; + } else { + srcY0 = srcGLRect.fBottom + srcGLRect.fHeight; + srcY1 = srcGLRect.fBottom; + } + GL_CALL(BlitFramebuffer(srcGLRect.fLeft, + srcY0, + srcGLRect.fLeft + srcGLRect.fWidth, + srcY1, + dstGLRect.fLeft, + dstGLRect.fBottom, + dstGLRect.fLeft + dstGLRect.fWidth, + dstGLRect.fBottom + dstGLRect.fHeight, + GR_GL_COLOR_BUFFER_BIT, GR_GL_NEAREST)); + if (dstFBO) { + GL_CALL(DeleteFramebuffers(1, &dstFBO)); + } + if (srcFBO) { + GL_CALL(DeleteFramebuffers(1, &srcFBO)); + } + copied = true; + } + } + if (!copied) { + copied = INHERITED::onCopySurface(dst, src, srcRect, dstPoint); + } + return copied; +} + +bool GrGpuGL::onCanCopySurface(GrSurface* dst, + GrSurface* src, + const SkIRect& srcRect, + const SkIPoint& dstPoint) { + // This mirrors the logic in onCopySurface. + bool canBlitFramebuffer = false; + 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); + } else { + canBlitFramebuffer = true; + } + } + if (canBlitFramebuffer) { + return true; + } else { + return INHERITED::onCanCopySurface(dst, src, srcRect, dstPoint); + } +} + + /////////////////////////////////////////////////////////////////////////////// GrGLAttribArrayState* GrGpuGL::HWGeometryState::bindArrayAndBuffersToDraw( diff --git a/src/gpu/gl/GrGpuGL.h b/src/gpu/gl/GrGpuGL.h index a71ff39e52..4ea4e1210b 100644 --- a/src/gpu/gl/GrGpuGL.h +++ b/src/gpu/gl/GrGpuGL.h @@ -85,6 +85,18 @@ public: void notifyTextureDelete(GrGLTexture* texture); void notifyRenderTargetDelete(GrRenderTarget* renderTarget); +protected: + virtual bool onCopySurface(GrSurface* dst, + GrSurface* src, + const SkIRect& srcRect, + const SkIPoint& dstPoint) SK_OVERRIDE; + + virtual bool onCanCopySurface(GrSurface* dst, + GrSurface* src, + const SkIRect& srcRect, + const SkIPoint& dstPoint) SK_OVERRIDE; + + private: // GrGpu overrides virtual void onResetContext() SK_OVERRIDE;