Add GrContext::releaseAndAbandonContext()

Like abandonContext() this disconnects the GrContext from the underlying 3D API. However, unlike abandonContext it first frees all allocated GPU resources.

BUG=skia:5142
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1852733002

Review URL: https://codereview.chromium.org/1852733002
This commit is contained in:
bsalomon 2016-04-01 11:54:31 -07:00 committed by Commit bot
parent 895f3f0544
commit 6e2aad4e9f
15 changed files with 184 additions and 30 deletions

View File

@ -84,6 +84,8 @@ public:
void destroyContexts() {}
void abandonContexts() {}
void releaseResourcesAndAbandonContexts() {}
};
} // namespace sk_gpu_test

View File

@ -1011,6 +1011,8 @@ Error GPUSink::draw(const Src& src, SkBitmap* dst, SkWStream*, SkString* log) co
canvas->readPixels(dst, 0, 0);
if (FLAGS_abandonGpuContext) {
factory.abandonContexts();
} else if (FLAGS_releaseAndAbandonGpuContext) {
factory.releaseResourcesAndAbandonContexts();
}
return "";
}

View File

@ -93,21 +93,29 @@ public:
}
/**
* Abandons all GPU resources and assumes the underlying backend 3D API
* context is not longer usable. Call this if you have lost the associated
* GPU context, and thus internal texture, buffer, etc. references/IDs are
* now invalid. Should be called even when GrContext is no longer going to
* be used for two reasons:
* 1) ~GrContext will not try to free the objects in the 3D API.
* 2) Any GrGpuResources created by this GrContext that outlive
* will be marked as invalid (GrGpuResource::wasDestroyed()) and
* when they're destroyed no 3D API calls will be made.
* Content drawn since the last GrContext::flush() may be lost. After this
* function is called the only valid action on the GrContext or
* GrGpuResources it created is to destroy them.
* Abandons all GPU resources and assumes the underlying backend 3D API context is not longer
* usable. Call this if you have lost the associated GPU context, and thus internal texture,
* buffer, etc. references/IDs are now invalid. Calling this ensures that the destructors of the
* GrContext and any of its created resource objects will not make backend 3D API calls. Content
* rendered but not previously flushed may be lost. After this function is called all subsequent
* calls on the GrContext will fail or be no-ops.
*
* The typical use case for this function is that the underlying 3D context was lost and further
* API calls may crash.
*/
void abandonContext();
/**
* This is similar to abandonContext() however the underlying 3D context is not yet lost and
* the GrContext will cleanup all allocated resources before returning. After returning it will
* assume that the underlying context may no longer be valid.
*
* The typical use case for this function is that the client is going to destroy the 3D context
* but can't guarantee that GrContext will be destroyed first (perhaps because it may be ref'ed
* elsewhere by either the client or Skia objects).
*/
void releaseResourcesAndAbandonContext();
///////////////////////////////////////////////////////////////////////////
// Resource Cache

View File

