Revert "Revert "Change promise image contract to for when Release and Done are called.""

This reverts commit d716d4402a.

Bug: skia:8800
Change-Id: Ic16cd4e960be2c3d2462bdf2b54b2e32abbd9f78
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/199081
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
This commit is contained in:
Brian Salomon 2019-03-08 13:28:46 -05:00 committed by Skia Commit-Bot
parent d25070bd0d
commit 0cc575488f
12 changed files with 260 additions and 242 deletions

View File

@ -58,31 +58,45 @@ public:
using PromiseImageTextureReleaseProc = void (*)(PromiseImageTextureContext);
using PromiseImageTextureDoneProc = void (*)(PromiseImageTextureContext);
/**
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.
enum class PromiseImageApiVersion { kLegacy, kNew };
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.
/**
Create a new SkImage that is very similar to an SkImage created by MakeFromTexture. The
difference is that the caller need not have created the texture nor populated it with the
image pixel data. Moreover, the SkImage may be created on a thread as the creation of the
image does not require access to the backend API or GrContext. Instead of passing a
GrBackendTexture the client supplies a description of the texture consisting of
GrBackendFormat, width, height, and GrMipMapped state. The resulting SkImage can be drawn
to a SkDeferredDisplayListRecorder or directly to a GPU-backed SkSurface.
When the actual texture is required to perform a backend API draw, textureFulfillProc will
be called to receive a GrBackendTexture. The properties of the GrBackendTexture must match
those set during the SkImage creation, and it must refer to a valid existing texture in the
backend API context/device, and be populated with the image pixel data. The texture contents
cannot be modified until textureReleaseProc is called. The texture cannot be deleted until
textureDoneProc is called.
When all the following are true:
* the promise image is deleted,
* the promise SkImage 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. There is at most one call to each of
textureFulfillProc, textureReleaseProc, and textureDoneProc. textureDoneProc is always
called even if image creation fails or if the image is never fulfilled (e.g. it is never
drawn). If textureFulfillProc is called then textureReleaseProc will always be called even
if textureFulfillProc fails.
* and all draws referencing the texture have been flushed (via GrContext::flush or
SkSurface::flush)
the textureReleaseProc is called. When the following additional constraint is met
* the texture is safe to delete in the underlying API
the textureDoneProc is called. For some APIs (e.g. GL) the two states are equivalent.
However, for others (e.g. Vulkan) they are not as it is not legal to delete a texture until
the GPU work referencing it has completed.
There is at most one call to each of textureFulfillProc, textureReleaseProc, and
textureDoneProc. textureDoneProc is always called even if image creation fails or if the
image is never fulfilled (e.g. it is never drawn or all draws are clipped out). If
textureFulfillProc is called then textureReleaseProc will always be called even if
textureFulfillProc failed.
This call is only valid if the SkDeferredDisplayListRecorder is backed by a gpu context.
If 'version' is set to kLegacy then the textureReleaseProc call is delayed until the
conditions for textureDoneProc are met and then they are both called.
This call is only valid if the SkDeferredDisplayListRecorder is backed by a GPU context.
@param backendFormat format of promised gpu texture
@param width width of promised gpu texture
@ -98,42 +112,51 @@ 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 textureDoneProc function called when we will no longer call textureFulfillProc
@param textureContext state passed to textureFulfillProc and textureReleaseProc
@param version controls when textureReleaseProc is called
@return created SkImage, or nullptr
*/
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);
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,
PromiseImageApiVersion version = PromiseImageApiVersion::kLegacy);
/**
This entry point operates the same as 'makePromiseTexture' except that its
textureFulfillProc can be called up to four times to fetch the required YUVA
planes (passing a different textureContext to each call). So, if the 'yuvaIndices'
indicate that only the first two backend textures are used, 'textureFulfillProc' will
be called with the first two 'textureContexts'.
This entry point operates like 'makePromiseTexture' but it is used to construct a SkImage
from YUV[A] data. The source data may be planar (i.e. spread across multiple textures). In
the extreme Y, U, V, and A are all in different planes and thus the image is specified by
four textures. 'yuvaIndices' specifies the mapping from texture color channels to Y, U, V,
and possibly A components. It therefore indicates how many unique textures compose the full
image. Separate textureFulfillProc, textureReleaseProc, and textureDoneProc calls are made
for each texture and each texture has its own PromiseImageTextureContext. 'yuvFormats',
'yuvaSizes', and 'textureContexts' have one entry for each of the up to four textures, as
indicated by 'yuvaIndices'.
*/
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[]);
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[],
PromiseImageApiVersion version = PromiseImageApiVersion::kLegacy);
private:
bool init();

