Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6acb4efc authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Support Surface and Layer Readback in the SkiaPipelines."

parents fb3d63ee c4fbada7
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ hwui_src_files := \
    pipeline/skia/ReorderBarrierDrawables.cpp \
    pipeline/skia/SkiaDisplayList.cpp \
    pipeline/skia/SkiaOpenGLPipeline.cpp \
    pipeline/skia/SkiaOpenGLReadback.cpp \
    pipeline/skia/SkiaPipeline.cpp \
    pipeline/skia/SkiaProfileRenderer.cpp \
    pipeline/skia/SkiaRecordingCanvas.cpp \
@@ -84,6 +85,7 @@ hwui_src_files := \
    LayerUpdateQueue.cpp \
    Matrix.cpp \
    OpDumper.cpp \
    OpenGLReadback.cpp \
    Patch.cpp \
    PatchCache.cpp \
    PathCache.cpp \
@@ -96,7 +98,6 @@ hwui_src_files := \
    Properties.cpp \
    PropertyValuesAnimatorSet.cpp \
    PropertyValuesHolder.cpp \
    Readback.cpp \
    RecordingCanvas.cpp \
    RenderBufferCache.cpp \
    RenderNode.cpp \
+77 −64
Original line number Diff line number Diff line
@@ -14,7 +14,7 @@
 * limitations under the License.
 */

#include "Readback.h"
#include "OpenGLReadback.h"

#include "Caches.h"
#include "Image.h"
@@ -31,8 +31,69 @@
namespace android {
namespace uirenderer {

static CopyResult copyTextureInto(Caches& caches, RenderState& renderState,
        Texture& sourceTexture, Matrix4& texTransform, const Rect& srcRect,
CopyResult OpenGLReadback::copySurfaceInto(Surface& surface, const Rect& srcRect,
        SkBitmap* bitmap) {
    ATRACE_CALL();
    mRenderThread.eglManager().initialize();

    // Setup the source
    sp<GraphicBuffer> sourceBuffer;
    sp<Fence> sourceFence;
    Matrix4 texTransform;
    status_t err = surface.getLastQueuedBuffer(&sourceBuffer, &sourceFence,
            texTransform.data);
    texTransform.invalidateType();
    if (err != NO_ERROR) {
        ALOGW("Failed to get last queued buffer, error = %d", err);
        return CopyResult::UnknownError;
    }
    if (!sourceBuffer.get()) {
        ALOGW("Surface doesn't have any previously queued frames, nothing to readback from");
        return CopyResult::SourceEmpty;
    }
    if (sourceBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) {
        ALOGW("Surface is protected, unable to copy from it");
        return CopyResult::SourceInvalid;
    }
    err = sourceFence->wait(500 /* ms */);
    if (err != NO_ERROR) {
        ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt");
        return CopyResult::Timeout;
    }

    // TODO: Can't use Image helper since it forces GL_TEXTURE_2D usage via
    // GL_OES_EGL_image, which doesn't work since we need samplerExternalOES
    // to be able to properly sample from the buffer.

    // Create the EGLImage object that maps the GraphicBuffer
    EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    EGLClientBuffer clientBuffer = (EGLClientBuffer) sourceBuffer->getNativeBuffer();
    EGLint attrs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE };

    EGLImageKHR sourceImage = eglCreateImageKHR(display, EGL_NO_CONTEXT,
            EGL_NATIVE_BUFFER_ANDROID, clientBuffer, attrs);

    if (sourceImage == EGL_NO_IMAGE_KHR) {
        ALOGW("eglCreateImageKHR failed (%#x)", eglGetError());
        return CopyResult::UnknownError;
    }

    CopyResult copyResult = copyImageInto(sourceImage, texTransform, sourceBuffer->getWidth(),
            sourceBuffer->getHeight(), srcRect, bitmap);

    // All we're flushing & finishing is the deletion of the texture since
    // copyImageInto already did a major flush & finish as an implicit
    // part of glReadPixels, so this shouldn't pose any major stalls.
    glFinish();
    eglDestroyImageKHR(display, sourceImage);
    return copyResult;
}

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

inline CopyResult copyTextureInto(Caches& caches, RenderState& renderState,
        Texture& sourceTexture, const Matrix4& texTransform, const Rect& srcRect,
        SkBitmap* bitmap) {
    int destWidth = bitmap->width();
    int destHeight = bitmap->height();
@@ -134,88 +195,40 @@ static CopyResult copyTextureInto(Caches& caches, RenderState& renderState,
    return CopyResult::Success;
}

CopyResult Readback::copySurfaceInto(renderthread::RenderThread& renderThread,
        Surface& surface, const Rect& srcRect, SkBitmap* bitmap) {
    ATRACE_CALL();
    renderThread.eglManager().initialize();
CopyResult OpenGLReadbackImpl::copyImageInto(EGLImageKHR eglImage,
        const Matrix4& imgTransform, int imgWidth, int imgHeight, const Rect& srcRect,
        SkBitmap* bitmap) {

    Caches& caches = Caches::getInstance();

    // Setup the source
    sp<GraphicBuffer> sourceBuffer;
    sp<Fence> sourceFence;
    Matrix4 texTransform;
    status_t err = surface.getLastQueuedBuffer(&sourceBuffer, &sourceFence,
            texTransform.data);
    texTransform.invalidateType();
    if (err != NO_ERROR) {
        ALOGW("Failed to get last queued buffer, error = %d", err);
        return CopyResult::UnknownError;
    }
    if (!sourceBuffer.get()) {
        ALOGW("Surface doesn't have any previously queued frames, nothing to readback from");
        return CopyResult::SourceEmpty;
    }
    if (sourceBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) {
        ALOGW("Surface is protected, unable to copy from it");
        return CopyResult::SourceInvalid;
    }
    err = sourceFence->wait(500 /* ms */);
    if (err != NO_ERROR) {
        ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt");
        return CopyResult::Timeout;
    }

    // TODO: Can't use Image helper since it forces GL_TEXTURE_2D usage via
    // GL_OES_EGL_image, which doesn't work since we need samplerExternalOES
    // to be able to properly sample from the buffer.

    // Create the EGLImage object that maps the GraphicBuffer
    EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    EGLClientBuffer clientBuffer = (EGLClientBuffer) sourceBuffer->getNativeBuffer();
    EGLint attrs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE };

    EGLImageKHR sourceImage = eglCreateImageKHR(display, EGL_NO_CONTEXT,
            EGL_NATIVE_BUFFER_ANDROID, clientBuffer, attrs);

    if (sourceImage == EGL_NO_IMAGE_KHR) {
        ALOGW("eglCreateImageKHR failed (%#x)", eglGetError());
        return CopyResult::UnknownError;
    }
    GLuint sourceTexId;
    // Create a 2D texture to sample from the EGLImage
    glGenTextures(1, &sourceTexId);
    caches.textureState().bindTexture(GL_TEXTURE_EXTERNAL_OES, sourceTexId);
    glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, sourceImage);
    glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, eglImage);

