From 160f24ce0e8d6dd7ca80b78871e063d4f4609cfb Mon Sep 17 00:00:00 2001 From: bsalomon Date: Tue, 17 Mar 2015 15:55:42 -0700 Subject: [PATCH] Improve tracking of bound FBOs in GrGLGpu. Committed: https://skia.googlesource.com/skia/+/d2ad8eb5801e2c8c0fa544a6a776bb46eedde2a0 Committed: https://skia.googlesource.com/skia/+/b2af2d8b83ca4774c3b3bb1e49bc72605faa9589 Committed: https://skia.googlesource.com/skia/+/0b70b86a7e9fda52ee7ebc1b9897eeaa09b9abef Committed: https://skia.googlesource.com/skia/+/6ba6fa15261be591f33cf0e5df7134e4fc6432ac Committed: https://skia.googlesource.com/skia/+/dc963b9264908f53650c40a97cff414101dd3e88 Review URL: https://codereview.chromium.org/949263002 --- src/gpu/gl/GrGLCaps.cpp | 9 + src/gpu/gl/GrGLCaps.h | 5 + src/gpu/gl/GrGLGpu.cpp | 390 +++++++++--------- src/gpu/gl/GrGLGpu.h | 51 ++- src/gpu/gl/GrGLNoOpInterface.cpp | 3 +- src/gpu/gl/GrGLRenderTarget.cpp | 61 ++- src/gpu/gl/GrGLRenderTarget.h | 100 ++++- src/gpu/gl/debug/GrGLCreateDebugInterface.cpp | 6 +- 8 files changed, 385 insertions(+), 240 deletions(-) diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp index 9911d53f43..e70e6f04fb 100644 --- a/src/gpu/gl/GrGLCaps.cpp +++ b/src/gpu/gl/GrGLCaps.cpp @@ -52,6 +52,8 @@ void GrGLCaps::reset() { fDropsTileOnZeroDivide = false; fFBFetchSupport = false; fFBFetchNeedsCustomOutput = false; + fPreferBindingToReadAndDrawFramebuffer = false; + fFBFetchColorName = NULL; fFBFetchExtensionString = NULL; @@ -99,6 +101,7 @@ GrGLCaps& GrGLCaps::operator= (const GrGLCaps& caps) { fFBFetchNeedsCustomOutput = caps.fFBFetchNeedsCustomOutput; fFBFetchColorName = caps.fFBFetchColorName; fFBFetchExtensionString = caps.fFBFetchExtensionString; + fPreferBindingToReadAndDrawFramebuffer = caps.fPreferBindingToReadAndDrawFramebuffer; return *this; } @@ -279,6 +282,12 @@ bool GrGLCaps::init(const GrGLContextInfo& ctxInfo, const GrGLInterface* gli) { this->initFSAASupport(ctxInfo, gli); this->initStencilFormats(ctxInfo); + // Workaround for Mac/Chromium issue. +#ifdef SK_BUILD_FOR_MAC + // This relies on the fact that initFSAASupport() was already called. + fPreferBindingToReadAndDrawFramebuffer = ctxInfo.isChromium() && this->usesMSAARenderBuffers(); +#endif + /************************************************************************** * GrDrawTargetCaps fields **************************************************************************/ diff --git a/src/gpu/gl/GrGLCaps.h b/src/gpu/gl/GrGLCaps.h index 1b77ed0e32..932e66fdfc 100644 --- a/src/gpu/gl/GrGLCaps.h +++ b/src/gpu/gl/GrGLCaps.h @@ -267,6 +267,10 @@ public: bool fullClearIsFree() const { return fFullClearIsFree; } bool dropsTileOnZeroDivide() const { return fDropsTileOnZeroDivide; } + + bool preferBindingToReadAndDrawFramebuffer() const { + return fPreferBindingToReadAndDrawFramebuffer; + } /** * Returns a string containing the caps info. @@ -389,6 +393,7 @@ private: bool fDropsTileOnZeroDivide : 1; bool fFBFetchSupport : 1; bool fFBFetchNeedsCustomOutput : 1; + bool fPreferBindingToReadAndDrawFramebuffer : 1; const char* fFBFetchColorName; const char* fFBFetchExtensionString; diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp index a2c8d7f3ea..5ebbe1f3b4 100644 --- a/src/gpu/gl/GrGLGpu.cpp +++ b/src/gpu/gl/GrGLGpu.cpp @@ -151,9 +151,6 @@ GrGLGpu::GrGLGpu(const GrGLContext& ctx, GrContext* context) fLastSuccessfulStencilFmtIdx = 0; fHWProgramID = 0; - fTempSrcFBOID = 0; - fTempDstFBOID = 0; - fStencilClearFBOID = 0; if (this->glCaps().pathRenderingSupport()) { fPathRendering.reset(new GrGLPathRendering(this)); @@ -168,14 +165,17 @@ GrGLGpu::~GrGLGpu() { GL_CALL(UseProgram(0)); } - if (0 != fTempSrcFBOID) { - GL_CALL(DeleteFramebuffers(1, &fTempSrcFBOID)); + if (fTempSrcFBO) { + fTempSrcFBO->release(this->glInterface()); + fTempSrcFBO.reset(NULL); } - if (0 != fTempDstFBOID) { - GL_CALL(DeleteFramebuffers(1, &fTempDstFBOID)); + if (fTempDstFBO) { + fTempDstFBO->release(this->glInterface()); + fTempDstFBO.reset(NULL); } - if (0 != fStencilClearFBOID) { - GL_CALL(DeleteFramebuffers(1, &fStencilClearFBOID)); + if (fStencilClearFBO) { + fStencilClearFBO->release(this->glInterface()); + fStencilClearFBO.reset(NULL); } delete fProgramCache; @@ -185,9 +185,19 @@ void GrGLGpu::contextAbandoned() { INHERITED::contextAbandoned(); fProgramCache->abandon(); fHWProgramID = 0; - fTempSrcFBOID = 0; - fTempDstFBOID = 0; - fStencilClearFBOID = 0; + if (fTempSrcFBO) { + fTempSrcFBO->abandon(); + fTempSrcFBO.reset(NULL); + } + if (fTempDstFBO) { + fTempDstFBO->abandon(); + fTempDstFBO.reset(NULL); + } + if (fStencilClearFBO) { + fStencilClearFBO->abandon(); + fStencilClearFBO.reset(NULL); + } + if (this->glCaps().pathRenderingSupport()) { this->glPathRendering()->abandonGpuResources(); } @@ -331,7 +341,9 @@ void GrGLGpu::onResetContext(uint32_t resetBits) { } if (resetBits & kRenderTarget_GrGLBackendState) { - fHWBoundRenderTargetUniqueID = SK_InvalidUniqueID; + for (size_t i = 0; i < SK_ARRAY_COUNT(fHWFBOBinding); ++i) { + fHWFBOBinding[i].invalidate(); + } } if (resetBits & kPathRendering_GrGLBackendState) { @@ -432,9 +444,9 @@ GrTexture* GrGLGpu::onWrapBackendTexture(const GrBackendTextureDesc& desc) { GrRenderTarget* GrGLGpu::onWrapBackendRenderTarget(const GrBackendRenderTargetDesc& wrapDesc) { GrGLRenderTarget::IDDesc idDesc; - idDesc.fRTFBOID = static_cast(wrapDesc.fRenderTargetHandle); + GrGLuint fboID = static_cast(wrapDesc.fRenderTargetHandle); + idDesc.fRenderFBO.reset(SkNEW_ARGS(GrGLFBO, (fboID))); idDesc.fMSColorRenderbufferID = 0; - idDesc.fTexFBOID = GrGLRenderTarget::kUnresolvableFBOID; idDesc.fLifeCycle = GrGpuResource::kWrapped_LifeCycle; GrSurfaceDesc desc; @@ -814,34 +826,34 @@ static bool renderbuffer_storage_msaa(GrGLContext& ctx, bool GrGLGpu::createRenderTargetObjects(const GrSurfaceDesc& desc, bool budgeted, GrGLuint texID, GrGLRenderTarget::IDDesc* idDesc) { idDesc->fMSColorRenderbufferID = 0; - idDesc->fRTFBOID = 0; - idDesc->fTexFBOID = 0; idDesc->fLifeCycle = budgeted ? GrGpuResource::kCached_LifeCycle : GrGpuResource::kUncached_LifeCycle; GrGLenum status; GrGLenum msColorFormat = 0; // suppress warning + GrGLenum fboTarget = 0; // suppress warning if (desc.fSampleCnt > 0 && GrGLCaps::kNone_MSFBOType == this->glCaps().msFBOType()) { goto FAILED; } - GL_CALL(GenFramebuffers(1, &idDesc->fTexFBOID)); - if (!idDesc->fTexFBOID) { + idDesc->fTextureFBO.reset(SkNEW_ARGS(GrGLFBO, (this->glInterface()))); + if (!idDesc->fTextureFBO->isValid()) { goto FAILED; } - // If we are using multisampling we will create two FBOS. We render to one and then resolve to // the texture bound to the other. The exception is the IMG multisample extension. With this // extension the texture is multisampled when rendered to and then auto-resolves it when it is // rendered from. if (desc.fSampleCnt > 0 && this->glCaps().usesMSAARenderBuffers()) { - GL_CALL(GenFramebuffers(1, &idDesc->fRTFBOID)); + idDesc->fRenderFBO.reset(SkNEW_ARGS(GrGLFBO, (this->glInterface()))); + if (!idDesc->fRenderFBO->isValid()) { + goto FAILED; + } GL_CALL(GenRenderbuffers(1, &idDesc->fMSColorRenderbufferID)); - if (!idDesc->fRTFBOID || - !idDesc->fMSColorRenderbufferID || + if (!idDesc->fMSColorRenderbufferID || !this->configToGLFormats(desc.fConfig, // ES2 and ES3 require sized internal formats for rb storage. kGLES_GrGLStandard == this->glStandard(), @@ -851,12 +863,10 @@ bool GrGLGpu::createRenderTargetObjects(const GrSurfaceDesc& desc, bool budgeted goto FAILED; } } else { - idDesc->fRTFBOID = idDesc->fTexFBOID; + idDesc->fRenderFBO.reset(SkRef(idDesc->fTextureFBO.get())); } - // below here we may bind the FBO - fHWBoundRenderTargetUniqueID = SK_InvalidUniqueID; - if (idDesc->fRTFBOID != idDesc->fTexFBOID) { + if (idDesc->fRenderFBO != idDesc->fTextureFBO) { SkASSERT(desc.fSampleCnt > 0); GL_CALL(BindRenderbuffer(GR_GL_RENDERBUFFER, idDesc->fMSColorRenderbufferID)); if (!renderbuffer_storage_msaa(fGLContext, @@ -865,12 +875,11 @@ bool GrGLGpu::createRenderTargetObjects(const GrSurfaceDesc& desc, bool budgeted desc.fWidth, desc.fHeight)) { goto FAILED; } - fStats.incRenderTargetBinds(); - GL_CALL(BindFramebuffer(GR_GL_FRAMEBUFFER, idDesc->fRTFBOID)); - GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER, - GR_GL_COLOR_ATTACHMENT0, - GR_GL_RENDERBUFFER, - idDesc->fMSColorRenderbufferID)); + fboTarget = this->bindFBO(kChangeAttachments_FBOBinding, idDesc->fRenderFBO); + GL_CALL(FramebufferRenderbuffer(fboTarget, + GR_GL_COLOR_ATTACHMENT0, + GR_GL_RENDERBUFFER, + idDesc->fMSColorRenderbufferID)); if ((desc.fFlags & kCheckAllocation_GrSurfaceFlag) || !this->glCaps().isConfigVerifiedColorAttachment(desc.fConfig)) { GL_CALL_RET(status, CheckFramebufferStatus(GR_GL_FRAMEBUFFER)); @@ -880,23 +889,22 @@ bool GrGLGpu::createRenderTargetObjects(const GrSurfaceDesc& desc, bool budgeted fGLContext.caps()->markConfigAsValidColorAttachment(desc.fConfig); } } - fStats.incRenderTargetBinds(); - GL_CALL(BindFramebuffer(GR_GL_FRAMEBUFFER, idDesc->fTexFBOID)); + fboTarget = this->bindFBO(kChangeAttachments_FBOBinding, idDesc->fTextureFBO); if (this->glCaps().usesImplicitMSAAResolve() && desc.fSampleCnt > 0) { - GL_CALL(FramebufferTexture2DMultisample(GR_GL_FRAMEBUFFER, + GL_CALL(FramebufferTexture2DMultisample(fboTarget, GR_GL_COLOR_ATTACHMENT0, GR_GL_TEXTURE_2D, texID, 0, desc.fSampleCnt)); } else { - GL_CALL(FramebufferTexture2D(GR_GL_FRAMEBUFFER, + GL_CALL(FramebufferTexture2D(fboTarget, GR_GL_COLOR_ATTACHMENT0, GR_GL_TEXTURE_2D, texID, 0)); } if ((desc.fFlags & kCheckAllocation_GrSurfaceFlag) || !this->glCaps().isConfigVerifiedColorAttachment(desc.fConfig)) { - GL_CALL_RET(status, CheckFramebufferStatus(GR_GL_FRAMEBUFFER)); + GL_CALL_RET(status, CheckFramebufferStatus(fboTarget)); if (status != GR_GL_FRAMEBUFFER_COMPLETE) { goto FAILED; } @@ -909,11 +917,11 @@ FAILED: if (idDesc->fMSColorRenderbufferID) { GL_CALL(DeleteRenderbuffers(1, &idDesc->fMSColorRenderbufferID)); } - if (idDesc->fRTFBOID != idDesc->fTexFBOID) { - GL_CALL(DeleteFramebuffers(1, &idDesc->fRTFBOID)); + if (idDesc->fRenderFBO) { + idDesc->fRenderFBO->release(this->glInterface()); } - if (idDesc->fTexFBOID) { - GL_CALL(DeleteFramebuffers(1, &idDesc->fTexFBOID)); + if (idDesc->fTextureFBO) { + idDesc->fTextureFBO->release(this->glInterface()); } return false; } @@ -1186,18 +1194,17 @@ bool GrGLGpu::createStencilBufferForRenderTarget(GrRenderTarget* rt, int width, // Clear the stencil buffer. We use a special purpose FBO for this so that the // entire stencil buffer is cleared, even if it is attached to an FBO with a // smaller color target. - if (0 == fStencilClearFBOID) { - GL_CALL(GenFramebuffers(1, &fStencilClearFBOID)); + if (!fStencilClearFBO) { + fStencilClearFBO.reset(SkNEW_ARGS(GrGLFBO, (this->glInterface()))); } + SkASSERT(fStencilClearFBO->isValid()); + GrGLenum fboTarget = this->bindFBO(kClear_FBOBinding, fStencilClearFBO); - GL_CALL(BindFramebuffer(GR_GL_FRAMEBUFFER, fStencilClearFBOID)); - fHWBoundRenderTargetUniqueID = SK_InvalidUniqueID; - fStats.incRenderTargetBinds(); - GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER, + GL_CALL(FramebufferRenderbuffer(fboTarget, GR_GL_STENCIL_ATTACHMENT, GR_GL_RENDERBUFFER, sbDesc.fRenderbufferID)); if (sFmt.fPacked) { - GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER, + GL_CALL(FramebufferRenderbuffer(fboTarget, GR_GL_DEPTH_ATTACHMENT, GR_GL_RENDERBUFFER, sbDesc.fRenderbufferID)); } @@ -1208,28 +1215,29 @@ bool GrGLGpu::createStencilBufferForRenderTarget(GrRenderTarget* rt, int width, GrGLuint tempRB; GL_CALL(GenRenderbuffers(1, &tempRB)); GL_CALL(BindRenderbuffer(GR_GL_RENDERBUFFER, tempRB)); + if (samples > 0) { renderbuffer_storage_msaa(fGLContext, samples, GR_GL_RGBA8, width, height); } else { GL_CALL(RenderbufferStorage(GR_GL_RENDERBUFFER, GR_GL_RGBA8, width, height)); } - GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER, + GL_CALL(FramebufferRenderbuffer(fboTarget, GR_GL_COLOR_ATTACHMENT0, GR_GL_RENDERBUFFER, tempRB)); GL_CALL(Clear(GR_GL_STENCIL_BUFFER_BIT)); - GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER, + GL_CALL(FramebufferRenderbuffer(fboTarget, GR_GL_COLOR_ATTACHMENT0, GR_GL_RENDERBUFFER, 0)); GL_CALL(DeleteRenderbuffers(1, &tempRB)); // Unbind the SB from the FBO so that we don't keep it alive. - GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER, + GL_CALL(FramebufferRenderbuffer(fboTarget, GR_GL_STENCIL_ATTACHMENT, GR_GL_RENDERBUFFER, 0)); if (sFmt.fPacked) { - GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER, + GL_CALL(FramebufferRenderbuffer(fboTarget, GR_GL_DEPTH_ATTACHMENT, GR_GL_RENDERBUFFER, 0)); } @@ -1249,9 +1257,6 @@ bool GrGLGpu::createStencilBufferForRenderTarget(GrRenderTarget* rt, int width, bool GrGLGpu::attachStencilBufferToRenderTarget(GrStencilBuffer* sb, GrRenderTarget* rt) { GrGLRenderTarget* glrt = static_cast(rt); - - GrGLuint fbo = glrt->renderFBOID(); - if (NULL == sb) { if (rt->renderTargetPriv().getStencilBuffer()) { GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER, @@ -1270,19 +1275,17 @@ bool GrGLGpu::attachStencilBufferToRenderTarget(GrStencilBuffer* sb, GrRenderTar } else { GrGLStencilBuffer* glsb = static_cast(sb); GrGLuint rb = glsb->renderbufferID(); + GrGLenum fboTarget = this->bindFBO(kChangeAttachments_FBOBinding, glrt->renderFBO()); - fHWBoundRenderTargetUniqueID = SK_InvalidUniqueID; - fStats.incRenderTargetBinds(); - GL_CALL(BindFramebuffer(GR_GL_FRAMEBUFFER, fbo)); - GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER, + GL_CALL(FramebufferRenderbuffer(fboTarget, GR_GL_STENCIL_ATTACHMENT, GR_GL_RENDERBUFFER, rb)); if (glsb->format().fPacked) { - GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER, + GL_CALL(FramebufferRenderbuffer(fboTarget, GR_GL_DEPTH_ATTACHMENT, GR_GL_RENDERBUFFER, rb)); } else { - GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER, + GL_CALL(FramebufferRenderbuffer(fboTarget, GR_GL_DEPTH_ATTACHMENT, GR_GL_RENDERBUFFER, 0)); } @@ -1291,13 +1294,13 @@ bool GrGLGpu::attachStencilBufferToRenderTarget(GrStencilBuffer* sb, GrRenderTar if (!this->glCaps().isColorConfigAndStencilFormatVerified(rt->config(), glsb->format())) { GL_CALL_RET(status, CheckFramebufferStatus(GR_GL_FRAMEBUFFER)); if (status != GR_GL_FRAMEBUFFER_COMPLETE) { - GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER, - GR_GL_STENCIL_ATTACHMENT, - GR_GL_RENDERBUFFER, 0)); + GL_CALL(FramebufferRenderbuffer(fboTarget, + GR_GL_STENCIL_ATTACHMENT, + GR_GL_RENDERBUFFER, 0)); if (glsb->format().fPacked) { - GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER, - GR_GL_DEPTH_ATTACHMENT, - GR_GL_RENDERBUFFER, 0)); + GL_CALL(FramebufferRenderbuffer(fboTarget, + GR_GL_DEPTH_ATTACHMENT, + GR_GL_RENDERBUFFER, 0)); } return false; } else { @@ -1429,20 +1432,23 @@ bool GrGLGpu::flushGLState(const DrawArgs& args) { fHWProgramID = programID; } - if (blendInfo.fWriteColor) { - this->flushBlend(blendInfo); - } fCurrentProgram->setData(*args.fPrimitiveProcessor, pipeline, *args.fBatchTracker); GrGLRenderTarget* glRT = static_cast(pipeline.getRenderTarget()); + this->flushStencil(pipeline.getStencil()); this->flushScissor(pipeline.getScissorState(), glRT->getViewport(), glRT->origin()); this->flushHWAAState(glRT, pipeline.isHWAntialiasState()); // This must come after textures are flushed because a texture may need - // to be msaa-resolved (which will modify bound FBO state). - this->flushRenderTarget(glRT, NULL); + // to be msaa-resolved (which will modify bound FBO and scissor state). + this->bindFBO(kDraw_FBOBinding, glRT->renderFBO()); + this->setViewport(glRT->getViewport()); + if (blendInfo.fWriteColor) { + this->flushBlend(blendInfo); + this->markSurfaceContentsDirty(glRT, NULL); + } return true; } @@ -1539,7 +1545,8 @@ void GrGLGpu::onClear(GrRenderTarget* target, const SkIRect* rect, GrColor color } } - this->flushRenderTarget(glRT, rect); + this->bindFBO(kClear_FBOBinding, glRT->renderFBO()); + this->markSurfaceContentsDirty(glRT, rect); GrScissorState scissorState; if (rect) { scissorState.set(*rect); @@ -1567,40 +1574,36 @@ void GrGLGpu::discard(GrRenderTarget* renderTarget) { } GrGLRenderTarget* glRT = static_cast(renderTarget); - if (renderTarget->getUniqueID() != fHWBoundRenderTargetUniqueID) { - fHWBoundRenderTargetUniqueID = SK_InvalidUniqueID; - fStats.incRenderTargetBinds(); - GL_CALL(BindFramebuffer(GR_GL_FRAMEBUFFER, glRT->renderFBOID())); - } + GrGLenum fboTarget = this->bindFBO(kDiscard_FBOBinding, glRT->renderFBO()); switch (this->glCaps().invalidateFBType()) { case GrGLCaps::kNone_InvalidateFBType: SkFAIL("Should never get here."); break; case GrGLCaps::kInvalidate_InvalidateFBType: - if (0 == glRT->renderFBOID()) { + if (glRT->renderFBO()->isDefaultFramebuffer()) { // When rendering to the default framebuffer the legal values for attachments // are GL_COLOR, GL_DEPTH, GL_STENCIL, ... rather than the various FBO attachment // types. static const GrGLenum attachments[] = { GR_GL_COLOR }; - GL_CALL(InvalidateFramebuffer(GR_GL_FRAMEBUFFER, SK_ARRAY_COUNT(attachments), + GL_CALL(InvalidateFramebuffer(fboTarget, SK_ARRAY_COUNT(attachments), attachments)); } else { static const GrGLenum attachments[] = { GR_GL_COLOR_ATTACHMENT0 }; - GL_CALL(InvalidateFramebuffer(GR_GL_FRAMEBUFFER, SK_ARRAY_COUNT(attachments), + GL_CALL(InvalidateFramebuffer(fboTarget, SK_ARRAY_COUNT(attachments), attachments)); } break; case GrGLCaps::kDiscard_InvalidateFBType: { - if (0 == glRT->renderFBOID()) { + if (glRT->renderFBO()->isDefaultFramebuffer()) { // When rendering to the default framebuffer the legal values for attachments // are GL_COLOR, GL_DEPTH, GL_STENCIL, ... rather than the various FBO attachment // types. See glDiscardFramebuffer() spec. static const GrGLenum attachments[] = { GR_GL_COLOR }; - GL_CALL(DiscardFramebuffer(GR_GL_FRAMEBUFFER, SK_ARRAY_COUNT(attachments), + GL_CALL(DiscardFramebuffer(fboTarget, SK_ARRAY_COUNT(attachments), attachments)); } else { static const GrGLenum attachments[] = { GR_GL_COLOR_ATTACHMENT0 }; - GL_CALL(DiscardFramebuffer(GR_GL_FRAMEBUFFER, SK_ARRAY_COUNT(attachments), + GL_CALL(DiscardFramebuffer(fboTarget, SK_ARRAY_COUNT(attachments), attachments)); } break; @@ -1615,7 +1618,7 @@ void GrGLGpu::clearStencil(GrRenderTarget* target) { return; } GrGLRenderTarget* glRT = static_cast(target); - this->flushRenderTarget(glRT, &SkIRect::EmptyIRect()); + this->bindFBO(kClear_FBOBinding, glRT->renderFBO()); this->disableScissor(); @@ -1651,7 +1654,7 @@ void GrGLGpu::onClearStencilClip(GrRenderTarget* target, const SkIRect& rect, bo value = 0; } GrGLRenderTarget* glRT = static_cast(target); - this->flushRenderTarget(glRT, &SkIRect::EmptyIRect()); + this->bindFBO(kClear_FBOBinding, glRT->renderFBO()); GrScissorState scissorState; scissorState.set(rect); @@ -1721,22 +1724,13 @@ bool GrGLGpu::onReadPixels(GrRenderTarget* target, // resolve the render target if necessary GrGLRenderTarget* tgt = static_cast(target); - switch (tgt->getResolveType()) { - case GrGLRenderTarget::kCantResolve_ResolveType: - return false; - case GrGLRenderTarget::kAutoResolves_ResolveType: - this->flushRenderTarget(static_cast(target), &SkIRect::EmptyIRect()); - break; - case GrGLRenderTarget::kCanResolve_ResolveType: - this->onResolveRenderTarget(tgt); - // we don't track the state of the READ FBO ID. - fStats.incRenderTargetBinds(); - GL_CALL(BindFramebuffer(GR_GL_READ_FRAMEBUFFER, - tgt->textureFBOID())); - break; - default: - SkFAIL("Unknown resolve type"); + if (tgt->getResolveType() == GrGLRenderTarget::kCantResolve_ResolveType) { + return false; + } + if (tgt->getResolveType() == GrGLRenderTarget::kCanResolve_ResolveType) { + this->onResolveRenderTarget(tgt); } + this->bindFBO(kReadPixels_FBOBinding, tgt->textureFBO()); const GrGLIRect& glvp = tgt->getViewport(); @@ -1801,7 +1795,8 @@ bool GrGLGpu::onReadPixels(GrRenderTarget* target, } } } else { - SkASSERT(readDst != buffer); SkASSERT(rowBytes != tightRowBytes); + SkASSERT(readDst != buffer); + SkASSERT(rowBytes != tightRowBytes); // copy from readDst to buffer while flipping y // const int halfY = height >> 1; const char* src = reinterpret_cast(readDst); @@ -1822,41 +1817,69 @@ bool GrGLGpu::onReadPixels(GrRenderTarget* target, return true; } -void GrGLGpu::flushRenderTarget(GrGLRenderTarget* target, const SkIRect* bound) { +GrGLenum GrGLGpu::bindFBO(FBOBinding binding, const GrGLFBO* fbo) { + SkASSERT(fbo); + SkASSERT(fbo->isValid()); + + enum { + kDraw = 0, + kRead = 1 + }; - SkASSERT(target); + bool useGLFramebuffer = !this->glCaps().usesMSAARenderBuffers() || + (this->glCaps().preferBindingToReadAndDrawFramebuffer() && + kBlitSrc_FBOBinding != binding && kBlitDst_FBOBinding != binding); - uint32_t rtID = target->getUniqueID(); - if (fHWBoundRenderTargetUniqueID != rtID) { + if (useGLFramebuffer) { + SkASSERT(kBlitSrc_FBOBinding != binding); fStats.incRenderTargetBinds(); - GL_CALL(BindFramebuffer(GR_GL_FRAMEBUFFER, target->renderFBOID())); -#ifdef SK_DEBUG - // don't do this check in Chromium -- this is causing - // lots of repeated command buffer flushes when the compositor is - // rendering with Ganesh, which is really slow; even too slow for - // Debug mode. - if (!this->glContext().isChromium()) { - GrGLenum status; - GL_CALL_RET(status, CheckFramebufferStatus(GR_GL_FRAMEBUFFER)); - if (status != GR_GL_FRAMEBUFFER_COMPLETE) { - SkDebugf("GrGLGpu::flushRenderTarget glCheckFramebufferStatus %x\n", status); - } - } -#endif - fHWBoundRenderTargetUniqueID = rtID; - const GrGLIRect& vp = target->getViewport(); - if (fHWViewport != vp) { - vp.pushToGLViewport(this->glInterface()); - fHWViewport = vp; - } + GL_CALL(BindFramebuffer(GR_GL_FRAMEBUFFER, fbo->fboID())); + fHWFBOBinding[kDraw].fFBO.reset(SkRef(fbo)); + fHWFBOBinding[kRead].fFBO.reset(SkRef(fbo)); + return GR_GL_FRAMEBUFFER; + } + GrGLenum target = 0; + HWFBOBinding* hwFBOBinding = NULL; + switch (binding) { + case kDraw_FBOBinding: + case kClear_FBOBinding: + case kDiscard_FBOBinding: + case kChangeAttachments_FBOBinding: + case kBlitDst_FBOBinding: + target = GR_GL_DRAW_FRAMEBUFFER; + hwFBOBinding = &fHWFBOBinding[kDraw]; + break; + + case kReadPixels_FBOBinding: + case kBlitSrc_FBOBinding: + case kCopyTexSrc_FBOBinding: + target = GR_GL_READ_FRAMEBUFFER; + hwFBOBinding = &fHWFBOBinding[kRead]; + break; } - if (NULL == bound || !bound->isEmpty()) { - target->flagAsNeedingResolve(bound); + fStats.incRenderTargetBinds(); + GL_CALL(BindFramebuffer(target, fbo->fboID())); + hwFBOBinding->fFBO.reset(SkRef(fbo)); + return target; +} + +void GrGLGpu::setViewport(const GrGLIRect& viewport) { + if (viewport != fHWViewport) { + viewport.pushToGLViewport(this->glInterface()); + fHWViewport = viewport; } +} - GrTexture *texture = target->asTexture(); - if (texture) { - texture->texturePriv().dirtyMipMaps(true); +void GrGLGpu::markSurfaceContentsDirty(GrSurface* surface, const SkIRect* bounds) { + if (NULL == bounds || !bounds->isEmpty()) { + GrGLRenderTarget* rt = static_cast(surface->asRenderTarget()); + if (rt) { + rt->flagAsNeedingResolve(bounds); + } + GrGLTexture* texture = static_cast(surface->asTexture()); + if (texture) { + texture->texturePriv().dirtyMipMaps(true); + } } } @@ -1938,8 +1961,8 @@ void GrGLGpu::onStencilPath(const GrPath* path, const StencilPathState& state) { this->glPathRendering()->setProjectionMatrix(*state.fViewMatrix, size, rt->origin()); this->flushScissor(*state.fScissor, rt->getViewport(), rt->origin()); this->flushHWAAState(rt, state.fUseHWAA); - this->flushRenderTarget(rt, NULL); - + this->bindFBO(kDraw_FBOBinding, rt->renderFBO()); + this->setViewport(rt->getViewport()); fPathRendering->stencilPath(path, *state.fStencil); } @@ -1971,14 +1994,9 @@ void GrGLGpu::onResolveRenderTarget(GrRenderTarget* target) { if (rt->needsResolve()) { // Some extensions automatically resolves the texture when it is read. if (this->glCaps().usesMSAARenderBuffers()) { - SkASSERT(rt->textureFBOID() != rt->renderFBOID()); - fStats.incRenderTargetBinds(); - fStats.incRenderTargetBinds(); - GL_CALL(BindFramebuffer(GR_GL_READ_FRAMEBUFFER, rt->renderFBOID())); - GL_CALL(BindFramebuffer(GR_GL_DRAW_FRAMEBUFFER, rt->textureFBOID())); - // make sure we go through flushRenderTarget() since we've modified - // the bound DRAW FBO ID. - fHWBoundRenderTargetUniqueID = SK_InvalidUniqueID; + SkASSERT(rt->textureFBO() != rt->renderFBO()); + this->bindFBO(kBlitSrc_FBOBinding, rt->renderFBO()); + this->bindFBO(kBlitDst_FBOBinding, rt->textureFBO()); const GrGLIRect& vp = rt->getViewport(); const SkIRect dirtyRect = rt->getResolveRect(); @@ -2532,13 +2550,13 @@ inline bool can_copy_texsubimage(const GrSurface* dst, 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 (dstRT && dstRT->renderFBOID() != dstRT->textureFBOID()) { + if (dstRT && dstRT->renderFBO() != dstRT->textureFBO()) { return false; } const GrGLRenderTarget* srcRT = static_cast(src->asRenderTarget()); // If the src is multisampled (and uses an extension where there is a separate MSAA // renderbuffer) then it is an invalid operation to call CopyTexSubImage - if (srcRT && srcRT->renderFBOID() != srcRT->textureFBOID()) { + if (srcRT && srcRT->renderFBO() != srcRT->textureFBO()) { return false; } if (gpu->glCaps().isConfigRenderable(src->config(), src->desc().fSampleCnt > 0) && @@ -2555,22 +2573,31 @@ inline bool can_copy_texsubimage(const GrSurface* dst, // If a temporary FBO was created, its non-zero ID is returned. The viewport that the copy rect is // relative to is output. -GrGLuint GrGLGpu::bindSurfaceAsFBO(GrSurface* surface, GrGLenum fboTarget, GrGLIRect* viewport, - TempFBOTarget tempFBOTarget) { +GrGLGpu::FBOBinding GrGLGpu::bindSurfaceAsFBOForCopy(GrSurface* surface, FBOBinding binding, + GrGLIRect* viewport) { GrGLRenderTarget* rt = static_cast(surface->asRenderTarget()); if (NULL == rt) { SkASSERT(surface->asTexture()); GrGLuint texID = static_cast(surface->asTexture())->textureID(); - GrGLuint* tempFBOID; - tempFBOID = kSrc_TempFBOTarget == tempFBOTarget ? &fTempSrcFBOID : &fTempDstFBOID; + GrGLFBO* tempFBO; - if (0 == *tempFBOID) { - GR_GL_CALL(this->glInterface(), GenFramebuffers(1, tempFBOID)); + if (kBlitSrc_FBOBinding == binding || kCopyTexSrc_FBOBinding == binding) { + if (!fTempSrcFBO) { + fTempSrcFBO.reset(SkNEW_ARGS(GrGLFBO, (this->glInterface()))); + SkASSERT(fTempSrcFBO->isValid()); + } + tempFBO = fTempSrcFBO; + } else { + SkASSERT(kBlitDst_FBOBinding == binding); + if (!fTempDstFBO) { + fTempDstFBO.reset(SkNEW_ARGS(GrGLFBO, (this->glInterface()))); + SkASSERT(fTempDstFBO->isValid()); + } + tempFBO = fTempDstFBO; } - fStats.incRenderTargetBinds(); - GR_GL_CALL(this->glInterface(), BindFramebuffer(fboTarget, *tempFBOID)); - GR_GL_CALL(this->glInterface(), FramebufferTexture2D(fboTarget, + GrGLenum target = this->bindFBO(binding, tempFBO); + GR_GL_CALL(this->glInterface(), FramebufferTexture2D(target, GR_GL_COLOR_ATTACHMENT0, GR_GL_TEXTURE_2D, texID, @@ -2579,18 +2606,21 @@ GrGLuint GrGLGpu::bindSurfaceAsFBO(GrSurface* surface, GrGLenum fboTarget, GrGLI viewport->fBottom = 0; viewport->fWidth = surface->width(); viewport->fHeight = surface->height(); - return *tempFBOID; + return binding; } else { - GrGLuint tempFBOID = 0; - fStats.incRenderTargetBinds(); - GR_GL_CALL(this->glInterface(), BindFramebuffer(fboTarget, rt->renderFBOID())); + this->bindFBO(binding, rt->renderFBO()); *viewport = rt->getViewport(); - return tempFBOID; + return kInvalidFBOBinding; } } -void GrGLGpu::unbindTextureFromFBO(GrGLenum fboTarget) { - GR_GL_CALL(this->glInterface(), FramebufferTexture2D(fboTarget, +void GrGLGpu::unbindSurfaceAsFBOForCopy(FBOBinding binding) { + if (kInvalidFBOBinding == binding) { + return; + } + GrGLFBO* tempFBO = kBlitDst_FBOBinding == binding ? fTempSrcFBO : fTempDstFBO; + GrGLenum target = this->bindFBO(binding, tempFBO); + GR_GL_CALL(this->glInterface(), FramebufferTexture2D(target, GR_GL_COLOR_ATTACHMENT0, GR_GL_TEXTURE_2D, 0, @@ -2621,7 +2651,7 @@ bool GrGLGpu::initCopySurfaceDstDesc(const GrSurface* src, GrSurfaceDesc* desc) } const GrGLRenderTarget* srcRT = static_cast(src->asRenderTarget()); - if (srcRT && srcRT->renderFBOID() != srcRT->textureFBOID()) { + if (srcRT && srcRT->renderFBO() != srcRT->textureFBO()) { // It's illegal to call CopyTexSubImage2D on a MSAA renderbuffer. Set up for FBO blit or // fail. if (this->caps()->isConfigRenderable(src->config(), false)) { @@ -2645,14 +2675,14 @@ bool GrGLGpu::copySurface(GrSurface* dst, const SkIRect& srcRect, const SkIPoint& dstPoint) { bool copied = false; + SkIRect dstRect = SkIRect::MakeXYWH(dstPoint.fX, dstPoint.fY, + srcRect.width(), srcRect.height()); if (can_copy_texsubimage(dst, src, this)) { - GrGLuint srcFBO; GrGLIRect srcVP; - srcFBO = this->bindSurfaceAsFBO(src, GR_GL_FRAMEBUFFER, &srcVP, kSrc_TempFBOTarget); + FBOBinding srcFBOBinding = this->bindSurfaceAsFBOForCopy(src, kCopyTexSrc_FBOBinding, + &srcVP); GrGLTexture* dstTex = static_cast(dst->asTexture()); SkASSERT(dstTex); - // We modified the bound FBO - fHWBoundRenderTargetUniqueID = SK_InvalidUniqueID; GrGLIRect srcGLRect; srcGLRect.setRelativeTo(srcVP, srcRect.fLeft, @@ -2674,28 +2704,21 @@ bool GrGLGpu::copySurface(GrSurface* dst, srcGLRect.fLeft, srcGLRect.fBottom, srcGLRect.fWidth, srcGLRect.fHeight)); copied = true; - if (srcFBO) { - this->unbindTextureFromFBO(GR_GL_FRAMEBUFFER); - } + this->unbindSurfaceAsFBOForCopy(srcFBOBinding); } else if (can_blit_framebuffer(dst, src, this)) { - SkIRect dstRect = SkIRect::MakeXYWH(dstPoint.fX, dstPoint.fY, - srcRect.width(), srcRect.height()); bool selfOverlap = false; if (dst == src) { selfOverlap = SkIRect::IntersectsNoEmptyCheck(dstRect, srcRect); } if (!selfOverlap) { - GrGLuint dstFBO; - GrGLuint srcFBO; GrGLIRect dstVP; GrGLIRect srcVP; - dstFBO = this->bindSurfaceAsFBO(dst, GR_GL_DRAW_FRAMEBUFFER, &dstVP, - kDst_TempFBOTarget); - srcFBO = this->bindSurfaceAsFBO(src, GR_GL_READ_FRAMEBUFFER, &srcVP, - kSrc_TempFBOTarget); - // We modified the bound FBO - fHWBoundRenderTargetUniqueID = SK_InvalidUniqueID; + FBOBinding dstFBOBinding = this->bindSurfaceAsFBOForCopy(dst, kBlitDst_FBOBinding, + &dstVP); + FBOBinding srcFBOBinding = this->bindSurfaceAsFBOForCopy(src, kBlitSrc_FBOBinding, + &srcVP); + GrGLIRect srcGLRect; GrGLIRect dstGLRect; srcGLRect.setRelativeTo(srcVP, @@ -2733,15 +2756,14 @@ bool GrGLGpu::copySurface(GrSurface* dst, dstGLRect.fLeft + dstGLRect.fWidth, dstGLRect.fBottom + dstGLRect.fHeight, GR_GL_COLOR_BUFFER_BIT, GR_GL_NEAREST)); - if (dstFBO) { - this->unbindTextureFromFBO(GR_GL_DRAW_FRAMEBUFFER); - } - if (srcFBO) { - this->unbindTextureFromFBO(GR_GL_READ_FRAMEBUFFER); - } + this->unbindSurfaceAsFBOForCopy(dstFBOBinding); + this->unbindSurfaceAsFBOForCopy(srcFBOBinding); copied = true; } } + if (copied) { + this->markSurfaceContentsDirty(dst, &dstRect); + } return copied; } diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h index c9e280c3cb..6bd8e4db66 100644 --- a/src/gpu/gl/GrGLGpu.h +++ b/src/gpu/gl/GrGLGpu.h @@ -244,11 +244,29 @@ private: // ensures that such operations don't negatively interact with tracking bound textures. void setScratchTextureUnit(); - // bounds is region that may be modified and therefore has to be resolved. - // NULL means whole target. Can be an empty rect. - void flushRenderTarget(GrGLRenderTarget*, const SkIRect* bounds); + // Enumerates the reasons for binding an FBO. + enum FBOBinding { + kDraw_FBOBinding, + kClear_FBOBinding, + kDiscard_FBOBinding, + kChangeAttachments_FBOBinding, + kReadPixels_FBOBinding, + kCopyTexSrc_FBOBinding, + kBlitSrc_FBOBinding, + kBlitDst_FBOBinding, + }; + + // binds the FBO and returns the GL enum of the framebuffer target it was bound to. + GrGLenum bindFBO(FBOBinding, const GrGLFBO*); + + // Tracks dirty area for resolve, and tracks whether mip maps need rebuilding. bounds is the + // region that may be modified. NULL means whole surface. Can be an empty rect. + void markSurfaceContentsDirty(GrSurface*, const SkIRect* bounds); + + void setViewport(const GrGLIRect& viewport); void flushStencil(const GrStencilSettings&); + void flushHWAAState(GrRenderTarget* rt, bool useHWAA); bool configToGLFormats(GrPixelConfig config, @@ -279,15 +297,14 @@ private: bool createRenderTargetObjects(const GrSurfaceDesc&, bool budgeted, GrGLuint texID, GrGLRenderTarget::IDDesc*); - enum TempFBOTarget { - kSrc_TempFBOTarget, - kDst_TempFBOTarget - }; + static const FBOBinding kInvalidFBOBinding = static_cast(-1); - GrGLuint bindSurfaceAsFBO(GrSurface* surface, GrGLenum fboTarget, GrGLIRect* viewport, - TempFBOTarget tempFBOTarget); + // Binds a surface as an FBO. A temporary FBO ID may be used if the surface is not already + // a render target. Afterwards unbindSurfaceAsFBOForCopy must be called with the value returned. + FBOBinding bindSurfaceAsFBOForCopy(GrSurface*, FBOBinding, GrGLIRect* viewport); - void unbindTextureFromFBO(GrGLenum fboTarget); + // Must be matched with bindSurfaceAsFBOForCopy. + void unbindSurfaceAsFBOForCopy(FBOBinding); GrGLContext fGLContext; @@ -307,10 +324,9 @@ private: kUnknown_TriState }; - GrGLuint fTempSrcFBOID; - GrGLuint fTempDstFBOID; - - GrGLuint fStencilClearFBOID; + SkAutoTUnref fTempSrcFBO; + SkAutoTUnref fTempDstFBO; + SkAutoTUnref fStencilClearFBO; // last scissor / viewport scissor state seen by the GL. struct { @@ -456,9 +472,14 @@ private: GrPipelineBuilder::DrawFace fHWDrawFace; TriState fHWWriteToColor; TriState fHWDitherEnabled; - uint32_t fHWBoundRenderTargetUniqueID; SkTArray fHWBoundTextureUniqueIDs; + // Track fbo binding state for GL_DRAW_FRAMEBUFFER and GL_READ_FRAMEBUFFER + struct HWFBOBinding { + SkAutoTUnref fFBO; + void invalidate() { fFBO.reset(NULL); } + } fHWFBOBinding[2]; + ///@} // we record what stencil format worked last time to hopefully exit early diff --git a/src/gpu/gl/GrGLNoOpInterface.cpp b/src/gpu/gl/GrGLNoOpInterface.cpp index 6b8880ed81..38ad65e5c1 100644 --- a/src/gpu/gl/GrGLNoOpInterface.cpp +++ b/src/gpu/gl/GrGLNoOpInterface.cpp @@ -440,7 +440,8 @@ GrGLvoid GR_GL_FUNCTION_TYPE noOpGLBindFragDataLocationIndexed(GrGLuint program, GrGLenum GR_GL_FUNCTION_TYPE noOpGLCheckFramebufferStatus(GrGLenum target) { - GrAlwaysAssert(GR_GL_FRAMEBUFFER == target); + GrAlwaysAssert(GR_GL_FRAMEBUFFER == target || GR_GL_READ_FRAMEBUFFER == target || + GR_GL_DRAW_FRAMEBUFFER == target); return GR_GL_FRAMEBUFFER_COMPLETE; } diff --git a/src/gpu/gl/GrGLRenderTarget.cpp b/src/gpu/gl/GrGLRenderTarget.cpp index 3eb2ae09fe..8b9bb98b6b 100644 --- a/src/gpu/gl/GrGLRenderTarget.cpp +++ b/src/gpu/gl/GrGLRenderTarget.cpp @@ -9,8 +9,20 @@ #include "GrGLGpu.h" -#define GPUGL static_cast(this->getGpu()) -#define GL_CALL(X) GR_GL_CALL(GPUGL->glInterface(), X) +void GrGLFBO::release(const GrGLInterface* gl) { + SkASSERT(gl); + if (this->isValid()) { + GR_GL_CALL(gl, DeleteFramebuffers(1, &fID)); + fIsValid = false; + } +} + +void GrGLFBO::abandon() { fIsValid = false; } + +////////////////////////////////////////////////////////////////////////////// + +#define GLGPU static_cast(this->getGpu()) +#define GL_CALL(X) GR_GL_CALL(GLGPU->glInterface(), X) // Because this class is virtually derived from GrSurface we must explicitly call its constructor. GrGLRenderTarget::GrGLRenderTarget(GrGLGpu* gpu, const GrSurfaceDesc& desc, const IDDesc& idDesc) @@ -28,8 +40,10 @@ GrGLRenderTarget::GrGLRenderTarget(GrGLGpu* gpu, const GrSurfaceDesc& desc, cons } void GrGLRenderTarget::init(const GrSurfaceDesc& desc, const IDDesc& idDesc) { - fRTFBOID = idDesc.fRTFBOID; - fTexFBOID = idDesc.fTexFBOID; + fRenderFBO.reset(SkRef(idDesc.fRenderFBO.get())); + fTextureFBO.reset(SkSafeRef(idDesc.fTextureFBO.get())); + SkASSERT(fRenderFBO->isValid()); + SkASSERT(!fTextureFBO || fTextureFBO->isValid()); fMSColorRenderbufferID = idDesc.fMSColorRenderbufferID; fIsWrapped = kWrapped_LifeCycle == idDesc.fLifeCycle; @@ -40,7 +54,7 @@ void GrGLRenderTarget::init(const GrSurfaceDesc& desc, const IDDesc& idDesc) { // We own one color value for each MSAA sample. fColorValuesPerPixel = SkTMax(1, fDesc.fSampleCnt); - if (fTexFBOID != fRTFBOID) { + if (fTextureFBO && fTextureFBO != fRenderFBO) { // If we own the resolve buffer then that is one more sample per pixel. fColorValuesPerPixel += 1; } @@ -56,27 +70,42 @@ size_t GrGLRenderTarget::onGpuMemorySize() const { void GrGLRenderTarget::onRelease() { if (!fIsWrapped) { - if (fTexFBOID) { - GL_CALL(DeleteFramebuffers(1, &fTexFBOID)); + const GrGLInterface* gl = GLGPU->glInterface(); + if (fRenderFBO) { + fRenderFBO->release(gl); + fRenderFBO.reset(NULL); } - if (fRTFBOID && fRTFBOID != fTexFBOID) { - GL_CALL(DeleteFramebuffers(1, &fRTFBOID)); + if (fTextureFBO) { + fTextureFBO->release(gl); + fTextureFBO.reset(NULL); } if (fMSColorRenderbufferID) { GL_CALL(DeleteRenderbuffers(1, &fMSColorRenderbufferID)); + fMSColorRenderbufferID = 0; } + } else { + if (fRenderFBO) { + fRenderFBO->abandon(); + fRenderFBO.reset(NULL); + } + if (fTextureFBO) { + fTextureFBO->abandon(); + fTextureFBO.reset(NULL); + } + fMSColorRenderbufferID = 0; } - fRTFBOID = 0; - fTexFBOID = 0; - fMSColorRenderbufferID = 0; - fIsWrapped = false; INHERITED::onRelease(); } void GrGLRenderTarget::onAbandon() { - fRTFBOID = 0; - fTexFBOID = 0; + if (fRenderFBO) { + fRenderFBO->abandon(); + fRenderFBO.reset(NULL); + } + if (fTextureFBO) { + fTextureFBO->abandon(); + fTextureFBO.reset(NULL); + } fMSColorRenderbufferID = 0; - fIsWrapped = false; INHERITED::onAbandon(); } diff --git a/src/gpu/gl/GrGLRenderTarget.h b/src/gpu/gl/GrGLRenderTarget.h index 7e7349257f..485ae4a791 100644 --- a/src/gpu/gl/GrGLRenderTarget.h +++ b/src/gpu/gl/GrGLRenderTarget.h @@ -15,15 +15,59 @@ class GrGLGpu; -class GrGLRenderTarget : public GrRenderTarget { +/** Represents a GL FBO object. It has a gen ID which is valid whenever the FBO ID owned by the + object is valid. The gen IDs are not recycled after FBOs are freed, unlike FBO IDs, and so + can be used to uniquely identity FBO ID instantiations. If this object owns an FBO ID, the ID + must be deleted or abandoned before this object is freed. FBO IDs should never be owned by + more than one instance. */ +class GrGLFBO : public SkNVRefCnt { public: - // set fTexFBOID to this value to indicate that it is multisampled but - // Gr doesn't know how to resolve it. - enum { kUnresolvableFBOID = 0 }; + SK_DECLARE_INST_COUNT(GrGLFBO); + + /** Initializes to an FBO. The FBO should already be valid in the relevant GL context. */ + GrGLFBO(GrGLint id) : fID(id), fIsValid(true) {} + + /** Initializes to an FBO ID generated using the interface. */ + GrGLFBO(const GrGLInterface* gl) { + GR_GL_CALL(gl, GenFramebuffers(1, &fID)); + fIsValid = SkToBool(fID); + } + + ~GrGLFBO() { SkASSERT(!this->isValid()); } + + /** Has this object been released or abandoned? */ + bool isValid() const { return fIsValid; } + + GrGLint fboID() const { SkASSERT(this->isValid()); return fID; } + + bool isDefaultFramebuffer() const { return fIsValid && 0 == fID; } + + /** Give up ownership of the FBO ID owned by this object without deleting it. */ + void abandon(); + + /** Delete and give up ownership of the the FBO ID if it is valid. */ + void release(const GrGLInterface*); + +private: + static uint32_t NextGenID() { + static int32_t gGenID = SK_InvalidGenID + 1; + return static_cast(sk_atomic_inc(&gGenID)); + } + + GrGLuint fID; + bool fIsValid; + + typedef SkRefCnt INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// +/** GL-specific subclass of GrRenderTarget. */ +class GrGLRenderTarget : public GrRenderTarget { +public: struct IDDesc { - GrGLuint fRTFBOID; - GrGLuint fTexFBOID; + SkAutoTUnref fRenderFBO; + SkAutoTUnref fTextureFBO; GrGLuint fMSColorRenderbufferID; GrGpuResource::LifeCycle fLifeCycle; }; @@ -33,21 +77,33 @@ public: void setViewport(const GrGLIRect& rect) { fViewport = rect; } const GrGLIRect& getViewport() const { return fViewport; } - // The following two functions return the same ID when a - // texture/render target is multisampled, and different IDs when - // it is. - // FBO ID used to render into - GrGLuint renderFBOID() const { return fRTFBOID; } - // FBO ID that has texture ID attached. - GrGLuint textureFBOID() const { return fTexFBOID; } + // For multisampled renderbuffer render targets, these will return different GrGLFBO objects. If + // the render target is not texturable, textureFBO() returns NULL. If the render target auto + // resolves to a texture, the same object is returned. + + // FBO that should be rendered into. Always non-NULL unless this resource is destroyed + // (this->wasDestroyed()). + const GrGLFBO* renderFBO() const { + SkASSERT(fRenderFBO && fRenderFBO->isValid()); + return fRenderFBO; + } + + // FBO that has the target's texture ID attached. The return value may be: + // * NULL when this render target is not a texture, + // * the same as renderFBO() when this surface is not multisampled or auto-resolves, + // * or different than renderFBO() when it requires explicit resolving via + // glBlitFramebuffer. + const GrGLFBO* textureFBO() const { + SkASSERT(!fTextureFBO || fTextureFBO->isValid()); + return fTextureFBO; + } // override of GrRenderTarget ResolveType getResolveType() const SK_OVERRIDE { - if (!this->isMultisampled() || - fRTFBOID == fTexFBOID) { + if (!this->isMultisampled() || this->renderFBO() == this->textureFBO()) { // catches FBO 0 and non MSAA case return kAutoResolves_ResolveType; - } else if (kUnresolvableFBOID == fTexFBOID) { + } else if (!this->textureFBO()) { return kCantResolve_ResolveType; } else { return kCanResolve_ResolveType; @@ -73,23 +129,23 @@ protected: size_t onGpuMemorySize() const SK_OVERRIDE; private: - GrGLuint fRTFBOID; - GrGLuint fTexFBOID; - GrGLuint fMSColorRenderbufferID; + SkAutoTUnref fRenderFBO; + SkAutoTUnref fTextureFBO; + GrGLuint fMSColorRenderbufferID; // We track this separately from GrGpuResource because this may be both a texture and a render // target, and the texture may be wrapped while the render target is not. - bool fIsWrapped; + bool fIsWrapped; // when we switch to this render target we want to set the viewport to // only render to content area (as opposed to the whole allocation) and // we want the rendering to be at top left (GL has origin in bottom left) - GrGLIRect fViewport; + GrGLIRect fViewport; // onGpuMemorySize() needs to know what how many color values are owned per pixel. However, // abandon and release zero out the IDs and the cache needs to know the size even after those // actions. - uint8_t fColorValuesPerPixel; + uint8_t fColorValuesPerPixel; typedef GrRenderTarget INHERITED; }; diff --git a/src/gpu/gl/debug/GrGLCreateDebugInterface.cpp b/src/gpu/gl/debug/GrGLCreateDebugInterface.cpp index 77a1422af1..b4643cc6d7 100644 --- a/src/gpu/gl/debug/GrGLCreateDebugInterface.cpp +++ b/src/gpu/gl/debug/GrGLCreateDebugInterface.cpp @@ -382,7 +382,8 @@ GrGLvoid GR_GL_FUNCTION_TYPE debugGLReadPixels(GrGLint x, GrGLenum renderbuffertarget, GrGLuint renderBufferID) { - GrAlwaysAssert(GR_GL_FRAMEBUFFER == target); + GrAlwaysAssert(GR_GL_FRAMEBUFFER == target || GR_GL_READ_FRAMEBUFFER == target || + GR_GL_DRAW_FRAMEBUFFER == target); GrAlwaysAssert(GR_GL_COLOR_ATTACHMENT0 == attachment || GR_GL_DEPTH_ATTACHMENT == attachment || GR_GL_STENCIL_ATTACHMENT == attachment); @@ -422,7 +423,8 @@ GrGLvoid GR_GL_FUNCTION_TYPE debugGLReadPixels(GrGLint x, GrGLuint textureID, GrGLint level) { - GrAlwaysAssert(GR_GL_FRAMEBUFFER == target); + GrAlwaysAssert(GR_GL_FRAMEBUFFER == target || GR_GL_READ_FRAMEBUFFER == target || + GR_GL_DRAW_FRAMEBUFFER == target); GrAlwaysAssert(GR_GL_COLOR_ATTACHMENT0 == attachment || GR_GL_DEPTH_ATTACHMENT == attachment || GR_GL_STENCIL_ATTACHMENT == attachment); -- 2.34.1