Add option to skip intermediate release/fulfill calls for promise images.

SkiaRenderer does not delete promise image textures when they are released
but not done. Refulfilling promise image textures takes a significant amount
of CPU time. This allows us to fulfill each promise image once.

Bug: skia:8736
Change-Id: I7ad7fa9678ed0ec4bb714b71fbf920ab4a845409
Reviewed-on: https://skia-review.googlesource.com/c/188039
Commit-Queue: Brian Salomon <bsalomon@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
This commit is contained in:
Brian Salomon 2019-01-30 17:28:20 -05:00 committed by Skia Commit-Bot
parent d20cf33472
commit f55e8d5232
13 changed files with 428 additions and 250 deletions

View File

@ -1954,7 +1954,15 @@ Error ViaDDL::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString
// this is our ultimate final drawing area/rect
SkIRect viewport = SkIRect::MakeWH(size.fWidth, size.fHeight);
DDLPromiseImageHelper promiseImageHelper;
// When we're only doing one replay we exercise the code path that delays calling release
// until the SkImage is destroyed.
SkDeferredDisplayListRecorder::DelayReleaseCallback delayReleaseCallback;
if (fNumReplays > 1) {
delayReleaseCallback = SkDeferredDisplayListRecorder::DelayReleaseCallback::kNo;
} else {
delayReleaseCallback = SkDeferredDisplayListRecorder::DelayReleaseCallback::kYes;
}
DDLPromiseImageHelper promiseImageHelper(delayReleaseCallback);
sk_sp<SkData> compressedPictureData = promiseImageHelper.deflateSKP(inputPicture.get());
if (!compressedPictureData) {
return SkStringPrintf("ViaDDL: Couldn't deflate SkPicture");

View File

@ -63,6 +63,8 @@ public:
GrBackendTexture*);
using TextureContext = PromiseImageTextureContext;
enum class DelayReleaseCallback : bool { kNo = false, kYes = true };
/**
Create a new SkImage that is very similar to an SkImage created by MakeFromTexture. The main
difference is that the client doesn't have the backend texture on the gpu yet but they know
@ -74,19 +76,31 @@ public:
match those set during the SkImage creation, and it must have a valid backend gpu texture.
The gpu texture supplied by the client must stay valid until we call the textureReleaseProc.
When we are done with the texture returned by the textureFulfillProc we will call the
textureReleaseProc passing in the textureContext. This is a signal to the client that they
are free to delete the underlying gpu texture. If future draws also use the same promise
image we will call the textureFulfillProc again if we've already called the
textureReleaseProc. We will always call textureFulfillProc and textureReleaseProc in pairs.
In other words we will never call textureFulfillProc or textureReleaseProc multiple times
for the same textureContext before calling the other.
The following applies when DelayReleaseCallback is kNo:
When we are done with the texture returned by the textureFulfillProc we will call the
textureReleaseProc passing in the textureContext. This is a signal to the client that
they are free to delete the underlying gpu texture. If future draws also use the same
promise image we will call the textureFulfillProc again if we've already called the
textureReleaseProc. We will always call textureFulfillProc and textureReleaseProc in
pairs. In other words we will never call textureFulfillProc or textureReleaseProc
multiple times for the same textureContext before calling the other.
We call the textureDoneProc when we will no longer call the textureFulfillProc again. We
pass in the textureContext as a parameter to the textureDoneProc. We also guarantee that
there will be no outstanding textureReleaseProcs that still need to be called when we
call the textureDoneProc. Thus when the textureDoneProc gets called the client is able
to cleanup all GPU objects and meta data needed for the textureFulfill call.
When delayReleaseCallback is kYes:
When all the following are true:
* the promise image is deleted,
* any SkDeferredDisplayLists that recorded draws referencing the image are deleted,
* and the texture is safe to delete in the underlying API with respect to drawn
SkDeferredDisplayLists that reference the image
the textureReleaseProc and then textureDoneProc are called. The texture can be deleted
by the client as soon as textureReleaseProc is called. In this mode there is only one
call to each of textureFulfillProc, textureReleaseProc, and textureDoneProc.
We call the promiseDoneProc when we will no longer call the textureFulfillProc again. We
pass in the textureContext as a parameter to the promiseDoneProc. We also guarantee that
there will be no outstanding textureReleaseProcs that still need to be called when we call
the textureDoneProc. Thus when the textureDoneProc gets called the client is able to cleanup
all GPU objects and meta data needed for the textureFulfill call.
This call is only valid if the SkDeferredDisplayListRecorder is backed by a gpu context.
@ -119,7 +133,27 @@ public:
PromiseImageTextureFulfillProc textureFulfillProc,
PromiseImageTextureReleaseProc textureReleaseProc,
PromiseImageTextureDoneProc textureDoneProc,
PromiseImageTextureContext textureContext);
PromiseImageTextureContext textureContext,
DelayReleaseCallback delayReleaseCallback);
/** Deprecated version that assumes DelayReleaseCallback::kNo. */
sk_sp<SkImage> makePromiseTexture(const GrBackendFormat& backendFormat,
int width,
int height,
GrMipMapped mipMapped,
GrSurfaceOrigin origin,
SkColorType colorType,
SkAlphaType alphaType,
sk_sp<SkColorSpace> colorSpace,
PromiseImageTextureFulfillProc textureFulfillProc,
PromiseImageTextureReleaseProc textureReleaseProc,
PromiseImageTextureDoneProc textureDoneProc,
PromiseImageTextureContext textureContext) {
return this->makePromiseTexture(backendFormat, width, height, mipMapped, origin, colorType,
alphaType, colorSpace, textureFulfillProc,
textureReleaseProc, textureDoneProc, textureContext,
DelayReleaseCallback::kNo);
}
/**
This entry point operates the same as 'makePromiseTexture' except that its
@ -139,7 +173,27 @@ public:
PromiseImageTextureFulfillProc textureFulfillProc,
PromiseImageTextureReleaseProc textureReleaseProc,
PromiseImageTextureDoneProc textureDoneProc,
PromiseImageTextureContext textureContexts[]);
PromiseImageTextureContext textureContexts[],
DelayReleaseCallback delayReleaseCallback);
/** Deprecated version that assumes DelayReleaseCallback::kNo. */
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,
PromiseImageTextureFulfillProc textureFulfillProc,
PromiseImageTextureReleaseProc textureReleaseProc,
PromiseImageTextureDoneProc textureDoneProc,
PromiseImageTextureContext textureContexts[]) {
return this->makeYUVAPromiseTexture(
yuvColorSpace, yuvaFormats, yuvaSizes, yuvaIndices, imageWidth, imageHeight,
imageOrigin, std::move(imageColorSpace), textureFulfillProc, textureReleaseProc,
textureDoneProc, textureContexts, DelayReleaseCallback::kNo);
}
private:
bool init();