    GLenum status = GL_NO_ERROR;
    while ((status = glGetError()) != GL_NO_ERROR) {
        ALOGW("glEGLImageTargetTexture2DOES failed (%#x)", status);
        eglDestroyImageKHR(display, sourceImage);
        return CopyResult::UnknownError;
    }

    Texture sourceTexture(caches);
    sourceTexture.wrap(sourceTexId, sourceBuffer->getWidth(),
            sourceBuffer->getHeight(), 0, 0 /* total lie */, GL_TEXTURE_EXTERNAL_OES);
    sourceTexture.wrap(sourceTexId, imgWidth, imgHeight, 0, 0 /* total lie */,
            GL_TEXTURE_EXTERNAL_OES);

    CopyResult copyResult = copyTextureInto(caches, renderThread.renderState(),
            sourceTexture, texTransform, srcRect, bitmap);
    CopyResult copyResult = copyTextureInto(caches, mRenderThread.renderState(),
            sourceTexture, imgTransform, srcRect, bitmap);
    sourceTexture.deleteTexture();
    // All we're flushing & finishing is the deletion of the texture since
    // copyTextureInto already did a major flush & finish as an implicit
    // part of glReadPixels, so this shouldn't pose any major stalls.
    glFinish();
    eglDestroyImageKHR(display, sourceImage);
    return copyResult;
}

CopyResult Readback::copyTextureLayerInto(renderthread::RenderThread& renderThread,
bool OpenGLReadbackImpl::copyLayerInto(renderthread::RenderThread& renderThread,
        Layer& layer, SkBitmap* bitmap) {
    ATRACE_CALL();
    return copyTextureInto(Caches::getInstance(), renderThread.renderState(),
            layer.getTexture(), layer.getTexTransform(), Rect(), bitmap);
    return CopyResult::Success == copyTextureInto(Caches::getInstance(),
            renderThread.renderState(), layer.getTexture(), layer.getTexTransform(),
            Rect(), bitmap);
}


} // namespace uirenderer
} // namespace android
+56 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#pragma once

#include "Readback.h"