View File

@ -49,7 +49,7 @@ struct SK_API SkYUVAIndex {
};
static constexpr int kIndexCount = kLast_Index + 1;
/** The index is a number between -1..3 which definies which image source to read from, where -1
/** The index is a number between -1..3 which defines which image source to read from, where -1
* means the image source doesn't exist. The assumption is we will always have image sources for
* each of YUV planes, but optionally have image source for A plane. */
int fIndex;

View File

@ -34,7 +34,8 @@ sk_sp<SkImage> SkDeferredDisplayListRecorder::makePromiseTexture(
PromiseImageTextureFulfillProc textureFulfillProc,
PromiseImageTextureReleaseProc textureReleaseProc,
PromiseImageTextureDoneProc textureDoneProc,
PromiseImageTextureContext textureContext) {
PromiseImageTextureContext textureContext,
PromiseImageApiVersion) {
return nullptr;
}
@ -50,7 +51,8 @@ sk_sp<SkImage> SkDeferredDisplayListRecorder::makeYUVAPromiseTexture(
PromiseImageTextureFulfillProc textureFulfillProc,
PromiseImageTextureReleaseProc textureReleaseProc,
PromiseImageTextureDoneProc textureDoneProc,
PromiseImageTextureContext textureContexts[]) {
PromiseImageTextureContext textureContexts[],
PromiseImageApiVersion) {
return nullptr;
}
@ -233,7 +235,8 @@ sk_sp<SkImage> SkDeferredDisplayListRecorder::makePromiseTexture(
PromiseImageTextureFulfillProc textureFulfillProc,
PromiseImageTextureReleaseProc textureReleaseProc,
PromiseImageTextureDoneProc textureDoneProc,
PromiseImageTextureContext textureContext) {
PromiseImageTextureContext textureContext,
PromiseImageApiVersion version) {
if (!fContext) {
return nullptr;
}
@ -250,7 +253,8 @@ sk_sp<SkImage> SkDeferredDisplayListRecorder::makePromiseTexture(
textureFulfillProc,
textureReleaseProc,
textureDoneProc,
textureContext);
textureContext,
version);
}
sk_sp<SkImage> SkDeferredDisplayListRecorder::makeYUVAPromiseTexture(
@ -265,7 +269,8 @@ sk_sp<SkImage> SkDeferredDisplayListRecorder::makeYUVAPromiseTexture(
PromiseImageTextureFulfillProc textureFulfillProc,
PromiseImageTextureReleaseProc textureReleaseProc,
PromiseImageTextureDoneProc textureDoneProc,
PromiseImageTextureContext textureContexts[]) {
PromiseImageTextureContext textureContexts[],
PromiseImageApiVersion version) {
if (!fContext) {
return nullptr;
}
@ -282,7 +287,8 @@ sk_sp<SkImage> SkDeferredDisplayListRecorder::makeYUVAPromiseTexture(
textureFulfillProc,
textureReleaseProc,
textureDoneProc,
textureContexts);
textureContexts,
version);
}
#endif

View File

