blob: fa0ce7b2f659d45674244f990d749832ed35e533 [file] [log] [blame]
/*
* Copyright 2023 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/SkCanvas.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/Image.h"
#include "include/gpu/graphite/Recording.h"
#include "include/gpu/graphite/Surface.h"
#include "include/gpu/graphite/YUVABackendTextures.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 PromiseImageChecker {
PromiseImageChecker() = default;
void checkImageReleased(skiatest::Reporter* reporter, int expectedReleaseCnt) {
REPORTER_ASSERT(reporter, expectedReleaseCnt == fImageReleaseCount);
}
int fImageReleaseCount = 0;
static void ImageRelease(void* self) {
auto checker = reinterpret_cast<PromiseImageChecker*>(self);
checker->fImageReleaseCount++;
}
};
struct PromiseTextureChecker {
PromiseTextureChecker() = default;
explicit PromiseTextureChecker(const BackendTexture& backendTex) {
fBackendTextures[0] = backendTex;
}
explicit PromiseTextureChecker(const BackendTexture& backendTex0,
const BackendTexture& backendTex1)
: fHasTwoBackendTextures(true) {
fBackendTextures[0] = backendTex0;
fBackendTextures[1] = backendTex1;
}
int totalReleaseCount() const { return fTextureReleaseCounts[0] + fTextureReleaseCounts[1]; }
bool fHasTwoBackendTextures = false;
BackendTexture fBackendTextures[2];
int fFulfillCount = 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 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 PromiseImageChecker& promiseImageChecker,
const PromiseTextureChecker* promiseTextureCheckers,
int expectedFulfillCnt,
ReleaseBalanceExpectation releaseBalanceExpectation) {
// If one fulfill fails the following ones won't be called, so we check the max value
int maxFulfillCnt = 0;
for (int i = 0; i < 4; ++i) {
maxFulfillCnt = std::max(promiseTextureCheckers[i].fFulfillCount, maxFulfillCnt);
if (!expectedFulfillCnt) {
// Release should only ever be called after Fulfill.
REPORTER_ASSERT(reporter, promiseImageChecker.fImageReleaseCount == 0);
REPORTER_ASSERT(reporter, promiseTextureCheckers[i].totalReleaseCount() == 0);
return;
}
if (promiseTextureCheckers[i].fFulfillCount) {
int releaseDiff = promiseTextureCheckers[i].fFulfillCount -
promiseTextureCheckers[i].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, promiseTextureCheckers[i].totalReleaseCount() == 0);
break;
}
}
}
SkASSERT(maxFulfillCnt == expectedFulfillCnt);
REPORTER_ASSERT(reporter, maxFulfillCnt == expectedFulfillCnt);
}
void check_unfulfilled(const PromiseImageChecker& promiseImageChecker,
const PromiseTextureChecker* promiseTextureCheckers,
skiatest::Reporter* reporter) {
check_fulfill_and_release_cnts(reporter, promiseImageChecker, promiseTextureCheckers,
/* expectedFulfillCnt= */ 0,
ReleaseBalanceExpectation::kBalanced);
}
void check_fulfilled_ahead_by_one(skiatest::Reporter* reporter,
const PromiseImageChecker& promiseImageChecker,
const PromiseTextureChecker* promiseTextureCheckers,
int expectedFulfillCnt) {
check_fulfill_and_release_cnts(reporter, promiseImageChecker, promiseTextureCheckers,
expectedFulfillCnt, ReleaseBalanceExpectation::kOffByOne);
}
void check_fulfilled_ahead_by_two(skiatest::Reporter* reporter,
const PromiseImageChecker& promiseImageChecker,
const PromiseTextureChecker* promiseTextureCheckers,
int expectedFulfillCnt) {
check_fulfill_and_release_cnts(reporter, promiseImageChecker, promiseTextureCheckers,
expectedFulfillCnt, ReleaseBalanceExpectation::kOffByTwo);
}
void check_all_done(skiatest::Reporter* reporter,
const PromiseImageChecker& promiseImageChecker,
const PromiseTextureChecker* promiseTextureCheckers,
int expectedFulfillCnt) {
check_fulfill_and_release_cnts(reporter, promiseImageChecker, promiseTextureCheckers,
expectedFulfillCnt, ReleaseBalanceExpectation::kBalanced);
}
void check_fulfills_only(skiatest::Reporter* reporter,
const PromiseImageChecker& promiseImageChecker,
const PromiseTextureChecker* promiseTextureCheckers,
int expectedFulfillCnt) {
check_fulfill_and_release_cnts(reporter, promiseImageChecker, promiseTextureCheckers,
expectedFulfillCnt, ReleaseBalanceExpectation::kFulfillsOnly);
}
struct TestCtx {
TestCtx() {}
~TestCtx() {
for (int i = 0; i < 8; ++i) {
if (fBackendTextures[i].isValid()) {
fContext->deleteBackendTexture(fBackendTextures[i]);
}
}
}
Context* fContext;
std::unique_ptr<Recorder> fRecorder;
BackendTexture fBackendTextures[8];
PromiseImageChecker fPromiseImageChecker;
SkImages::GraphitePromiseImageContext fImageContext = &fPromiseImageChecker;
PromiseTextureChecker fPromiseTextureCheckers[4];
SkImages::GraphitePromiseTextureContext fTextureContexts[4] = {
&fPromiseTextureCheckers[0],
&fPromiseTextureCheckers[1],
&fPromiseTextureCheckers[2],
&fPromiseTextureCheckers[3],
};
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();
testCtx->fPromiseImageChecker = PromiseImageChecker();
TextureInfo textureInfo[4];
for (int i = 0; i < 4; ++i) {
textureInfo[i] = caps->getDefaultSampledTextureInfo(kAlpha_8_SkColorType,
skgpu::Mipmapped::kNo,
skgpu::Protected::kNo,
skgpu::Renderable::kYes);
if (invalidBackendTex) {
// Having invalid backend textures will invalidate all the fulfill calls
REPORTER_ASSERT(reporter, !testCtx->fBackendTextures[i].isValid());
REPORTER_ASSERT(reporter, !testCtx->fBackendTextures[i+4].isValid());
} else {
testCtx->fBackendTextures[i] =
testCtx->fRecorder->createBackendTexture(dimensions, textureInfo[i]);
REPORTER_ASSERT(reporter, testCtx->fBackendTextures[i].isValid());
if (isVolatile == Volatile::kYes) {
testCtx->fBackendTextures[i+4] =
testCtx->fRecorder->createBackendTexture(dimensions, textureInfo[i]);
REPORTER_ASSERT(reporter, testCtx->fBackendTextures[i+4].isValid());
}
}
if (isVolatile == Volatile::kYes) {
testCtx->fPromiseTextureCheckers[i] =
PromiseTextureChecker(testCtx->fBackendTextures[i],
testCtx->fBackendTextures[i+4]);
} else {
testCtx->fPromiseTextureCheckers[i] =
PromiseTextureChecker(testCtx->fBackendTextures[i]);
}
}
SkYUVAInfo yuvaInfo(dimensions,
SkYUVAInfo::PlaneConfig::kY_U_V_A,
SkYUVAInfo::Subsampling::k444,
kJPEG_Full_SkYUVColorSpace);
YUVABackendTextureInfo yuvaBackendInfo(testCtx->fRecorder.get(),
yuvaInfo,
textureInfo,
skgpu::Mipmapped::kNo);
testCtx->fImg = SkImages::PromiseTextureFromYUVA(testCtx->fRecorder.get(),
yuvaBackendInfo,
SkColorSpace::MakeSRGBLinear(),
isVolatile,
PromiseTextureChecker::Fulfill,
PromiseImageChecker::ImageRelease,
PromiseTextureChecker::TextureRelease,
testCtx->fImageContext,
testCtx->fTextureContexts);
SkImageInfo ii = SkImageInfo::Make(dimensions.fWidth,
dimensions.fHeight,
kRGBA_8888_SkColorType,
kPremul_SkAlphaType);
testCtx->fSurface = SkSurfaces::RenderTarget(testCtx->fRecorder.get(), ii);
}
} // anonymous namespace
DEF_GRAPHITE_TEST_FOR_RENDERING_CONTEXTS(NonVolatileGraphiteYUVAPromiseImageTest,
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.fPromiseImageChecker, testContext.fPromiseTextureCheckers,
reporter);
std::unique_ptr<Recording> recording = testContext.fRecorder->snap();
check_unfulfilled(testContext.fPromiseImageChecker, testContext.fPromiseTextureCheckers,
reporter); // NVPIs not fulfilled at snap
REPORTER_ASSERT(reporter, context->insertRecording({ recording.get() }));
check_fulfilled_ahead_by_one(reporter, testContext.fPromiseImageChecker,
testContext.fPromiseTextureCheckers,
/* 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.fPromiseImageChecker,
testContext.fPromiseTextureCheckers,
/* expectedFulfillCnt= */ 1);
context->submit(SyncToCpu::kYes);
check_fulfilled_ahead_by_one(reporter, testContext.fPromiseImageChecker,
testContext.fPromiseTextureCheckers,
/* 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.fPromiseImageChecker,
testContext.fPromiseTextureCheckers,
/* 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.fPromiseImageChecker,
testContext.fPromiseTextureCheckers,
/* expectedFulfillCnt= */ 1);
}
context->submit(SyncToCpu::kYes);
check_fulfilled_ahead_by_one(reporter, testContext.fPromiseImageChecker,
testContext.fPromiseTextureCheckers, /* 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.fPromiseImageChecker,
testContext.fPromiseTextureCheckers,
/* expectedFulfillCnt= */ 1);
REPORTER_ASSERT(reporter, context->insertRecording({ recording.get() }));
check_fulfilled_ahead_by_one(reporter, testContext.fPromiseImageChecker,
testContext.fPromiseTextureCheckers,
/* 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.fPromiseImageChecker.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.fPromiseImageChecker,
testContext.fPromiseTextureCheckers, /* expectedFulfillCnt= */ 1);
context->submit(SyncToCpu::kYes);
// Now TextureRelease should definitely have been called.
check_all_done(reporter, testContext.fPromiseImageChecker, testContext.fPromiseTextureCheckers,
/* expectedFulfillCnt= */ 1);
}
DEF_GRAPHITE_TEST_FOR_RENDERING_CONTEXTS(NonVolatileGraphiteYUVAPromiseImageFulfillFailureTest,
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.fPromiseImageChecker, testContext.fPromiseTextureCheckers,
reporter);
std::unique_ptr<Recording> recording = testContext.fRecorder->snap();
check_unfulfilled(testContext.fPromiseImageChecker, testContext.fPromiseTextureCheckers,
reporter);
REPORTER_ASSERT(reporter, !context->insertRecording({ recording.get() }));
check_fulfilled_ahead_by_one(reporter, testContext.fPromiseImageChecker,
testContext.fPromiseTextureCheckers,
/* expectedFulfillCnt= */ 1);
// Test that reinserting gives uninstantiated PromiseImages a second chance
REPORTER_ASSERT(reporter, !context->insertRecording({ recording.get() }));
check_fulfills_only(reporter, testContext.fPromiseImageChecker,
testContext.fPromiseTextureCheckers, /* 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.fPromiseImageChecker,
testContext.fPromiseTextureCheckers, /* expectedFulfillCnt= */ 2);
REPORTER_ASSERT(reporter, !context->insertRecording({ recording.get() }));
check_fulfills_only(reporter, testContext.fPromiseImageChecker,
testContext.fPromiseTextureCheckers, /* 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.fPromiseImageChecker,
testContext.fPromiseTextureCheckers, /* expectedFulfillCnt= */ 3);
REPORTER_ASSERT(reporter, !context->insertRecording({ recording.get() }));
check_fulfills_only(reporter, testContext.fPromiseImageChecker,
testContext.fPromiseTextureCheckers, /* expectedFulfillCnt= */ 4);
}
testContext.fSurface.reset();
testContext.fImg.reset();
// Despite fulfill failing 4x, the imageRelease callback still fires
testContext.fPromiseImageChecker.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.fPromiseImageChecker,
testContext.fPromiseTextureCheckers, /* expectedFulfillCnt= */ 4);
}
DEF_GRAPHITE_TEST_FOR_RENDERING_CONTEXTS(NonVolatileGraphiteYUVAPromiseImageCreationFailureTest,
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.fPromiseImageChecker.fImageReleaseCount == 1);
for (int i = 0; i < 4; ++i) {
REPORTER_ASSERT(reporter, testContext.fPromiseTextureCheckers[i].fFulfillCount == 0);
REPORTER_ASSERT(reporter, testContext.fPromiseTextureCheckers[i].totalReleaseCount() == 0);
}
}
DEF_GRAPHITE_TEST_FOR_RENDERING_CONTEXTS(VolatileGraphiteYUVAPromiseImageTest,
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.fPromiseImageChecker, testContext.fPromiseTextureCheckers,
reporter);
std::unique_ptr<Recording> recording = testContext.fRecorder->snap();
// Nothing happens at snap time for VPIs
check_unfulfilled(testContext.fPromiseImageChecker, testContext.fPromiseTextureCheckers,
reporter);
REPORTER_ASSERT(reporter, context->insertRecording({ recording.get() }));
check_fulfilled_ahead_by_one(reporter, testContext.fPromiseImageChecker,
testContext.fPromiseTextureCheckers,
/* 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.fPromiseImageChecker,
testContext.fPromiseTextureCheckers,
/* expectedFulfillCnt= */ 2);
}
context->submit(SyncToCpu::kYes);
check_all_done(reporter, testContext.fPromiseImageChecker, testContext.fPromiseTextureCheckers,
/* expectedFulfillCnt= */ 2);
for (int i = 0; i < 4; ++i) {
REPORTER_ASSERT(reporter,
testContext.fPromiseTextureCheckers[i].fTextureReleaseCounts[0] == 1);
REPORTER_ASSERT(reporter,
testContext.fPromiseTextureCheckers[i].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.fPromiseImageChecker,
testContext.fPromiseTextureCheckers, /* expectedFulfillCnt= */ 2);
REPORTER_ASSERT(reporter, context->insertRecording({ recording.get() }));
check_fulfilled_ahead_by_one(reporter, testContext.fPromiseImageChecker,
testContext.fPromiseTextureCheckers,
/* expectedFulfillCnt= */ 3);
REPORTER_ASSERT(reporter, context->insertRecording({ recording.get() }));
check_fulfilled_ahead_by_two(reporter, testContext.fPromiseImageChecker,
testContext.fPromiseTextureCheckers,
/* expectedFulfillCnt= */ 4);
}
context->submit(SyncToCpu::kYes);
check_all_done(reporter, testContext.fPromiseImageChecker, testContext.fPromiseTextureCheckers,
/* expectedFulfillCnt= */ 4);
for (int i = 0; i < 4; ++i) {
REPORTER_ASSERT(reporter,
testContext.fPromiseTextureCheckers[i].fTextureReleaseCounts[0] == 2);
REPORTER_ASSERT(reporter,
testContext.fPromiseTextureCheckers[i].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.fPromiseImageChecker,
testContext.fPromiseTextureCheckers, /* expectedFulfillCnt= */ 4);
REPORTER_ASSERT(reporter, context->insertRecording({ recording.get() }));
check_fulfilled_ahead_by_one(reporter, testContext.fPromiseImageChecker,
testContext.fPromiseTextureCheckers,
/* expectedFulfillCnt= */ 5);
REPORTER_ASSERT(reporter, context->insertRecording({ recording.get() }));
check_fulfilled_ahead_by_two(reporter, testContext.fPromiseImageChecker,
testContext.fPromiseTextureCheckers,
/* expectedFulfillCnt= */ 6);
}
// testContext.fImg no longer holds a ref but the last recordings are still not submitted.
check_fulfilled_ahead_by_two(reporter, testContext.fPromiseImageChecker,
testContext.fPromiseTextureCheckers,
/* expectedFulfillCnt= */ 6);
context->submit(SyncToCpu::kYes);
// Now all Releases should definitely have been called.
check_all_done(reporter, testContext.fPromiseImageChecker, testContext.fPromiseTextureCheckers,
/* expectedFulfillCnt= */ 6);
for (int i = 0; i < 4; ++i) {
REPORTER_ASSERT(reporter,
testContext.fPromiseTextureCheckers[i].fTextureReleaseCounts[0] == 3);
REPORTER_ASSERT(reporter,
testContext.fPromiseTextureCheckers[i].fTextureReleaseCounts[1] == 3);
}
}
DEF_GRAPHITE_TEST_FOR_RENDERING_CONTEXTS(VolatileGraphiteYUVAPromiseImageFulfillFailureTest,
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.fPromiseImageChecker, testContext.fPromiseTextureCheckers,
reporter);
std::unique_ptr<Recording> recording = testContext.fRecorder->snap();
check_unfulfilled(testContext.fPromiseImageChecker, testContext.fPromiseTextureCheckers,
reporter);
REPORTER_ASSERT(reporter, !context->insertRecording({ recording.get() }));
check_fulfills_only(reporter, testContext.fPromiseImageChecker,
testContext.fPromiseTextureCheckers, /* expectedFulfillCnt= */ 1);
REPORTER_ASSERT(reporter, !context->insertRecording({ recording.get() }));
check_fulfills_only(reporter, testContext.fPromiseImageChecker,
testContext.fPromiseTextureCheckers, /* 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.fPromiseImageChecker,
testContext.fPromiseTextureCheckers, /* expectedFulfillCnt= */ 2);
REPORTER_ASSERT(reporter, !context->insertRecording({ recording.get() }));
check_fulfills_only(reporter, testContext.fPromiseImageChecker,
testContext.fPromiseTextureCheckers, /* expectedFulfillCnt= */ 3);
REPORTER_ASSERT(reporter, !context->insertRecording({ recording.get() }));
check_fulfills_only(reporter, testContext.fPromiseImageChecker,
testContext.fPromiseTextureCheckers, /* 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.fPromiseImageChecker,
testContext.fPromiseTextureCheckers, /* expectedFulfillCnt= */ 4);
REPORTER_ASSERT(reporter, !context->insertRecording({ recording.get() }));
check_fulfills_only(reporter, testContext.fPromiseImageChecker,
testContext.fPromiseTextureCheckers, /* expectedFulfillCnt= */ 5);
REPORTER_ASSERT(reporter, !context->insertRecording({ recording.get() }));
check_fulfills_only(reporter, testContext.fPromiseImageChecker,
testContext.fPromiseTextureCheckers, /* expectedFulfillCnt= */ 6);
}
testContext.fSurface.reset();
testContext.fImg.reset();
context->submit(SyncToCpu::kYes);
check_fulfills_only(reporter, testContext.fPromiseImageChecker,
testContext.fPromiseTextureCheckers, /* expectedFulfillCnt= */ 6);
}
// Test out dropping the Recorder prior to inserting the Recording
DEF_GRAPHITE_TEST_FOR_RENDERING_CONTEXTS(GraphiteYUVAPromiseImageRecorderLoss,
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.fPromiseImageChecker, testContext.fPromiseTextureCheckers,
reporter);
std::unique_ptr<Recording> recording = testContext.fRecorder->snap();
check_unfulfilled(testContext.fPromiseImageChecker, testContext.fPromiseTextureCheckers,
reporter);
testContext.fRecorder.reset(); // Recorder drop
REPORTER_ASSERT(reporter, context->insertRecording({ recording.get() }));
check_fulfills_only(reporter, testContext.fPromiseImageChecker,
testContext.fPromiseTextureCheckers, /* expectedFulfillCnt= */ 1);
context->submit(SyncToCpu::kYes);
testContext.fSurface.reset();
testContext.fImg.reset();
recording.reset();
check_all_done(reporter, testContext.fPromiseImageChecker,
testContext.fPromiseTextureCheckers, /* 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(GraphiteYUVAPromiseImageMultipleImgUses,
reporter,
context) {
constexpr SkISize kDimensions{ 16, 16 };
static constexpr int kNumRecordings = 3;
for (Volatile isVolatile : { Volatile::kNo, Volatile::kYes }) {
int expectedVolatile = (isVolatile == Volatile::kYes) ? 4 : 0;
int expectedNonVolatile = 4 - 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.fPromiseImageChecker,
testContext.fPromiseTextureCheckers,
/* expectedFulfillCnt= */ i);
} else {
check_fulfills_only(reporter, testContext.fPromiseImageChecker,
testContext.fPromiseTextureCheckers,
/* 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.fPromiseImageChecker,
testContext.fPromiseTextureCheckers,
/* expectedFulfillCnt= */ i+1);
} else {
check_fulfills_only(reporter, testContext.fPromiseImageChecker,
testContext.fPromiseTextureCheckers,
/* 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.fPromiseImageChecker,
testContext.fPromiseTextureCheckers,
/* expectedFulfillCnt= */ kNumRecordings);
} else {
check_all_done(reporter, testContext.fPromiseImageChecker,
testContext.fPromiseTextureCheckers, /* expectedFulfillCnt= */ 1);
}
}
}