blob: 8a130bef53a8de97a19c8448966fbe9c72a5ae91 [file] [log] [blame]
* Copyright 2017 Google Inc.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
#include "include/gpu/GrDirectContext.h"
#include "include/gpu/GrRecordingContext.h"
#include "src/core/SkMessageBus.h"
#include "src/gpu/GrBackendTextureImageGenerator.h"
#include "src/gpu/GrContextPriv.h"
#include "src/gpu/GrGpu.h"
#include "src/gpu/GrProxyProvider.h"
#include "src/gpu/GrRecordingContextPriv.h"
#include "src/gpu/GrRenderTargetContext.h"
#include "src/gpu/GrResourceCache.h"
#include "src/gpu/GrResourceProvider.h"
#include "src/gpu/GrResourceProviderPriv.h"
#include "src/gpu/GrSemaphore.h"
#include "src/gpu/GrTexture.h"
#include "src/gpu/GrTextureProxyPriv.h"
#include "src/gpu/SkGr.h"
#include "src/gpu/gl/GrGLTexture.h"
GrBackendTextureImageGenerator::RefHelper::RefHelper(GrTexture* texture, uint32_t owningContextID,
std::unique_ptr<GrSemaphore> semaphore)
: fOriginalTexture(texture)
, fOwningContextID(owningContextID)
, fBorrowingContextReleaseProc(nullptr)
, fBorrowingContextID(SK_InvalidGenID)
, fSemaphore(std::move(semaphore)) {}
GrBackendTextureImageGenerator::RefHelper::~RefHelper() {
SkASSERT(fBorrowingContextID == SK_InvalidUniqueID);
// Generator has been freed, and no one is borrowing the texture. Notify the original cache
// that it can free the last ref, so it happens on the correct thread.
GrTextureFreedMessage msg { fOriginalTexture, fOwningContextID };
GrBackendTextureImageGenerator::Make(sk_sp<GrTexture> texture, GrSurfaceOrigin origin,
std::unique_ptr<GrSemaphore> semaphore, SkColorType colorType,
SkAlphaType alphaType, sk_sp<SkColorSpace> colorSpace) {
GrDirectContext* context = texture->getContext();
// 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. That ref will be released when the generator's RefHelper is freed.
GrBackendTexture backendTexture = texture->getBackendTexture();
if (!context->priv().caps()->areColorTypeAndFormatCompatible(
SkColorTypeToGrColorType(colorType), backendTexture.getBackendFormat())) {
return nullptr;
SkImageInfo info = SkImageInfo::Make(texture->width(), texture->height(), colorType, alphaType,
return std::unique_ptr<SkImageGenerator>(new GrBackendTextureImageGenerator(
info, texture.get(), origin, context->priv().contextID(),
std::move(semaphore), backendTexture));
const SkImageInfo& info,
GrTexture* texture,
GrSurfaceOrigin origin,
uint32_t owningContextID,
std::unique_ptr<GrSemaphore> semaphore,
const GrBackendTexture& backendTex)
, fRefHelper(new RefHelper(texture, owningContextID, std::move(semaphore)))
, fBackendTexture(backendTex)
, fSurfaceOrigin(origin) {}
GrBackendTextureImageGenerator::~GrBackendTextureImageGenerator() {
void GrBackendTextureImageGenerator::ReleaseRefHelper_TextureReleaseProc(void* ctx) {
RefHelper* refHelper = static_cast<RefHelper*>(ctx);
refHelper->fBorrowingContextReleaseProc = nullptr;
refHelper->fBorrowingContextID = SK_InvalidGenID;
GrSurfaceProxyView GrBackendTextureImageGenerator::onGenerateTexture(
GrRecordingContext* context,
const SkImageInfo& info,
const SkIPoint& origin,
GrMipmapped mipMapped,
GrImageTexGenPolicy texGenPolicy) {
if (context->backend() != fBackendTexture.backend()) {
return {};
if (info.colorType() != this->getInfo().colorType()) {
return {};
auto proxyProvider = context->priv().proxyProvider();
sk_sp<GrRefCntedCallback> releaseProcHelper;
if (SK_InvalidGenID != fRefHelper->fBorrowingContextID) {
if (fRefHelper->fBorrowingContextID != context->priv().contextID()) {
"GrBackendTextureImageGenerator: Trying to use texture on two GrContexts!\n");
return {};
} else {
// Ref the release proc to be held by the proxy we make below
releaseProcHelper = sk_ref_sp(fRefHelper->fBorrowingContextReleaseProc);
} else {
// The ref we add to fRefHelper here will be passed into and owned by the
// GrRefCntedCallback.
new GrRefCntedCallback(ReleaseRefHelper_TextureReleaseProc, fRefHelper));
fRefHelper->fBorrowingContextReleaseProc = releaseProcHelper.get();
fRefHelper->fBorrowingContextID = context->priv().contextID();
if (!fRefHelper->fBorrowedTextureKey.isValid()) {
static const auto kDomain = GrUniqueKey::GenerateDomain();
GrUniqueKey::Builder builder(&fRefHelper->fBorrowedTextureKey, kDomain, 1);
builder[0] = this->uniqueID();
SkASSERT(fRefHelper->fBorrowingContextID == context->priv().contextID());
GrBackendFormat backendFormat = fBackendTexture.getBackendFormat();
GrColorType grColorType = SkColorTypeToGrColorType(info.colorType());
GrMipmapped textureIsMipMapped = fBackendTexture.hasMipmaps() ? GrMipmapped::kYes
: GrMipmapped::kNo;
// Ganesh assumes that, when wrapping a mipmapped backend texture from a client, that its
// mipmaps are fully fleshed out.
GrMipmapStatus mipmapStatus = fBackendTexture.hasMipmaps()
? GrMipmapStatus::kValid : GrMipmapStatus::kNotAllocated;
GrSwizzle readSwizzle = context->priv().caps()->getReadSwizzle(backendFormat, grColorType);
// Must make copies of member variables to capture in the lambda since this image generator may
// be deleted before we actually execute the lambda.
sk_sp<GrTextureProxy> proxy = proxyProvider->createLazyProxy(
[refHelper = fRefHelper, releaseProcHelper, backendTexture = fBackendTexture](
GrResourceProvider* resourceProvider,
const GrSurfaceProxy::LazySurfaceDesc&) -> GrSurfaceProxy::LazyCallbackResult {
if (refHelper->fSemaphore) {
// If a client re-draws the same image multiple times, the texture we return
// will be cached and re-used. If they draw a subset, though, we may be
// re-called. In that case, we want to re-use the borrowed texture we've
// previously created.
sk_sp<GrTexture> tex;
auto surf = resourceProvider->findByUniqueKey<GrSurface>(
if (surf) {
tex = sk_ref_sp(surf->asTexture());
} else {
// We just gained access to the texture. If we're on the original
// context, we could use the original texture, but we'd have no way of
// detecting that it's no longer in-use. So we always make a wrapped
// copy, where the release proc informs us that the context is done with
// it. This is unfortunate - we'll have two texture objects referencing
// the same GPU object. However, no client can ever see the original
// texture, so this should be safe. We make the texture uncacheable so
// that the release proc is called ASAP.
tex = resourceProvider->wrapBackendTexture(
backendTexture, kBorrow_GrWrapOwnership, GrWrapCacheable::kNo,
if (!tex) {
return {};
// We use keys to avoid re-wrapping the GrBackendTexture in a GrTexture.
// This is unrelated to the whatever SkImage key may be assigned to the
// proxy.
return {std::move(tex), true, GrSurfaceProxy::LazyInstantiationKeyMode::kUnsynced};
backendFormat, fBackendTexture.dimensions(), textureIsMipMapped, mipmapStatus,
GrInternalSurfaceFlags::kReadOnly, SkBackingFit::kExact, SkBudgeted::kNo,
GrProtected::kNo, GrSurfaceProxy::UseAllocator::kYes);
if (!proxy) {
return {};
if (texGenPolicy == GrImageTexGenPolicy::kDraw && origin.isZero() &&
info.dimensions() == fBackendTexture.dimensions() &&
(mipMapped == GrMipmapped::kNo || proxy->mipmapped() == GrMipmapped::kYes)) {
// If the caller wants the entire texture and we have the correct mip support, we're done
return GrSurfaceProxyView(std::move(proxy), fSurfaceOrigin, readSwizzle);
} else {
SkIRect subset = SkIRect::MakeXYWH(origin.fX, origin.fY, info.width(), info.height());
SkBudgeted budgeted = texGenPolicy == GrImageTexGenPolicy::kNew_Uncached_Unbudgeted
? SkBudgeted::kNo
: SkBudgeted::kYes;
auto copy = GrSurfaceProxy::Copy(context, proxy.get(), fSurfaceOrigin, mipMapped, subset,
SkBackingFit::kExact, budgeted);
return {std::move(copy), fSurfaceOrigin, readSwizzle};