@ -148,7 +148,26 @@ void GrContext::abandonContext() {
// don't try to free the resources in the API.
fResourceCache->abandonAll();
fGpu->contextAbandoned();
fGpu->disconnect(GrGpu::DisconnectType::kAbandon);
fBatchFontCache->freeAll();
fLayerCache->freeAll();
fTextBlobCache->freeAll();
}
void GrContext::releaseResourcesAndAbandonContext() {
ASSERT_SINGLE_OWNER
fResourceProvider->abandon();
// Need to abandon the drawing manager first so all the render targets
// will be released/forgotten before they too are abandoned.
fDrawingManager->abandon();
// Release all resources in the backend 3D API.
fResourceCache->releaseAll();
fGpu->disconnect(GrGpu::DisconnectType::kCleanup);
fBatchFontCache->freeAll();
fLayerCache->freeAll();

View File

@ -51,7 +51,7 @@ GrGpu::GrGpu(GrContext* context)
GrGpu::~GrGpu() {}
void GrGpu::contextAbandoned() {}
void GrGpu::disconnect(DisconnectType) {}
////////////////////////////////////////////////////////////////////////////////

View File

@ -61,11 +61,17 @@ public:
GrPathRendering* pathRendering() { return fPathRendering.get(); }
// Called by GrContext when the underlying backend context has been destroyed.
// GrGpu should use this to ensure that no backend API calls will be made from
// here onward, including in its destructor. Subclasses should call
// INHERITED::contextAbandoned() if they override this.
virtual void contextAbandoned();
enum class DisconnectType {
// No cleanup should be attempted, immediately cease making backend API calls
kAbandon,
// Free allocated resources (not known by GrResourceCache) before returning and
// ensure no backend backend 3D API calls will be made after disconnect() returns.
kCleanup,
};
// Called by GrContext when the underlying backend context is already or will be destroyed
// before GrContext.
virtual void disconnect(DisconnectType);
/**
* The GrGpu object normally assumes that no outsider is setting state

View File

@ -386,9 +386,51 @@ void GrGLGpu::createPLSSetupProgram() {
GR_GL_STATIC_DRAW));
}
void GrGLGpu::contextAbandoned() {
INHERITED::contextAbandoned();
fProgramCache->abandon();
void GrGLGpu::disconnect(DisconnectType type) {
INHERITED::disconnect(type);
if (DisconnectType::kCleanup == type) {
if (fHWProgramID) {
GL_CALL(UseProgram(0));
}
if (fTempSrcFBOID) {
GL_CALL(DeleteFramebuffers(1, &fTempSrcFBOID));
}
if (fTempDstFBOID) {
GL_CALL(DeleteFramebuffers(1, &fTempDstFBOID));
}
if (fStencilClearFBOID) {
GL_CALL(DeleteFramebuffers(1, &fStencilClearFBOID));
}
if (fCopyProgramArrayBuffer) {
GL_CALL(DeleteBuffers(1, &fCopyProgramArrayBuffer));
}
for (size_t i = 0; i < SK_ARRAY_COUNT(fCopyPrograms); ++i) {
if (fCopyPrograms[i].fProgram) {
GL_CALL(DeleteProgram(fCopyPrograms[i].fProgram));
}
}
if (fWireRectProgram.fProgram) {
GL_CALL(DeleteProgram(fWireRectProgram.fProgram));
}
if (fWireRectArrayBuffer) {
GL_CALL(DeleteBuffers(1, &fWireRectArrayBuffer));
}
if (fPLSSetupProgram.fProgram) {
GL_CALL(DeleteProgram(fPLSSetupProgram.fProgram));
}
if (fPLSSetupProgram.fArrayBuffer) {
GL_CALL(DeleteBuffers(1, &fPLSSetupProgram.fArrayBuffer));
}
} else {
if (fProgramCache) {
fProgramCache->abandon();
}
}
delete fProgramCache;
fProgramCache = nullptr;
fHWProgramID = 0;
fTempSrcFBOID = 0;
fTempDstFBOID = 0;
@ -399,8 +441,10 @@ void GrGLGpu::contextAbandoned() {
}
fWireRectProgram.fProgram = 0;
fWireRectArrayBuffer = 0;
fPLSSetupProgram.fProgram = 0;
fPLSSetupProgram.fArrayBuffer = 0;
if (this->glCaps().shaderCaps()->pathRenderingSupport()) {
this->glPathRendering()->abandonGpuResources();
this->glPathRendering()->disconnect(type);
}
}

View File

@ -38,7 +38,7 @@ public:
GrContext* context);
~GrGLGpu() override;
void contextAbandoned() override;
void disconnect(DisconnectType) override;
const GrGLContext& glContext() const { return *fGLContext; }

View File

@ -91,7 +91,10 @@ GrGLPathRendering::~GrGLPathRendering() {
}
}
void GrGLPathRendering::abandonGpuResources() {
void GrGLPathRendering::disconnect(GrGpu::DisconnectType type) {
if (GrGpu::DisconnectType::kCleanup == type) {
this->deletePaths(fFirstPreallocatedPathID, fPreallocatedPathCount);
};
fPreallocatedPathCount = 0;
}

View File

@ -41,10 +41,10 @@ public:
void resetContext();
/**
* Called when the GPU resources have been lost and need to be abandoned
* (for example after a context loss).
* Called when the context either is about to be lost or is lost. DisconnectType indicates
* whether GPU resources should be cleaned up or abandoned when this is called.
*/
void abandonGpuResources();
void disconnect(GrGpu::DisconnectType);
bool shouldBindFragmentInputs() const {
return fCaps.bindFragmentInputSupport;

View File

@ -0,0 +1,53 @@
/*
* Copyright 2016 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkTypes.h"
#if SK_SUPPORT_GPU
#include "GrContextFactory.h"
#include "Test.h"
using sk_gpu_test::GrContextFactory;
DEF_GPUTEST(GrContext_abandonContext, reporter, /*factory*/) {
for (int testType = 0; testType < 6; ++testType) {
for (int i = 0; i < GrContextFactory::kGLContextTypeCnt; ++i) {
GrContextFactory testFactory;
GrContextFactory::GLContextType ctxType = (GrContextFactory::GLContextType) i;
GrContextFactory::ContextInfo info = testFactory.getContextInfo(ctxType);
if (GrContext* context = info.fGrContext) {
switch (testType) {
case 0:
context->abandonContext();
break;
case 1:
context->releaseResourcesAndAbandonContext();
break;
case 2:
context->abandonContext();
context->abandonContext();
break;
case 3:
context->abandonContext();
context->releaseResourcesAndAbandonContext();
break;
case 4:
context->releaseResourcesAndAbandonContext();
context->abandonContext();
break;
case 5:
context->releaseResourcesAndAbandonContext();
context->releaseResourcesAndAbandonContext();
break;
}
}
}
}
}
#endif

View File

@ -30,9 +30,13 @@ DEFINE_string2(match, m, nullptr,
DEFINE_bool2(quiet, q, false, "if true, don't print status updates.");
DEFINE_bool(preAbandonGpuContext, false, "Abandons the GrContext before running the test.");
DEFINE_bool(preAbandonGpuContext, false, "Test abandoning the GrContext before running the test.");
DEFINE_bool(abandonGpuContext, false, "Abandon the GrContext after running each test.");
DEFINE_bool(abandonGpuContext, false, "Test abandoning the GrContext after running each test.");
DEFINE_bool(releaseAndAbandonGpuContext, false,
"Test releasing all gpu resources and abandoning the GrContext after running each "
"test");
DEFINE_string(skps, "skps", "Directory to read skps from.");

View File

@ -21,6 +21,7 @@ DECLARE_bool(quiet);
DECLARE_bool(resetGpuContext);
DECLARE_bool(preAbandonGpuContext);
DECLARE_bool(abandonGpuContext);
DECLARE_bool(releaseAndAbandonGpuContext);
DECLARE_string(skps);
DECLARE_int32(threads);
DECLARE_string(resourcePath);

View File

@ -63,6 +63,17 @@ void GrContextFactory::abandonContexts() {
}
}
void GrContextFactory::releaseResourcesAndAbandonContexts() {
for (Context& context : fContexts) {
if (context.fGLContext) {
context.fGLContext->makeCurrent();
context.fGrContext->releaseResourcesAndAbandonContext();
delete(context.fGLContext);
context.fGLContext = nullptr;
}
}
}
GrContextFactory::ContextInfo GrContextFactory::getContextInfo(GLContextType type,
GLContextOptions options) {
for (int i = 0; i < fContexts.count(); ++i) {

View File

@ -107,6 +107,7 @@ public:
void destroyContexts();
void abandonContexts();
void releaseResourcesAndAbandonContexts();
struct ContextInfo {
ContextInfo()