Fix crash when EGLSurface is no longer valid.
The EGLSurface stored in the pipeline can become obsolete if the EglManager/RenderThread has to destroy the context. This CL enables the RenderThread to notify all active pipelines that their surface is invalid. Bug: 115290937 Test: hwui_unit_tests Change-Id: Ib3054822273bc35406630b7442229a81b39a2c91
This commit is contained in:
@@ -43,7 +43,13 @@ namespace uirenderer {
|
||||
namespace skiapipeline {
|
||||
|
||||
SkiaOpenGLPipeline::SkiaOpenGLPipeline(RenderThread& thread)
|
||||
: SkiaPipeline(thread), mEglManager(thread.eglManager()) {}
|
||||
: SkiaPipeline(thread), mEglManager(thread.eglManager()) {
|
||||
thread.renderState().registerContextCallback(this);
|
||||
}
|
||||
|
||||
SkiaOpenGLPipeline::~SkiaOpenGLPipeline() {
|
||||
mRenderThread.renderState().removeContextCallback(this);
|
||||
}
|
||||
|
||||
MakeCurrentResult SkiaOpenGLPipeline::makeCurrent() {
|
||||
// TODO: Figure out why this workaround is needed, see b/13913604
|
||||
@@ -135,6 +141,13 @@ DeferredLayerUpdater* SkiaOpenGLPipeline::createTextureLayer() {
|
||||
return new DeferredLayerUpdater(mRenderThread.renderState());
|
||||
}
|
||||
|
||||
void SkiaOpenGLPipeline::onContextDestroyed() {
|
||||
if (mEglSurface != EGL_NO_SURFACE) {
|
||||
mEglManager.destroySurface(mEglSurface);
|
||||
mEglSurface = EGL_NO_SURFACE;
|
||||
}
|
||||
}
|
||||
|
||||
void SkiaOpenGLPipeline::onStop() {
|
||||
if (mEglManager.isCurrent(mEglSurface)) {
|
||||
mEglManager.makeCurrent(EGL_NO_SURFACE);
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
#include "SkiaPipeline.h"
|
||||
|
||||
#include "renderstate/RenderState.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
class Bitmap;
|
||||
@@ -25,10 +27,10 @@ class Bitmap;
|
||||
namespace uirenderer {
|
||||
namespace skiapipeline {
|
||||
|
||||
class SkiaOpenGLPipeline : public SkiaPipeline {
|
||||
class SkiaOpenGLPipeline : public SkiaPipeline, public IGpuContextCallback {
|
||||
public:
|
||||
SkiaOpenGLPipeline(renderthread::RenderThread& thread);
|
||||
virtual ~SkiaOpenGLPipeline() {}
|
||||
virtual ~SkiaOpenGLPipeline();
|
||||
|
||||
renderthread::MakeCurrentResult makeCurrent() override;
|
||||
renderthread::Frame getFrame() override;
|
||||
@@ -50,6 +52,9 @@ public:
|
||||
|
||||
static void invokeFunctor(const renderthread::RenderThread& thread, Functor* functor);
|
||||
|
||||
protected:
|
||||
void onContextDestroyed() override;
|
||||
|
||||
private:
|
||||
renderthread::EglManager& mEglManager;
|
||||
EGLSurface mEglSurface = EGL_NO_SURFACE;
|
||||
|
||||
@@ -41,8 +41,15 @@ void RenderState::onContextCreated() {
|
||||
GpuMemoryTracker::onGpuContextCreated();
|
||||
}
|
||||
|
||||
static void destroyLayerInUpdater(DeferredLayerUpdater* layerUpdater) {
|
||||
layerUpdater->destroyLayer();
|
||||
}
|
||||
|
||||
void RenderState::onContextDestroyed() {
|
||||
destroyLayersInUpdater();
|
||||
std::for_each(mActiveLayerUpdaters.begin(), mActiveLayerUpdaters.end(), destroyLayerInUpdater);
|
||||
for(auto callback : mContextCallbacks) {
|
||||
callback->onContextDestroyed();
|
||||
}
|
||||
GpuMemoryTracker::onGpuContextDestroyed();
|
||||
}
|
||||
|
||||
@@ -91,14 +98,6 @@ void RenderState::debugOverdraw(bool enable, bool clear) {
|
||||
// DEAD CODE
|
||||
}
|
||||
|
||||
static void destroyLayerInUpdater(DeferredLayerUpdater* layerUpdater) {
|
||||
layerUpdater->destroyLayer();
|
||||
}
|
||||
|
||||
void RenderState::destroyLayersInUpdater() {
|
||||
std::for_each(mActiveLayerUpdaters.begin(), mActiveLayerUpdaters.end(), destroyLayerInUpdater);
|
||||
}
|
||||
|
||||
void RenderState::postDecStrong(VirtualLightRefBase* object) {
|
||||
if (pthread_equal(mThreadId, pthread_self())) {
|
||||
object->decStrong(nullptr);
|
||||
|
||||
@@ -41,6 +41,13 @@ class CanvasContext;
|
||||
class RenderThread;
|
||||
}
|
||||
|
||||
class IGpuContextCallback {
|
||||
public:
|
||||
virtual void onContextDestroyed() = 0;
|
||||
protected:
|
||||
virtual ~IGpuContextCallback() {}
|
||||
};
|
||||
|
||||
// wrapper of Caches for users to migrate to.
|
||||
class RenderState {
|
||||
PREVENT_COPY_AND_ASSIGN(RenderState);
|
||||
@@ -48,9 +55,6 @@ class RenderState {
|
||||
friend class renderthread::CacheManager;
|
||||
|
||||
public:
|
||||
void onContextCreated();
|
||||
void onContextDestroyed();
|
||||
|
||||
void onBitmapDestroyed(uint32_t pixelRefId);
|
||||
|
||||
void setViewport(GLsizei width, GLsizei height);
|
||||
@@ -63,6 +67,9 @@ public:
|
||||
|
||||
void debugOverdraw(bool enable, bool clear);
|
||||
|
||||
void registerContextCallback(IGpuContextCallback* cb) { mContextCallbacks.insert(cb); }
|
||||
void removeContextCallback(IGpuContextCallback* cb) { mContextCallbacks.erase(cb); }
|
||||
|
||||
void registerLayer(Layer* layer) { mActiveLayers.insert(layer); }
|
||||
void unregisterLayer(Layer* layer) { mActiveLayers.erase(layer); }
|
||||
|
||||
@@ -93,13 +100,16 @@ public:
|
||||
renderthread::RenderThread& getRenderThread();
|
||||
|
||||
private:
|
||||
void destroyLayersInUpdater();
|
||||
|
||||
explicit RenderState(renderthread::RenderThread& thread);
|
||||
~RenderState();
|
||||
|
||||
// Context notifications are only to be triggered by renderthread::RenderThread
|
||||
void onContextCreated();
|
||||
void onContextDestroyed();
|
||||
|
||||
renderthread::RenderThread& mRenderThread;
|
||||
|
||||
std::set<IGpuContextCallback*> mContextCallbacks;
|
||||
std::set<Layer*> mActiveLayers;
|
||||
std::set<DeferredLayerUpdater*> mActiveLayerUpdaters;
|
||||
std::set<renderthread::CanvasContext*> mRegisteredContexts;
|
||||
|
||||
@@ -177,7 +177,6 @@ void RenderThread::requireGlContext() {
|
||||
return;
|
||||
}
|
||||
mEglManager->initialize();
|
||||
renderState().onContextCreated();
|
||||
|
||||
#ifdef HWUI_GLES_WRAP_ENABLED
|
||||
debug::GlesDriver* driver = debug::GlesDriver::get();
|
||||
@@ -201,7 +200,6 @@ void RenderThread::requireGlContext() {
|
||||
void RenderThread::destroyGlContext() {
|
||||
if (mEglManager->hasEglContext()) {
|
||||
setGrContext(nullptr);
|
||||
renderState().onContextDestroyed();
|
||||
mEglManager->destroy();
|
||||
}
|
||||
}
|
||||
@@ -243,10 +241,12 @@ Readback& RenderThread::readback() {
|
||||
void RenderThread::setGrContext(sk_sp<GrContext> context) {
|
||||
mCacheManager->reset(context);
|
||||
if (mGrContext) {
|
||||
mRenderState->onContextDestroyed();
|
||||
mGrContext->releaseResourcesAndAbandonContext();
|
||||
}
|
||||
mGrContext = std::move(context);
|
||||
if (mGrContext) {
|
||||
mRenderState->onContextCreated();
|
||||
DeviceInfo::setMaxTextureSize(mGrContext->maxRenderTargetSize());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,6 @@ namespace renderthread {
|
||||
VulkanManager::VulkanManager(RenderThread& thread) : mRenderThread(thread) {}
|
||||
|
||||
void VulkanManager::destroy() {
|
||||
mRenderThread.renderState().onContextDestroyed();
|
||||
mRenderThread.setGrContext(nullptr);
|
||||
|
||||
if (VK_NULL_HANDLE != mCommandPool) {
|
||||
@@ -401,8 +400,6 @@ void VulkanManager::initialize() {
|
||||
if (Properties::enablePartialUpdates && Properties::useBufferAge) {
|
||||
mSwapBehavior = SwapBehavior::BufferAge;
|
||||
}
|
||||
|
||||
mRenderThread.renderState().onContextCreated();
|
||||
}
|
||||
|
||||
// Returns the next BackbufferInfo to use for the next draw. The function will make sure all
|
||||
|
||||
@@ -382,3 +382,13 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clip_replace) {
|
||||
SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), surface);
|
||||
EXPECT_EQ(1, surface->canvas()->mDrawCounter);
|
||||
}
|
||||
|
||||
RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, context_lost) {
|
||||
auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
|
||||
EXPECT_FALSE(pipeline->isSurfaceReady());
|
||||
EXPECT_TRUE(pipeline->setSurface((Surface*)0x01, SwapBehavior::kSwap_default, ColorMode::Legacy));
|
||||
EXPECT_TRUE(pipeline->isSurfaceReady());
|
||||
renderThread.destroyGlContext();
|
||||
EXPECT_FALSE(pipeline->isSurfaceReady());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user