Support Surface and Layer Readback in the SkiaPipelines.

Test: CTS TextureViewTests and UIRendering
Change-Id: I2969c8f5a975bfd9aebcbb585c64d1fcbb2487c2
This commit is contained in:
Derek Sollenberger
2016-11-07 16:05:41 -05:00
parent de4355dd59
commit c4fbada76a
13 changed files with 359 additions and 95 deletions

View File

@@ -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 \

View File

@@ -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

View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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());

View File

@@ -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() {

View 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 */

View 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 */

View File

@@ -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() {

View File

@@ -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,

View File

@@ -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. "

View File

@@ -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;