diff options
| -rw-r--r-- | libs/hwui/Android.bp | 1 | ||||
| -rw-r--r-- | libs/hwui/pipeline/skia/GLFunctorDrawable.cpp | 137 | 
2 files changed, 119 insertions, 19 deletions
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 2454ae3d9f15..0db779906545 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -41,6 +41,7 @@ cc_defaults {          "external/skia/src/image",          "external/skia/src/utils",          "external/skia/src/gpu", +        "external/skia/src/shaders",      ],      product_variables: { 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  |