Cache GrTexture in GrAHardwareBufferImageGenerator

Cache last GrTexture needed by onGenerateTexture. Make sure
GrTexture is destroyed by the thread that owns its GrContext.
This CL avoids frequent eglDestroyImageKHR calls, which can take
more than 7ms.

Bug: skia:
Change-Id: Ic8472e7e4c55c0f559d96e16845054dc54ec8efa
Reviewed-on: https://skia-review.googlesource.com/20989
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Stan Iliev <stani@google.com>
This commit is contained in:
Stan Iliev 2017-06-28 13:24:41 -04:00 committed by Skia Commit-Bot
parent c2d207603e
commit dbba55de70
2 changed files with 57 additions and 31 deletions

View File

@ -17,9 +17,11 @@
#include "GrBackendSurface.h"
#include "GrContext.h"
#include "GrContextPriv.h"
#include "GrResourceCache.h"
#include "GrResourceProvider.h"
#include "GrTexture.h"
#include "GrTextureProxy.h"
#include "SkMessageBus.h"
#include <EGL/egl.h>
#include <EGL/eglext.h>
@ -72,11 +74,22 @@ GrAHardwareBufferImageGenerator::GrAHardwareBufferImageGenerator(const SkImageIn
GrAHardwareBufferImageGenerator::~GrAHardwareBufferImageGenerator() {
AHardwareBuffer_release(fGraphicBuffer);
this->clear();
}
void GrAHardwareBufferImageGenerator::clear() {
if (fOriginalTexture) {
// Notify the original cache that it can free the last ref, so it happens on the correct
// thread.
GrGpuResourceFreedMessage msg { fOriginalTexture, fOwningContextID };
SkMessageBus<GrGpuResourceFreedMessage>::Post(msg);
fOriginalTexture = nullptr;
}
}
void GrAHardwareBufferImageGenerator::deleteImageTexture(void* context) {
BufferCleanupHelper* cleanupHelper = static_cast<BufferCleanupHelper*>(context);
delete cleanupHelper;
BufferCleanupHelper* cleanupHelper = static_cast<BufferCleanupHelper*>(context);
delete cleanupHelper;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
@ -86,14 +99,36 @@ void GrAHardwareBufferImageGenerator::deleteImageTexture(void* context) {
sk_sp<GrTextureProxy> GrAHardwareBufferImageGenerator::onGenerateTexture(
GrContext* context, const SkImageInfo& info, const SkIPoint& origin) {
// TODO: return a cached GrTextureProxy if invoked with the same context
// TODO: if we cache GrTextureProxy, then deleteImageTexture may be invoked on the wrong thread
auto proxy = this->makeProxy(context);
if (!proxy) {
return nullptr;
}
if (0 == origin.fX && 0 == origin.fY &&
info.width() == getInfo().width() && info.height() == getInfo().height()) {
// If the caller wants the entire texture, we're done
return proxy;
} else {
// Otherwise, make a copy of the requested subset.
return GrSurfaceProxy::Copy(context, proxy.get(),
SkIRect::MakeXYWH(origin.fX, origin.fY, info.width(),
info.height()),
SkBudgeted::kYes);
}
}
#endif
sk_sp<GrTextureProxy> GrAHardwareBufferImageGenerator::makeProxy(GrContext* context) {
if (!context->getGpu() || kOpenGL_GrBackend != context->contextPriv().getBackend()) {
// Check if GrContext is not abandoned and the backend is GL.
return nullptr;
}
// return a cached GrTexture if invoked with the same context
if (fOriginalTexture && fOwningContextID == context->uniqueID()) {
return GrSurfaceProxy::MakeWrapped(sk_ref_sp(fOriginalTexture));
}
while (GL_NO_ERROR != glGetError()) {} //clear GL errors
EGLClientBuffer clientBuffer = eglGetNativeClientBufferANDROID(fGraphicBuffer);
@ -168,36 +203,23 @@ sk_sp<GrTextureProxy> GrAHardwareBufferImageGenerator::onGenerateTexture(
return nullptr;
}
tex->setRelease(deleteImageTexture, new BufferCleanupHelper(image, display));
sk_sp<GrTextureProxy> proxy(GrSurfaceProxy::MakeWrapped(std::move(tex)));
if (0 == origin.fX && 0 == origin.fY &&
info.width() == backendTex.width() && info.height() == backendTex.height()) {
// If the caller wants the entire texture, we're done
return proxy;
} else {
// Otherwise, make a copy of the requested subset.
GrSurfaceDesc desc;
desc.fConfig = proxy->config();
desc.fWidth = info.width();
desc.fHeight = info.height();
desc.fOrigin = proxy->origin();
desc.fIsMipMapped = proxy->isMipMapped();
// We fail this assert, if the context has changed. This will be fully handled after
// skbug.com/6812 is ready.
SkASSERT(!fOriginalTexture);
sk_sp<GrSurfaceContext> sContext(context->contextPriv().makeDeferredSurfaceContext(
desc, SkBackingFit::kExact, SkBudgeted::kYes));
if (!sContext) {
return nullptr;
}
SkIRect subset = SkIRect::MakeXYWH(origin.fX, origin.fY, info.width(), info.height());
if (!sContext->copy(proxy.get(), subset, SkIPoint::Make(0, 0))) {
return nullptr;
}
return sContext->asTextureProxyRef();
}
this->clear();
fOriginalTexture = tex.get();
fOwningContextID = context->uniqueID();
// Attach our texture to this context's resource cache. This ensures that deletion will happen
// in the correct thread/context. This adds the only ref to the texture that will persist from
// this point. To trigger GrTexture deletion a message is sent by generator dtor or by
// makeProxy when it is invoked with a different context.
//TODO: GrResourceCache should delete GrTexture, when GrContext is deleted. Currently
//TODO: SkMessageBus ignores messages for deleted contexts and GrTexture will leak.
context->getResourceCache()->insertCrossContextGpuResource(fOriginalTexture);
return GrSurfaceProxy::MakeWrapped(std::move(tex));
}
#endif
bool GrAHardwareBufferImageGenerator::onIsValid(GrContext* context) const {
if (nullptr == context) {

View File

@ -41,10 +41,14 @@ protected:
private:
GrAHardwareBufferImageGenerator(const SkImageInfo&, AHardwareBuffer*, SkAlphaType);
sk_sp<GrTextureProxy> makeProxy(GrContext* context);
void clear();
static void deleteImageTexture(void* ctx);
AHardwareBuffer* fGraphicBuffer;
GrTexture* fOriginalTexture = nullptr;
uint32_t fOwningContextID;
typedef SkImageGenerator INHERITED;
};