View File

@ -34,7 +34,8 @@ sk_sp<SkImage> SkDeferredDisplayListRecorder::makePromiseTexture(
PromiseImageTextureFulfillProc textureFulfillProc,
PromiseImageTextureReleaseProc textureReleaseProc,
PromiseImageTextureDoneProc textureDoneProc,
PromiseImageTextureContext textureContext) {
PromiseImageTextureContext textureContext,
DelayReleaseCallback delayReleaseCallback) {
return nullptr;
}
@ -50,7 +51,8 @@ sk_sp<SkImage> SkDeferredDisplayListRecorder::makeYUVAPromiseTexture(
PromiseImageTextureFulfillProc textureFulfillProc,
PromiseImageTextureReleaseProc textureReleaseProc,
PromiseImageTextureDoneProc textureDoneProc,
PromiseImageTextureContext textureContexts[]) {
PromiseImageTextureContext textureContexts[],
DelayReleaseCallback delayReleaseCallback) {
return nullptr;
}
@ -220,7 +222,8 @@ sk_sp<SkImage> SkDeferredDisplayListRecorder::makePromiseTexture(
PromiseImageTextureFulfillProc textureFulfillProc,
PromiseImageTextureReleaseProc textureReleaseProc,
PromiseImageTextureDoneProc textureDoneProc,
PromiseImageTextureContext textureContext) {
PromiseImageTextureContext textureContext,
DelayReleaseCallback delayReleaseCallback) {
if (!fContext) {
return nullptr;
}
@ -237,7 +240,8 @@ sk_sp<SkImage> SkDeferredDisplayListRecorder::makePromiseTexture(
textureFulfillProc,
textureReleaseProc,
textureDoneProc,
textureContext);
textureContext,
delayReleaseCallback);
}
sk_sp<SkImage> SkDeferredDisplayListRecorder::makeYUVAPromiseTexture(
@ -252,7 +256,8 @@ sk_sp<SkImage> SkDeferredDisplayListRecorder::makeYUVAPromiseTexture(
PromiseImageTextureFulfillProc textureFulfillProc,
PromiseImageTextureReleaseProc textureReleaseProc,
PromiseImageTextureDoneProc textureDoneProc,
PromiseImageTextureContext textureContexts[]) {
PromiseImageTextureContext textureContexts[],
DelayReleaseCallback delayReleaseCallback) {
if (!fContext) {
return nullptr;
}
@ -269,7 +274,8 @@ sk_sp<SkImage> SkDeferredDisplayListRecorder::makeYUVAPromiseTexture(
textureFulfillProc,
textureReleaseProc,
textureDoneProc,
textureContexts);
textureContexts,
delayReleaseCallback);
}
#endif

View File

