Support Surface and Layer Readback in the SkiaPipelines.
Test: CTS TextureViewTests and UIRendering Change-Id: I2969c8f5a975bfd9aebcbb585c64d1fcbb2487c2
This commit is contained in:
@@ -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 \
|
||||
|
||||
@@ -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
libs/hwui/OpenGLReadback.h
Normal file
56
libs/hwui/OpenGLReadback.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -33,6 +33,7 @@ class LayerDrawable : public SkDrawable {
|
||||
explicit LayerDrawable(Layer* layer)
|
||||
: mLayer(layer) {}
|
||||
|
||||
static bool DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer);
|
||||
protected:
|
||||
virtual SkRect onGetBounds() override {
|
||||
return SkRect::MakeWH(mLayer->getWidth(), mLayer->getHeight());
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
#include "SkiaOpenGLPipeline.h"
|
||||
|
||||
#include "DeferredLayerUpdater.h"
|
||||
#include "LayerDrawable.h"
|
||||
#include "renderthread/EglManager.h"
|
||||
#include "renderstate/RenderState.h"
|
||||
#include "Readback.h"
|
||||
#include "SkiaPipeline.h"
|
||||
#include "SkiaProfileRenderer.h"
|
||||
#include "utils/TraceUtils.h"
|
||||
@@ -121,10 +121,16 @@ bool SkiaOpenGLPipeline::swapBuffers(const Frame& frame, bool drew,
|
||||
return *requireSwap;
|
||||
}
|
||||
|
||||
bool SkiaOpenGLPipeline::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
|
||||
layer->apply();
|
||||
return Readback::copyTextureLayerInto(mRenderThread, *(layer->backingLayer()), bitmap)
|
||||
== CopyResult::Success;
|
||||
bool SkiaOpenGLPipeline::copyLayerInto(DeferredLayerUpdater* deferredLayer, SkBitmap* bitmap) {
|
||||
if (!mRenderThread.getGrContext()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
deferredLayer->apply();
|
||||
|
||||
SkCanvas canvas(*bitmap);
|
||||
Layer* layer = deferredLayer->backingLayer();
|
||||
return LayerDrawable::DrawLayer(mRenderThread.getGrContext(), &canvas, layer);
|
||||
}
|
||||
|
||||
DeferredLayerUpdater* SkiaOpenGLPipeline::createTextureLayer() {
|
||||
|
||||
121
libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
Normal file
121
libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
Normal file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "SkiaOpenGLReadback.h"
|
||||
|
||||
#include "Matrix.h"
|
||||
#include "Properties.h"
|
||||
#include <SkCanvas.h>
|
||||
#include <SkSurface.h>
|
||||
#include <gl/GrGLInterface.h>
|
||||
#include <gl/GrGLTypes.h>
|
||||
#include <GLES2/gl2.h>
|
||||
#include <GLES2/gl2ext.h>
|
||||
|
||||
using namespace android::uirenderer::renderthread;
|
||||
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
namespace skiapipeline {
|
||||
|
||||
CopyResult SkiaOpenGLReadback::copyImageInto(EGLImageKHR eglImage, const Matrix4& imgTransform,
|
||||
int imgWidth, int imgHeight, const Rect& srcRect, SkBitmap* bitmap) {
|
||||
|
||||
GLuint sourceTexId;
|
||||
glGenTextures(1, &sourceTexId);
|
||||
glBindTexture(GL_TEXTURE_EXTERNAL_OES, sourceTexId);
|
||||
glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, eglImage);
|
||||
|
||||
sk_sp<GrContext> grContext = sk_ref_sp(mRenderThread.getGrContext());
|
||||
if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
|
||||
sk_sp<const GrGLInterface> glInterface(GrGLCreateNativeInterface());
|
||||
LOG_ALWAYS_FATAL_IF(!glInterface.get());
|
||||
grContext.reset(GrContext::Create(GrBackend::kOpenGL_GrBackend,
|
||||
(GrBackendContext)glInterface.get()));
|
||||
} else {
|
||||
grContext->resetContext();
|
||||
}
|
||||
|
||||
GrGLTextureInfo externalTexture;
|
||||
externalTexture.fTarget = GL_TEXTURE_EXTERNAL_OES;
|
||||
externalTexture.fID = sourceTexId;
|
||||
|
||||
GrBackendTextureDesc textureDescription;
|
||||
textureDescription.fWidth = imgWidth;
|
||||
textureDescription.fHeight = imgHeight;
|
||||
textureDescription.fConfig = kRGBA_8888_GrPixelConfig;
|
||||
textureDescription.fOrigin = kTopLeft_GrSurfaceOrigin;
|
||||
textureDescription.fTextureHandle = reinterpret_cast<GrBackendObject>(&externalTexture);
|
||||
|
||||
CopyResult copyResult = CopyResult::UnknownError;
|
||||
sk_sp<SkImage> image(SkImage::MakeFromAdoptedTexture(grContext.get(), textureDescription));
|
||||
if (image) {
|
||||
SkAutoLockPixels alp(*bitmap);
|
||||
|
||||
// convert to Skia data structures
|
||||
const SkRect bufferRect = SkRect::MakeIWH(imgWidth, imgHeight);
|
||||
SkRect skiaSrcRect = srcRect.toSkRect();
|
||||
SkMatrix textureMatrix;
|
||||
imgTransform.copyTo(textureMatrix);
|
||||
|
||||
// remove the y-flip applied to the matrix so that we can scale the srcRect.
|
||||
// This flip is not needed as we specify the origin of the texture when we
|
||||
// wrap it as an SkImage.
|
||||
SkMatrix yFlip = SkMatrix::MakeScale(1, -1);
|
||||
yFlip.postTranslate(0,1);
|
||||
textureMatrix.preConcat(yFlip);
|
||||
|
||||
// copy the entire src if the rect is empty
|
||||
if (skiaSrcRect.isEmpty()) {
|
||||
skiaSrcRect = bufferRect;
|
||||
}
|
||||
|
||||
// since the y-flip has been removed we can simply scale & translate
|
||||
// the source rectangle
|
||||
textureMatrix.mapRect(&skiaSrcRect);
|
||||
|
||||
if (skiaSrcRect.intersect(bufferRect)) {
|
||||
SkPoint srcOrigin = SkPoint::Make(skiaSrcRect.fLeft, skiaSrcRect.fTop);
|
||||
|
||||
// if we need to scale the result we must render to an offscreen buffer
|
||||
if (bitmap->width() != skiaSrcRect.width()
|
||||
|| bitmap->height() != skiaSrcRect.height()) {
|
||||
sk_sp<SkSurface> scaledSurface = SkSurface::MakeRenderTarget(
|
||||
grContext.get(), SkBudgeted::kYes, bitmap->info());
|
||||
SkPaint paint;
|
||||
paint.setBlendMode(SkBlendMode::kSrc);
|
||||
scaledSurface->getCanvas()->drawImageRect(image, skiaSrcRect,
|
||||
SkRect::MakeWH(bitmap->width(), bitmap->height()), &paint);
|
||||
image = scaledSurface->makeImageSnapshot();
|
||||
srcOrigin.set(0,0);
|
||||
}
|
||||
|
||||
if (image->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(),
|
||||
srcOrigin.fX, srcOrigin.fY)) {
|
||||
copyResult = CopyResult::Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// make sure that we have deleted the texture (in the SkImage) before we
|
||||
// destroy the EGLImage that it was created from
|
||||
image.reset();
|
||||
return copyResult;
|
||||
}
|
||||
|
||||
} /* namespace skiapipeline */
|
||||
} /* namespace uirenderer */
|
||||
} /* namespace android */
|
||||
35
libs/hwui/pipeline/skia/SkiaOpenGLReadback.h
Normal file
35
libs/hwui/pipeline/skia/SkiaOpenGLReadback.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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 "OpenGLReadback.h"
|
||||
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
namespace skiapipeline {
|
||||
|
||||
class SkiaOpenGLReadback : public OpenGLReadback {
|
||||
public:
|
||||
SkiaOpenGLReadback(renderthread::RenderThread& thread) : OpenGLReadback(thread) {}
|
||||
protected:
|
||||
virtual CopyResult copyImageInto(EGLImageKHR eglImage, const Matrix4& imgTransform,
|
||||
int imgWidth, int imgHeight, const Rect& srcRect, SkBitmap* bitmap) override;
|
||||
};
|
||||
|
||||
} /* namespace skiapipeline */
|
||||
} /* namespace uirenderer */
|
||||
} /* namespace android */
|
||||
@@ -20,7 +20,7 @@
|
||||
#include "EglManager.h"
|
||||
#include "ProfileRenderer.h"
|
||||
#include "renderstate/RenderState.h"
|
||||
#include "Readback.h"
|
||||
#include "OpenGLReadback.h"
|
||||
|
||||
#include <android/native_window.h>
|
||||
#include <cutils/properties.h>
|
||||
@@ -117,9 +117,9 @@ bool OpenGLPipeline::swapBuffers(const Frame& frame, bool drew, const SkRect& sc
|
||||
}
|
||||
|
||||
bool OpenGLPipeline::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
|
||||
ATRACE_CALL();
|
||||
layer->apply();
|
||||
return Readback::copyTextureLayerInto(mRenderThread, *(layer->backingLayer()), bitmap)
|
||||
== CopyResult::Success;
|
||||
return OpenGLReadbackImpl::copyLayerInto(mRenderThread, *(layer->backingLayer()), bitmap);
|
||||
}
|
||||
|
||||
DeferredLayerUpdater* OpenGLPipeline::createTextureLayer() {
|
||||
|
||||
@@ -602,8 +602,8 @@ void RenderProxy::removeFrameMetricsObserver(FrameMetricsObserver* observer) {
|
||||
|
||||
CREATE_BRIDGE4(copySurfaceInto, RenderThread* thread,
|
||||
Surface* surface, Rect srcRect, SkBitmap* bitmap) {
|
||||
return (void*) Readback::copySurfaceInto(*args->thread,
|
||||
*args->surface, args->srcRect, args->bitmap);
|
||||
return (void*)args->thread->readback().copySurfaceInto(*args->surface,
|
||||
args->srcRect, args->bitmap);
|
||||
}
|
||||
|
||||
int RenderProxy::copySurfaceInto(sp<Surface>& surface, int left, int top,
|
||||
|
||||
@@ -17,8 +17,10 @@
|
||||
#include "RenderThread.h"
|
||||
|
||||
#include "../renderstate/RenderState.h"
|
||||
#include "../pipeline/skia/SkiaOpenGLReadback.h"
|
||||
#include "CanvasContext.h"
|
||||
#include "EglManager.h"
|
||||
#include "OpenGLReadback.h"
|
||||
#include "RenderProxy.h"
|
||||
#include "VulkanManager.h"
|
||||
|
||||
@@ -196,6 +198,30 @@ void RenderThread::initThreadLocals() {
|
||||
mVkManager = new VulkanManager(*this);
|
||||
}
|
||||
|
||||
Readback& RenderThread::readback() {
|
||||
|
||||
if (!mReadback) {
|
||||
auto renderType = Properties::getRenderPipelineType();
|
||||
switch (renderType) {
|
||||
case RenderPipelineType::OpenGL:
|
||||
mReadback = new OpenGLReadbackImpl(*this);
|
||||
break;
|
||||
case RenderPipelineType::SkiaGL:
|
||||
case RenderPipelineType::SkiaVulkan:
|
||||
// It works to use the OpenGL pipeline for Vulkan but this is not
|
||||
// ideal as it causes us to create an OpenGL context in addition
|
||||
// to the Vulkan one.
|
||||
mReadback = new skiapipeline::SkiaOpenGLReadback(*this);
|
||||
break;
|
||||
default:
|
||||
LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t) renderType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return *mReadback;
|
||||
}
|
||||
|
||||
int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) {
|
||||
if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) {
|
||||
ALOGE("Display event receiver pipe was closed or an error occurred. "
|
||||
|
||||
@@ -37,6 +37,7 @@ class DisplayEventReceiver;
|
||||
|
||||
namespace uirenderer {
|
||||
|
||||
class Readback;
|
||||
class RenderState;
|
||||
class TestUtils;
|
||||
|
||||
@@ -93,6 +94,7 @@ public:
|
||||
RenderState& renderState() const { return *mRenderState; }
|
||||
EglManager& eglManager() const { return *mEglManager; }
|
||||
JankTracker& jankTracker() { return *mJankTracker; }
|
||||
Readback& readback();
|
||||
|
||||
const DisplayInfo& mainDisplayInfo() { return mDisplayInfo; }
|
||||
|
||||
@@ -151,6 +153,7 @@ private:
|
||||
EglManager* mEglManager;
|
||||
|
||||
JankTracker* mJankTracker = nullptr;
|
||||
Readback* mReadback = nullptr;
|
||||
|
||||
sk_sp<GrContext> mGrContext;
|
||||
VulkanManager* mVkManager;
|
||||
|
||||
Reference in New Issue
Block a user