Reuse GrTexture instances when the same GrBackendTexture is used to
repeatedly fulfill a promise SkImage. Bug: skia:8613 Change-Id: I35c76435d630d2daa034e0c3efb59666bfd6882a Reviewed-on: https://skia-review.googlesource.com/c/175820 Commit-Queue: Brian Salomon <bsalomon@google.com> Reviewed-by: Robert Phillips <robertphillips@google.com>
This commit is contained in:
parent
cc10c4342e
commit
559c617137
@ -1975,7 +1975,10 @@ Error ViaDDL::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString
|
||||
|
||||
// This is here bc this is the first point where we have access to the context
|
||||
promiseImageHelper.uploadAllToGPU(context);
|
||||
// We draw N times, with a clear between.
|
||||
// We draw N times, with a clear between. Between each run we invalidate and delete half of
|
||||
// the textures backing promise images. So half the images exercise reusing a cached
|
||||
// GrTexture and the other half exercise the case whem the client provides a different
|
||||
// backing texture in fulfill.
|
||||
for (int replay = 0; replay < fNumReplays; ++replay) {
|
||||
if (replay > 0) {
|
||||
// Clear the drawing of the previous replay
|
||||
@ -1994,6 +1997,11 @@ Error ViaDDL::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString
|
||||
// This drops the promiseImageHelper's refs on all the promise images if we're in
|
||||
// the last run.
|
||||
promiseImageHelper.reset();
|
||||
} else {
|
||||
// This ought to ensure that all promise image textures from the last pass are
|
||||
// released.
|
||||
context->contextPriv().getGpu()->testingOnly_flushGpuAndSync();
|
||||
promiseImageHelper.replaceEveryOtherPromiseTexture(context);
|
||||
}
|
||||
|
||||
// Fourth, synchronously render the display lists into the dest tiles
|
||||
|
@ -207,6 +207,7 @@ skia_core_sources = [
|
||||
"$_src/core/SkPixmap.cpp",
|
||||
"$_src/core/SkPoint.cpp",
|
||||
"$_src/core/SkPoint3.cpp",
|
||||
"$_src/core/SkPromiseImageTexture.cpp",
|
||||
"$_src/core/SkPtrRecorder.cpp",
|
||||
"$_src/core/SkQuadClipper.cpp",
|
||||
"$_src/core/SkQuadClipper.h",
|
||||
@ -388,6 +389,7 @@ skia_core_sources = [
|
||||
"$_include/core/SkPoint.h",
|
||||
"$_include/core/SkPoint3.h",
|
||||
"$_include/core/SkPreConfig.h",
|
||||
"$_include/core/SkPromiseImageTexture.h",
|
||||
"$_include/core/SkRect.h",
|
||||
"$_include/core/SkRefCnt.h",
|
||||
"$_include/core/SkRegion.h",
|
||||
|
@ -5,22 +5,21 @@
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef SkDeferredDisplayListMaker_DEFINED
|
||||
#define SkDeferredDisplayListMaker_DEFINED
|
||||
#ifndef SkDeferredDisplayListRecorder_DEFINED
|
||||
#define SkDeferredDisplayListRecorder_DEFINED
|
||||
|
||||
#include "../private/SkDeferredDisplayList.h"
|
||||
#include "SkImageInfo.h"
|
||||
#include "SkRefCnt.h"
|
||||
#include "SkSurfaceCharacterization.h"
|
||||
#include "SkTypes.h"
|
||||
|
||||
#include "../private/SkDeferredDisplayList.h"
|
||||
|
||||
class GrBackendFormat;
|
||||
class GrBackendTexture;
|
||||
class GrContext;
|
||||
|
||||
class SkCanvas;
|
||||
class SkImage;
|
||||
class SkPromiseImageTexture;
|
||||
class SkSurface;
|
||||
struct SkYUVAIndex;
|
||||
struct SkYUVASizeInfo;
|
||||
@ -53,11 +52,17 @@ public:
|
||||
|
||||
std::unique_ptr<SkDeferredDisplayList> detach();
|
||||
|
||||
// Matches the defines in SkImage_GpuBase.h
|
||||
typedef void* TextureContext;
|
||||
typedef void (*TextureReleaseProc)(TextureContext textureContext);
|
||||
typedef void (*TextureFulfillProc)(TextureContext textureContext, GrBackendTexture* outTexture);
|
||||
typedef void (*PromiseDoneProc)(TextureContext textureContext);
|
||||
using PromiseImageTextureContext = void*;
|
||||
using PromiseImageTextureFulfillProc = SkPromiseImageTexture* (*)(PromiseImageTextureContext);
|
||||
using PromiseImageTextureReleaseProc = void (*)(PromiseImageTextureContext,
|
||||
const SkPromiseImageTexture*);
|
||||
using PromiseImageTextureDoneProc = void (*)(PromiseImageTextureContext);
|
||||
|
||||
// Deprecated types. To be removed.
|
||||
using LegacyPromiseImageTextureFulfillProc = void (*)(PromiseImageTextureContext,
|
||||
GrBackendTexture*);
|
||||
using LegacyPromiseImageTextureReleaseProc = void (*)(PromiseImageTextureContext);
|
||||
using TextureContext = PromiseImageTextureContext;
|
||||
|
||||
/**
|
||||
Create a new SkImage that is very similar to an SkImage created by MakeFromTexture. The main
|
||||
@ -112,10 +117,23 @@ public:
|
||||
SkColorType colorType,
|
||||
SkAlphaType alphaType,
|
||||
sk_sp<SkColorSpace> colorSpace,
|
||||
TextureFulfillProc textureFulfillProc,
|
||||
TextureReleaseProc textureReleaseProc,
|
||||
PromiseDoneProc promiseDoneProc,
|
||||
TextureContext textureContext);
|
||||
PromiseImageTextureFulfillProc textureFulfillProc,
|
||||
PromiseImageTextureReleaseProc textureReleaseProc,
|
||||
PromiseImageTextureDoneProc textureDoneProc,
|
||||
PromiseImageTextureContext textureContext);
|
||||
/** Deprecated variant of above. */
|
||||
sk_sp<SkImage> makePromiseTexture(const GrBackendFormat& backendFormat,
|
||||
int width,
|
||||
int height,
|
||||
GrMipMapped mipMapped,
|
||||
GrSurfaceOrigin origin,
|
||||
SkColorType colorType,
|
||||
SkAlphaType alphaType,
|
||||
sk_sp<SkColorSpace> colorSpace,
|
||||
LegacyPromiseImageTextureFulfillProc textureFulfillProc,
|
||||
LegacyPromiseImageTextureReleaseProc textureReleaseProc,
|
||||
PromiseImageTextureDoneProc textureDoneProc,
|
||||
PromiseImageTextureContext textureContext);
|
||||
|
||||
/**
|
||||
This entry point operates the same as 'makePromiseTexture' except that its
|
||||
@ -132,12 +150,25 @@ public:
|
||||
int imageHeight,
|
||||
GrSurfaceOrigin imageOrigin,
|
||||
sk_sp<SkColorSpace> imageColorSpace,
|
||||
TextureFulfillProc textureFulfillProc,
|
||||
TextureReleaseProc textureReleaseProc,
|
||||
PromiseDoneProc promiseDoneProc,
|
||||
TextureContext textureContexts[]);
|
||||
PromiseImageTextureFulfillProc textureFulfillProc,
|
||||
PromiseImageTextureReleaseProc textureReleaseProc,
|
||||
PromiseImageTextureDoneProc textureDoneProc,
|
||||
PromiseImageTextureContext textureContexts[]);
|
||||
/** Deprecated variant of above. */
|
||||
sk_sp<SkImage> makeYUVAPromiseTexture(SkYUVColorSpace yuvColorSpace,
|
||||
const GrBackendFormat yuvaFormats[],
|
||||
const SkISize yuvaSizes[],
|
||||
const SkYUVAIndex yuvaIndices[4],
|
||||
int imageWidth,
|
||||
int imageHeight,
|
||||
GrSurfaceOrigin imageOrigin,
|
||||
sk_sp<SkColorSpace> imageColorSpace,
|
||||
LegacyPromiseImageTextureFulfillProc textureFulfillProc,
|
||||
LegacyPromiseImageTextureReleaseProc textureReleaseProc,
|
||||
PromiseImageTextureDoneProc textureDoneProc,
|
||||
PromiseImageTextureContext textureContexts[]);
|
||||
|
||||
private:
|
||||
private:
|
||||
bool init();
|
||||
|
||||
const SkSurfaceCharacterization fCharacterization;
|
||||
|
50
include/core/SkPromiseImageTexture.h
Normal file
50
include/core/SkPromiseImageTexture.h
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef SkPromiseImageTexture_DEFINED
|
||||
#define SkPromiseImageTexture_DEFINED
|
||||
|
||||
#include "../private/GrResourceKey.h"
|
||||
#include "GrBackendSurface.h"
|
||||
|
||||
#if SK_SUPPORT_GPU
|
||||
/**
|
||||
* This type is used to fulfill textures for PromiseImages. Once an instance is returned from a
|
||||
* PromiseImageTextureFulfillProc it must remain valid until the corresponding
|
||||
* PromiseImageTextureReleaseProc is called. For performance reasons it is recommended that the
|
||||
* the client reuse a single PromiseImageTexture every time a given texture is returned by
|
||||
* the PromiseImageTextureFulfillProc rather than recreating PromiseImageTextures representing
|
||||
* the same underlying backend API texture.
|
||||
*/
|
||||
class SK_API SkPromiseImageTexture {
|
||||
public:
|
||||
SkPromiseImageTexture() = default;
|
||||
SkPromiseImageTexture(const SkPromiseImageTexture&) = delete;
|
||||
explicit SkPromiseImageTexture(const GrBackendTexture& backendTexture);
|
||||
SkPromiseImageTexture(SkPromiseImageTexture&&);
|
||||
~SkPromiseImageTexture();
|
||||
SkPromiseImageTexture& operator=(const SkPromiseImageTexture&) = delete;
|
||||
SkPromiseImageTexture& operator=(SkPromiseImageTexture&&);
|
||||
const GrBackendTexture& backendTexture() const { return fBackendTexture; }
|
||||
bool isValid() const { return SkToBool(fUniqueID); }
|
||||
|
||||
void addKeyToInvalidate(uint32_t contextID, const GrUniqueKey& key);
|
||||
uint32_t uniqueID() const { return fUniqueID; }
|
||||
|
||||
#if GR_TEST_UTILS
|
||||
SkTArray<GrUniqueKey> testingOnly_uniqueKeysToInvalidate() const;
|
||||
#endif
|
||||
|
||||
private:
|
||||
SkSTArray<1, GrUniqueKeyInvalidatedMessage> fMessages;
|
||||
GrBackendTexture fBackendTexture;
|
||||
uint32_t fUniqueID = SK_InvalidUniqueID;
|
||||
static std::atomic<uint32_t> gUniqueID;
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
@ -56,13 +56,16 @@ protected:
|
||||
}
|
||||
|
||||
GrResourceKey& operator=(const GrResourceKey& that) {
|
||||
SkASSERT(that.isValid());
|
||||
if (this != &that) {
|
||||
size_t bytes = that.size();
|
||||
SkASSERT(SkIsAlign4(bytes));
|
||||
fKey.reset(SkToInt(bytes / sizeof(uint32_t)));
|
||||
memcpy(fKey.get(), that.fKey.get(), bytes);
|
||||
this->validate();
|
||||
if (!that.isValid()) {
|
||||
this->reset();
|
||||
} else {
|
||||
size_t bytes = that.size();
|
||||
SkASSERT(SkIsAlign4(bytes));
|
||||
fKey.reset(SkToInt(bytes / sizeof(uint32_t)));
|
||||
memcpy(fKey.get(), that.fKey.get(), bytes);
|
||||
this->validate();
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
@ -146,6 +149,7 @@ private:
|
||||
size_t internalSize() const { return fKey[kDomainAndSize_MetaDataIdx] >> 16; }
|
||||
|
||||
void validate() const {
|
||||
SkASSERT(this->isValid());
|
||||
SkASSERT(fKey[kHash_MetaDataIdx] ==
|
||||
GrResourceKeyHash(&fKey[kHash_MetaDataIdx] + 1,
|
||||
this->internalSize() - sizeof(uint32_t)));
|
||||
@ -327,6 +331,7 @@ static inline void gr_init_static_unique_key_once(SkAlignedSTStorage<1, GrUnique
|
||||
// The cache listens for these messages to purge junk resources proactively.
|
||||
class GrUniqueKeyInvalidatedMessage {
|
||||
public:
|
||||
GrUniqueKeyInvalidatedMessage() = default;
|
||||
GrUniqueKeyInvalidatedMessage(const GrUniqueKey& key, uint32_t contextUniqueID)
|
||||
: fKey(key), fContextID(contextUniqueID) {
|
||||
SkASSERT(SK_InvalidUniqueID != contextUniqueID);
|
||||
@ -341,7 +346,7 @@ public:
|
||||
|
||||
private:
|
||||
GrUniqueKey fKey;
|
||||
uint32_t fContextID;
|
||||
uint32_t fContextID = SK_InvalidUniqueID;
|
||||
};
|
||||
|
||||
static inline bool SkShouldPostMessageToBus(const GrUniqueKeyInvalidatedMessage& msg,
|
||||
|
@ -1389,17 +1389,23 @@ static inline GrPixelConfig GrColorTypeToPixelConfig(GrColorType config,
|
||||
return kUnknown_GrPixelConfig;
|
||||
}
|
||||
|
||||
class GrReleaseProcHelper : public SkWeakRefCnt {
|
||||
class GrReleaseProcHelper : public SkRefCnt {
|
||||
public:
|
||||
// These match the definitions in SkImage, from whence they came
|
||||
typedef void* ReleaseCtx;
|
||||
typedef void (*ReleaseProc)(ReleaseCtx);
|
||||
|
||||
GrReleaseProcHelper(ReleaseProc proc, ReleaseCtx ctx) : fReleaseProc(proc), fReleaseCtx(ctx) {}
|
||||
~GrReleaseProcHelper() override {}
|
||||
~GrReleaseProcHelper() override {
|
||||
if (fReleaseProc) {
|
||||
fReleaseProc(fReleaseCtx);
|
||||
}
|
||||
}
|
||||
|
||||
void weak_dispose() const override {
|
||||
void callAndClear() {
|
||||
fReleaseProc(fReleaseCtx);
|
||||
fReleaseProc = nullptr;
|
||||
fReleaseCtx = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -498,6 +498,7 @@
|
||||
"~^GrPipelineDynamicStateTest$",
|
||||
"~^InitialTextureClear$",
|
||||
"~^PromiseImageTest$",
|
||||
"~^PromiseImageTextureReuse$",
|
||||
"~^ResourceAllocatorTest$",
|
||||
"~^RGB565TextureTest$",
|
||||
"~^RGBA4444TextureTest$",
|
||||
|
@ -765,6 +765,7 @@ def dm_flags(api, bot):
|
||||
match.append('~^GrPipelineDynamicStateTest$')
|
||||
match.append('~^InitialTextureClear$')
|
||||
match.append('~^PromiseImageTest$')
|
||||
match.append('~^PromiseImageTextureReuse$')
|
||||
match.append('~^ResourceAllocatorTest$')
|
||||
match.append('~^RGB565TextureTest$')
|
||||
match.append('~^RGBA4444TextureTest$')
|
||||
|
@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
#include "SkDeferredDisplayListRecorder.h"
|
||||
|
||||
#include "SkMessageBus.h"
|
||||
#include "SkDeferredDisplayList.h"
|
||||
#include "SkSurface.h"
|
||||
#include "SkSurfaceCharacterization.h"
|
||||
@ -31,26 +31,58 @@ sk_sp<SkImage> SkDeferredDisplayListRecorder::makePromiseTexture(
|
||||
SkColorType colorType,
|
||||
SkAlphaType alphaType,
|
||||
sk_sp<SkColorSpace> colorSpace,
|
||||
TextureFulfillProc textureFulfillProc,
|
||||
TextureReleaseProc textureReleaseProc,
|
||||
PromiseDoneProc promiseDoneProc,
|
||||
TextureContext textureContext) {
|
||||
PromiseImageTextureFulfillProc textureFulfillProc,
|
||||
PromiseImageTextureReleaseProc textureReleaseProc,
|
||||
PromiseImageTextureDoneProc textureDoneProc,
|
||||
PromiseImageTextureContext textureContext) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
sk_sp<SkImage> SkDeferredDisplayListRecorder::makePromiseTexture(
|
||||
const GrBackendFormat& backendFormat,
|
||||
int width,
|
||||
int height,
|
||||
GrMipMapped mipMapped,
|
||||
GrSurfaceOrigin origin,
|
||||
SkColorType colorType,
|
||||
SkAlphaType alphaType,
|
||||
sk_sp<SkColorSpace> colorSpace,
|
||||
LegacyPromiseImageTextureFulfillProc textureFulfillProc,
|
||||
LegacyPromiseImageTextureReleaseProc textureReleaseProc,
|
||||
PromiseImageTextureDoneProc textureDoneProc,
|
||||
PromiseImageTextureContext textureContext) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
sk_sp<SkImage> SkDeferredDisplayListRecorder::makeYUVAPromiseTexture(
|
||||
SkYUVColorSpace yuvColorSpace,
|
||||
const GrBackendFormat yuvaFormats[],
|
||||
const SkISize yuvaSizes[],
|
||||
const SkYUVAIndex yuvaIndices[4],
|
||||
int imageWidth,
|
||||
int imageHeight,
|
||||
GrSurfaceOrigin imageOrigin,
|
||||
sk_sp<SkColorSpace> imageColorSpace,
|
||||
TextureFulfillProc textureFulfillProc,
|
||||
TextureReleaseProc textureReleaseProc,
|
||||
PromiseDoneProc promiseDoneProc,
|
||||
TextureContext textureContexts[]) {
|
||||
SkYUVColorSpace yuvColorSpace,
|
||||
const GrBackendFormat yuvaFormats[],
|
||||
const SkISize yuvaSizes[],
|
||||
const SkYUVAIndex yuvaIndices[4],
|
||||
int imageWidth,
|
||||
int imageHeight,
|
||||
GrSurfaceOrigin imageOrigin,
|
||||
sk_sp<SkColorSpace> imageColorSpace,
|
||||
PromiseImageTextureFulfillProc textureFulfillProc,
|
||||
PromiseImageTextureReleaseProc textureReleaseProc,
|
||||
PromiseImageTextureDoneProc textureDoneProc,
|
||||
PromiseImageTextureContext textureContexts[]) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
sk_sp<SkImage> SkDeferredDisplayListRecorder::makeYUVAPromiseTexture(
|
||||
SkYUVColorSpace yuvColorSpace,
|
||||
const GrBackendFormat yuvaFormats[],
|
||||
const SkISize yuvaSizes[],
|
||||
const SkYUVAIndex yuvaIndices[4],
|
||||
int imageWidth,
|
||||
int imageHeight,
|
||||
GrSurfaceOrigin imageOrigin,
|
||||
sk_sp<SkColorSpace> imageColorSpace,
|
||||
LegacyPromiseImageTextureFulfillProc textureFulfillProc,
|
||||
LegacyPromiseImageTextureReleaseProc textureReleaseProc,
|
||||
PromiseImageTextureDoneProc textureDoneProc,
|
||||
PromiseImageTextureContext textureContexts[]) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -60,10 +92,11 @@ sk_sp<SkImage> SkDeferredDisplayListRecorder::makeYUVAPromiseTexture(
|
||||
#include "GrProxyProvider.h"
|
||||
#include "GrRenderTargetContext.h"
|
||||
#include "GrTexture.h"
|
||||
|
||||
#include "SkGr.h"
|
||||
#include "SkImage_Gpu.h"
|
||||
#include "SkImage_GpuYUVA.h"
|
||||
#include "SkMakeUnique.h"
|
||||
#include "SkPromiseImageTexture.h"
|
||||
#include "SkSurface_Gpu.h"
|
||||
#include "SkYUVASizeInfo.h"
|
||||
|
||||
@ -216,10 +249,10 @@ sk_sp<SkImage> SkDeferredDisplayListRecorder::makePromiseTexture(
|
||||
SkColorType colorType,
|
||||
SkAlphaType alphaType,
|
||||
sk_sp<SkColorSpace> colorSpace,
|
||||
TextureFulfillProc textureFulfillProc,
|
||||
TextureReleaseProc textureReleaseProc,
|
||||
PromiseDoneProc promiseDoneProc,
|
||||
TextureContext textureContext) {
|
||||
PromiseImageTextureFulfillProc textureFulfillProc,
|
||||
PromiseImageTextureReleaseProc textureReleaseProc,
|
||||
PromiseImageTextureDoneProc textureDoneProc,
|
||||
PromiseImageTextureContext textureContext) {
|
||||
if (!fContext) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -235,10 +268,107 @@ sk_sp<SkImage> SkDeferredDisplayListRecorder::makePromiseTexture(
|
||||
std::move(colorSpace),
|
||||
textureFulfillProc,
|
||||
textureReleaseProc,
|
||||
promiseDoneProc,
|
||||
textureDoneProc,
|
||||
textureContext);
|
||||
}
|
||||
|
||||
// Converts from the old legacy APIs based on GrBackendTexture to the new implementation based on
|
||||
// PromiseImageTexture.
|
||||
static void wrap_legacy(
|
||||
SkDeferredDisplayListRecorder::LegacyPromiseImageTextureFulfillProc textureFulfillProc,
|
||||
SkDeferredDisplayListRecorder::LegacyPromiseImageTextureReleaseProc textureReleaseProc,
|
||||
SkDeferredDisplayListRecorder::PromiseImageTextureDoneProc textureDoneProc,
|
||||
const SkDeferredDisplayListRecorder::PromiseImageTextureContext textureContexts[],
|
||||
int numTextures,
|
||||
SkDeferredDisplayListRecorder::PromiseImageTextureFulfillProc* wrappedFulfillProc,
|
||||
SkDeferredDisplayListRecorder::PromiseImageTextureReleaseProc* wrappedReleaseProc,
|
||||
SkDeferredDisplayListRecorder::PromiseImageTextureDoneProc* wrappedDoneProc,
|
||||
SkDeferredDisplayListRecorder::PromiseImageTextureContext wrappedTextureContext[]) {
|
||||
struct WrapperContext {
|
||||
SkDeferredDisplayListRecorder::LegacyPromiseImageTextureFulfillProc fLegacyFulfill;
|
||||
SkDeferredDisplayListRecorder::LegacyPromiseImageTextureReleaseProc fLegacyRelease;
|
||||
SkDeferredDisplayListRecorder::PromiseImageTextureDoneProc fDone;
|
||||
SkDeferredDisplayListRecorder::PromiseImageTextureContext fOriginalContext;
|
||||
std::unique_ptr<SkPromiseImageTexture> fPromiseImageTexture;
|
||||
};
|
||||
*wrappedFulfillProc = [](SkDeferredDisplayListRecorder::PromiseImageTextureContext context) {
|
||||
auto* wc = static_cast<WrapperContext*>(context);
|
||||
GrBackendTexture backendTexture;
|
||||
wc->fLegacyFulfill(wc->fOriginalContext, &backendTexture);
|
||||
wc->fPromiseImageTexture = skstd::make_unique<SkPromiseImageTexture>(backendTexture);
|
||||
return wc->fPromiseImageTexture.get();
|
||||
};
|
||||
*wrappedReleaseProc = [](SkDeferredDisplayListRecorder::PromiseImageTextureContext context,
|
||||
const SkPromiseImageTexture*) {
|
||||
auto* wc = static_cast<WrapperContext*>(context);
|
||||
wc->fLegacyRelease(wc->fOriginalContext);
|
||||
wc->fPromiseImageTexture.reset();
|
||||
};
|
||||
*wrappedDoneProc = [](SkDeferredDisplayListRecorder::PromiseImageTextureContext context) {
|
||||
const auto* wc = static_cast<WrapperContext*>(context);
|
||||
wc->fDone(wc->fOriginalContext);
|
||||
SkASSERT(!wc->fPromiseImageTexture);
|
||||
delete wc;
|
||||
};
|
||||
for (int i = 0; i < numTextures; ++i) {
|
||||
wrappedTextureContext[i] = new WrapperContext{textureFulfillProc, textureReleaseProc,
|
||||
textureDoneProc, textureContexts[i], nullptr};
|
||||
}
|
||||
}
|
||||
|
||||
sk_sp<SkImage> SkDeferredDisplayListRecorder::makePromiseTexture(
|
||||
const GrBackendFormat& backendFormat,
|
||||
int width,
|
||||
int height,
|
||||
GrMipMapped mipMapped,
|
||||
GrSurfaceOrigin origin,
|
||||
SkColorType colorType,
|
||||
SkAlphaType alphaType,
|
||||
sk_sp<SkColorSpace> colorSpace,
|
||||
LegacyPromiseImageTextureFulfillProc textureFulfillProc,
|
||||
LegacyPromiseImageTextureReleaseProc textureReleaseProc,
|
||||
PromiseImageTextureDoneProc textureDoneProc,
|
||||
PromiseImageTextureContext textureContext) {
|
||||
if (!fContext) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SkDeferredDisplayListRecorder::PromiseImageTextureFulfillProc wrappedFulfillProc;
|
||||
SkDeferredDisplayListRecorder::PromiseImageTextureReleaseProc wrappedReleaseProc;
|
||||
SkDeferredDisplayListRecorder::PromiseImageTextureDoneProc wrappedDoneProc;
|
||||
SkDeferredDisplayListRecorder::PromiseImageTextureContext wrappedTextureContext;
|
||||
if (!textureDoneProc) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!textureFulfillProc || !textureReleaseProc) {
|
||||
textureDoneProc(textureContext);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
wrap_legacy(textureFulfillProc,
|
||||
textureReleaseProc,
|
||||
textureDoneProc,
|
||||
&textureContext,
|
||||
1,
|
||||
&wrappedFulfillProc,
|
||||
&wrappedReleaseProc,
|
||||
&wrappedDoneProc,
|
||||
&wrappedTextureContext);
|
||||
return SkImage_Gpu::MakePromiseTexture(fContext.get(),
|
||||
backendFormat,
|
||||
width,
|
||||
height,
|
||||
mipMapped,
|
||||
origin,
|
||||
colorType,
|
||||
alphaType,
|
||||
std::move(colorSpace),
|
||||
wrappedFulfillProc,
|
||||
wrappedReleaseProc,
|
||||
wrappedDoneProc,
|
||||
wrappedTextureContext);
|
||||
}
|
||||
|
||||
sk_sp<SkImage> SkDeferredDisplayListRecorder::makeYUVAPromiseTexture(
|
||||
SkYUVColorSpace yuvColorSpace,
|
||||
const GrBackendFormat yuvaFormats[],
|
||||
@ -248,10 +378,10 @@ sk_sp<SkImage> SkDeferredDisplayListRecorder::makeYUVAPromiseTexture(
|
||||
int imageHeight,
|
||||
GrSurfaceOrigin imageOrigin,
|
||||
sk_sp<SkColorSpace> imageColorSpace,
|
||||
TextureFulfillProc textureFulfillProc,
|
||||
TextureReleaseProc textureReleaseProc,
|
||||
PromiseDoneProc promiseDoneProc,
|
||||
TextureContext textureContexts[]) {
|
||||
PromiseImageTextureFulfillProc textureFulfillProc,
|
||||
PromiseImageTextureReleaseProc textureReleaseProc,
|
||||
PromiseImageTextureDoneProc textureDoneProc,
|
||||
PromiseImageTextureContext textureContexts[]) {
|
||||
if (!fContext) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -267,8 +397,66 @@ sk_sp<SkImage> SkDeferredDisplayListRecorder::makeYUVAPromiseTexture(
|
||||
std::move(imageColorSpace),
|
||||
textureFulfillProc,
|
||||
textureReleaseProc,
|
||||
promiseDoneProc,
|
||||
textureDoneProc,
|
||||
textureContexts);
|
||||
}
|
||||
|
||||
sk_sp<SkImage> SkDeferredDisplayListRecorder::makeYUVAPromiseTexture(
|
||||
SkYUVColorSpace yuvColorSpace,
|
||||
const GrBackendFormat yuvaFormats[],
|
||||
const SkISize yuvaSizes[],
|
||||
const SkYUVAIndex yuvaIndices[4],
|
||||
int imageWidth,
|
||||
int imageHeight,
|
||||
GrSurfaceOrigin imageOrigin,
|
||||
sk_sp<SkColorSpace> imageColorSpace,
|
||||
LegacyPromiseImageTextureFulfillProc textureFulfillProc,
|
||||
LegacyPromiseImageTextureReleaseProc textureReleaseProc,
|
||||
PromiseImageTextureDoneProc textureDoneProc,
|
||||
PromiseImageTextureContext textureContexts[]) {
|
||||
if (!fContext) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int numTextures;
|
||||
bool valid = SkYUVAIndex::AreValidIndices(yuvaIndices, &numTextures);
|
||||
|
||||
SkDeferredDisplayListRecorder::PromiseImageTextureFulfillProc wrappedFulfillProc;
|
||||
SkDeferredDisplayListRecorder::PromiseImageTextureReleaseProc wrappedReleaseProc;
|
||||
SkDeferredDisplayListRecorder::PromiseImageTextureDoneProc wrappedDoneProc;
|
||||
SkDeferredDisplayListRecorder::PromiseImageTextureContext wrappedTextureContexts[4];
|
||||
if (!textureDoneProc) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!valid || !textureFulfillProc || !textureReleaseProc) {
|
||||
for (int i = 0; i < numTextures; ++i) {
|
||||
textureDoneProc(textureContexts[i]);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
wrap_legacy(textureFulfillProc,
|
||||
textureReleaseProc,
|
||||
textureDoneProc,
|
||||
textureContexts,
|
||||
numTextures,
|
||||
&wrappedFulfillProc,
|
||||
&wrappedReleaseProc,
|
||||
&wrappedDoneProc,
|
||||
wrappedTextureContexts);
|
||||
|
||||
return SkImage_GpuYUVA::MakePromiseYUVATexture(fContext.get(),
|
||||
yuvColorSpace,
|
||||
yuvaFormats,
|
||||
yuvaSizes,
|
||||
yuvaIndices,
|
||||
imageWidth,
|
||||
imageHeight,
|
||||
imageOrigin,
|
||||
std::move(imageColorSpace),
|
||||
wrappedFulfillProc,
|
||||
wrappedReleaseProc,
|
||||
wrappedDoneProc,
|
||||
wrappedTextureContexts);
|
||||
}
|
||||
#endif
|
||||
|
61
src/core/SkPromiseImageTexture.cpp
Normal file
61
src/core/SkPromiseImageTexture.cpp
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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 "SkPromiseImageTexture.h"
|
||||
#include "SkMessageBus.h"
|
||||
|
||||
#if SK_SUPPORT_GPU
|
||||
|
||||
std::atomic<uint32_t> SkPromiseImageTexture::gUniqueID{1};
|
||||
|
||||
SkPromiseImageTexture::SkPromiseImageTexture(const GrBackendTexture& backendTexture) {
|
||||
if (backendTexture.isValid()) {
|
||||
fBackendTexture = backendTexture;
|
||||
fUniqueID = gUniqueID++;
|
||||
}
|
||||
}
|
||||
|
||||
SkPromiseImageTexture::SkPromiseImageTexture(SkPromiseImageTexture&& that) {
|
||||
*this = std::move(that);
|
||||
}
|
||||
|
||||
SkPromiseImageTexture& SkPromiseImageTexture::operator=(SkPromiseImageTexture&& that) {
|
||||
for (const auto& msg : fMessages) {
|
||||
SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(msg);
|
||||
}
|
||||
fMessages = that.fMessages;
|
||||
that.fMessages.reset();
|
||||
fBackendTexture = that.fBackendTexture;
|
||||
that.fBackendTexture = {};
|
||||
fUniqueID = that.fUniqueID;
|
||||
that.fUniqueID = SK_InvalidUniqueID;
|
||||
return *this;
|
||||
}
|
||||
|
||||
SkPromiseImageTexture::~SkPromiseImageTexture() {
|
||||
for (const auto& msg : fMessages) {
|
||||
SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(msg);
|
||||
}
|
||||
}
|
||||
|
||||
void SkPromiseImageTexture::addKeyToInvalidate(uint32_t contextID, const GrUniqueKey& key) {
|
||||
SkASSERT(contextID != SK_InvalidUniqueID);
|
||||
SkASSERT(key.isValid());
|
||||
fMessages.emplace_back(key, contextID);
|
||||
}
|
||||
|
||||
#if GR_TEST_UTILS
|
||||
SkTArray<GrUniqueKey> SkPromiseImageTexture::testingOnly_uniqueKeysToInvalidate() const {
|
||||
SkTArray<GrUniqueKey> results;
|
||||
for (const auto& msg : fMessages) {
|
||||
results.push_back(msg.key());
|
||||
}
|
||||
return results;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -66,8 +66,7 @@ public:
|
||||
* must be sure that if a resource of exists in the cache with the given unique key then it is
|
||||
* of type T.
|
||||
*/
|
||||
template <typename T>
|
||||
sk_sp<T> findByUniqueKey(const GrUniqueKey& key) {
|
||||
template <typename T = GrGpuResource> sk_sp<T> findByUniqueKey(const GrUniqueKey& key) {
|
||||
return sk_sp<T>(static_cast<T*>(this->findResourceByUniqueKey(key).release()));
|
||||
}
|
||||
|
||||
|
@ -322,10 +322,10 @@ sk_sp<SkImage> SkImage_Gpu::MakePromiseTexture(GrContext* context,
|
||||
SkColorType colorType,
|
||||
SkAlphaType alphaType,
|
||||
sk_sp<SkColorSpace> colorSpace,
|
||||
TextureFulfillProc textureFulfillProc,
|
||||
TextureReleaseProc textureReleaseProc,
|
||||
PromiseDoneProc promiseDoneProc,
|
||||
TextureContext textureContext) {
|
||||
PromiseImageTextureFulfillProc textureFulfillProc,
|
||||
PromiseImageTextureReleaseProc textureReleaseProc,
|
||||
PromiseImageTextureDoneProc promiseDoneProc,
|
||||
PromiseImageTextureContext textureContext) {
|
||||
// The contract here is that if 'promiseDoneProc' is passed in it should always be called,
|
||||
// even if creation of the SkImage fails. Once we call MakePromiseImageLazyProxy it takes
|
||||
// responsibility for calling the done proc.
|
||||
|
@ -76,8 +76,9 @@ public:
|
||||
@param colorSpace range of colors; may be nullptr
|
||||
@param textureFulfillProc function called to get actual gpu texture
|
||||
@param textureReleaseProc function called when texture can be released
|
||||
@param promiseDoneProc function called when we will no longer call textureFulfillProc
|
||||
@param textureContext state passed to textureFulfillProc and textureReleaseProc
|
||||
@param textureDoneProc function called when we will no longer call textureFulfillProc
|
||||
@param textureContext state passed to textureFulfillProc, textureReleaseProc, and
|
||||
promiseDoneProc
|
||||
@return created SkImage, or nullptr
|
||||
*/
|
||||
static sk_sp<SkImage> MakePromiseTexture(GrContext* context,
|
||||
@ -89,10 +90,10 @@ public:
|
||||
SkColorType colorType,
|
||||
SkAlphaType alphaType,
|
||||
sk_sp<SkColorSpace> colorSpace,
|
||||
TextureFulfillProc textureFulfillProc,
|
||||
TextureReleaseProc textureReleaseProc,
|
||||
PromiseDoneProc promiseDoneProc,
|
||||
TextureContext textureContext);
|
||||
PromiseImageTextureFulfillProc textureFulfillProc,
|
||||
PromiseImageTextureReleaseProc textureReleaseProc,
|
||||
PromiseImageTextureDoneProc textureDoneProc,
|
||||
PromiseImageTextureContext textureContext);
|
||||
|
||||
static sk_sp<SkImage> ConvertYUVATexturesToRGB(GrContext*, SkYUVColorSpace yuvColorSpace,
|
||||
const GrBackendTexture yuvaTextures[],
|
||||
|
@ -5,6 +5,7 @@
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "SkImage_GpuBase.h"
|
||||
#include "GrBackendSurface.h"
|
||||
#include "GrClip.h"
|
||||
#include "GrContext.h"
|
||||
@ -12,11 +13,11 @@
|
||||
#include "GrRenderTargetContext.h"
|
||||
#include "GrTexture.h"
|
||||
#include "GrTextureAdjuster.h"
|
||||
#include "effects/GrYUVtoRGBEffect.h"
|
||||
#include "SkBitmapCache.h"
|
||||
#include "SkImage_Gpu.h"
|
||||
#include "SkImage_GpuBase.h"
|
||||
#include "SkPromiseImageTexture.h"
|
||||
#include "SkReadPixelsRec.h"
|
||||
#include "effects/GrYUVtoRGBEffect.h"
|
||||
|
||||
SkImage_GpuBase::SkImage_GpuBase(sk_sp<GrContext> context, int width, int height, uint32_t uniqueID,
|
||||
SkAlphaType at, sk_sp<SkColorSpace> cs)
|
||||
@ -391,9 +392,10 @@ bool SkImage_GpuBase::RenderYUVAToRGBA(GrContext* ctx, GrRenderTargetContext* re
|
||||
sk_sp<GrTextureProxy> SkImage_GpuBase::MakePromiseImageLazyProxy(
|
||||
GrContext* context, int width, int height, GrSurfaceOrigin origin, GrPixelConfig config,
|
||||
GrBackendFormat backendFormat, GrMipMapped mipMapped,
|
||||
SkImage_GpuBase::TextureFulfillProc fulfillProc,
|
||||
SkImage_GpuBase::TextureReleaseProc releaseProc, SkImage_GpuBase::PromiseDoneProc doneProc,
|
||||
SkImage_GpuBase::TextureContext textureContext) {
|
||||
PromiseImageTextureFulfillProc fulfillProc,
|
||||
PromiseImageTextureReleaseProc releaseProc,
|
||||
PromiseImageTextureDoneProc doneProc,
|
||||
PromiseImageTextureContext textureContext) {
|
||||
SkASSERT(context);
|
||||
SkASSERT(width > 0 && height > 0);
|
||||
SkASSERT(doneProc);
|
||||
@ -413,115 +415,169 @@ sk_sp<GrTextureProxy> SkImage_GpuBase::MakePromiseImageLazyProxy(
|
||||
}
|
||||
|
||||
/**
|
||||
* This helper class manages the ref counting for the the ReleaseProc and DoneProc for promise
|
||||
* images. It holds a weak ref on the ReleaseProc (hard refs are owned by GrTextures). The weak
|
||||
* ref allows us to reuse an outstanding ReleaseProc (because we dropped our GrTexture but the
|
||||
* GrTexture isn't done on the GPU) without needing to call FulfillProc again. It also holds a
|
||||
* hard ref on the DoneProc. The idea is that after every flush we may call the ReleaseProc so
|
||||
* that the client can free up their GPU memory if they want to. The life time of the DoneProc
|
||||
* matches that of any outstanding ReleaseProc as well as the PromiseLazyInstantiateCallback.
|
||||
* Thus we won't call the DoneProc until all ReleaseProcs are finished and we are finished with
|
||||
* the PromiseImageHelper (i.e. won't call FulfillProc again).
|
||||
* This class is the lazy instantiation callback for promise images. It manages calling the
|
||||
* client's Fulfill, Release, and Done procs. It attempts to reuse a GrTexture instance in
|
||||
* cases where the client provides the same SkPromiseImageTexture for successive Fulfill calls.
|
||||
* The created GrTexture is given a key based on a unique ID associated with the
|
||||
* SkPromiseImageTexture. When the texture enters "idle" state (meaning it is not being used by
|
||||
* the GPU and is at rest in the resource cache) the client's Release proc is called
|
||||
* using GrTexture's idle proc mechanism. If the same SkPromiseImageTexture is provided for
|
||||
* another fulfill we find the cached GrTexture. If the proxy, and therefore this object,
|
||||
* is destroyed, we invalidate the GrTexture's key. Also if the client overwrites or
|
||||
* destroys their SkPromiseImageTexture we invalidate the key.
|
||||
*
|
||||
* Currently a GrTexture is only reused for a given SkPromiseImageTexture if the
|
||||
* SkPromiseImageTexture is reused in Fulfill for the same promise SkImage. However, we'd
|
||||
* like to relax that so that a SkPromiseImageTexture can be reused with different promise
|
||||
* SkImages that will reuse a single GrTexture.
|
||||
*/
|
||||
class PromiseLazyInstantiateCallback {
|
||||
public:
|
||||
PromiseLazyInstantiateCallback(SkImage_GpuBase::TextureFulfillProc fulfillProc,
|
||||
SkImage_GpuBase::TextureReleaseProc releaseProc,
|
||||
SkImage_GpuBase::PromiseDoneProc doneProc,
|
||||
SkImage_GpuBase::TextureContext context,
|
||||
PromiseLazyInstantiateCallback(PromiseImageTextureFulfillProc fulfillProc,
|
||||
PromiseImageTextureReleaseProc releaseProc,
|
||||
PromiseImageTextureDoneProc doneProc,
|
||||
PromiseImageTextureContext context,
|
||||
GrPixelConfig config)
|
||||
: fFulfillProc(fulfillProc)
|
||||
, fReleaseProc(releaseProc)
|
||||
, fContext(context)
|
||||
, fConfig(config) {
|
||||
fDoneHelper.reset(new GrReleaseProcHelper(doneProc, context));
|
||||
fDoneHelper = sk_make_sp<GrReleaseProcHelper>(doneProc, context);
|
||||
static std::atomic<uint32_t> gUniqueID;
|
||||
fUniqueID = gUniqueID.fetch_add(1) + 1;
|
||||
}
|
||||
~PromiseLazyInstantiateCallback() {
|
||||
// If we've already released the texture then it is safe to call done now. Here we may
|
||||
// be on any thread.
|
||||
if (fIdleContext && fIdleContext->fWasReleased.load()) {
|
||||
// We still own a ref on fDoneHelper so no other thread can be calling the done
|
||||
// proc.
|
||||
fDoneHelper->callAndClear();
|
||||
}
|
||||
// Remove the key from the texture so that the texture will be removed from the cache.
|
||||
// If we didn't just call the done proc above then it will get called when the texture
|
||||
// is removed from the cache after this message is processed.
|
||||
if (fLastFulfilledKey.isValid()) {
|
||||
SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(
|
||||
GrUniqueKeyInvalidatedMessage(fLastFulfilledKey, fContextID));
|
||||
}
|
||||
}
|
||||
|
||||
sk_sp<GrSurface> operator()(GrResourceProvider* resourceProvider) {
|
||||
if (!resourceProvider) {
|
||||
this->reset();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
sk_sp<GrTexture> cachedTexture;
|
||||
SkASSERT(fLastFulfilledKey.isValid() == (fLastFulfillID > 0));
|
||||
if (fLastFulfilledKey.isValid()) {
|
||||
auto surf = resourceProvider->findByUniqueKey<GrSurface>(fLastFulfilledKey);
|
||||
if (surf) {
|
||||
cachedTexture = sk_ref_sp(surf->asTexture());
|
||||
SkASSERT(cachedTexture);
|
||||
}
|
||||
}
|
||||
// If the release callback hasn't been called already by releasing the GrTexture
|
||||
// then we can be sure that won't happen so long as we have a ref to the texture.
|
||||
// Moreoever, only this thread should be able to change the atomic to true, hence the
|
||||
// relaxed memory order.
|
||||
if (cachedTexture && !fIdleContext->fWasReleased.load(std::memory_order_relaxed)) {
|
||||
return std::move(cachedTexture);
|
||||
}
|
||||
GrBackendTexture backendTexture;
|
||||
SkPromiseImageTexture* promiseTexture = fFulfillProc(fContext);
|
||||
if (!promiseTexture) {
|
||||
fReleaseProc(fContext, nullptr);
|
||||
return sk_sp<GrTexture>();
|
||||
}
|
||||
bool same = promiseTexture->uniqueID() == fLastFulfillID;
|
||||
SkASSERT(!same || fLastFulfilledKey.isValid());
|
||||
if (same && cachedTexture) {
|
||||
SkASSERT(fIdleContext->unique());
|
||||
// Reset the purgeable context so that we balance the new fulfill with a release.
|
||||
fIdleContext->ref();
|
||||
SkASSERT(fIdleContext->fReleaseProc == fReleaseProc);
|
||||
SkASSERT(fIdleContext->fTextureContext == fContext);
|
||||
// Memory order relaxed because only this thread can change fWasReleased to true.
|
||||
fIdleContext->fWasReleased.store(false, std::memory_order_relaxed);
|
||||
cachedTexture->setIdleProc(IdleProc, fIdleContext.get());
|
||||
return std::move(cachedTexture);
|
||||
} else if (cachedTexture) {
|
||||
cachedTexture->resourcePriv().removeUniqueKey();
|
||||
// We don't want calling the client's done proc to be tied to the old texture.
|
||||
cachedTexture->setRelease(nullptr);
|
||||
}
|
||||
fLastFulfillID = promiseTexture->uniqueID();
|
||||
|
||||
backendTexture = promiseTexture->backendTexture();
|
||||
backendTexture.fConfig = fConfig;
|
||||
if (!backendTexture.isValid()) {
|
||||
// Even though the GrBackendTexture is not valid, we must call the release
|
||||
// proc to keep our contract of always calling Fulfill and Release in pairs.
|
||||
fReleaseProc(fContext, promiseTexture);
|
||||
return sk_sp<GrTexture>();
|
||||
}
|
||||
|
||||
// Releases the promise helper if there are no outstanding hard refs. This means that we
|
||||
// don't have any ReleaseProcs waiting to be called so we will need to do a fulfill.
|
||||
if (fReleaseHelper && fReleaseHelper->weak_expired()) {
|
||||
this->resetReleaseHelper();
|
||||
auto tex = resourceProvider->wrapBackendTexture(backendTexture, kBorrow_GrWrapOwnership,
|
||||
kRead_GrIOType);
|
||||
if (!tex) {
|
||||
// Even though we failed to wrap the backend texture, we must call the release
|
||||
// proc to keep our contract of always calling Fulfill and Release in pairs.
|
||||
fReleaseProc(fContext, promiseTexture);
|
||||
return sk_sp<GrTexture>();
|
||||
}
|
||||
|
||||
sk_sp<GrTexture> tex;
|
||||
if (!fReleaseHelper) {
|
||||
fFulfillProc(fContext, &fBackendTex);
|
||||
fBackendTex.fConfig = fConfig;
|
||||
if (!fBackendTex.isValid()) {
|
||||
// Even though the GrBackendTexture is not valid, we must call the release
|
||||
// proc to keep our contract of always calling Fulfill and Release in pairs.
|
||||
fReleaseProc(fContext);
|
||||
return sk_sp<GrTexture>();
|
||||
}
|
||||
|
||||
tex = resourceProvider->wrapBackendTexture(fBackendTex, kBorrow_GrWrapOwnership,
|
||||
kRead_GrIOType);
|
||||
if (!tex) {
|
||||
// Even though the GrBackendTexture is not valid, we must call the release
|
||||
// proc to keep our contract of always calling Fulfill and Release in pairs.
|
||||
fReleaseProc(fContext);
|
||||
return sk_sp<GrTexture>();
|
||||
}
|
||||
fReleaseHelper =
|
||||
new SkPromiseReleaseProcHelper(fReleaseProc, fContext, fDoneHelper);
|
||||
// Take a weak ref
|
||||
fReleaseHelper->weak_ref();
|
||||
} else {
|
||||
SkASSERT(fBackendTex.isValid());
|
||||
tex = resourceProvider->wrapBackendTexture(fBackendTex, kBorrow_GrWrapOwnership,
|
||||
kRead_GrIOType);
|
||||
if (!tex) {
|
||||
// We weren't able to make a texture here, but since we are in this branch
|
||||
// of the calls (promiseHelper.fReleaseHelper is valid) there is already a
|
||||
// texture out there which will call the release proc so we don't need to
|
||||
// call it here.
|
||||
return sk_sp<GrTexture>();
|
||||
}
|
||||
|
||||
SkAssertResult(fReleaseHelper->try_ref());
|
||||
}
|
||||
SkASSERT(tex);
|
||||
// Pass the hard ref off to the texture
|
||||
tex->setRelease(sk_sp<GrReleaseProcHelper>(fReleaseHelper));
|
||||
fIdleContext = sk_make_sp<IdleContext>(fReleaseProc, fContext, promiseTexture);
|
||||
// The texture gets a ref, which is balanced when the idle callback is called.
|
||||
fIdleContext->ref();
|
||||
tex->setIdleProc(IdleProc, fIdleContext.get());
|
||||
tex->setRelease(fDoneHelper);
|
||||
static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
|
||||
GrUniqueKey::Builder builder(&fLastFulfilledKey, kDomain, 2, "promise");
|
||||
builder[0] = promiseTexture->uniqueID();
|
||||
builder[1] = fUniqueID;
|
||||
builder.finish();
|
||||
tex->resourcePriv().setUniqueKey(fLastFulfilledKey);
|
||||
SkASSERT(fContextID == SK_InvalidUniqueID ||
|
||||
fContextID == tex->getContext()->uniqueID());
|
||||
fContextID = tex->getContext()->uniqueID();
|
||||
promiseTexture->addKeyToInvalidate(fContextID, fLastFulfilledKey);
|
||||
return std::move(tex);
|
||||
}
|
||||
|
||||
private:
|
||||
void reset() {
|
||||
this->resetReleaseHelper();
|
||||
fDoneHelper.reset();
|
||||
struct IdleContext : public SkNVRefCnt<IdleContext> {
|
||||
IdleContext(PromiseImageTextureReleaseProc proc, PromiseImageTextureContext context,
|
||||
const SkPromiseImageTexture* texture)
|
||||
: fReleaseProc(proc), fTextureContext(context), fPromiseImageTexture(texture) {}
|
||||
PromiseImageTextureReleaseProc fReleaseProc;
|
||||
PromiseImageTextureContext fTextureContext;
|
||||
const SkPromiseImageTexture* fPromiseImageTexture;
|
||||
std::atomic<bool> fWasReleased{false};
|
||||
};
|
||||
static void IdleProc(void* context) {
|
||||
IdleContext* rc = static_cast<IdleContext*>(context);
|
||||
SkASSERT(!rc->fWasReleased.load());
|
||||
rc->fReleaseProc(rc->fTextureContext, rc->fPromiseImageTexture);
|
||||
rc->fWasReleased.store(true);
|
||||
// Drop the texture's implicit ref on the IdleContext.
|
||||
rc->unref();
|
||||
}
|
||||
|
||||
// Weak unrefs fReleaseHelper and sets it to null
|
||||
void resetReleaseHelper() {
|
||||
if (fReleaseHelper) {
|
||||
fReleaseHelper->weak_unref();
|
||||
fReleaseHelper = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
SkImage_GpuBase::TextureFulfillProc fFulfillProc;
|
||||
SkImage_GpuBase::TextureReleaseProc fReleaseProc;
|
||||
SkImage_GpuBase::TextureContext fContext;
|
||||
PromiseImageTextureFulfillProc fFulfillProc;
|
||||
PromiseImageTextureReleaseProc fReleaseProc;
|
||||
PromiseImageTextureContext fContext;
|
||||
|
||||
GrPixelConfig fConfig;
|
||||
// We cache the GrBackendTexture so that if we deleted the GrTexture but the the release
|
||||
// proc has yet not been called (this can happen on Vulkan), then we can create a new
|
||||
// texture without needing to call the fulfill proc again.
|
||||
GrBackendTexture fBackendTex;
|
||||
// The fReleaseHelper is used to track a weak ref on the release proc. This helps us make
|
||||
// sure we are always pairing fulfill and release proc calls correctly.
|
||||
SkPromiseReleaseProcHelper* fReleaseHelper = nullptr;
|
||||
// We don't want to call the fDoneHelper until we are done with the PromiseImageHelper and
|
||||
// all ReleaseHelpers are finished. Thus we hold a hard ref here and we will pass a hard ref
|
||||
// to each fReleaseHelper we make.
|
||||
sk_sp<IdleContext> fIdleContext;
|
||||
sk_sp<GrReleaseProcHelper> fDoneHelper;
|
||||
|
||||
// ID of the last SkPromiseImageTexture given to us by the client.
|
||||
uint32_t fLastFulfillID = 0;
|
||||
// ID of the GrContext that we are interacting with.
|
||||
uint32_t fContextID = SK_InvalidUniqueID;
|
||||
// Unique ID of this lazy instantiation callback.
|
||||
uint32_t fUniqueID;
|
||||
GrUniqueKey fLastFulfilledKey;
|
||||
} callback(fulfillProc, releaseProc, doneProc, textureContext, config);
|
||||
|
||||
GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider();
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
#include "GrBackendSurface.h"
|
||||
#include "GrTypesPriv.h"
|
||||
#include "SkDeferredDisplayListRecorder.h"
|
||||
#include "SkImage_Base.h"
|
||||
#include "SkYUVAIndex.h"
|
||||
|
||||
@ -72,9 +73,12 @@ public:
|
||||
: kOpaque_SkAlphaType;
|
||||
}
|
||||
|
||||
typedef ReleaseContext TextureContext;
|
||||
typedef void(*TextureFulfillProc)(TextureContext textureContext, GrBackendTexture* outTexture);
|
||||
typedef void(*PromiseDoneProc)(TextureContext textureContext);
|
||||
using PromiseImageTextureContext = SkDeferredDisplayListRecorder::PromiseImageTextureContext;
|
||||
using PromiseImageTextureFulfillProc =
|
||||
SkDeferredDisplayListRecorder::PromiseImageTextureFulfillProc;
|
||||
using PromiseImageTextureReleaseProc =
|
||||
SkDeferredDisplayListRecorder::PromiseImageTextureReleaseProc;
|
||||
using PromiseImageTextureDoneProc = SkDeferredDisplayListRecorder::PromiseImageTextureDoneProc;
|
||||
|
||||
protected:
|
||||
// Helper for making a lazy proxy for a promise image. The PromiseDoneProc we be called,
|
||||
@ -83,8 +87,8 @@ protected:
|
||||
// be null.
|
||||
static sk_sp<GrTextureProxy> MakePromiseImageLazyProxy(
|
||||
GrContext*, int width, int height, GrSurfaceOrigin, GrPixelConfig, GrBackendFormat,
|
||||
GrMipMapped, SkImage_GpuBase::TextureFulfillProc, SkImage_GpuBase::TextureReleaseProc,
|
||||
SkImage_GpuBase::PromiseDoneProc, SkImage_GpuBase::TextureContext);
|
||||
GrMipMapped, PromiseImageTextureFulfillProc, PromiseImageTextureReleaseProc,
|
||||
PromiseImageTextureDoneProc, PromiseImageTextureContext);
|
||||
|
||||
static bool RenderYUVAToRGBA(GrContext* ctx, GrRenderTargetContext* renderTargetContext,
|
||||
const SkRect& rect, SkYUVColorSpace yuvColorSpace,
|
||||
@ -99,31 +103,4 @@ private:
|
||||
typedef SkImage_Base INHERITED;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* This helper holds the normal hard ref for the Release proc as well as a hard ref on the DoneProc.
|
||||
* Thus when a GrTexture is being released, it will unref both the ReleaseProc and DoneProc.
|
||||
*/
|
||||
class SkPromiseReleaseProcHelper : public GrReleaseProcHelper {
|
||||
public:
|
||||
SkPromiseReleaseProcHelper(SkImage_GpuBase::TextureReleaseProc releaseProc,
|
||||
SkImage_GpuBase::TextureContext context,
|
||||
sk_sp<GrReleaseProcHelper> doneHelper)
|
||||
: INHERITED(releaseProc, context)
|
||||
, fDoneProcHelper(std::move(doneHelper)) {
|
||||
}
|
||||
|
||||
void weak_dispose() const override {
|
||||
// Call the inherited weak_dispose first so that we call the ReleaseProc before the DoneProc
|
||||
// if we hold the last ref to the DoneProc.
|
||||
INHERITED::weak_dispose();
|
||||
fDoneProcHelper.reset();
|
||||
}
|
||||
|
||||
private:
|
||||
mutable sk_sp<GrReleaseProcHelper> fDoneProcHelper;
|
||||
|
||||
typedef GrReleaseProcHelper INHERITED;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -206,19 +206,20 @@ sk_sp<SkImage> SkImage::MakeFromYUVAPixmaps(
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
sk_sp<SkImage> SkImage_GpuYUVA::MakePromiseYUVATexture(GrContext* context,
|
||||
SkYUVColorSpace yuvColorSpace,
|
||||
const GrBackendFormat yuvaFormats[],
|
||||
const SkISize yuvaSizes[],
|
||||
const SkYUVAIndex yuvaIndices[4],
|
||||
int imageWidth,
|
||||
int imageHeight,
|
||||
GrSurfaceOrigin imageOrigin,
|
||||
sk_sp<SkColorSpace> imageColorSpace,
|
||||
TextureFulfillProc textureFulfillProc,
|
||||
TextureReleaseProc textureReleaseProc,
|
||||
PromiseDoneProc promiseDoneProc,
|
||||
TextureContext textureContexts[]) {
|
||||
sk_sp<SkImage> SkImage_GpuYUVA::MakePromiseYUVATexture(
|
||||
GrContext* context,
|
||||
SkYUVColorSpace yuvColorSpace,
|
||||
const GrBackendFormat yuvaFormats[],
|
||||
const SkISize yuvaSizes[],
|
||||
const SkYUVAIndex yuvaIndices[4],
|
||||
int imageWidth,
|
||||
int imageHeight,
|
||||
GrSurfaceOrigin imageOrigin,
|
||||
sk_sp<SkColorSpace> imageColorSpace,
|
||||
PromiseImageTextureFulfillProc textureFulfillProc,
|
||||
PromiseImageTextureReleaseProc textureReleaseProc,
|
||||
PromiseImageTextureDoneProc promiseDoneProc,
|
||||
PromiseImageTextureContext textureContexts[]) {
|
||||
int numTextures;
|
||||
bool valid = SkYUVAIndex::AreValidIndices(yuvaIndices, &numTextures);
|
||||
|
||||
@ -289,4 +290,3 @@ sk_sp<SkImage> SkImage_GpuYUVA::MakePromiseYUVATexture(GrContext* context,
|
||||
kNeedNewImageUniqueID, yuvColorSpace, proxies, numTextures,
|
||||
yuvaIndices, imageOrigin, std::move(imageColorSpace));
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,6 @@ public:
|
||||
// Returns a ref-ed texture proxy with miplevels
|
||||
sk_sp<GrTextureProxy> asMippedTextureProxyRef() const;
|
||||
|
||||
|
||||
/**
|
||||
Create a new SkImage_GpuYUVA that's very similar to SkImage created by MakeFromYUVATextures.
|
||||
The main difference is that the client doesn't have the backend textures on the gpu yet but
|
||||
@ -90,8 +89,9 @@ public:
|
||||
@param imageColorSpace range of colors; may be nullptr
|
||||
@param textureFulfillProc function called to get actual gpu texture
|
||||
@param textureReleaseProc function called when texture can be released
|
||||
@param promiseDoneProc function called when we will no longer call textureFulfillProc
|
||||
@param textureContext state passed to textureFulfillProc and textureReleaseProc
|
||||
@param textureDoneProc function called when we will no longer call textureFulfillProc
|
||||
@param textureContexts per-texture state passed to textureFulfillProc,
|
||||
textureReleaseProc, and textureDoneProc
|
||||
@return created SkImage, or nullptr
|
||||
*/
|
||||
static sk_sp<SkImage> MakePromiseYUVATexture(GrContext* context,
|
||||
@ -103,10 +103,10 @@ public:
|
||||
int height,
|
||||
GrSurfaceOrigin imageOrigin,
|
||||
sk_sp<SkColorSpace> imageColorSpace,
|
||||
TextureFulfillProc textureFulfillProc,
|
||||
TextureReleaseProc textureReleaseProc,
|
||||
PromiseDoneProc promiseDoneProc,
|
||||
TextureContext textureContexts[]);
|
||||
PromiseImageTextureFulfillProc textureFulfillProc,
|
||||
PromiseImageTextureReleaseProc textureReleaseProc,
|
||||
PromiseImageTextureDoneProc textureDoneProc,
|
||||
PromiseImageTextureContext textureContexts[]);
|
||||
|
||||
private:
|
||||
// This array will usually only be sparsely populated.
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "GrContextPriv.h"
|
||||
#include "GrGpu.h"
|
||||
#include "SkImage_Gpu.h"
|
||||
#include "SkPromiseImageTexture.h"
|
||||
|
||||
using namespace sk_gpu_test;
|
||||
|
||||
@ -20,15 +21,39 @@ struct PromiseTextureChecker {
|
||||
, fFulfillCount(0)
|
||||
, fReleaseCount(0)
|
||||
, fDoneCount(0) {}
|
||||
GrBackendTexture fTexture;
|
||||
SkPromiseImageTexture fTexture;
|
||||
int fFulfillCount;
|
||||
int fReleaseCount;
|
||||
int fDoneCount;
|
||||
static void Fulfill(void* self, GrBackendTexture* outTexture) {
|
||||
static_cast<PromiseTextureChecker*>(self)->fFulfillCount++;
|
||||
*outTexture = static_cast<PromiseTextureChecker*>(self)->fTexture;
|
||||
GrBackendTexture fLastFulfilledTexture;
|
||||
|
||||
/**
|
||||
* Replaces the backend texture that this checker will return from fulfill. Also, transfers
|
||||
* ownership of the previous PromiseImageTexture to the caller, if they want to control when
|
||||
* it is deleted. The default argument will remove the existing texture without installing a
|
||||
* valid replacement.
|
||||
*/
|
||||
SkPromiseImageTexture replaceTexture(const GrBackendTexture& tex = GrBackendTexture()) {
|
||||
// Can't change this while in active fulfillment.
|
||||
SkASSERT(fFulfillCount == fReleaseCount);
|
||||
auto temp = std::move(fTexture);
|
||||
fTexture = SkPromiseImageTexture(tex);
|
||||
return temp;
|
||||
}
|
||||
static void Release(void* self) {
|
||||
|
||||
SkTArray<GrUniqueKey> uniqueKeys() const {
|
||||
return fTexture.testingOnly_uniqueKeysToInvalidate();
|
||||
}
|
||||
|
||||
static SkPromiseImageTexture* Fulfill(void* self) {
|
||||
auto checker = static_cast<PromiseTextureChecker*>(self);
|
||||
SkASSERT(checker->fTexture.isValid());
|
||||
checker->fFulfillCount++;
|
||||
checker->fLastFulfilledTexture = checker->fTexture.backendTexture();
|
||||
return &checker->fTexture;
|
||||
}
|
||||
static void Release(void* self, const SkPromiseImageTexture* texture) {
|
||||
SkASSERT(&static_cast<PromiseTextureChecker*>(self)->fTexture == texture);
|
||||
static_cast<PromiseTextureChecker*>(self)->fReleaseCount++;
|
||||
}
|
||||
static void Done(void* self) {
|
||||
@ -236,3 +261,266 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTest, reporter, ctxInfo) {
|
||||
gpu->deleteTestingOnlyBackendTexture(backendTex);
|
||||
}
|
||||
}
|
||||
|
||||
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTextureReuse, reporter, ctxInfo) {
|
||||
const int kWidth = 10;
|
||||
const int kHeight = 10;
|
||||
|
||||
GrContext* ctx = ctxInfo.grContext();
|
||||
GrGpu* gpu = ctx->contextPriv().getGpu();
|
||||
|
||||
GrBackendTexture backendTex1 = gpu->createTestingOnlyBackendTexture(
|
||||
nullptr, kWidth, kHeight, GrColorType::kRGBA_8888, true, GrMipMapped::kNo);
|
||||
GrBackendTexture backendTex2 = gpu->createTestingOnlyBackendTexture(
|
||||
nullptr, kWidth, kHeight, GrColorType::kRGBA_8888, true, GrMipMapped::kNo);
|
||||
GrBackendTexture backendTex3 = gpu->createTestingOnlyBackendTexture(
|
||||
nullptr, kWidth, kHeight, GrColorType::kRGBA_8888, true, GrMipMapped::kNo);
|
||||
REPORTER_ASSERT(reporter, backendTex1.isValid());
|
||||
REPORTER_ASSERT(reporter, backendTex2.isValid());
|
||||
REPORTER_ASSERT(reporter, backendTex3.isValid());
|
||||
|
||||
GrBackendFormat backendFormat = backendTex1.getBackendFormat();
|
||||
REPORTER_ASSERT(reporter, backendFormat.isValid());
|
||||
REPORTER_ASSERT(reporter, backendFormat == backendTex2.getBackendFormat());
|
||||
REPORTER_ASSERT(reporter, backendFormat == backendTex3.getBackendFormat());
|
||||
|
||||
PromiseTextureChecker promiseChecker(backendTex1);
|
||||
GrSurfaceOrigin texOrigin = kTopLeft_GrSurfaceOrigin;
|
||||
sk_sp<SkImage> refImg(
|
||||
SkImage_Gpu::MakePromiseTexture(ctx, backendFormat, kWidth, kHeight,
|
||||
GrMipMapped::kNo, texOrigin,
|
||||
kRGBA_8888_SkColorType, kPremul_SkAlphaType,
|
||||
nullptr,
|
||||
PromiseTextureChecker::Fulfill,
|
||||
PromiseTextureChecker::Release,
|
||||
PromiseTextureChecker::Done,
|
||||
&promiseChecker));
|
||||
|
||||
SkImageInfo info = SkImageInfo::MakeN32Premul(kWidth, kHeight);
|
||||
sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info);
|
||||
SkCanvas* canvas = surface->getCanvas();
|
||||
|
||||
int expectedFulfillCnt = 0;
|
||||
int expectedReleaseCnt = 0;
|
||||
int expectedDoneCnt = 0;
|
||||
|
||||
canvas->drawImage(refImg, 0, 0);
|
||||
canvas->drawImage(refImg, 5, 5);
|
||||
REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
|
||||
true,
|
||||
expectedFulfillCnt,
|
||||
expectedReleaseCnt,
|
||||
true,
|
||||
expectedDoneCnt,
|
||||
reporter));
|
||||
|
||||
bool isVulkan = GrBackendApi::kVulkan == ctx->contextPriv().getBackend();
|
||||
canvas->flush();
|
||||
expectedFulfillCnt++;
|
||||
expectedReleaseCnt++;
|
||||
REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
|
||||
!isVulkan,
|
||||
expectedFulfillCnt,
|
||||
expectedReleaseCnt,
|
||||
!isVulkan,
|
||||
expectedDoneCnt,
|
||||
reporter));
|
||||
REPORTER_ASSERT(reporter, GrBackendTexture::TestingOnly_Equals(
|
||||
promiseChecker.fLastFulfilledTexture, backendTex1));
|
||||
// We should have put a GrTexture for this fulfillment into the cache.
|
||||
auto keys = promiseChecker.uniqueKeys();
|
||||
REPORTER_ASSERT(reporter, keys.count() == 1);
|
||||
GrUniqueKey texKey1;
|
||||
if (keys.count()) {
|
||||
texKey1 = keys[0];
|
||||
}
|
||||
REPORTER_ASSERT(reporter, texKey1.isValid());
|
||||
REPORTER_ASSERT(reporter, ctx->contextPriv().resourceProvider()->findByUniqueKey<>(texKey1));
|
||||
|
||||
gpu->testingOnly_flushGpuAndSync();
|
||||
REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
|
||||
true,
|
||||
expectedFulfillCnt,
|
||||
expectedReleaseCnt,
|
||||
true,
|
||||
expectedDoneCnt,
|
||||
reporter));
|
||||
REPORTER_ASSERT(reporter,
|
||||
GrBackendTexture::TestingOnly_Equals(
|
||||
promiseChecker.replaceTexture().backendTexture(), backendTex1));
|
||||
gpu->deleteTestingOnlyBackendTexture(backendTex1);
|
||||
|
||||
ctx->contextPriv().getResourceCache()->purgeAsNeeded();
|
||||
// We should have invalidated the key on the previously cached texture (after ensuring
|
||||
// invalidation messages have been processed by calling purgeAsNeeded.)
|
||||
REPORTER_ASSERT(reporter, !ctx->contextPriv().resourceProvider()->findByUniqueKey<>(texKey1));
|
||||
|
||||
promiseChecker.replaceTexture(backendTex2);
|
||||
|
||||
canvas->drawImage(refImg, 0, 0);
|
||||
REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
|
||||
true,
|
||||
expectedFulfillCnt,
|
||||
expectedReleaseCnt,
|
||||
true,
|
||||
expectedDoneCnt,
|
||||
reporter));
|
||||
|
||||
canvas->flush();
|
||||
expectedFulfillCnt++;
|
||||
expectedReleaseCnt++;
|
||||
// Second texture should be in the cache.
|
||||
keys = promiseChecker.uniqueKeys();
|
||||
REPORTER_ASSERT(reporter, keys.count() == 1);
|
||||
GrUniqueKey texKey2;
|
||||
if (keys.count()) {
|
||||
texKey2 = keys[0];
|
||||
}
|
||||
REPORTER_ASSERT(reporter, texKey2.isValid() && texKey2 != texKey1);
|
||||
REPORTER_ASSERT(reporter, ctx->contextPriv().resourceProvider()->findByUniqueKey<>(texKey2));
|
||||
|
||||
REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
|
||||
!isVulkan,
|
||||
expectedFulfillCnt,
|
||||
expectedReleaseCnt,
|
||||
!isVulkan,
|
||||
expectedDoneCnt,
|
||||
reporter));
|
||||
REPORTER_ASSERT(reporter, GrBackendTexture::TestingOnly_Equals(
|
||||
promiseChecker.fLastFulfilledTexture, backendTex2));
|
||||
|
||||
gpu->testingOnly_flushGpuAndSync();
|
||||
REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
|
||||
true,
|
||||
expectedFulfillCnt,
|
||||
expectedReleaseCnt,
|
||||
true,
|
||||
expectedDoneCnt,
|
||||
reporter));
|
||||
|
||||
// Because we have kept the SkPromiseImageTexture alive, we should be able to use it again and
|
||||
// hit the cache.
|
||||
ctx->contextPriv().getResourceCache()->purgeAsNeeded();
|
||||
REPORTER_ASSERT(reporter, ctx->contextPriv().resourceProvider()->findByUniqueKey<>(texKey2));
|
||||
|
||||
canvas->drawImage(refImg, 0, 0);
|
||||
|
||||
canvas->flush();
|
||||
gpu->testingOnly_flushGpuAndSync();
|
||||
expectedFulfillCnt++;
|
||||
expectedReleaseCnt++;
|
||||
REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
|
||||
true,
|
||||
expectedFulfillCnt,
|
||||
expectedReleaseCnt,
|
||||
true,
|
||||
expectedDoneCnt,
|
||||
reporter));
|
||||
|
||||
// Make sure we didn't add another key and that the second texture is still alive in the cache.
|
||||
keys = promiseChecker.uniqueKeys();
|
||||
REPORTER_ASSERT(reporter, keys.count() == 1);
|
||||
if (keys.count()) {
|
||||
REPORTER_ASSERT(reporter, texKey2 == keys[0]);
|
||||
}
|
||||
ctx->contextPriv().getResourceCache()->purgeAsNeeded();
|
||||
REPORTER_ASSERT(reporter, ctx->contextPriv().resourceProvider()->findByUniqueKey<>(texKey2));
|
||||
|
||||
// Now we test keeping tex2 alive but fulfilling with a new texture.
|
||||
SkPromiseImageTexture promiseImageTexture2 = promiseChecker.replaceTexture(backendTex3);
|
||||
REPORTER_ASSERT(reporter, GrBackendTexture::TestingOnly_Equals(
|
||||
promiseImageTexture2.backendTexture(), backendTex2));
|
||||
|
||||
canvas->drawImage(refImg, 0, 0);
|
||||
|
||||
canvas->flush();
|
||||
gpu->testingOnly_flushGpuAndSync();
|
||||
expectedFulfillCnt++;
|
||||
expectedReleaseCnt++;
|
||||
REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
|
||||
true,
|
||||
expectedFulfillCnt,
|
||||
expectedReleaseCnt,
|
||||
true,
|
||||
expectedDoneCnt,
|
||||
reporter));
|
||||
|
||||
keys = promiseChecker.uniqueKeys();
|
||||
REPORTER_ASSERT(reporter, keys.count() == 1);
|
||||
GrUniqueKey texKey3;
|
||||
if (keys.count()) {
|
||||
texKey3 = keys[0];
|
||||
}
|
||||
ctx->contextPriv().getResourceCache()->purgeAsNeeded();
|
||||
REPORTER_ASSERT(reporter, !ctx->contextPriv().resourceProvider()->findByUniqueKey<>(texKey2));
|
||||
REPORTER_ASSERT(reporter, ctx->contextPriv().resourceProvider()->findByUniqueKey<>(texKey3));
|
||||
gpu->deleteTestingOnlyBackendTexture(promiseImageTexture2.backendTexture());
|
||||
|
||||
// Make a new promise image also backed by texture 3.
|
||||
sk_sp<SkImage> refImg2(
|
||||
SkImage_Gpu::MakePromiseTexture(ctx, backendFormat, kWidth, kHeight,
|
||||
GrMipMapped::kNo, texOrigin,
|
||||
kRGBA_8888_SkColorType, kPremul_SkAlphaType,
|
||||
nullptr,
|
||||
PromiseTextureChecker::Fulfill,
|
||||
PromiseTextureChecker::Release,
|
||||
PromiseTextureChecker::Done,
|
||||
&promiseChecker));
|
||||
canvas->drawImage(refImg, 0, 0);
|
||||
canvas->drawImage(refImg2, 1, 1);
|
||||
|
||||
canvas->flush();
|
||||
gpu->testingOnly_flushGpuAndSync();
|
||||
expectedFulfillCnt += 2;
|
||||
expectedReleaseCnt += 2;
|
||||
REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
|
||||
true,
|
||||
expectedFulfillCnt,
|
||||
expectedReleaseCnt,
|
||||
true,
|
||||
expectedDoneCnt,
|
||||
reporter));
|
||||
|
||||
// We currently expect each promise image to make and cache its own GrTexture. We will likely
|
||||
// try to make these share in the future.
|
||||
keys = promiseChecker.uniqueKeys();
|
||||
REPORTER_ASSERT(reporter, keys.count() == 2);
|
||||
GrUniqueKey texKey4;
|
||||
if (keys.count() == 2) {
|
||||
REPORTER_ASSERT(reporter, texKey3 == keys[0]);
|
||||
texKey4 = keys[1];
|
||||
}
|
||||
ctx->contextPriv().getResourceCache()->purgeAsNeeded();
|
||||
REPORTER_ASSERT(reporter, ctx->contextPriv().resourceProvider()->findByUniqueKey<>(texKey3));
|
||||
REPORTER_ASSERT(reporter, ctx->contextPriv().resourceProvider()->findByUniqueKey<>(texKey4));
|
||||
|
||||
// If we delete the SkPromiseImageTexture we should trigger both key removals.
|
||||
REPORTER_ASSERT(reporter,
|
||||
GrBackendTexture::TestingOnly_Equals(
|
||||
promiseChecker.replaceTexture().backendTexture(), backendTex3));
|
||||
|
||||
ctx->contextPriv().getResourceCache()->purgeAsNeeded();
|
||||
REPORTER_ASSERT(reporter, !ctx->contextPriv().resourceProvider()->findByUniqueKey<>(texKey3));
|
||||
REPORTER_ASSERT(reporter, !ctx->contextPriv().resourceProvider()->findByUniqueKey<>(texKey4));
|
||||
gpu->deleteTestingOnlyBackendTexture(backendTex3);
|
||||
|
||||
// After deleting each image we should get a done call.
|
||||
refImg.reset();
|
||||
++expectedDoneCnt;
|
||||
REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
|
||||
true,
|
||||
expectedFulfillCnt,
|
||||
expectedReleaseCnt,
|
||||
true,
|
||||
expectedDoneCnt,
|
||||
reporter));
|
||||
refImg2.reset();
|
||||
++expectedDoneCnt;
|
||||
REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
|
||||
true,
|
||||
expectedFulfillCnt,
|
||||
expectedReleaseCnt,
|
||||
true,
|
||||
expectedDoneCnt,
|
||||
reporter));
|
||||
}
|
||||
|
@ -17,13 +17,30 @@
|
||||
#include "SkYUVASizeInfo.h"
|
||||
|
||||
DDLPromiseImageHelper::PromiseImageCallbackContext::~PromiseImageCallbackContext() {
|
||||
SkASSERT(fDoneCnt == fNumImages);
|
||||
SkASSERT(!fUnreleasedFulfills);
|
||||
SkASSERT(fTotalReleases == fTotalFulfills);
|
||||
SkASSERT(!fTotalFulfills || fDoneCnt);
|
||||
GrGpu* gpu = fContext->contextPriv().getGpu();
|
||||
|
||||
if (fBackendTexture.isValid()) {
|
||||
gpu->deleteTestingOnlyBackendTexture(fBackendTexture);
|
||||
if (fPromiseImageTexture.isValid()) {
|
||||
gpu->deleteTestingOnlyBackendTexture(fPromiseImageTexture.backendTexture());
|
||||
}
|
||||
}
|
||||
|
||||
void DDLPromiseImageHelper::PromiseImageCallbackContext::setBackendTexture(
|
||||
const GrBackendTexture& backendTexture) {
|
||||
SkASSERT(!fUnreleasedFulfills);
|
||||
if (fPromiseImageTexture.isValid()) {
|
||||
GrGpu* gpu = fContext->contextPriv().getGpu();
|
||||
|
||||
if (fPromiseImageTexture.isValid()) {
|
||||
gpu->deleteTestingOnlyBackendTexture(fPromiseImageTexture.backendTexture());
|
||||
}
|
||||
}
|
||||
fPromiseImageTexture = SkPromiseImageTexture{backendTexture};
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DDLPromiseImageHelper::~DDLPromiseImageHelper() {}
|
||||
@ -96,7 +113,7 @@ void DDLPromiseImageHelper::uploadAllToGPU(GrContext* context) {
|
||||
GrGpu* gpu = context->contextPriv().getGpu();
|
||||
SkASSERT(gpu);
|
||||
|
||||
for (int i = 0; i < fImageInfo.count(); ++i) {
|
||||
for (int i = 0; i < fImageInfo.count(); i += 2) {
|
||||
const PromiseImageInfo& info = fImageInfo[i];
|
||||
|
||||
// DDL TODO: how can we tell if we need mipmapping!
|
||||
@ -111,7 +128,7 @@ void DDLPromiseImageHelper::uploadAllToGPU(GrContext* context) {
|
||||
|
||||
callbackContext->setBackendTexture(create_yuva_texture(gpu, yuvPixmap,
|
||||
info.yuvaIndices(), j));
|
||||
SkAssertResult(callbackContext->backendTexture().isValid());
|
||||
SkASSERT(callbackContext->promiseImageTexture()->isValid());
|
||||
|
||||
fImageInfo[i].setCallbackContext(j, std::move(callbackContext));
|
||||
}
|
||||
@ -133,7 +150,34 @@ void DDLPromiseImageHelper::uploadAllToGPU(GrContext* context) {
|
||||
|
||||
fImageInfo[i].setCallbackContext(0, std::move(callbackContext));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DDLPromiseImageHelper::replaceEveryOtherPromiseTexture(GrContext* context) {
|
||||
GrGpu* gpu = context->contextPriv().getGpu();
|
||||
SkASSERT(gpu);
|
||||
|
||||
for (int i = 0; i < fImageInfo.count(); ++i) {
|
||||
PromiseImageInfo& info = fImageInfo[i];
|
||||
|
||||
// DDL TODO: how can we tell if we need mipmapping!
|
||||
if (info.isYUV()) {
|
||||
int numPixmaps;
|
||||
SkAssertResult(SkYUVAIndex::AreValidIndices(info.yuvaIndices(), &numPixmaps));
|
||||
for (int j = 0; j < numPixmaps; ++j) {
|
||||
const SkPixmap& yuvPixmap = info.yuvPixmap(j);
|
||||
info.callbackContext(j)->setBackendTexture(
|
||||
create_yuva_texture(gpu, yuvPixmap, info.yuvaIndices(), j));
|
||||
SkASSERT(info.callbackContext(j)->promiseImageTexture()->isValid());
|
||||
}
|
||||
} else {
|
||||
const SkBitmap& bm = info.normalBitmap();
|
||||
info.callbackContext(0)->setBackendTexture(gpu->createTestingOnlyBackendTexture(
|
||||
bm.getPixels(), bm.width(), bm.height(), bm.colorType(), false,
|
||||
GrMipMapped::kNo, bm.rowBytes()));
|
||||
// The GMs sometimes request too large an image
|
||||
// SkAssertResult(callbackContext->backendTexture().isValid());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -206,7 +250,9 @@ sk_sp<SkImage> DDLPromiseImageHelper::PromiseImageCreator(const void* rawData,
|
||||
DDLPromiseImageHelper::PromiseImageReleaseProc,
|
||||
DDLPromiseImageHelper::PromiseImageDoneProc,
|
||||
contexts);
|
||||
|
||||
for (int i = 0; i < textureCount; ++i) {
|
||||
curImage.callbackContext(i)->wasAddedToImage();
|
||||
}
|
||||
} else {
|
||||
const GrBackendTexture& backendTex = curImage.backendTexture(0);
|
||||
GrBackendFormat backendFormat = backendTex.getBackendFormat();
|
||||
@ -227,6 +273,7 @@ sk_sp<SkImage> DDLPromiseImageHelper::PromiseImageCreator(const void* rawData,
|
||||
DDLPromiseImageHelper::PromiseImageReleaseProc,
|
||||
DDLPromiseImageHelper::PromiseImageDoneProc,
|
||||
(void*) curImage.refCallbackContext(0).release());
|
||||
curImage.callbackContext(0)->wasAddedToImage();
|
||||
}
|
||||
perRecorderContext->fPromiseImages->push_back(image);
|
||||
SkASSERT(image);
|
||||
|
@ -8,16 +8,17 @@
|
||||
#ifndef PromiseImageHelper_DEFINED
|
||||
#define PromiseImageHelper_DEFINED
|
||||
|
||||
#include "SkBitmap.h"
|
||||
#include "SkTArray.h"
|
||||
|
||||
#include "GrBackendSurface.h"
|
||||
#include "SkBitmap.h"
|
||||
#include "SkCachedData.h"
|
||||
#include "SkDeferredDisplayListRecorder.h"
|
||||
#include "SkPromiseImageTexture.h"
|
||||
#include "SkTArray.h"
|
||||
#include "SkTLazy.h"
|
||||
#include "SkYUVAIndex.h"
|
||||
#include "SkYUVASizeInfo.h"
|
||||
|
||||
class GrContext;
|
||||
class SkDeferredDisplayListRecorder;
|
||||
class SkImage;
|
||||
class SkPicture;
|
||||
struct SkYUVAIndex;
|
||||
@ -54,6 +55,10 @@ public:
|
||||
|
||||
void uploadAllToGPU(GrContext* context);
|
||||
|
||||
// Change the backing store texture for half the images. (Must ensure all fulfilled images are
|
||||
// released before calling this.).
|
||||
void replaceEveryOtherPromiseTexture(GrContext*);
|
||||
|
||||
// reinflate a deflated SKP, replacing all the indices with promise images.
|
||||
sk_sp<SkPicture> reinflateSKP(SkDeferredDisplayListRecorder*,
|
||||
SkData* compressedPicture,
|
||||
@ -76,16 +81,37 @@ private:
|
||||
|
||||
~PromiseImageCallbackContext();
|
||||
|
||||
void setBackendTexture(const GrBackendTexture& backendTexture) {
|
||||
SkASSERT(!fBackendTexture.isValid());
|
||||
fBackendTexture = backendTexture;
|
||||
void setBackendTexture(const GrBackendTexture& backendTexture);
|
||||
|
||||
void fulfill() {
|
||||
SkASSERT(fUnreleasedFulfills >= 0);
|
||||
++fUnreleasedFulfills;
|
||||
++fTotalFulfills;
|
||||
}
|
||||
|
||||
const GrBackendTexture& backendTexture() const { return fBackendTexture; }
|
||||
void release() {
|
||||
SkASSERT(fUnreleasedFulfills > 0);
|
||||
--fUnreleasedFulfills;
|
||||
++fTotalReleases;
|
||||
}
|
||||
|
||||
void done() {
|
||||
++fDoneCnt;
|
||||
SkASSERT(fDoneCnt <= fNumImages);
|
||||
}
|
||||
|
||||
void wasAddedToImage() { fNumImages++; }
|
||||
|
||||
SkPromiseImageTexture* promiseImageTexture() { return &fPromiseImageTexture; }
|
||||
|
||||
private:
|
||||
GrContext* fContext;
|
||||
GrBackendTexture fBackendTexture;
|
||||
GrContext* fContext;
|
||||
SkPromiseImageTexture fPromiseImageTexture;
|
||||
int fNumImages = 0;
|
||||
int fTotalFulfills = 0;
|
||||
int fTotalReleases = 0;
|
||||
int fUnreleasedFulfills = 0;
|
||||
int fDoneCnt = 0;
|
||||
|
||||
typedef SkRefCnt INHERITED;
|
||||
};
|
||||
@ -134,7 +160,7 @@ private:
|
||||
SkASSERT(index >= 0 && index < (this->isYUV() ? SkYUVASizeInfo::kMaxCount : 1));
|
||||
fCallbackContexts[index] = callbackContext;
|
||||
}
|
||||
PromiseImageCallbackContext* callbackContext(int index) {
|
||||
PromiseImageCallbackContext* callbackContext(int index) const {
|
||||
SkASSERT(index >= 0 && index < (this->isYUV() ? SkYUVASizeInfo::kMaxCount : 1));
|
||||
return fCallbackContexts[index].get();
|
||||
}
|
||||
@ -145,7 +171,7 @@ private:
|
||||
|
||||
const GrBackendTexture& backendTexture(int index) const {
|
||||
SkASSERT(index >= 0 && index < (this->isYUV() ? SkYUVASizeInfo::kMaxCount : 1));
|
||||
return fCallbackContexts[index]->backendTexture();
|
||||
return fCallbackContexts[index]->promiseImageTexture()->backendTexture();
|
||||
}
|
||||
|
||||
void setNormalBitmap(const SkBitmap& bm) { fBitmap = bm; }
|
||||
@ -190,21 +216,24 @@ private:
|
||||
SkTArray<sk_sp<SkImage>>* fPromiseImages;
|
||||
};
|
||||
|
||||
static void PromiseImageFulfillProc(void* textureContext, GrBackendTexture* outTexture) {
|
||||
static SkPromiseImageTexture* PromiseImageFulfillProc(void* textureContext) {
|
||||
auto callbackContext = static_cast<PromiseImageCallbackContext*>(textureContext);
|
||||
SkASSERT(callbackContext->backendTexture().isValid());
|
||||
*outTexture = callbackContext->backendTexture();
|
||||
SkASSERT(callbackContext->promiseImageTexture()->isValid());
|
||||
callbackContext->fulfill();
|
||||
return callbackContext->promiseImageTexture();
|
||||
}
|
||||
|
||||
static void PromiseImageReleaseProc(void* textureContext) {
|
||||
#ifdef SK_DEBUG
|
||||
static void PromiseImageReleaseProc(void* textureContext,
|
||||
const SkPromiseImageTexture* texture) {
|
||||
auto callbackContext = static_cast<PromiseImageCallbackContext*>(textureContext);
|
||||
SkASSERT(callbackContext->backendTexture().isValid());
|
||||
#endif
|
||||
callbackContext->release();
|
||||
SkASSERT(texture == callbackContext->promiseImageTexture());
|
||||
SkASSERT(callbackContext->promiseImageTexture()->isValid());
|
||||
}
|
||||
|
||||
static void PromiseImageDoneProc(void* textureContext) {
|
||||
auto callbackContext = static_cast<PromiseImageCallbackContext*>(textureContext);
|
||||
callbackContext->done();
|
||||
callbackContext->unref();
|
||||
}
|
||||
|
||||
@ -212,6 +241,7 @@ private:
|
||||
|
||||
bool isValidID(int id) const { return id >= 0 && id < fImageInfo.count(); }
|
||||
const PromiseImageInfo& getInfo(int id) const { return fImageInfo[id]; }
|
||||
void uploadImage(GrContext*, PromiseImageInfo*);
|
||||
|
||||
// returns -1 if not found
|
||||
int findImage(SkImage* image) const;
|
||||
|
Loading…
Reference in New Issue
Block a user