@ -405,7 +405,8 @@ sk_sp<SkImage> SkImage_Gpu::MakePromiseTexture(GrContext* context,
PromiseImageTextureFulfillProc textureFulfillProc,
PromiseImageTextureReleaseProc textureReleaseProc,
PromiseImageTextureDoneProc textureDoneProc,
PromiseImageTextureContext textureContext) {
PromiseImageTextureContext textureContext,
PromiseImageApiVersion version) {
// 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.
@ -436,7 +437,7 @@ sk_sp<SkImage> SkImage_Gpu::MakePromiseTexture(GrContext* context,
callDone.clear();
auto proxy = MakePromiseImageLazyProxy(context, width, height, origin, config, backendFormat,
mipMapped, textureFulfillProc, textureReleaseProc,
textureDoneProc, textureContext);
textureDoneProc, textureContext, version);
if (!proxy) {
return nullptr;
}

View File

@ -55,7 +55,8 @@ public:
PromiseImageTextureFulfillProc textureFulfillProc,
PromiseImageTextureReleaseProc textureReleaseProc,
PromiseImageTextureDoneProc textureDoneProc,
PromiseImageTextureContext textureContext);
PromiseImageTextureContext textureContext,
PromiseImageApiVersion);
static sk_sp<SkImage> ConvertYUVATexturesToRGB(GrContext*, SkYUVColorSpace yuvColorSpace,
const GrBackendTexture yuvaTextures[],

View File

@ -386,7 +386,8 @@ sk_sp<GrTextureProxy> SkImage_GpuBase::MakePromiseImageLazyProxy(
PromiseImageTextureFulfillProc fulfillProc,
PromiseImageTextureReleaseProc releaseProc,
PromiseImageTextureDoneProc doneProc,
PromiseImageTextureContext textureContext) {
PromiseImageTextureContext textureContext,
PromiseImageApiVersion version) {
SkASSERT(context);
SkASSERT(width > 0 && height > 0);
SkASSERT(doneProc);
@ -425,8 +426,12 @@ sk_sp<GrTextureProxy> SkImage_GpuBase::MakePromiseImageLazyProxy(
PromiseImageTextureReleaseProc releaseProc,
PromiseImageTextureDoneProc doneProc,
PromiseImageTextureContext context,
GrPixelConfig config)
: fFulfillProc(fulfillProc), fReleaseProc(releaseProc), fConfig(config) {
GrPixelConfig config,
PromiseImageApiVersion version)
: fFulfillProc(fulfillProc)
, fReleaseProc(releaseProc)
, fConfig(config)
, fVersion(version) {
fDoneCallback = sk_make_sp<GrRefCntedCallback>(doneProc, context);
}
PromiseLazyInstantiateCallback(PromiseLazyInstantiateCallback&&) = default;
@ -492,7 +497,10 @@ sk_sp<GrTextureProxy> SkImage_GpuBase::MakePromiseImageLazyProxy(
return sk_sp<GrTexture>();
}
}
tex->addIdleProc(std::move(releaseCallback), GrTexture::IdleState::kFinished);
auto releaseIdleState = fVersion == PromiseImageApiVersion::kLegacy
? GrTexture::IdleState::kFinished
: GrTexture::IdleState::kFlushed;
tex->addIdleProc(std::move(releaseCallback), releaseIdleState);
tex->addIdleProc(std::move(fDoneCallback), GrTexture::IdleState::kFinished);
promiseTexture->addKeyToInvalidate(tex->getContext()->priv().contextID(), key);
fTexture = tex.get();
@ -513,7 +521,8 @@ sk_sp<GrTextureProxy> SkImage_GpuBase::MakePromiseImageLazyProxy(
GrTexture* fTexture = nullptr;
uint32_t fTextureContextID = SK_InvalidUniqueID;
GrPixelConfig fConfig;
} callback(fulfillProc, releaseProc, doneProc, textureContext, config);
PromiseImageApiVersion fVersion;
} callback(fulfillProc, releaseProc, doneProc, textureContext, config, version);
GrProxyProvider* proxyProvider = context->priv().proxyProvider();

View File

@ -78,6 +78,7 @@ public:
using PromiseImageTextureDoneProc = SkDeferredDisplayListRecorder::PromiseImageTextureDoneProc;
protected:
using PromiseImageApiVersion = SkDeferredDisplayListRecorder::PromiseImageApiVersion;
// Helper for making a lazy proxy for a promise image. The PromiseDoneProc we be called,
// if not null, immediately if this function fails. Othwerwise, it is installed in the
// proxy along with the TextureFulfillProc and TextureReleaseProc. PromiseDoneProc must not
@ -85,7 +86,7 @@ protected:
static sk_sp<GrTextureProxy> MakePromiseImageLazyProxy(
GrContext*, int width, int height, GrSurfaceOrigin, GrPixelConfig, GrBackendFormat,
GrMipMapped, PromiseImageTextureFulfillProc, PromiseImageTextureReleaseProc,
PromiseImageTextureDoneProc, PromiseImageTextureContext);
PromiseImageTextureDoneProc, PromiseImageTextureContext, PromiseImageApiVersion);
static bool RenderYUVAToRGBA(GrContext* ctx, GrRenderTargetContext* renderTargetContext,
const SkRect& rect, SkYUVColorSpace yuvColorSpace,

View File

@ -282,7 +282,8 @@ sk_sp<SkImage> SkImage_GpuYUVA::MakePromiseYUVATexture(
PromiseImageTextureFulfillProc textureFulfillProc,
PromiseImageTextureReleaseProc textureReleaseProc,
PromiseImageTextureDoneProc promiseDoneProc,
PromiseImageTextureContext textureContexts[]) {
PromiseImageTextureContext textureContexts[],
PromiseImageApiVersion version) {
int numTextures;
bool valid = SkYUVAIndex::AreValidIndices(yuvaIndices, &numTextures);
@ -342,7 +343,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], version);
++proxiesCreated;
if (!proxies[texIdx]) {
return nullptr;

View File

@ -73,7 +73,8 @@ public:
PromiseImageTextureFulfillProc textureFulfillProc,
PromiseImageTextureReleaseProc textureReleaseProc,
PromiseImageTextureDoneProc textureDoneProc,
PromiseImageTextureContext textureContexts[]);
PromiseImageTextureContext textureContexts[],
PromiseImageApiVersion);
private:
SkImage_GpuYUVA(const SkImage_GpuYUVA* image, sk_sp<SkColorSpace>);

