diff options
author | Derek Sollenberger <djsollen@google.com> | 2018-05-30 18:08:57 -0400 |
---|---|---|
committer | Derek Sollenberger <djsollen@google.com> | 2018-05-31 15:55:13 -0400 |
commit | 02456f0ce16b4d3eb0c2709ca101d15d4d97faed (patch) | |
tree | c0f2050e7c3d324afff0bebf3ab98ed23905de1e /libs/hwui/pipeline/skia/GLFunctorDrawable.cpp | |
parent | 4769f85fad6b61ae1842ef02fa4970aea9485673 (diff) |
Bind correct FBO when drawing a WebView into a layer.
The WebView was unable to draw into either a standard clipped
layer or the "fading edges" unclipped layer. This CL and its
companion test cases ensure that both work with simple and
complex clips.
Bug: 79619253
Bug: 80443556
Bug: 80477645
Test: atest CtsUiRenderingTestCases:.LayerTests
Change-Id: I0e16b724f74415a61cc2a841ccf4a491f293ac94
Diffstat (limited to 'libs/hwui/pipeline/skia/GLFunctorDrawable.cpp')
-rw-r--r-- | libs/hwui/pipeline/skia/GLFunctorDrawable.cpp | 137 |
1 files changed, 118 insertions, 19 deletions
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp index 08f8da89758e..b0fec7a70699 100644 --- a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp +++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp @@ -22,6 +22,10 @@ #include "RenderNode.h" #include "SkAndroidFrameworkUtils.h" #include "SkClipStack.h" +#include "SkRect.h" +#include "GrBackendSurface.h" +#include "GrRenderTarget.h" +#include "GrRenderTargetContext.h" namespace android { namespace uirenderer { @@ -45,6 +49,31 @@ static void setScissor(int viewportHeight, const SkIRect& clip) { glScissor(clip.fLeft, y, clip.width(), height); } +static bool GetFboDetails(SkCanvas* canvas, GLuint* outFboID, SkISize* outFboSize) { + GrRenderTargetContext *renderTargetContext = + canvas->internal_private_accessTopLayerRenderTargetContext(); + if (!renderTargetContext) { + ALOGW("Unable to extract renderTarget info from canvas; aborting GLFunctor draw"); + return false; + } + + GrRenderTarget *renderTarget = renderTargetContext->accessRenderTarget(); + if (!renderTarget) { + ALOGW("Unable to extract renderTarget info from canvas; aborting GLFunctor draw"); + return false; + } + + GrGLFramebufferInfo fboInfo; + if (!renderTarget->getBackendRenderTarget().getGLFramebufferInfo(&fboInfo)) { + ALOGW("Unable to extract renderTarget info from canvas; aborting GLFunctor draw"); + return false; + } + + *outFboID = fboInfo.fFBOID; + *outFboSize = SkISize::Make(renderTargetContext->width(), renderTargetContext->height()); + return true; +} + void GLFunctorDrawable::onDraw(SkCanvas* canvas) { if (canvas->getGrContext() == nullptr) { SkDEBUGF(("Attempting to draw GLFunctor into an unsupported surface")); @@ -56,41 +85,97 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) { return; } - SkImageInfo canvasInfo = canvas->imageInfo(); + GLuint fboID = 0; + SkISize fboSize; + if (!GetFboDetails(canvas, &fboID, &fboSize)) { + return; + } + + SkIRect surfaceBounds = canvas->internal_private_getTopLayerBounds(); + SkIRect clipBounds = canvas->getDeviceClipBounds(); SkMatrix44 mat4(canvas->getTotalMatrix()); + SkRegion clipRegion; + canvas->temporary_internal_getRgnClip(&clipRegion); - SkIRect ibounds = canvas->getDeviceClipBounds(); + sk_sp<SkSurface> tmpSurface; + // we are in a state where there is an unclipped saveLayer + if (fboID != 0 && !surfaceBounds.contains(clipBounds)) { + + // create an offscreen layer and clear it + SkImageInfo surfaceInfo = canvas->imageInfo().makeWH(clipBounds.width(), clipBounds.height()); + tmpSurface = SkSurface::MakeRenderTarget(canvas->getGrContext(), SkBudgeted::kYes, + surfaceInfo); + tmpSurface->getCanvas()->clear(SK_ColorTRANSPARENT); + + GrGLFramebufferInfo fboInfo; + if (!tmpSurface->getBackendRenderTarget(SkSurface::kFlushWrite_BackendHandleAccess) + .getGLFramebufferInfo(&fboInfo)) { + ALOGW("Unable to extract renderTarget info from offscreen canvas; aborting GLFunctor"); + return; + } + + fboSize = SkISize::Make(surfaceInfo.width(), surfaceInfo.height()); + fboID = fboInfo.fFBOID; + + // update the matrix and clip that we pass to the WebView to match the coordinates of + // the offscreen layer + mat4.preTranslate(-clipBounds.fLeft, -clipBounds.fTop, 0); + clipBounds.offsetTo(0, 0); + clipRegion.translate(-surfaceBounds.fLeft, -surfaceBounds.fTop); + + } else if (fboID != 0) { + // we are drawing into a (clipped) offscreen layer so we must update the clip and matrix + // from device coordinates to the layer's coordinates + clipBounds.offset(-surfaceBounds.fLeft, -surfaceBounds.fTop); + mat4.preTranslate(-surfaceBounds.fLeft, -surfaceBounds.fTop, 0); + } DrawGlInfo info; - info.clipLeft = ibounds.fLeft; - info.clipTop = ibounds.fTop; - info.clipRight = ibounds.fRight; - info.clipBottom = ibounds.fBottom; - // info.isLayer = hasLayer(); - info.isLayer = false; - info.width = canvasInfo.width(); - info.height = canvasInfo.height(); + info.clipLeft = clipBounds.fLeft; + info.clipTop = clipBounds.fTop; + info.clipRight = clipBounds.fRight; + info.clipBottom = clipBounds.fBottom; + info.isLayer = fboID != 0; + info.width = fboSize.width(); + info.height = fboSize.height(); mat4.asColMajorf(&info.transform[0]); - bool clearStencilAfterFunctor = false; - - // apply a simple clip with a scissor or a complex clip with a stencil - SkRegion clipRegion; - canvas->temporary_internal_getRgnClip(&clipRegion); + // ensure that the framebuffer that the webview will render into is bound before we clear + // the stencil and/or draw the functor. canvas->flush(); - glBindFramebuffer(GL_FRAMEBUFFER, 0); glViewport(0, 0, info.width, info.height); + glBindFramebuffer(GL_FRAMEBUFFER, fboID); + + // apply a simple clip with a scissor or a complex clip with a stencil + bool clearStencilAfterFunctor = false; if (CC_UNLIKELY(clipRegion.isComplex())) { + // clear the stencil //TODO: move stencil clear and canvas flush to SkAndroidFrameworkUtils::clipWithStencil glDisable(GL_SCISSOR_TEST); glStencilMask(0x1); glClearStencil(0); glClear(GL_STENCIL_BUFFER_BIT); + + // notify Skia that we just updated the FBO and stencil + const uint32_t grState = kStencil_GrGLBackendState | kRenderTarget_GrGLBackendState; + canvas->getGrContext()->resetContext(grState); + + SkCanvas* tmpCanvas = canvas; + if (tmpSurface) { + tmpCanvas = tmpSurface->getCanvas(); + // set the clip on the new canvas + tmpCanvas->clipRegion(clipRegion); + } + // GL ops get inserted here if previous flush is missing, which could dirty the stencil - bool stencilWritten = SkAndroidFrameworkUtils::clipWithStencil(canvas); - canvas->flush(); //need this flush for the single op that draws into the stencil - glBindFramebuffer(GL_FRAMEBUFFER, 0); + bool stencilWritten = SkAndroidFrameworkUtils::clipWithStencil(tmpCanvas); + tmpCanvas->flush(); //need this flush for the single op that draws into the stencil + + // ensure that the framebuffer that the webview will render into is bound before after we + // draw into the stencil glViewport(0, 0, info.width, info.height); + glBindFramebuffer(GL_FRAMEBUFFER, fboID); + if (stencilWritten) { glStencilMask(0x1); glStencilFunc(GL_EQUAL, 0x1, 0x1); @@ -121,6 +206,20 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) { } canvas->getGrContext()->resetContext(); + + // if there were unclipped save layers involved we draw our offscreen surface to the canvas + if (tmpSurface) { + SkAutoCanvasRestore acr(canvas, true); + SkMatrix invertedMatrix; + if (!canvas->getTotalMatrix().invert(&invertedMatrix)) { + ALOGW("Unable to extract invert canvas matrix; aborting GLFunctor draw"); + return; + } + canvas->concat(invertedMatrix); + + const SkIRect deviceBounds = canvas->getDeviceClipBounds(); + tmpSurface->draw(canvas, deviceBounds.fLeft, deviceBounds.fTop, nullptr); + } } }; // namespace skiapipeline |