namespace android {
namespace uirenderer {

class Matrix4;
class Layer;

class OpenGLReadback : public Readback {
public:
    virtual CopyResult copySurfaceInto(Surface& surface, const Rect& srcRect,
            SkBitmap* bitmap) override;

protected:
    explicit OpenGLReadback(renderthread::RenderThread& thread) : Readback(thread) {}
    virtual ~OpenGLReadback() {}

    virtual CopyResult copyImageInto(EGLImageKHR eglImage, const Matrix4& imgTransform,
            int imgWidth, int imgHeight, const Rect& srcRect, SkBitmap* bitmap) = 0;
};

class OpenGLReadbackImpl : public OpenGLReadback {
public:
    OpenGLReadbackImpl(renderthread::RenderThread& thread) : OpenGLReadback(thread) {}

    /**
     * Copies the layer's contents into the provided bitmap.
     */
    static bool copyLayerInto(renderthread::RenderThread& renderThread, Layer& layer,
            SkBitmap* bitmap);

protected:
    virtual CopyResult copyImageInto(EGLImageKHR eglImage, const Matrix4& imgTransform,
            int imgWidth, int imgHeight, const Rect& srcRect, SkBitmap* bitmap) override;
};

} // namespace uirenderer
} // namespace android
+7 −10
Original line number Diff line number Diff line
@@ -25,8 +25,6 @@
namespace android {
namespace uirenderer {

class Layer;

// Keep in sync with PixelCopy.java codes
enum class CopyResult {
    Success = 0,
@@ -42,15 +40,14 @@ public:
    /**
     * Copies the surface's most recently queued buffer into the provided bitmap.
     */
    static CopyResult copySurfaceInto(renderthread::RenderThread& renderThread,
            Surface& surface, const Rect& srcRect, SkBitmap* bitmap);
    virtual CopyResult copySurfaceInto(Surface& surface, const Rect& srcRect,
            SkBitmap* bitmap) = 0;

    /**
     * Copies the TextureLayer's texture content (thus, the currently rendering buffer) into the
     * provided bitmap.
     */
    static CopyResult copyTextureLayerInto(renderthread::RenderThread& renderThread,
            Layer& layer, SkBitmap* bitmap);
protected:
    explicit Readback(renderthread::RenderThread& thread) : mRenderThread(thread) {}
    virtual ~Readback() {}

    renderthread::RenderThread& mRenderThread;
};

} // namespace uirenderer
+15 −10
Original line number Diff line number Diff line
@@ -23,36 +23,41 @@ namespace uirenderer {
namespace skiapipeline {

void LayerDrawable::onDraw(SkCanvas* canvas) {
    DrawLayer(canvas->getGrContext(), canvas, mLayer.get());
}

bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer) {
    // transform the matrix based on the layer
    int saveCount = -1;
    if (!mLayer->getTransform().isIdentity()) {
    if (!layer->getTransform().isIdentity()) {
        saveCount = canvas->save();
        SkMatrix transform;
        mLayer->getTransform().copyTo(transform);
        layer->getTransform().copyTo(transform);
        canvas->concat(transform);
    }
    GrGLTextureInfo externalTexture;
    externalTexture.fTarget = mLayer->getRenderTarget();
    externalTexture.fID = mLayer->getTextureId();
    GrContext* context = canvas->getGrContext();
    externalTexture.fTarget = layer->getRenderTarget();
    externalTexture.fID = layer->getTextureId();
    GrBackendTextureDesc textureDescription;
    textureDescription.fWidth = mLayer->getWidth();
    textureDescription.fHeight = mLayer->getHeight();
    textureDescription.fWidth = layer->getWidth();
    textureDescription.fHeight = layer->getHeight();
    textureDescription.fConfig = kRGBA_8888_GrPixelConfig;
    textureDescription.fOrigin = kTopLeft_GrSurfaceOrigin;
    textureDescription.fTextureHandle = reinterpret_cast<GrBackendObject>(&externalTexture);
    sk_sp<SkImage> layerImage = SkImage::MakeFromTexture(context, textureDescription);
    if (layerImage) {
        SkPaint paint;
        paint.setAlpha(mLayer->getAlpha());
        paint.setBlendMode(mLayer->getMode());
        paint.setColorFilter(sk_ref_sp(mLayer->getColorFilter()));
        paint.setAlpha(layer->getAlpha());
        paint.setBlendMode(layer->getMode());
        paint.setColorFilter(sk_ref_sp(layer->getColorFilter()));
        canvas->drawImage(layerImage, 0, 0, &paint);
    }
    // restore the original matrix
    if (saveCount >= 0) {
        canvas->restoreToCount(saveCount);
    }

    return layerImage;
}

}; // namespace skiapipeline
Loading