View File

@ -675,7 +675,8 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DDLInvalidRecorder, reporter, ctxInfo) {
dummy_fulfill_proc,
dummy_release_proc,
dummy_done_proc,
nullptr);
nullptr,
SkDeferredDisplayListRecorder::PromiseImageApiVersion::kNew);
REPORTER_ASSERT(reporter, !image);
}
@ -779,7 +780,8 @@ DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(DDLTextureFlagsTest, reporter, ctxInfo) {
dummy_fulfill_proc,
dummy_release_proc,
dummy_done_proc,
nullptr);
nullptr,
SkDeferredDisplayListRecorder::PromiseImageApiVersion::kNew);
if (GR_GL_TEXTURE_2D != target && mipMapped == GrMipMapped::kYes) {
REPORTER_ASSERT(reporter, !image);
continue;

View File

@ -63,66 +63,100 @@ struct PromiseTextureChecker {
}
};
enum class ReleaseBalanceExpecation {
enum class ReleaseBalanceExpectation {
kBalanced,
kBalancedOrPlusOne,
kAny
kAllUnbalanced,
kUnbalancedByOne,
};
static bool check_fulfill_and_release_cnts(const PromiseTextureChecker& promiseChecker,
ReleaseBalanceExpecation balanceExpecation,
enum class DoneBalanceExpectation {
kBalanced,
kAllUnbalanced,
kUnknown,
kUnbalancedByOne,
kBalancedOrOffByOne,
};
static void check_fulfill_and_release_cnts(skiatest::Reporter* reporter,
const PromiseTextureChecker& promiseChecker,
int expectedFulfillCnt,
int expectedReleaseCnt,
bool expectedRequired,
int expectedDoneCnt,
skiatest::Reporter* reporter) {
bool result = true;
int countDiff = promiseChecker.fFulfillCount - promiseChecker.fReleaseCount;
// FulfillCount should always equal ReleaseCount or be at most one higher
if (countDiff != 0) {
if (balanceExpecation == ReleaseBalanceExpecation::kBalanced) {
result = false;
REPORTER_ASSERT(reporter, 0 == countDiff);
} 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);
}
ReleaseBalanceExpectation releaseBalanceExpecation,
DoneBalanceExpectation doneBalanceExpecation) {
REPORTER_ASSERT(reporter, promiseChecker.fFulfillCount == expectedFulfillCnt);
if (!expectedFulfillCnt) {
// Release and Done should only ever be called after Fulfill.
REPORTER_ASSERT(reporter, !promiseChecker.fReleaseCount);
REPORTER_ASSERT(reporter, !promiseChecker.fDoneCount);
return;
}
int fulfillDiff = expectedFulfillCnt - promiseChecker.fFulfillCount;
REPORTER_ASSERT(reporter, fulfillDiff >= 0);
if (fulfillDiff != 0) {
if (expectedRequired) {
result = false;
REPORTER_ASSERT(reporter, expectedFulfillCnt == promiseChecker.fFulfillCount);
} else if (fulfillDiff > 1) {
result = false;
REPORTER_ASSERT(reporter, fulfillDiff <= 1);
}
int releaseDiff = promiseChecker.fFulfillCount - promiseChecker.fReleaseCount;
switch (releaseBalanceExpecation) {
case ReleaseBalanceExpectation::kBalanced:
REPORTER_ASSERT(reporter, !releaseDiff);
break;
case ReleaseBalanceExpectation::kAllUnbalanced:
REPORTER_ASSERT(reporter, releaseDiff == promiseChecker.fFulfillCount);
break;
case ReleaseBalanceExpectation::kUnbalancedByOne:
REPORTER_ASSERT(reporter, releaseDiff == 1);
break;
}
int releaseDiff = expectedReleaseCnt - promiseChecker.fReleaseCount;
REPORTER_ASSERT(reporter, releaseDiff >= 0);
if (releaseDiff != 0) {
if (expectedRequired) {
result = false;
REPORTER_ASSERT(reporter, expectedReleaseCnt == promiseChecker.fReleaseCount);
} else if (releaseDiff > 1) {
result = false;
REPORTER_ASSERT(reporter, releaseDiff <= 1);
}
int doneDiff = promiseChecker.fFulfillCount - promiseChecker.fDoneCount;
switch (doneBalanceExpecation) {
case DoneBalanceExpectation::kBalanced:
REPORTER_ASSERT(reporter, !doneDiff);
break;
case DoneBalanceExpectation::kAllUnbalanced:
REPORTER_ASSERT(reporter, doneDiff == promiseChecker.fFulfillCount);
break;
case DoneBalanceExpectation::kUnknown:
REPORTER_ASSERT(reporter, doneDiff >= 0 && doneDiff <= promiseChecker.fFulfillCount);
break;
case DoneBalanceExpectation::kUnbalancedByOne:
REPORTER_ASSERT(reporter, doneDiff == 1);
break;
case DoneBalanceExpectation::kBalancedOrOffByOne:
REPORTER_ASSERT(reporter, doneDiff == 0 || doneDiff == 1);
break;
}
}
if (expectedDoneCnt != promiseChecker.fDoneCount) {
result = false;
REPORTER_ASSERT(reporter, expectedDoneCnt == promiseChecker.fDoneCount);
static void check_unfulfilled(const PromiseTextureChecker& promiseChecker,
skiatest::Reporter* reporter) {
check_fulfill_and_release_cnts(reporter, promiseChecker, 0,
ReleaseBalanceExpectation::kBalanced,
DoneBalanceExpectation::kBalanced);
}
static void check_only_fulfilled(skiatest::Reporter* reporter,
const PromiseTextureChecker& promiseChecker,
int expectedFulfillCnt = 1) {
check_fulfill_and_release_cnts(reporter, promiseChecker, expectedFulfillCnt,
ReleaseBalanceExpectation::kAllUnbalanced,
DoneBalanceExpectation::kAllUnbalanced);
}
static void check_all_flushed_but_not_synced(skiatest::Reporter* reporter,
const PromiseTextureChecker& promiseChecker,
GrBackendApi api,
int expectedFulfillCnt = 1) {
DoneBalanceExpectation doneBalanceExpectation = DoneBalanceExpectation::kBalanced;
// On Vulkan Done isn't guaranteed to be called until a sync has occurred.
if (api == GrBackendApi::kVulkan) {
doneBalanceExpectation = expectedFulfillCnt == 1
? DoneBalanceExpectation::kBalancedOrOffByOne
: DoneBalanceExpectation::kUnknown;
}
check_fulfill_and_release_cnts(reporter, promiseChecker, expectedFulfillCnt,
ReleaseBalanceExpectation::kBalanced, doneBalanceExpectation);
}
return result;
static void check_all_done(skiatest::Reporter* reporter,
const PromiseTextureChecker& promiseChecker,
int expectedFulfillCnt = 1) {
check_fulfill_and_release_cnts(reporter, promiseChecker, expectedFulfillCnt,
ReleaseBalanceExpectation::kBalanced,
DoneBalanceExpectation::kBalanced);
}
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTest, reporter, ctxInfo) {
@ -150,47 +184,22 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTest, reporter, ctxInfo) {
PromiseTextureChecker::Fulfill,
PromiseTextureChecker::Release,
PromiseTextureChecker::Done,
&promiseChecker));
&promiseChecker,
SkDeferredDisplayListRecorder::PromiseImageApiVersion::kNew));
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));
check_unfulfilled(promiseChecker, reporter);
bool isVulkan = GrBackendApi::kVulkan == ctx->backend();
surface->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));
// We still own the image so we should not have called Release or Done.
check_only_fulfilled(reporter, promiseChecker);
gpu->testingOnly_flushGpuAndSync();
REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
balanceExpecation,
expectedFulfillCnt,
expectedReleaseCnt,
true,
expectedDoneCnt,
reporter));
check_only_fulfilled(reporter, promiseChecker);
canvas->drawImage(refImg, 0, 0);
canvas->drawImage(refImg, 0, 0);
@ -198,49 +207,25 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTest, reporter, ctxInfo) {
surface->flush();
gpu->testingOnly_flushGpuAndSync();
REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
balanceExpecation,
expectedFulfillCnt,
expectedReleaseCnt,
true,
expectedDoneCnt,
reporter));
// Image should still be fulfilled from the first time we drew/flushed it.
check_only_fulfilled(reporter, promiseChecker);
canvas->drawImage(refImg, 0, 0);
surface->flush();
REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
balanceExpecation,
expectedFulfillCnt,
expectedReleaseCnt,
!isVulkan,
expectedDoneCnt,
reporter));
check_only_fulfilled(reporter, promiseChecker);
canvas->drawImage(refImg, 0, 0);
refImg.reset();
REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
balanceExpecation,
expectedFulfillCnt,
expectedReleaseCnt,
!isVulkan,
expectedDoneCnt,
reporter));
// We no longer own the image but the last draw is still unflushed.
check_only_fulfilled(reporter, promiseChecker);
surface->flush();
// Flushing should have called Release. Depending on the backend and timing it may have called
// done.
check_all_flushed_but_not_synced(reporter, promiseChecker, ctx->backend());
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));
// Now Done should definitely have been called.
check_all_done(reporter, promiseChecker);
gpu->deleteTestingOnlyBackendTexture(backendTex);
}
@ -280,32 +265,23 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTextureReuseDifferentConfig, repo
ctx, backendTex1.getBackendFormat(), kWidth, kHeight, GrMipMapped::kNo,
kTopLeft_GrSurfaceOrigin, kAlpha_8_SkColorType, kPremul_SkAlphaType, nullptr,
PromiseTextureChecker::Fulfill, PromiseTextureChecker::Release,
PromiseTextureChecker::Done, &promiseChecker));
PromiseTextureChecker::Done, &promiseChecker,
SkDeferredDisplayListRecorder::PromiseImageApiVersion::kNew));
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));
PromiseTextureChecker::Done, &promiseChecker,
SkDeferredDisplayListRecorder::PromiseImageApiVersion::kNew));
REPORTER_ASSERT(reporter, grayImg);
canvas->drawImage(alphaImg, 0, 0);
canvas->drawImage(grayImg, 1, 1);
surface->flush();
gpu->testingOnly_flushGpuAndSync();
int expectedFulfillCnt = 2;
int expectedReleaseCnt = 0;
int expectedDoneCnt = 0;
ReleaseBalanceExpecation balanceExpecation = ReleaseBalanceExpecation::kAny;
REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
balanceExpecation,
expectedFulfillCnt,
expectedReleaseCnt,
true,
expectedDoneCnt,
reporter));
check_only_fulfilled(reporter, promiseChecker, 2);
// Because they use different configs, each image should have created a different GrTexture
// and they both should still be cached.
@ -323,7 +299,7 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTextureReuseDifferentConfig, repo
}
}
// Change the backing texture, this should invalidate the keys.
// Invalidate the backing texture, this should invalidate the keys.
promiseChecker.releaseTexture();
ctx->priv().getResourceCache()->purgeAsNeeded();
@ -331,13 +307,14 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTextureReuseDifferentConfig, repo
auto surf = ctx->priv().resourceProvider()->findByUniqueKey<GrSurface>(key);
REPORTER_ASSERT(reporter, !surf);
}
// Must do this to ensure all callbacks occur before the PromiseImageChecker goes out of scope.
alphaImg.reset();
ctx->flush(); // We do this to pick up any unref messages that are sent by unref'ing the image.
check_fulfill_and_release_cnts(reporter, promiseChecker, 2,
ReleaseBalanceExpectation::kUnbalancedByOne,
DoneBalanceExpectation::kUnbalancedByOne);
grayImg.reset();
ctx->flush();
ctx->priv().getGpu()->testingOnly_flushGpuAndSync();
ctx->flush(); // We do this to pick up any unref messages that are sent by unref'ing the image.
check_all_done(reporter, promiseChecker, 2);
gpu->deleteTestingOnlyBackendTexture(backendTex1);
}
@ -389,7 +366,8 @@ DEF_GPUTEST(PromiseImageTextureShutdown, reporter, ctxInfo) {
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::PromiseImageApiVersion::kNew));
REPORTER_ASSERT(reporter, image);
canvas->drawImage(image, 0, 0);
@ -401,17 +379,7 @@ DEF_GPUTEST(PromiseImageTextureShutdown, reporter, ctxInfo) {
ctx->flush();
contextDeath(&factory, ctx);
int expectedFulfillCnt = 1;
int expectedReleaseCnt = 1;
int expectedDoneCnt = 1;
ReleaseBalanceExpecation balanceExpecation = ReleaseBalanceExpecation::kBalanced;
REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
balanceExpecation,
expectedFulfillCnt,
expectedReleaseCnt,
true,
expectedDoneCnt,
reporter));
check_all_done(reporter, promiseChecker);
}
}
}
@ -437,7 +405,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::PromiseImageApiVersion::kNew));
REPORTER_ASSERT(reporter, image);
// Make the cache full. This tests that we don't preemptively purge cached textures for