@ -392,15 +392,16 @@ sk_sp<SkImage> SkImage_Gpu::MakePromiseTexture(GrContext* context,
sk_sp<SkColorSpace> colorSpace,
PromiseImageTextureFulfillProc textureFulfillProc,
PromiseImageTextureReleaseProc textureReleaseProc,
PromiseImageTextureDoneProc promiseDoneProc,
PromiseImageTextureContext textureContext) {
PromiseImageTextureDoneProc textureDoneProc,
PromiseImageTextureContext textureContext,
DelayReleaseCallback delayReleaseCallback) {
// 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.
if (!promiseDoneProc) {
if (!textureDoneProc) {
return nullptr;
}
SkScopeExit callDone([promiseDoneProc, textureContext]() { promiseDoneProc(textureContext); });
SkScopeExit callDone([textureDoneProc, textureContext]() { textureDoneProc(textureContext); });
SkImageInfo info = SkImageInfo::Make(width, height, colorType, alphaType, colorSpace);
if (!SkImageInfoIsValid(info)) {
@ -424,7 +425,7 @@ sk_sp<SkImage> SkImage_Gpu::MakePromiseTexture(GrContext* context,
callDone.clear();
auto proxy = MakePromiseImageLazyProxy(context, width, height, origin, config, backendFormat,
mipMapped, textureFulfillProc, textureReleaseProc,
promiseDoneProc, textureContext);
textureDoneProc, textureContext, delayReleaseCallback);
if (!proxy) {
return nullptr;
}

View File

@ -40,48 +40,7 @@ public:
sk_sp<SkImage> onMakeColorTypeAndColorSpace(SkColorType, sk_sp<SkColorSpace>) const final;
/**
Create a new SkImage that is very similar to an SkImage created by MakeFromTexture. The main
difference is that the client doesn't have the backend texture on the gpu yet but they know
all the properties of the texture. So instead of passing in a GrBackendTexture the client
supplies a GrBackendFormat, width, height, and GrMipMapped state.
When we actually send the draw calls to the GPU, we will call the textureFulfillProc and
the client will return a GrBackendTexture to us. The properties of the GrBackendTexture must
match those set during the SkImage creation, and it must have a valid backend gpu texture.
The gpu texture supplied by the client must stay valid until we call the textureReleaseProc.
When we are done with the texture returned by the textureFulfillProc we will call the
textureReleaseProc passing in the textureContext. This is a signal to the client that they
are free to delete the underlying gpu texture. If future draws also use the same promise
image we will call the textureFulfillProc again if we've already called the
textureReleaseProc. We will always call textureFulfillProc and textureReleaseProc in pairs.
In other words we will never call textureFulfillProc or textureReleaseProc multiple times
for the same textureContext before calling the other.
We we call the promiseDoneProc when we will no longer call the textureFulfillProc again. We
also guarantee that there will be no outstanding textureReleaseProcs that still need to be
called when we call the textureDoneProc. Thus when the textureDoneProc gets called the
client is able to cleanup all GPU objects and meta data needed for the textureFulfill call.
@param context Gpu context
@param backendFormat format of promised gpu texture
@param width width of promised gpu texture
@param height height of promised gpu texture
@param mipMapped mip mapped state of promised gpu texture
@param origin one of: kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin
@param colorType one of: kUnknown_SkColorType, kAlpha_8_SkColorType,
kRGB_565_SkColorType, kARGB_4444_SkColorType,
kRGBA_8888_SkColorType, kBGRA_8888_SkColorType,
kGray_8_SkColorType, kRGBA_F16_SkColorType
@param alphaType one of: kUnknown_SkAlphaType, kOpaque_SkAlphaType,
kPremul_SkAlphaType, kUnpremul_SkAlphaType
@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 textureDoneProc function called when we will no longer call textureFulfillProc
@param textureContext state passed to textureFulfillProc, textureReleaseProc, and
promiseDoneProc
@return created SkImage, or nullptr
* This is the implementation of SkDeferredDisplayListRecorder::makePromiseImage.
*/
static sk_sp<SkImage> MakePromiseTexture(GrContext* context,
const GrBackendFormat& backendFormat,
@ -95,7 +54,8 @@ public:
PromiseImageTextureFulfillProc textureFulfillProc,
PromiseImageTextureReleaseProc textureReleaseProc,
PromiseImageTextureDoneProc textureDoneProc,
PromiseImageTextureContext textureContext);
PromiseImageTextureContext textureContext,
DelayReleaseCallback delayReleaseCallback);
static sk_sp<SkImage> ConvertYUVATexturesToRGB(GrContext*, SkYUVColorSpace yuvColorSpace,
const GrBackendTexture yuvaTextures[],

View File

@ -360,7 +360,8 @@ sk_sp<GrTextureProxy> SkImage_GpuBase::MakePromiseImageLazyProxy(
PromiseImageTextureFulfillProc fulfillProc,
PromiseImageTextureReleaseProc releaseProc,
PromiseImageTextureDoneProc doneProc,
PromiseImageTextureContext textureContext) {
PromiseImageTextureContext textureContext,
DelayReleaseCallback delayReleaseCallback) {
SkASSERT(context);
SkASSERT(width > 0 && height > 0);
SkASSERT(doneProc);
@ -402,9 +403,11 @@ sk_sp<GrTextureProxy> SkImage_GpuBase::MakePromiseImageLazyProxy(
PromiseImageTextureReleaseProc releaseProc,
PromiseImageTextureDoneProc doneProc,
PromiseImageTextureContext context,
DelayReleaseCallback delayReleaseCallback,
GrPixelConfig config)
: fFulfillProc(fulfillProc)
, fConfig(config) {
, fConfig(config)
, fDelayReleaseCallback(delayReleaseCallback) {
auto doneHelper = sk_make_sp<GrReleaseProcHelper>(doneProc, context);
fReleaseContext = sk_make_sp<IdleContext::PromiseImageReleaseContext>(
releaseProc, context, std::move(doneHelper));
@ -414,8 +417,14 @@ sk_sp<GrTextureProxy> SkImage_GpuBase::MakePromiseImageLazyProxy(
sk_sp<GrSurface> operator()(GrResourceProvider* resourceProvider) {
if (!resourceProvider) {
if (fDelayedReleaseTexture) {
fDelayedReleaseTexture.reset();
}
return nullptr;
}
if (fDelayedReleaseTexture) {
return fDelayedReleaseTexture;
}
sk_sp<GrTexture> cachedTexture;
SkASSERT(fLastFulfilledKey.isValid() == (fLastFulfillID > 0));
@ -459,6 +468,7 @@ sk_sp<GrTextureProxy> SkImage_GpuBase::MakePromiseImageLazyProxy(
return sk_sp<GrTexture>();
}
sk_sp<GrTexture> tex;
static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
GrUniqueKey::Builder builder(&fLastFulfilledKey, kDomain, 2, "promise");
builder[0] = promiseTexture->uniqueID();
@ -467,7 +477,6 @@ sk_sp<GrTextureProxy> SkImage_GpuBase::MakePromiseImageLazyProxy(
// A texture with this key may already exist from a different instance of this lazy
// callback. This could happen if the client fulfills a promise image with a texture
// that was previously used to fulfill a different promise image.
sk_sp<GrTexture> tex;
if (auto surf = resourceProvider->findByUniqueKey<GrSurface>(fLastFulfilledKey)) {
tex = sk_ref_sp(surf->asTexture());
SkASSERT(tex);
@ -484,6 +493,9 @@ sk_sp<GrTextureProxy> SkImage_GpuBase::MakePromiseImageLazyProxy(
}
}
this->addToIdleContext(tex.get());
if (fDelayReleaseCallback == DelayReleaseCallback::kYes) {
fDelayedReleaseTexture = tex;
}
tex->resourcePriv().setUniqueKey(fLastFulfilledKey);
SkASSERT(fContextID == SK_InvalidUniqueID ||
fContextID == tex->getContext()->uniqueID());
@ -567,15 +579,17 @@ sk_sp<GrTextureProxy> SkImage_GpuBase::MakePromiseImageLazyProxy(
}
sk_sp<IdleContext::PromiseImageReleaseContext> fReleaseContext;
sk_sp<GrTexture> fDelayedReleaseTexture;
PromiseImageTextureFulfillProc fFulfillProc;
GrPixelConfig fConfig;
DelayReleaseCallback fDelayReleaseCallback;
// 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;
GrUniqueKey fLastFulfilledKey;
} callback(fulfillProc, releaseProc, doneProc, textureContext, config);
} callback(fulfillProc, releaseProc, doneProc, textureContext, delayReleaseCallback, config);
GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider();

View File

@ -78,6 +78,7 @@ public:
using PromiseImageTextureReleaseProc =
SkDeferredDisplayListRecorder::PromiseImageTextureReleaseProc;
using PromiseImageTextureDoneProc = SkDeferredDisplayListRecorder::PromiseImageTextureDoneProc;
using DelayReleaseCallback = SkDeferredDisplayListRecorder::DelayReleaseCallback;
protected:
// Helper for making a lazy proxy for a promise image. The PromiseDoneProc we be called,
@ -87,7 +88,7 @@ protected:
static sk_sp<GrTextureProxy> MakePromiseImageLazyProxy(
GrContext*, int width, int height, GrSurfaceOrigin, GrPixelConfig, GrBackendFormat,
GrMipMapped, PromiseImageTextureFulfillProc, PromiseImageTextureReleaseProc,
PromiseImageTextureDoneProc, PromiseImageTextureContext);
PromiseImageTextureDoneProc, PromiseImageTextureContext, DelayReleaseCallback);
static bool RenderYUVAToRGBA(GrContext* ctx, GrRenderTargetContext* renderTargetContext,
const SkRect& rect, SkYUVColorSpace yuvColorSpace,

View File

@ -260,7 +260,8 @@ sk_sp<SkImage> SkImage_GpuYUVA::MakePromiseYUVATexture(
PromiseImageTextureFulfillProc textureFulfillProc,
PromiseImageTextureReleaseProc textureReleaseProc,
PromiseImageTextureDoneProc promiseDoneProc,
PromiseImageTextureContext textureContexts[]) {
PromiseImageTextureContext textureContexts[],
DelayReleaseCallback delayReleaseCallback) {
int numTextures;
bool valid = SkYUVAIndex::AreValidIndices(yuvaIndices, &numTextures);
@ -320,7 +321,7 @@ sk_sp<SkImage> SkImage_GpuYUVA::MakePromiseYUVATexture(
proxies[texIdx] = MakePromiseImageLazyProxy(
context, yuvaSizes[texIdx].width(), yuvaSizes[texIdx].height(), imageOrigin, config,
yuvaFormats[texIdx], GrMipMapped::kNo, textureFulfillProc, textureReleaseProc,
promiseDoneProc, textureContexts[texIdx]);
promiseDoneProc, textureContexts[texIdx], delayReleaseCallback);
++proxiesCreated;
if (!proxies[texIdx]) {
return nullptr;

View File

@ -58,45 +58,7 @@ public:
SkColorSpace* targetColorSpace() const { return fTargetColorSpace.get(); }
/**
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
they know all the properties of the texture. So instead of passing in GrBackendTextures the
client supplies GrBackendFormats and the image size.
When we actually send the draw calls to the GPU, we will call the textureFulfillProc and
the client will return the GrBackendTextures to us. The properties of the GrBackendTextures
must match those set during the SkImage creation, and it must have valid backend gpu
textures. The gpu textures supplied by the client must stay valid until we call the
textureReleaseProc.
When we are done with the texture returned by the textureFulfillProc we will call the
textureReleaseProc passing in the textureContext. This is a signal to the client that they
are free to delete the underlying gpu textures. If future draws also use the same promise
image we will call the textureFulfillProc again if we've already called the
textureReleaseProc. We will always call textureFulfillProc and textureReleaseProc in pairs.
In other words we will never call textureFulfillProc or textureReleaseProc multiple times
for the same textureContext before calling the other.
We call the promiseDoneProc when we will no longer call the textureFulfillProc again. We
also guarantee that there will be no outstanding textureReleaseProcs that still need to be
called when we call the textureDoneProc. Thus when the textureDoneProc gets called the
client is able to cleanup all GPU objects and meta data needed for the textureFulfill call.
@param context Gpu context
@param yuvColorSpace color range of expected YUV pixels
@param yuvaFormats formats of promised gpu textures for each YUVA plane
@param yuvaSizes width and height of promised gpu textures
@param yuvaIndices mapping from yuv plane index to texture representing that plane
@param width width of promised gpu texture
@param height height of promised gpu texture
@param imageOrigin one of: kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin
@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 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
* This is the implementation of SkDeferredDisplayListRecorder::makeYUVAPromiseTexture.
*/
static sk_sp<SkImage> MakePromiseYUVATexture(GrContext* context,
SkYUVColorSpace yuvColorSpace,
@ -110,7 +72,8 @@ public:
PromiseImageTextureFulfillProc textureFulfillProc,
PromiseImageTextureReleaseProc textureReleaseProc,
PromiseImageTextureDoneProc textureDoneProc,
PromiseImageTextureContext textureContexts[]);
PromiseImageTextureContext textureContexts[],
DelayReleaseCallback delayReleaseCallback);
private:
SkImage_GpuYUVA(const SkImage_GpuYUVA* image, sk_sp<SkColorSpace>);

View File

@ -771,14 +771,16 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DDLInvalidRecorder, reporter, ctxInfo) {
GrBackendFormat format = create_backend_format(context, kRGBA_8888_SkColorType,
kRGBA_8888_GrPixelConfig);
sk_sp<SkImage> image = recorder.makePromiseTexture(format, 32, 32, GrMipMapped::kNo,
kTopLeft_GrSurfaceOrigin,
kRGBA_8888_SkColorType,
kPremul_SkAlphaType, nullptr,
dummy_fulfill_proc,
dummy_release_proc,
dummy_done_proc,
nullptr);
sk_sp<SkImage> image = recorder.makePromiseTexture(
format, 32, 32, GrMipMapped::kNo,
kTopLeft_GrSurfaceOrigin,
kRGBA_8888_SkColorType,
kPremul_SkAlphaType, nullptr,
dummy_fulfill_proc,
dummy_release_proc,
dummy_done_proc,
nullptr,
SkDeferredDisplayListRecorder::DelayReleaseCallback::kNo);
REPORTER_ASSERT(reporter, !image);
}
@ -875,14 +877,16 @@ DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(DDLTextureFlagsTest, reporter, ctxInfo) {
for (auto mipMapped : { GrMipMapped::kNo, GrMipMapped::kYes }) {
GrBackendFormat format = GrBackendFormat::MakeGL(GR_GL_RGBA8, target);
sk_sp<SkImage> image = recorder.makePromiseTexture(format, 32, 32, mipMapped,
kTopLeft_GrSurfaceOrigin,
kRGBA_8888_SkColorType,
kPremul_SkAlphaType, nullptr,
dummy_fulfill_proc,
dummy_release_proc,
dummy_done_proc,
nullptr);
sk_sp<SkImage> image = recorder.makePromiseTexture(
format, 32, 32, mipMapped,
kTopLeft_GrSurfaceOrigin,
kRGBA_8888_SkColorType,
kPremul_SkAlphaType, nullptr,
dummy_fulfill_proc,
dummy_release_proc,
dummy_done_proc,
nullptr,
SkDeferredDisplayListRecorder::DelayReleaseCallback::kNo);
if (GR_GL_TEXTURE_2D != target && mipMapped == GrMipMapped::kYes) {
REPORTER_ASSERT(reporter, !image);
continue;

View File

@ -5,6 +5,7 @@
* found in the LICENSE file.
*/
#include "SkExchange.h"
#include "Test.h"
#include "GrBackendSurface.h"
@ -43,11 +44,7 @@ struct PromiseTextureChecker {
*/
sk_sp<const SkPromiseImageTexture> replaceTexture(
const GrBackendTexture& tex = GrBackendTexture()) {
// Can't change this while in active fulfillment.
REPORTER_ASSERT(fReporter, fFulfillCount == fReleaseCount);
auto temp = std::move(fTexture);
fTexture = SkPromiseImageTexture::Make(tex);
return std::move(temp);
return skstd::exchange(fTexture, SkPromiseImageTexture::Make(tex));
}
SkTArray<GrUniqueKey> uniqueKeys() const {
@ -74,11 +71,14 @@ struct PromiseTextureChecker {
}
};
// Because Vulkan may delay when it actually calls the ReleaseProcs depending on when command
// buffers finish their work, we need some slight wiggle room in what values we expect for fulfill
// and release counts.
enum class ReleaseBalanceExpecation {
kBalanced,
kBalancedOrPlusOne,
kAny
};
static bool check_fulfill_and_release_cnts(const PromiseTextureChecker& promiseChecker,
bool countsMustBeEqual,
ReleaseBalanceExpecation balanceExpecation,
int expectedFulfillCnt,
int expectedReleaseCnt,
bool expectedRequired,
@ -88,12 +88,16 @@ static bool check_fulfill_and_release_cnts(const PromiseTextureChecker& promiseC
int countDiff = promiseChecker.fFulfillCount - promiseChecker.fReleaseCount;
// FulfillCount should always equal ReleaseCount or be at most one higher
if (countDiff != 0) {
if (countsMustBeEqual) {
if (balanceExpecation == ReleaseBalanceExpecation::kBalanced) {
result = false;
REPORTER_ASSERT(reporter, 0 == countDiff);
} else if (countDiff != 1) {
} else if (countDiff != 1 &&
balanceExpecation == ReleaseBalanceExpecation::kBalancedOrPlusOne) {
result = false;
REPORTER_ASSERT(reporter, 0 == countDiff || 1 == countDiff);
} else if (countDiff < 0) {
result = false;
REPORTER_ASSERT(reporter, countDiff >= 0);
}
}
@ -129,7 +133,7 @@ static bool check_fulfill_and_release_cnts(const PromiseTextureChecker& promiseC
return result;
}
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTest, reporter, ctxInfo) {
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTestNoDelayedRelease, reporter, ctxInfo) {
const int kWidth = 10;
const int kHeight = 10;
@ -147,14 +151,16 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTest, reporter, ctxInfo) {
PromiseTextureChecker promiseChecker(backendTex, reporter, false);
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));
SkImage_Gpu::MakePromiseTexture(
ctx, backendFormat, kWidth, kHeight,
GrMipMapped::kNo, texOrigin,
kRGBA_8888_SkColorType, kPremul_SkAlphaType,
nullptr,
PromiseTextureChecker::Fulfill,
PromiseTextureChecker::Release,
PromiseTextureChecker::Done,
&promiseChecker,
SkDeferredDisplayListRecorder::DelayReleaseCallback::kNo));
SkImageInfo info = SkImageInfo::MakeN32Premul(kWidth, kHeight);
sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info);
@ -163,10 +169,11 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTest, reporter, ctxInfo) {
int expectedFulfillCnt = 0;
int expectedReleaseCnt = 0;
int expectedDoneCnt = 0;
ReleaseBalanceExpecation balanceExpecation = ReleaseBalanceExpecation::kBalanced;
canvas->drawImage(refImg, 0, 0);
REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
true,
balanceExpecation,
expectedFulfillCnt,
expectedReleaseCnt,
true,
@ -177,8 +184,11 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTest, reporter, ctxInfo) {
canvas->flush();
expectedFulfillCnt++;
expectedReleaseCnt++;
if (isVulkan) {
balanceExpecation = ReleaseBalanceExpecation::kBalancedOrPlusOne;
}
REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
!isVulkan,
balanceExpecation,
expectedFulfillCnt,
expectedReleaseCnt,
!isVulkan,
@ -186,8 +196,9 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTest, reporter, ctxInfo) {
reporter));
gpu->testingOnly_flushGpuAndSync();
balanceExpecation = ReleaseBalanceExpecation::kBalanced;
REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
true,
balanceExpecation,
expectedFulfillCnt,
expectedReleaseCnt,
true,
@ -203,7 +214,7 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTest, reporter, ctxInfo) {
gpu->testingOnly_flushGpuAndSync();
REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
true,
balanceExpecation,
expectedFulfillCnt,
expectedReleaseCnt,
true,
@ -218,8 +229,11 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTest, reporter, ctxInfo) {
canvas->flush();
expectedFulfillCnt++;
expectedReleaseCnt++;
if (isVulkan) {
balanceExpecation = ReleaseBalanceExpecation::kBalancedOrPlusOne;
}
REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
!isVulkan,
balanceExpecation,
expectedFulfillCnt,
expectedReleaseCnt,
!isVulkan,
@ -233,7 +247,7 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTest, reporter, ctxInfo) {
}
REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
!isVulkan,
balanceExpecation,
expectedFulfillCnt,
expectedReleaseCnt,
!isVulkan,
@ -248,8 +262,9 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTest, reporter, ctxInfo) {
if (releaseImageEarly) {
expectedDoneCnt++;
}
balanceExpecation = ReleaseBalanceExpecation::kBalanced;
REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
true,
balanceExpecation,
expectedFulfillCnt,
expectedReleaseCnt,
!isVulkan,
@ -264,7 +279,7 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTest, reporter, ctxInfo) {
}
REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
true,
balanceExpecation,
expectedFulfillCnt,
expectedReleaseCnt,
true,
@ -275,6 +290,129 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTest, reporter, ctxInfo) {
}
}
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTestDelayedRelease, reporter, ctxInfo) {
const int kWidth = 10;
const int kHeight = 10;
GrContext* ctx = ctxInfo.grContext();
GrGpu* gpu = ctx->contextPriv().getGpu();
GrBackendTexture backendTex = gpu->createTestingOnlyBackendTexture(
nullptr, kWidth, kHeight, GrColorType::kRGBA_8888, true, GrMipMapped::kNo);
REPORTER_ASSERT(reporter, backendTex.isValid());
GrBackendFormat backendFormat = backendTex.getBackendFormat();
REPORTER_ASSERT(reporter, backendFormat.isValid());
PromiseTextureChecker promiseChecker(backendTex, reporter, false);
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,
SkDeferredDisplayListRecorder::DelayReleaseCallback::kYes));
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;
ReleaseBalanceExpecation balanceExpecation = ReleaseBalanceExpecation::kBalanced;
canvas->drawImage(refImg, 0, 0);
REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
balanceExpecation,
expectedFulfillCnt,
expectedReleaseCnt,
true,
expectedDoneCnt,
reporter));
bool isVulkan = GrBackendApi::kVulkan == ctx->backend();
canvas->flush();
expectedFulfillCnt++;
// Because we've delayed release, we expect a +1 balance.
balanceExpecation = ReleaseBalanceExpecation::kBalancedOrPlusOne;
REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
balanceExpecation,
expectedFulfillCnt,
expectedReleaseCnt,
!isVulkan,
expectedDoneCnt,
reporter));
gpu->testingOnly_flushGpuAndSync();
REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
balanceExpecation,
expectedFulfillCnt,
expectedReleaseCnt,
true,
expectedDoneCnt,
reporter));
canvas->drawImage(refImg, 0, 0);
canvas->drawImage(refImg, 0, 0);
canvas->flush();
gpu->testingOnly_flushGpuAndSync();
REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
balanceExpecation,
expectedFulfillCnt,
expectedReleaseCnt,
true,
expectedDoneCnt,
reporter));
canvas->drawImage(refImg, 0, 0);
canvas->flush();
REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
balanceExpecation,
expectedFulfillCnt,
expectedReleaseCnt,
!isVulkan,
expectedDoneCnt,
reporter));
canvas->drawImage(refImg, 0, 0);
refImg.reset();
REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
balanceExpecation,
expectedFulfillCnt,
expectedReleaseCnt,
!isVulkan,
expectedDoneCnt,
reporter));
canvas->flush();
gpu->testingOnly_flushGpuAndSync();
// We released the image already and we flushed and synced.
balanceExpecation = ReleaseBalanceExpecation::kBalanced;
expectedReleaseCnt++;
expectedDoneCnt++;
REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
balanceExpecation,
expectedFulfillCnt,
expectedReleaseCnt,
!isVulkan,
expectedDoneCnt,
reporter));
gpu->deleteTestingOnlyBackendTexture(backendTex);
}
// Tests replacing the backing texture for a promise image after a release and then refulfilling in
// the SkDeferredDisplayListRecorder::DelayReleaseCallback::kNo case.
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTextureReuse, reporter, ctxInfo) {
const int kWidth = 10;
const int kHeight = 10;
@ -300,14 +438,16 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTextureReuse, reporter, ctxInfo)
PromiseTextureChecker promiseChecker(backendTex1, reporter, true);
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));
SkImage_Gpu::MakePromiseTexture(
ctx, backendFormat, kWidth, kHeight,
GrMipMapped::kNo, texOrigin,
kRGBA_8888_SkColorType, kPremul_SkAlphaType,
nullptr,
PromiseTextureChecker::Fulfill,
PromiseTextureChecker::Release,
PromiseTextureChecker::Done,
&promiseChecker,
SkDeferredDisplayListRecorder::DelayReleaseCallback::kNo));
SkImageInfo info =
SkImageInfo::Make(kWidth, kHeight, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
@ -320,8 +460,9 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTextureReuse, reporter, ctxInfo)
canvas->drawImage(refImg, 0, 0);
canvas->drawImage(refImg, 5, 5);
ReleaseBalanceExpecation balanceExpecation = ReleaseBalanceExpecation::kBalanced;
REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
true,
balanceExpecation,
expectedFulfillCnt,
expectedReleaseCnt,
true,
@ -332,8 +473,11 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTextureReuse, reporter, ctxInfo)
canvas->flush();
expectedFulfillCnt++;
expectedReleaseCnt++;
if (isVulkan) {
balanceExpecation = ReleaseBalanceExpecation::kBalancedOrPlusOne;
}
REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
!isVulkan,
balanceExpecation,
expectedFulfillCnt,
expectedReleaseCnt,
!isVulkan,
@ -352,8 +496,9 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTextureReuse, reporter, ctxInfo)
REPORTER_ASSERT(reporter, ctx->contextPriv().resourceProvider()->findByUniqueKey<>(texKey1));
gpu->testingOnly_flushGpuAndSync();
balanceExpecation = ReleaseBalanceExpecation::kBalanced;
REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
true,
balanceExpecation,
expectedFulfillCnt,
expectedReleaseCnt,
true,
@ -373,7 +518,7 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTextureReuse, reporter, ctxInfo)
canvas->drawImage(refImg, 0, 0);
REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
true,
balanceExpecation,
expectedFulfillCnt,
expectedReleaseCnt,
true,
@ -383,6 +528,9 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTextureReuse, reporter, ctxInfo)
canvas->flush();
expectedFulfillCnt++;
expectedReleaseCnt++;
if (isVulkan) {
balanceExpecation = ReleaseBalanceExpecation::kBalancedOrPlusOne;
}
// Second texture should be in the cache.
keys = promiseChecker.uniqueKeys();
REPORTER_ASSERT(reporter, keys.count() == 1);
@ -394,7 +542,7 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTextureReuse, reporter, ctxInfo)
REPORTER_ASSERT(reporter, ctx->contextPriv().resourceProvider()->findByUniqueKey<>(texKey2));
REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
!isVulkan,
balanceExpecation,
expectedFulfillCnt,
expectedReleaseCnt,
!isVulkan,
@ -404,8 +552,9 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTextureReuse, reporter, ctxInfo)
promiseChecker.fLastFulfilledTexture, backendTex2));
gpu->testingOnly_flushGpuAndSync();
balanceExpecation = ReleaseBalanceExpecation::kBalanced;
REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
true,
balanceExpecation,
expectedFulfillCnt,
expectedReleaseCnt,
true,
@ -424,7 +573,7 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTextureReuse, reporter, ctxInfo)
expectedFulfillCnt++;
expectedReleaseCnt++;
REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
true,
balanceExpecation,
expectedFulfillCnt,
expectedReleaseCnt,
true,
@ -453,7 +602,7 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTextureReuse, reporter, ctxInfo)
expectedFulfillCnt++;
expectedReleaseCnt++;
REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
true,
balanceExpecation,
expectedFulfillCnt,
expectedReleaseCnt,
true,
@ -473,14 +622,16 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTextureReuse, reporter, ctxInfo)
// 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));
SkImage_Gpu::MakePromiseTexture(
ctx, backendFormat, kWidth, kHeight,
GrMipMapped::kNo, texOrigin,
kRGBA_8888_SkColorType, kPremul_SkAlphaType,
nullptr,
PromiseTextureChecker::Fulfill,
PromiseTextureChecker::Release,
PromiseTextureChecker::Done,
&promiseChecker,
SkDeferredDisplayListRecorder::DelayReleaseCallback::kNo));
canvas->drawImage(refImg, 0, 0);
canvas->drawImage(refImg2, 1, 1);
@ -489,7 +640,7 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTextureReuse, reporter, ctxInfo)
expectedFulfillCnt += 2;
expectedReleaseCnt += 2;
REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
true,
balanceExpecation,
expectedFulfillCnt,
expectedReleaseCnt,
true,
@ -518,7 +669,7 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTextureReuse, reporter, ctxInfo)
refImg.reset();
++expectedDoneCnt;
REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
true,
balanceExpecation,
expectedFulfillCnt,
expectedReleaseCnt,
true,
@ -527,7 +678,7 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTextureReuse, reporter, ctxInfo)
refImg2.reset();
++expectedDoneCnt;
REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
true,
balanceExpecation,
expectedFulfillCnt,
expectedReleaseCnt,
true,
@ -553,75 +704,82 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTextureReuseDifferentConfig, repo
GrBackendTexture backendTex2 = gpu->createTestingOnlyBackendTexture(
nullptr, kWidth, kHeight, GrColorType::kAlpha_8, false, GrMipMapped::kNo);
REPORTER_ASSERT(reporter, backendTex2.isValid());
if (backendTex1.getBackendFormat() != backendTex2.getBackendFormat()) {
gpu->deleteTestingOnlyBackendTexture(backendTex1);
return;
}
// We only needed this texture to check that alpha and gray color types use the same format.
gpu->deleteTestingOnlyBackendTexture(backendTex2);
SkImageInfo info =
SkImageInfo::Make(kWidth, kHeight, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info);
SkCanvas* canvas = surface->getCanvas();
if (backendTex1.getBackendFormat() != backendTex2.getBackendFormat()) {
gpu->deleteTestingOnlyBackendTexture(backendTex1);
gpu->deleteTestingOnlyBackendTexture(backendTex2);
return;
}
PromiseTextureChecker promiseChecker(backendTex1, reporter, true);
sk_sp<SkImage> alphaImg(SkImage_Gpu::MakePromiseTexture(
ctx, backendTex1.getBackendFormat(), kWidth, kHeight, GrMipMapped::kNo,
kTopLeft_GrSurfaceOrigin, kAlpha_8_SkColorType, kPremul_SkAlphaType, nullptr,
PromiseTextureChecker::Fulfill, PromiseTextureChecker::Release,
PromiseTextureChecker::Done, &promiseChecker));
REPORTER_ASSERT(reporter, alphaImg);
for (auto delayRelease : {SkDeferredDisplayListRecorder::DelayReleaseCallback::kNo,
SkDeferredDisplayListRecorder::DelayReleaseCallback::kYes}) {
PromiseTextureChecker promiseChecker(backendTex1, reporter, true);
sk_sp<SkImage> alphaImg(SkImage_Gpu::MakePromiseTexture(
ctx, backendTex1.getBackendFormat(), kWidth, kHeight, GrMipMapped::kNo,
kTopLeft_GrSurfaceOrigin, kAlpha_8_SkColorType, kPremul_SkAlphaType, nullptr,
PromiseTextureChecker::Fulfill, PromiseTextureChecker::Release,
PromiseTextureChecker::Done, &promiseChecker, delayRelease));
REPORTER_ASSERT(reporter, alphaImg);
sk_sp<SkImage> grayImg(SkImage_Gpu::MakePromiseTexture(
ctx, backendTex1.getBackendFormat(), kWidth, kHeight, GrMipMapped::kNo,
kBottomLeft_GrSurfaceOrigin, kGray_8_SkColorType, kOpaque_SkAlphaType, nullptr,
PromiseTextureChecker::Fulfill, PromiseTextureChecker::Release,
PromiseTextureChecker::Done, &promiseChecker));
REPORTER_ASSERT(reporter, grayImg);
sk_sp<SkImage> grayImg(SkImage_Gpu::MakePromiseTexture(
ctx, backendTex1.getBackendFormat(), kWidth, kHeight, GrMipMapped::kNo,
kBottomLeft_GrSurfaceOrigin, kGray_8_SkColorType, kOpaque_SkAlphaType, nullptr,
PromiseTextureChecker::Fulfill, PromiseTextureChecker::Release,
PromiseTextureChecker::Done, &promiseChecker, delayRelease));
REPORTER_ASSERT(reporter, grayImg);
canvas->drawImage(alphaImg, 0, 0);
canvas->drawImage(grayImg, 1, 1);
canvas->flush();
gpu->testingOnly_flushGpuAndSync();
canvas->drawImage(alphaImg, 0, 0);
canvas->drawImage(grayImg, 1, 1);
canvas->flush();
gpu->testingOnly_flushGpuAndSync();
int expectedFulfillCnt = 2;
int expectedReleaseCnt = 2;
int expectedDoneCnt = 0;
REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
true,
expectedFulfillCnt,
expectedReleaseCnt,
true,
expectedDoneCnt,
reporter));
int expectedFulfillCnt = 2;
int expectedReleaseCnt = 0;
int expectedDoneCnt = 0;
ReleaseBalanceExpecation balanceExpecation = ReleaseBalanceExpecation::kAny;
if (delayRelease == SkDeferredDisplayListRecorder::DelayReleaseCallback::kNo) {
expectedReleaseCnt = 2;
balanceExpecation = ReleaseBalanceExpecation::kBalanced;
}
REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
balanceExpecation,
expectedFulfillCnt,
expectedReleaseCnt,
true,
expectedDoneCnt,
reporter));
// Because they use different configs, each image should have created a different GrTexture
// and they both should still be cached.
ctx->contextPriv().getResourceCache()->purgeAsNeeded();
// Because they use different configs, each image should have created a different GrTexture
// and they both should still be cached.
ctx->contextPriv().getResourceCache()->purgeAsNeeded();
auto keys = promiseChecker.uniqueKeys();
REPORTER_ASSERT(reporter, keys.count() == 2);
for (const auto& key : keys) {
auto surf = ctx->contextPriv().resourceProvider()->findByUniqueKey<GrSurface>(key);
REPORTER_ASSERT(reporter, surf && surf->asTexture());
if (surf && surf->asTexture()) {
REPORTER_ASSERT(reporter, !GrBackendTexture::TestingOnly_Equals(
backendTex1, surf->asTexture()->getBackendTexture()));
auto keys = promiseChecker.uniqueKeys();
REPORTER_ASSERT(reporter, keys.count() == 2);
for (const auto& key : keys) {
auto surf = ctx->contextPriv().resourceProvider()->findByUniqueKey<GrSurface>(key);
REPORTER_ASSERT(reporter, surf && surf->asTexture());
if (surf && surf->asTexture()) {
REPORTER_ASSERT(reporter,
!GrBackendTexture::TestingOnly_Equals(
backendTex1, surf->asTexture()->getBackendTexture()));
}
}
// Change the backing texture, this should invalidate the keys.
promiseChecker.replaceTexture();
ctx->contextPriv().getResourceCache()->purgeAsNeeded();
for (const auto& key : keys) {
auto surf = ctx->contextPriv().resourceProvider()->findByUniqueKey<GrSurface>(key);
REPORTER_ASSERT(reporter, !surf);
}
}
// Change the backing texture, this should invalidate the keys. The cached textures should
// get purged after purgeAsNeeded is called.
promiseChecker.replaceTexture(backendTex2);
ctx->contextPriv().getResourceCache()->purgeAsNeeded();
for (const auto& key : keys) {
auto surf = ctx->contextPriv().resourceProvider()->findByUniqueKey<GrSurface>(key);
REPORTER_ASSERT(reporter, !surf);
}
gpu->deleteTestingOnlyBackendTexture(backendTex1);
gpu->deleteTestingOnlyBackendTexture(backendTex2);
}
DEF_GPUTEST(PromiseImageTextureShutdown, reporter, ctxInfo) {
@ -670,9 +828,10 @@ DEF_GPUTEST(PromiseImageTextureShutdown, reporter, ctxInfo) {
PromiseTextureChecker promiseChecker(backendTex, reporter, false);
sk_sp<SkImage> image(SkImage_Gpu::MakePromiseTexture(
ctx, backendTex.getBackendFormat(), kWidth, kHeight, GrMipMapped::kNo,
kTopLeft_GrSurfaceOrigin, kAlpha_8_SkColorType, kPremul_SkAlphaType,
nullptr, PromiseTextureChecker::Fulfill, PromiseTextureChecker::Release,
PromiseTextureChecker::Done, &promiseChecker));
kTopLeft_GrSurfaceOrigin, kAlpha_8_SkColorType, kPremul_SkAlphaType, nullptr,
PromiseTextureChecker::Fulfill, PromiseTextureChecker::Release,
PromiseTextureChecker::Done, &promiseChecker,
SkDeferredDisplayListRecorder::DelayReleaseCallback::kNo));
REPORTER_ASSERT(reporter, image);
canvas->drawImage(image, 0, 0);
@ -687,8 +846,9 @@ DEF_GPUTEST(PromiseImageTextureShutdown, reporter, ctxInfo) {
int expectedFulfillCnt = 1;
int expectedReleaseCnt = 1;
int expectedDoneCnt = 1;
ReleaseBalanceExpecation balanceExpecation = ReleaseBalanceExpecation::kBalanced;
REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
true,
balanceExpecation,
expectedFulfillCnt,
expectedReleaseCnt,
true,
@ -719,7 +879,8 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTextureFullCache, reporter, ctxIn
ctx, backendTex.getBackendFormat(), kWidth, kHeight, GrMipMapped::kNo,
kTopLeft_GrSurfaceOrigin, kAlpha_8_SkColorType, kPremul_SkAlphaType, nullptr,
PromiseTextureChecker::Fulfill, PromiseTextureChecker::Release,
PromiseTextureChecker::Done, &promiseChecker));
PromiseTextureChecker::Done, &promiseChecker,
SkDeferredDisplayListRecorder::DelayReleaseCallback::kNo));
REPORTER_ASSERT(reporter, image);
// Make the cache full. This tests that we don't preemptively purge cached textures for

