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

Commit c4fbada7 authored by Derek Sollenberger's avatar Derek Sollenberger
Browse files

Support Surface and Layer Readback in the SkiaPipelines.

Test: CTS TextureViewTests and UIRendering
Change-Id: I2969c8f5a975bfd9aebcbb585c64d1fcbb2487c2
parent de4355dd
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