View File

@ -202,18 +202,20 @@ sk_sp<SkImage> DDLPromiseImageHelper::PromiseImageCreator(const void* rawData,
sizes[i] = SkISize::MakeEmpty();
}
image = recorder->makeYUVAPromiseTexture(curImage.yuvColorSpace(),
backendFormats,
sizes,
curImage.yuvaIndices(),
curImage.overallWidth(),
curImage.overallHeight(),
GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin,
curImage.refOverallColorSpace(),
DDLPromiseImageHelper::PromiseImageFulfillProc,
DDLPromiseImageHelper::PromiseImageReleaseProc,
DDLPromiseImageHelper::PromiseImageDoneProc,
contexts);
image = recorder->makeYUVAPromiseTexture(
curImage.yuvColorSpace(),
backendFormats,
sizes,
curImage.yuvaIndices(),
curImage.overallWidth(),
curImage.overallHeight(),
GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin,
curImage.refOverallColorSpace(),
DDLPromiseImageHelper::PromiseImageFulfillProc,
DDLPromiseImageHelper::PromiseImageReleaseProc,
DDLPromiseImageHelper::PromiseImageDoneProc,
contexts,
SkDeferredDisplayListRecorder::PromiseImageApiVersion::kNew);
for (int i = 0; i < textureCount; ++i) {
curImage.callbackContext(i)->wasAddedToImage();
}
@ -235,18 +237,20 @@ sk_sp<SkImage> DDLPromiseImageHelper::PromiseImageCreator(const void* rawData,
// Each DDL recorder gets its own ref on the promise callback context for the
// promise images it creates.
// DDL TODO: sort out mipmapping
image = recorder->makePromiseTexture(backendFormat,
curImage.overallWidth(),
curImage.overallHeight(),
GrMipMapped::kNo,
GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin,
curImage.overallColorType(),
curImage.overallAlphaType(),
curImage.refOverallColorSpace(),
DDLPromiseImageHelper::PromiseImageFulfillProc,
DDLPromiseImageHelper::PromiseImageReleaseProc,
DDLPromiseImageHelper::PromiseImageDoneProc,
(void*)curImage.refCallbackContext(0).release());
image = recorder->makePromiseTexture(
backendFormat,
curImage.overallWidth(),
curImage.overallHeight(),
GrMipMapped::kNo,
GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin,
curImage.overallColorType(),
curImage.overallAlphaType(),
curImage.refOverallColorSpace(),
DDLPromiseImageHelper::PromiseImageFulfillProc,
DDLPromiseImageHelper::PromiseImageReleaseProc,
DDLPromiseImageHelper::PromiseImageDoneProc,
(void*)curImage.refCallbackContext(0).release(),
SkDeferredDisplayListRecorder::PromiseImageApiVersion::kNew);
curImage.callbackContext(0)->wasAddedToImage();
}
perRecorderContext->fPromiseImages->push_back(image);