View File

@ -246,7 +246,8 @@ sk_sp<SkImage> DDLPromiseImageHelper::PromiseImageCreator(const void* rawData,
DDLPromiseImageHelper::PromiseImageFulfillProc,
DDLPromiseImageHelper::PromiseImageReleaseProc,
DDLPromiseImageHelper::PromiseImageDoneProc,
contexts);
contexts,
helper->fDelayReleaseCallback);
for (int i = 0; i < textureCount; ++i) {
curImage.callbackContext(i)->wasAddedToImage();
}
@ -269,7 +270,8 @@ sk_sp<SkImage> DDLPromiseImageHelper::PromiseImageCreator(const void* rawData,
DDLPromiseImageHelper::PromiseImageFulfillProc,
DDLPromiseImageHelper::PromiseImageReleaseProc,
DDLPromiseImageHelper::PromiseImageDoneProc,
(void*) curImage.refCallbackContext(0).release());
(void*)curImage.refCallbackContext(0).release(),
helper->fDelayReleaseCallback);
curImage.callbackContext(0)->wasAddedToImage();
}
perRecorderContext->fPromiseImages->push_back(image);

View File

@ -47,7 +47,9 @@ struct SkYUVAIndex;
// all the replaying is complete. This will pin the GrBackendTextures in VRAM.
class DDLPromiseImageHelper {
public:
DDLPromiseImageHelper() { }
using DelayReleaseCallback = SkDeferredDisplayListRecorder::DelayReleaseCallback;
DDLPromiseImageHelper(DelayReleaseCallback delayReleaseCallback = DelayReleaseCallback::kNo)
: fDelayReleaseCallback(delayReleaseCallback) {}
~DDLPromiseImageHelper();
// Convert the SkPicture into SkData replacing all the SkImages with an index.
@ -251,6 +253,7 @@ private:
// returns -1 on failure
int findOrDefineImage(SkImage* image);
DelayReleaseCallback fDelayReleaseCallback;
SkTArray<PromiseImageInfo> fImageInfo;
};