| /* | |
| * Copyright 2021 Google LLC | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "include/core/SkDeferredDisplayListRecorder.h" | |
| #include "include/core/SkImageInfo.h" | |
| #include "include/gpu/GrDirectContext.h" | |
| #include "include/gpu/GrTypes.h" | |
| #include "src/core/SkAutoPixmapStorage.h" | |
| #include "src/core/SkCanvasPriv.h" | |
| #include "src/gpu/GrDirectContextPriv.h" | |
| #include "src/gpu/GrProxyProvider.h" | |
| #include "src/gpu/GrSurfaceProxy.h" | |
| #include "src/gpu/SurfaceFillContext.h" | |
| #include "tests/Test.h" | |
| #include "tests/TestUtils.h" | |
| #include "tools/gpu/BackendSurfaceFactory.h" | |
| #include "tools/gpu/ProxyUtils.h" | |
| DEF_GPUTEST_FOR_ALL_CONTEXTS(WrappedSurfaceCopyOnWrite, | |
| reporter, | |
| ctxInfo, | |
| CtsEnforcement::kApiLevel_T) { | |
| GrDirectContext* dContext = ctxInfo.directContext(); | |
| auto makeDirectBackendSurface = [&]() { | |
| auto info = SkImageInfo::Make({10, 10}, kRGBA_8888_SkColorType, kPremul_SkAlphaType); | |
| return sk_gpu_test::MakeBackendTextureSurface(dContext, | |
| info, | |
| kTopLeft_GrSurfaceOrigin, | |
| /*sample count*/ 1); | |
| }; | |
| auto imageProxyID = [&](const sk_sp<SkImage>& img) { | |
| return sk_gpu_test::GetTextureImageProxy(img.get(), dContext)->uniqueID(); | |
| }; | |
| auto surfaceProxyID = [&](const sk_sp<SkSurface>& surf) { | |
| GrRenderTargetProxy* rtp = SkCanvasPriv::TopDeviceTargetProxy(surf->getCanvas()); | |
| return rtp->uniqueID(); | |
| }; | |
| sk_sp<SkSurface> surf = makeDirectBackendSurface(); | |
| surf->getCanvas()->clear(SkColor4f{1, 0, 0, 1}); | |
| sk_sp<SkImage> img = surf->makeImageSnapshot(); | |
| // Initially they share | |
| REPORTER_ASSERT(reporter, surfaceProxyID(surf) == imageProxyID(img)); | |
| // Using the image on the direct context shouldn't affect sharing. | |
| sk_sp<SkSurface> surf2 = makeDirectBackendSurface(); | |
| surf2->getCanvas()->drawImage(img, 0, 0); | |
| REPORTER_ASSERT(reporter, surfaceProxyID(surf) == imageProxyID(img)); | |
| // Modifying the original surface should trigger using the copy proxy. | |
| surf->getCanvas()->clear({0, 0, 1, 1}); | |
| REPORTER_ASSERT(reporter, surfaceProxyID(surf) != imageProxyID(img)); | |
| // Image caching on surface should mean another snapshot gives us the same image. | |
| GrSurfaceProxy::UniqueID imageID = imageProxyID(img); | |
| img = surf->makeImageSnapshot(); | |
| REPORTER_ASSERT(reporter, imageProxyID(img) != imageID); | |
| SkSurfaceCharacterization characterization; | |
| REPORTER_ASSERT(reporter, surf->characterize(&characterization)); | |
| SkDeferredDisplayListRecorder recorder(characterization); | |
| // Using an image from a direct context on a recording context should trigger using the copy. | |
| surf = makeDirectBackendSurface(); | |
| img = surf->makeImageSnapshot(); | |
| REPORTER_ASSERT(reporter, surfaceProxyID(surf) == imageProxyID(img)); | |
| recorder.getCanvas()->drawImage(img, 0, 0); | |
| REPORTER_ASSERT(reporter, surfaceProxyID(surf) != imageProxyID(img)); | |
| // Same as above but if the surface goes out of scope first we keep using the original | |
| surf = makeDirectBackendSurface(); | |
| img = surf->makeImageSnapshot(); | |
| GrSurfaceProxy::UniqueID surfID = surfaceProxyID(surf); | |
| REPORTER_ASSERT(reporter, surfaceProxyID(surf) == imageProxyID(img)); | |
| surf.reset(); | |
| recorder.getCanvas()->drawImage(img, 0, 0); | |
| REPORTER_ASSERT(reporter, surfID == imageProxyID(img)); | |
| } | |
| // Make sure GrCopyRenderTasks's skip actually skips the copy. | |
| DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkipCopyTaskTest, reporter, ctxInfo, CtsEnforcement::kNever) { | |
| GrDirectContext* dContext = ctxInfo.directContext(); | |
| GrImageInfo info(GrColorType::kRGBA_8888, | |
| kPremul_SkAlphaType, | |
| /*color space*/ nullptr, | |
| 10, 10); | |
| auto dstSC = CreateSurfaceContext(dContext, | |
| info, | |
| SkBackingFit::kExact, | |
| kBottomLeft_GrSurfaceOrigin, | |
| GrRenderable::kYes); | |
| dstSC->asFillContext()->clear(SkPMColor4f{1, 0, 0, 1}); | |
| auto srcSC = CreateSurfaceContext(dContext, | |
| info, | |
| SkBackingFit::kExact, | |
| kBottomLeft_GrSurfaceOrigin, | |
| GrRenderable::kYes); | |
| srcSC->asFillContext()->clear(SkPMColor4f{0, 0, 1, 1}); | |
| sk_sp<GrRenderTask> task = | |
| dContext->priv().drawingManager()->newCopyRenderTask(srcSC->asSurfaceProxyRef(), | |
| SkIRect::MakeWH(10, 10), | |
| dstSC->asSurfaceProxyRef(), | |
| {0, 0}, | |
| kTopLeft_GrSurfaceOrigin); | |
| if (!task) { | |
| ERRORF(reporter, "Couldn't make a copy task."); | |
| return; | |
| } | |
| task->makeSkippable(); | |
| SkAutoPixmapStorage pixels; | |
| pixels.alloc(SkImageInfo::Make({10, 10}, kRGBA_8888_SkColorType, kPremul_SkAlphaType)); | |
| dstSC->readPixels(dContext, pixels, {0, 0}); | |
| float kTol[4] = {}; | |
| std::function<ComparePixmapsErrorReporter> errorReporter( | |
| [&](int x, int y, const float diffs[4]) { | |
| ERRORF(reporter, "Expected {1, 0, 0, 1}. diff {%f, %f, %f, %f}", | |
| diffs[0], diffs[1], diffs[2], diffs[3]); | |
| }); | |
| CheckSolidPixels(SkColor4f{1, 0, 0, 1}, pixels, kTol, errorReporter); | |
| } | |
| #if SK_GPU_V1 | |
| // Make sure OpsTask are skippable | |
| DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkipOpsTaskTest, reporter, ctxInfo, CtsEnforcement::kNever) { | |
| GrDirectContext* dContext = ctxInfo.directContext(); | |
| GrImageInfo ii(GrColorType::kRGBA_8888, kPremul_SkAlphaType, /*color space*/ nullptr, 10, 10); | |
| auto dst = dContext->priv().makeSFC(ii, SkBackingFit::kExact); | |
| dst->clear(SkPMColor4f{1, 0, 0, 1}); | |
| dContext->flush(); | |
| dst->clear(SkPMColor4f{0, 0, 1, 1}); | |
| sk_sp<GrRenderTask> task = dst->refRenderTask(); | |
| // GrDrawingManager maintains an "active ops task" and doesn't like having it closed behind | |
| // its back. temp exists just to replace dst's ops task as the active one. | |
| auto temp = dContext->priv().makeSFC(ii, SkBackingFit::kExact); | |
| temp->clear(SkPMColor4f{0, 0, 0, 0}); | |
| GrSurfaceProxyView readView = dst->readSurfaceView(); | |
| task->makeSkippable(); | |
| SkAutoPixmapStorage pixels; | |
| pixels.alloc(SkImageInfo::Make({10, 10}, kRGBA_8888_SkColorType, kPremul_SkAlphaType)); | |
| dst->readPixels(dContext, pixels, {0, 0}); | |
| float kTol[4] = {}; | |
| std::function<ComparePixmapsErrorReporter> errorReporter( | |
| [&](int x, int y, const float diffs[4]) { | |
| ERRORF(reporter, "Expected {1, 0, 0, 1}. diff {%f, %f, %f, %f}", | |
| diffs[0], diffs[1], diffs[2], diffs[3]); | |
| }); | |
| CheckSolidPixels(SkColor4f{1, 0, 0, 1}, pixels, kTol, errorReporter); | |
| } | |
| #endif // SK_GPU_V1 |