summaryrefslogtreecommitdiff
path: root/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
diff options
context:
space:
mode:
authorDerek Sollenberger <djsollen@google.com>2018-05-30 18:08:57 -0400
committerDerek Sollenberger <djsollen@google.com>2018-05-31 15:55:13 -0400
commit02456f0ce16b4d3eb0c2709ca101d15d4d97faed (patch)
treec0f2050e7c3d324afff0bebf3ab98ed23905de1e /libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
parent4769f85fad6b61ae1842ef02fa4970aea9485673 (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.cpp137
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