blob: 6bb0ffcb5a948fc95241347b6045da7203b6527e [file] [log] [blame]
/*
* Copyright 2022 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "include/gpu/graphite/BackendTexture.h"
#include "include/core/SkColorFilter.h"
#include "include/core/SkColorSpace.h"
#include "include/core/SkSurface.h"
#include "include/gpu/graphite/Context.h"
#include "include/gpu/graphite/Recording.h"
#include "src/gpu/graphite/Caps.h"
#include "src/gpu/graphite/ContextPriv.h"
#include "src/gpu/graphite/RecordingPriv.h"
#include "tests/Test.h"
using namespace skgpu::graphite;
namespace {
struct PromiseTextureChecker {
PromiseTextureChecker() = default;
explicit PromiseTextureChecker(const BackendTexture& backendTex,
skiatest::Reporter* reporter)
: fReporter(reporter) {
fBackendTextures[0] = backendTex;
}
explicit PromiseTextureChecker(const BackendTexture& backendTex0,
const BackendTexture& backendTex1,
skiatest::Reporter* reporter)
: fReporter(reporter)
, fHasTwoBackendTextures(true) {
fBackendTextures[0] = backendTex0;
fBackendTextures[1] = backendTex1;
}
void checkImageReleased(skiatest::Reporter* reporter, int expectedReleaseCnt) {
REPORTER_ASSERT(reporter, expectedReleaseCnt == fImageReleaseCount);
}
int totalReleaseCount() const { return fTextureReleaseCounts[0] + fTextureReleaseCounts[1]; }
skiatest::Reporter* fReporter = nullptr;
bool fHasTwoBackendTextures = false;
BackendTexture fBackendTextures[2];
int fFulfillCount = 0;
int fImageReleaseCount = 0;
int fTextureReleaseCounts[2] = { 0, 0 };
static std::tuple<BackendTexture, void*> Fulfill(void* self) {
auto checker = reinterpret_cast<PromiseTextureChecker*>(self);
checker->fFulfillCount++;
if (checker->fHasTwoBackendTextures) {
int whichToUse = checker->fFulfillCount % 2;
return { checker->fBackendTextures[whichToUse],
&checker->fTextureReleaseCounts[whichToUse] };
} else {
return { checker->fBackendTextures[0], &checker->fTextureReleaseCounts[0] };
}
}
static void ImageRelease(void* self) {
auto checker = reinterpret_cast<PromiseTextureChecker*>(self);
checker->fImageReleaseCount++;
}
static void TextureRelease(void* context) {
int* releaseCount = reinterpret_cast<int*>(context);
(*releaseCount)++;
}
};
enum class ReleaseBalanceExpectation {
kBalanced,
kOffByOne, // fulfill calls ahead of release calls by 1
kOffByTwo, // fulfill calls ahead of release calls by 2
kFulfillsOnly, // 'n' fulfill calls, 0 release calls
};
void check_fulfill_and_release_cnts(skiatest::Reporter* reporter,
const PromiseTextureChecker& promiseChecker,
int expectedFulfillCnt,
ReleaseBalanceExpectation releaseBalanceExpectation) {
SkASSERT(promiseChecker.fFulfillCount == expectedFulfillCnt);
REPORTER_ASSERT(reporter, promiseChecker.fFulfillCount == expectedFulfillCnt);
if (!expectedFulfillCnt) {
// Release should only ever be called after Fulfill.
REPORTER_ASSERT(reporter, !promiseChecker.fImageReleaseCount);
REPORTER_ASSERT(reporter, !promiseChecker.totalReleaseCount());
return;
}
int releaseDiff = promiseChecker.fFulfillCount - promiseChecker.totalReleaseCount();
switch (releaseBalanceExpectation) {
case ReleaseBalanceExpectation::kBalanced:
SkASSERT(!releaseDiff);
REPORTER_ASSERT(reporter, !releaseDiff);
break;
case ReleaseBalanceExpectation::kOffByOne:
SkASSERT(releaseDiff == 1);
REPORTER_ASSERT(reporter, releaseDiff == 1);
break;
case ReleaseBalanceExpectation::kOffByTwo:
SkASSERT(releaseDiff == 2);
REPORTER_ASSERT(reporter, releaseDiff == 2);
break;
case ReleaseBalanceExpectation::kFulfillsOnly:
REPORTER_ASSERT(reporter, promiseChecker.totalReleaseCount() == 0);
break;
}
}
void check_unfulfilled(const PromiseTextureChecker& promiseChecker,
skiatest::Reporter* reporter) {
check_fulfill_and_release_cnts(reporter, promiseChecker, /* expectedFulfillCnt= */ 0,
ReleaseBalanceExpectation::kBalanced);
}
void check_fulfilled_ahead_by_one(skiatest::Reporter* reporter,
const PromiseTextureChecker& promiseChecker,
int expectedFulfillCnt) {
check_fulfill_and_release_cnts(reporter, promiseChecker, expectedFulfillCnt,
ReleaseBalanceExpectation::kOffByOne);
}
void check_fulfilled_ahead_by_two(skiatest::Reporter* reporter,
const PromiseTextureChecker& promiseChecker,
int expectedFulfillCnt) {
check_fulfill_and_release_cnts(reporter, promiseChecker, expectedFulfillCnt,
ReleaseBalanceExpectation::kOffByTwo);
}
void check_all_done(skiatest::Reporter* reporter,
const PromiseTextureChecker& promiseChecker,
int expectedFulfillCnt) {
check_fulfill_and_release_cnts(reporter, promiseChecker, expectedFulfillCnt,
ReleaseBalanceExpectation::kBalanced);
}
void check_fulfills_only(skiatest::Reporter* reporter,
const PromiseTextureChecker& promiseChecker,
int expectedFulfillCnt) {
check_fulfill_and_release_cnts(reporter, promiseChecker, expectedFulfillCnt,
ReleaseBalanceExpectation::kFulfillsOnly);
}
struct TestCtx {
TestCtx() {}
~TestCtx() {
for (int i = 0; i < 2; ++i) {
if (fBackendTextures[i].isValid()) {
fContext->deleteBackendTexture(fBackendTextures[i]);
}
}
}
Context* fContext;
std::unique_ptr<Recorder> fRecorder;
BackendTexture fBackendTextures[2];
PromiseTextureChecker fPromiseChecker;
sk_sp<SkImage> fImg;
sk_sp<SkSurface> fSurface;
};
void setup_test_context(Context* context,
skiatest::Reporter* reporter,
TestCtx* testCtx,
SkISize dimensions,
Volatile isVolatile,
bool invalidBackendTex) {
testCtx->fContext = context;
const Caps* caps = context->priv().caps();
testCtx->fRecorder = context->makeRecorder();
TextureInfo textureInfo = caps->getDefaultSampledTextureInfo(kRGBA_8888_SkColorType,
Mipmapped::kNo,
skgpu::Protected::kNo,
Renderable::kYes);
if (invalidBackendTex) {
// Having invalid backend textures will invalidate all the fulfill calls
REPORTER_ASSERT(reporter, !testCtx->fBackendTextures[0].isValid());
REPORTER_ASSERT(reporter, !testCtx->fBackendTextures[1].isValid());
} else {
testCtx->fBackendTextures[0] = testCtx->fRecorder->createBackendTexture(dimensions,
textureInfo);
REPORTER_ASSERT(reporter, testCtx->fBackendTextures[0].isValid());
if (isVolatile == Volatile::kYes) {
testCtx->fBackendTextures[1] = testCtx->fRecorder->createBackendTexture(dimensions,
textureInfo);
REPORTER_ASSERT(reporter, testCtx->fBackendTextures[1].isValid());
}
}
if (isVolatile == Volatile::kYes) {
testCtx->fPromiseChecker = PromiseTextureChecker(testCtx->fBackendTextures[0],
testCtx->fBackendTextures[1],
reporter);
} else {
testCtx->fPromiseChecker = PromiseTextureChecker(testCtx->fBackendTextures[0],
reporter);
}
SkImageInfo ii = SkImageInfo::Make(dimensions.fWidth,
dimensions.fHeight,
kRGBA_8888_SkColorType,
kPremul_SkAlphaType);
testCtx->fImg = SkImage::MakeGraphitePromiseTexture(testCtx->fRecorder.get(),
dimensions,
textureInfo,
ii.colorInfo(),
isVolatile,
PromiseTextureChecker::Fulfill,
PromiseTextureChecker::ImageRelease,
PromiseTextureChecker::TextureRelease,
&testCtx->fPromiseChecker);
testCtx->fSurface = SkSurface::MakeGraphite(testCtx->fRecorder.get(), ii);
}
} // anonymous namespace
DEF_GRAPHITE_TEST_FOR_RENDERING_CONTEXTS(NonVolatileGraphitePromiseImageTest,
reporter,
context) {
constexpr SkISize kDimensions { 16, 16 };
TestCtx testContext;
setup_test_context(context, reporter, &testContext,
kDimensions, Volatile::kNo, /* invalidBackendTex= */ false);
{
SkCanvas* canvas = testContext.fSurface->getCanvas();
canvas->drawImage(testContext.fImg, 0, 0);
check_unfulfilled(testContext.fPromiseChecker, reporter);
std::unique_ptr<Recording> recording = testContext.fRecorder->snap();
check_unfulfilled(testContext.fPromiseChecker, reporter); // NVPIs not fulfilled at snap
REPORTER_ASSERT(reporter, context->insertRecording({ recording.get() }));
check_fulfilled_ahead_by_one(reporter, testContext.fPromiseChecker,
/* expectedFulfillCnt= */ 1); // NVPIs fulfilled at insert
}
context->submit(SyncToCpu::kNo);
// testContext.fImg still has a ref so we should not have called TextureRelease.
check_fulfilled_ahead_by_one(reporter, testContext.fPromiseChecker,
/* expectedFulfillCnt= */ 1);
context->submit(SyncToCpu::kYes);
check_fulfilled_ahead_by_one(reporter, testContext.fPromiseChecker,
/* expectedFulfillCnt= */ 1);
// Test that more draws and insertions don't refulfill the NVPI
{
SkCanvas* canvas = testContext.fSurface->getCanvas();
canvas->drawImage(testContext.fImg, 0, 0);
canvas->drawImage(testContext.fImg, 0, 0);
std::unique_ptr<Recording> recording = testContext.fRecorder->snap();
check_fulfilled_ahead_by_one(reporter, testContext.fPromiseChecker,
/* expectedFulfillCnt= */ 1); // No new fulfill
REPORTER_ASSERT(reporter, context->insertRecording({ recording.get() }));
// testContext.fImg should still be fulfilled from the first time we inserted a Recording.
check_fulfilled_ahead_by_one(reporter, testContext.fPromiseChecker,
/* expectedFulfillCnt= */ 1);
}
context->submit(SyncToCpu::kYes);
check_fulfilled_ahead_by_one(reporter, testContext.fPromiseChecker,
/* expectedFulfillCnt= */ 1);
// Test that dropping the SkImage's ref doesn't change anything
{
SkCanvas* canvas = testContext.fSurface->getCanvas();
canvas->drawImage(testContext.fImg, 0, 0);
testContext.fImg.reset();
std::unique_ptr<Recording> recording = testContext.fRecorder->snap();
check_fulfilled_ahead_by_one(reporter, testContext.fPromiseChecker,
/* expectedFulfillCnt= */ 1);
REPORTER_ASSERT(reporter, context->insertRecording({ recording.get() }));
check_fulfilled_ahead_by_one(reporter, testContext.fPromiseChecker,
/* expectedFulfillCnt= */ 1);
}
// fImg's proxy is reffed by the recording so, despite fImg being reset earlier,
// the imageRelease callback doesn't occur until the recording is deleted.
testContext.fPromiseChecker.checkImageReleased(reporter, /* expectedReleaseCnt= */ 1);
// testContext.fImg no longer holds a ref but the last recording is still not submitted.
check_fulfilled_ahead_by_one(reporter, testContext.fPromiseChecker,
/* expectedFulfillCnt= */ 1);
context->submit(SyncToCpu::kYes);
// Now TextureRelease should definitely have been called.
check_all_done(reporter, testContext.fPromiseChecker, /* expectedFulfillCnt= */ 1);
}
DEF_GRAPHITE_TEST_FOR_RENDERING_CONTEXTS(NonVolatileGraphitePromiseImageFulfillFailureTest,
reporter,
context) {
constexpr SkISize kDimensions { 16, 16 };
TestCtx testContext;
setup_test_context(context, reporter, &testContext,
kDimensions, Volatile::kNo, /* invalidBackendTex= */ true);
// Draw the image a few different ways.
{
SkCanvas* canvas = testContext.fSurface->getCanvas();
canvas->drawImage(testContext.fImg, 0, 0);
check_unfulfilled(testContext.fPromiseChecker, reporter);
std::unique_ptr<Recording> recording = testContext.fRecorder->snap();
check_unfulfilled(testContext.fPromiseChecker, reporter);
REPORTER_ASSERT(reporter, !context->insertRecording({ recording.get() }));
check_fulfilled_ahead_by_one(reporter, testContext.fPromiseChecker,
/* expectedFulfillCnt= */ 1);
// Test that reinserting gives uninstantiated PromiseImages a second chance
REPORTER_ASSERT(reporter, !context->insertRecording({ recording.get() }));
check_fulfills_only(reporter, testContext.fPromiseChecker, /* expectedFulfillCnt= */ 2);
}
{
SkCanvas* canvas = testContext.fSurface->getCanvas();
SkPaint paint;
paint.setColorFilter(SkColorFilters::LinearToSRGBGamma());
canvas->drawImage(testContext.fImg, 0, 0, SkSamplingOptions(), &paint);
std::unique_ptr<Recording> recording = testContext.fRecorder->snap();
check_fulfills_only(reporter, testContext.fPromiseChecker, /* expectedFulfillCnt= */ 2);
REPORTER_ASSERT(reporter, !context->insertRecording({ recording.get() }));
check_fulfills_only(reporter, testContext.fPromiseChecker, /* expectedFulfillCnt= */ 3);
}
{
SkCanvas* canvas = testContext.fSurface->getCanvas();
sk_sp<SkShader> shader = testContext.fImg->makeShader(SkSamplingOptions());
REPORTER_ASSERT(reporter, shader);
SkPaint paint;
paint.setShader(std::move(shader));
canvas->drawRect(SkRect::MakeWH(1, 1), paint);
std::unique_ptr<Recording> recording = testContext.fRecorder->snap();
check_fulfills_only(reporter, testContext.fPromiseChecker, /* expectedFulfillCnt= */ 3);
REPORTER_ASSERT(reporter, !context->insertRecording({ recording.get() }));
check_fulfills_only(reporter, testContext.fPromiseChecker, /* expectedFulfillCnt= */ 4);
}
testContext.fSurface.reset();
testContext.fImg.reset();
// Despite fulfill failing 4x, the imageRelease callback still fires
testContext.fPromiseChecker.checkImageReleased(reporter, /* expectedReleaseCnt= */ 1);
context->submit(SyncToCpu::kYes);
// fulfill should've been called 4x while release should never have been called
check_fulfills_only(reporter, testContext.fPromiseChecker, /* expectedFulfillCnt= */ 4);
}
DEF_GRAPHITE_TEST_FOR_RENDERING_CONTEXTS(NonVolatileGraphitePromiseImageCreationFailureTest,
reporter,
context) {
// Note: these dimensions are invalid and will cause MakeGraphitePromiseTexture to fail
constexpr SkISize kDimensions { 0, 0 };
TestCtx testContext;
setup_test_context(context, reporter, &testContext,
kDimensions, Volatile::kNo, /* invalidBackendTex= */ true);
SkASSERT(!testContext.fImg);
// Despite MakeGraphitePromiseTexture failing, ImageRelease is called
REPORTER_ASSERT(reporter, testContext.fPromiseChecker.fFulfillCount == 0);
REPORTER_ASSERT(reporter, testContext.fPromiseChecker.fImageReleaseCount == 1);
REPORTER_ASSERT(reporter, testContext.fPromiseChecker.totalReleaseCount() == 0);
}
DEF_GRAPHITE_TEST_FOR_RENDERING_CONTEXTS(VolatileGraphitePromiseImageTest,
reporter,
context) {
constexpr SkISize kDimensions { 16, 16 };
TestCtx testContext;
setup_test_context(context, reporter, &testContext,
kDimensions, Volatile::kYes, /* invalidBackendTex= */ false);
{
SkCanvas* canvas = testContext.fSurface->getCanvas();
canvas->drawImage(testContext.fImg, 0, 0);
check_unfulfilled(testContext.fPromiseChecker, reporter);
std::unique_ptr<Recording> recording = testContext.fRecorder->snap();
// Nothing happens at snap time for VPIs
check_unfulfilled(testContext.fPromiseChecker, reporter);
REPORTER_ASSERT(reporter, context->insertRecording({ recording.get() }));
check_fulfilled_ahead_by_one(reporter, testContext.fPromiseChecker,
/* expectedFulfillCnt= */ 1); // VPIs fulfilled on insert
// Test that multiple insertions will clobber prior fulfills
REPORTER_ASSERT(reporter, context->insertRecording({ recording.get() }));
check_fulfilled_ahead_by_two(reporter, testContext.fPromiseChecker,
/* expectedFulfillCnt= */ 2);
}
context->submit(SyncToCpu::kYes);
check_all_done(reporter, testContext.fPromiseChecker, /* expectedFulfillCnt= */ 2);
REPORTER_ASSERT(reporter, testContext.fPromiseChecker.fTextureReleaseCounts[0] == 1);
REPORTER_ASSERT(reporter, testContext.fPromiseChecker.fTextureReleaseCounts[1] == 1);
{
SkCanvas* canvas = testContext.fSurface->getCanvas();
canvas->drawImage(testContext.fImg, 0, 0);
canvas->drawImage(testContext.fImg, 0, 0);
std::unique_ptr<Recording> recording = testContext.fRecorder->snap();
// Nothing happens at snap time for volatile images
check_all_done(reporter, testContext.fPromiseChecker, /* expectedFulfillCnt= */ 2);
REPORTER_ASSERT(reporter, context->insertRecording({ recording.get() }));
check_fulfilled_ahead_by_one(reporter, testContext.fPromiseChecker,
/* expectedFulfillCnt= */ 3);
REPORTER_ASSERT(reporter, context->insertRecording({ recording.get() }));
check_fulfilled_ahead_by_two(reporter, testContext.fPromiseChecker,
/* expectedFulfillCnt= */ 4);
}
context->submit(SyncToCpu::kYes);
check_all_done(reporter, testContext.fPromiseChecker, /* expectedFulfillCnt= */ 4);
REPORTER_ASSERT(reporter, testContext.fPromiseChecker.fTextureReleaseCounts[0] == 2);
REPORTER_ASSERT(reporter, testContext.fPromiseChecker.fTextureReleaseCounts[1] == 2);
{
SkCanvas* canvas = testContext.fSurface->getCanvas();
canvas->drawImage(testContext.fImg, 0, 0);
testContext.fImg.reset();
std::unique_ptr<Recording> recording = testContext.fRecorder->snap();
// Nothing happens at snap time for volatile images
check_all_done(reporter, testContext.fPromiseChecker, /* expectedFulfillCnt= */ 4);
REPORTER_ASSERT(reporter, context->insertRecording({ recording.get() }));
check_fulfilled_ahead_by_one(reporter, testContext.fPromiseChecker,
/* expectedFulfillCnt= */ 5);
REPORTER_ASSERT(reporter, context->insertRecording({ recording.get() }));
check_fulfilled_ahead_by_two(reporter, testContext.fPromiseChecker,
/* expectedFulfillCnt= */ 6);
}
// testContext.fImg no longer holds a ref but the last recordings are still not submitted.
check_fulfilled_ahead_by_two(reporter, testContext.fPromiseChecker,
/* expectedFulfillCnt= */ 6);
context->submit(SyncToCpu::kYes);
// Now all Releases should definitely have been called.
check_all_done(reporter, testContext.fPromiseChecker, /* expectedFulfillCnt= */ 6);
REPORTER_ASSERT(reporter, testContext.fPromiseChecker.fTextureReleaseCounts[0] == 3);
REPORTER_ASSERT(reporter, testContext.fPromiseChecker.fTextureReleaseCounts[1] == 3);
}
DEF_GRAPHITE_TEST_FOR_RENDERING_CONTEXTS(VolatileGraphitePromiseImageFulfillFailureTest,
reporter,
context) {
constexpr SkISize kDimensions { 16, 16 };
TestCtx testContext;
setup_test_context(context, reporter, &testContext,
kDimensions, Volatile::kYes, /* invalidBackendTex= */ true);
// Draw the image a few different ways.
{
SkCanvas* canvas = testContext.fSurface->getCanvas();
canvas->drawImage(testContext.fImg, 0, 0);
check_unfulfilled(testContext.fPromiseChecker, reporter);
std::unique_ptr<Recording> recording = testContext.fRecorder->snap();
check_unfulfilled(testContext.fPromiseChecker, reporter);
REPORTER_ASSERT(reporter, !context->insertRecording({ recording.get() }));
check_fulfills_only(reporter, testContext.fPromiseChecker, /* expectedFulfillCnt= */ 1);
REPORTER_ASSERT(reporter, !context->insertRecording({ recording.get() }));
check_fulfills_only(reporter, testContext.fPromiseChecker, /* expectedFulfillCnt= */ 2);
}
{
SkCanvas* canvas = testContext.fSurface->getCanvas();
SkPaint paint;
paint.setColorFilter(SkColorFilters::LinearToSRGBGamma());
canvas->drawImage(testContext.fImg, 0, 0, SkSamplingOptions(), &paint);
std::unique_ptr<Recording> recording = testContext.fRecorder->snap();
check_fulfills_only(reporter, testContext.fPromiseChecker, /* expectedFulfillCnt= */ 2);
REPORTER_ASSERT(reporter, !context->insertRecording({ recording.get() }));
check_fulfills_only(reporter, testContext.fPromiseChecker, /* expectedFulfillCnt= */ 3);
REPORTER_ASSERT(reporter, !context->insertRecording({ recording.get() }));
check_fulfills_only(reporter, testContext.fPromiseChecker, /* expectedFulfillCnt= */ 4);
}
{
SkCanvas* canvas = testContext.fSurface->getCanvas();
sk_sp<SkShader> shader = testContext.fImg->makeShader(SkSamplingOptions());
REPORTER_ASSERT(reporter, shader);
SkPaint paint;
paint.setShader(std::move(shader));
canvas->drawRect(SkRect::MakeWH(1, 1), paint);
std::unique_ptr<Recording> recording = testContext.fRecorder->snap();
check_fulfills_only(reporter, testContext.fPromiseChecker, /* expectedFulfillCnt= */ 4);
REPORTER_ASSERT(reporter, !context->insertRecording({ recording.get() }));
check_fulfills_only(reporter, testContext.fPromiseChecker, /* expectedFulfillCnt= */ 5);
REPORTER_ASSERT(reporter, !context->insertRecording({ recording.get() }));
check_fulfills_only(reporter, testContext.fPromiseChecker, /* expectedFulfillCnt= */ 6);
}
testContext.fSurface.reset();
testContext.fImg.reset();
context->submit(SyncToCpu::kYes);
check_fulfills_only(reporter, testContext.fPromiseChecker, /* expectedFulfillCnt= */ 6);
}
// Test out dropping the Recorder prior to inserting the Recording
DEF_GRAPHITE_TEST_FOR_RENDERING_CONTEXTS(GraphitePromiseImageRecorderLoss,
reporter,
context) {
constexpr SkISize kDimensions{ 16, 16 };
for (Volatile isVolatile : { Volatile::kNo, Volatile::kYes }) {
TestCtx testContext;
setup_test_context(context, reporter, &testContext,
kDimensions, isVolatile, /* invalidBackendTex= */ false);
SkCanvas* canvas = testContext.fSurface->getCanvas();
canvas->drawImage(testContext.fImg, 0, 0);
check_unfulfilled(testContext.fPromiseChecker, reporter);
std::unique_ptr<Recording> recording = testContext.fRecorder->snap();
check_unfulfilled(testContext.fPromiseChecker, reporter);
testContext.fRecorder.reset(); // Recorder drop
REPORTER_ASSERT(reporter, context->insertRecording({ recording.get() }));
check_fulfills_only(reporter, testContext.fPromiseChecker, /* expectedFulfillCnt= */ 1);
context->submit(SyncToCpu::kYes);
testContext.fSurface.reset();
testContext.fImg.reset();
recording.reset();
check_all_done(reporter, testContext.fPromiseChecker, /* expectedFulfillCnt= */ 1);
}
}
// Test out PromiseImages appearing in multiple Recordings. In particular, test that
// previous instantiations don't impact the Recording's collection of PromiseImages.
DEF_GRAPHITE_TEST_FOR_RENDERING_CONTEXTS(GraphitePromiseImageMultipleImgUses,
reporter,
context) {
constexpr SkISize kDimensions{ 16, 16 };
static constexpr int kNumRecordings = 3;
for (Volatile isVolatile : { Volatile::kNo, Volatile::kYes }) {
int expectedVolatile = (isVolatile == Volatile::kYes) ? 1 : 0;
int expectedNonVolatile = 1 - expectedVolatile;
TestCtx testContext;
setup_test_context(context, reporter, &testContext,
kDimensions, isVolatile, /* invalidBackendTex= */ false);
std::unique_ptr<Recording> recordings[kNumRecordings];
SkCanvas* canvas = testContext.fSurface->getCanvas();
for (int i = 0; i < kNumRecordings; ++i) {
canvas->drawImage(testContext.fImg, 0, 0);
recordings[i] = testContext.fRecorder->snap();
if (isVolatile == Volatile::kYes) {
check_fulfills_only(reporter, testContext.fPromiseChecker,
/* expectedFulfillCnt= */ i);
} else {
check_fulfills_only(reporter, testContext.fPromiseChecker,
/* expectedFulfillCnt= */ i > 0 ? 1 : 0);
}
REPORTER_ASSERT(reporter,
recordings[i]->priv().numVolatilePromiseImages() == expectedVolatile);
REPORTER_ASSERT(reporter,
recordings[i]->priv().numNonVolatilePromiseImages() ==
expectedNonVolatile);
REPORTER_ASSERT(reporter, context->insertRecording({ recordings[i].get() }));
if (isVolatile == Volatile::kYes) {
check_fulfills_only(reporter, testContext.fPromiseChecker,
/* expectedFulfillCnt= */ i+1);
} else {
check_fulfills_only(reporter, testContext.fPromiseChecker,
/* expectedFulfillCnt= */ 1);
}
// Non-volatiles are cleared out after a successful insertion
REPORTER_ASSERT(reporter, recordings[i]->priv().numNonVolatilePromiseImages() == 0);
}
context->submit(SyncToCpu::kYes);
testContext.fSurface.reset();
testContext.fImg.reset();
for (int i = 0; i < kNumRecordings; ++i) {
recordings[i].reset();
}
if (isVolatile == Volatile::kYes) {
check_all_done(reporter, testContext.fPromiseChecker,
/* expectedFulfillCnt= */ kNumRecordings);
} else {
check_all_done(reporter, testContext.fPromiseChecker, /* expectedFulfillCnt= */ 1);
}
}
}