| /* |
| * 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/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/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); |
| } |
| } |
| } |