1 /* 2 * Copyright 2017 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #include "SkTypes.h" 9 10 #if SK_SUPPORT_GPU 11 12 #include "GrContextFactory.h" 13 #include "Resources.h" 14 #include "SkAutoPixmapStorage.h" 15 #include "SkBitmap.h" 16 #include "SkCanvas.h" 17 #include "SkCrossContextImageData.h" 18 #include "SkSemaphore.h" 19 #include "SkSurface.h" 20 #include "SkThreadUtils.h" 21 #include "Test.h" 22 23 using namespace sk_gpu_test; 24 25 static SkImageInfo read_pixels_info(SkImage* image) { 26 return SkImageInfo::MakeN32(image->width(), image->height(), image->alphaType()); 27 } 28 29 static bool colors_are_close(SkColor a, SkColor b, int error) { 30 return SkTAbs((int)SkColorGetR(a) - (int)SkColorGetR(b)) <= error && 31 SkTAbs((int)SkColorGetG(a) - (int)SkColorGetG(b)) <= error && 32 SkTAbs((int)SkColorGetB(a) - (int)SkColorGetB(b)) <= error; 33 } 34 35 static void assert_equal(skiatest::Reporter* reporter, SkImage* a, SkImage* b, int error) { 36 REPORTER_ASSERT(reporter, a->width() == b->width()); 37 REPORTER_ASSERT(reporter, a->height() == b->height()); 38 39 SkAutoPixmapStorage pmapA, pmapB; 40 pmapA.alloc(read_pixels_info(a)); 41 pmapB.alloc(read_pixels_info(b)); 42 43 REPORTER_ASSERT(reporter, a->readPixels(pmapA, 0, 0)); 44 REPORTER_ASSERT(reporter, b->readPixels(pmapB, 0, 0)); 45 46 for (int y = 0; y < a->height(); ++y) { 47 for (int x = 0; x < a->width(); ++x) { 48 SkColor ca = pmapA.getColor(x, y); 49 SkColor cb = pmapB.getColor(x, y); 50 if (!error) { 51 if (ca != cb) { 52 ERRORF(reporter, "Expected 0x%08x but got 0x%08x at (%d, %d)", ca, cb, x, y); 53 return; 54 } 55 } else { 56 if (!colors_are_close(ca, cb, error)) { 57 ERRORF(reporter, "Expected 0x%08x +-%d but got 0x%08x at (%d, %d)", 58 ca, error, cb, x, y); 59 return; 60 } 61 } 62 } 63 } 64 } 65 66 static void draw_image_test_pattern(SkCanvas* canvas) { 67 canvas->clear(SK_ColorWHITE); 68 SkPaint paint; 69 paint.setColor(SK_ColorBLACK); 70 canvas->drawRect(SkRect::MakeXYWH(5, 5, 10, 10), paint); 71 } 72 73 static sk_sp<SkImage> create_test_image() { 74 SkBitmap bm; 75 bm.allocN32Pixels(20, 20, true); 76 SkCanvas canvas(bm); 77 draw_image_test_pattern(&canvas); 78 79 return SkImage::MakeFromBitmap(bm); 80 } 81 82 static sk_sp<SkData> create_test_data(SkEncodedImageFormat format) { 83 auto image = create_test_image(); 84 return sk_sp<SkData>(image->encode(format, 100)); 85 } 86 87 DEF_GPUTEST(CrossContextImage_SameContext, reporter, /*factory*/) { 88 GrContextFactory factory; 89 sk_sp<SkImage> testImage = create_test_image(); 90 91 // Test both PNG and JPG, to exercise GPU YUV conversion 92 for (auto format : { SkEncodedImageFormat::kPNG, SkEncodedImageFormat::kJPEG }) { 93 sk_sp<SkData> encoded = create_test_data(format); 94 95 for (int i = 0; i < GrContextFactory::kContextTypeCnt; ++i) { 96 GrContextFactory::ContextType ctxType = static_cast<GrContextFactory::ContextType>(i); 97 if (!sk_gpu_test::GrContextFactory::IsRenderingContext(ctxType)) { 98 continue; 99 } 100 101 ContextInfo info = factory.getContextInfo(ctxType); 102 if (!info.grContext()) { 103 continue; 104 } 105 106 auto ccid = SkCrossContextImageData::MakeFromEncoded(info.grContext(), encoded, 107 nullptr); 108 REPORTER_ASSERT(reporter, ccid != nullptr); 109 110 auto image = SkImage::MakeFromCrossContextImageData(info.grContext(), std::move(ccid)); 111 REPORTER_ASSERT(reporter, image != nullptr); 112 113 // JPEG encode -> decode won't round trip the image perfectly 114 assert_equal(reporter, testImage.get(), image.get(), 115 SkEncodedImageFormat::kJPEG == format ? 2 : 0); 116 } 117 } 118 } 119 120 DEF_GPUTEST(CrossContextImage_SharedContextSameThread, reporter, /*factory*/) { 121 GrContextFactory factory; 122 sk_sp<SkImage> testImage = create_test_image(); 123 124 // Test both PNG and JPG, to exercise GPU YUV conversion 125 for (auto format : { SkEncodedImageFormat::kPNG, SkEncodedImageFormat::kJPEG }) { 126 sk_sp<SkData> encoded = create_test_data(format); 127 128 for (int i = 0; i < GrContextFactory::kContextTypeCnt; ++i) { 129 GrContextFactory::ContextType ctxType = static_cast<GrContextFactory::ContextType>(i); 130 if (!sk_gpu_test::GrContextFactory::IsRenderingContext(ctxType)) { 131 continue; 132 } 133 134 ContextInfo info = factory.getContextInfo(ctxType); 135 if (!info.grContext()) { 136 continue; 137 } 138 auto ccid = SkCrossContextImageData::MakeFromEncoded(info.grContext(), encoded, 139 nullptr); 140 REPORTER_ASSERT(reporter, ccid != nullptr); 141 142 ContextInfo info2 = factory.getSharedContextInfo(info.grContext()); 143 GrContext* ctx2 = info2.grContext(); 144 int resourceCountBefore = 0, resourceCountAfter = 0; 145 size_t resourceBytesBefore = 0, resourceBytesAfter = 0; 146 if (ctx2 && info.grContext()->caps()->crossContextTextureSupport()) { 147 ctx2->getResourceCacheUsage(&resourceCountBefore, &resourceBytesBefore); 148 } 149 150 auto image = SkImage::MakeFromCrossContextImageData(ctx2, std::move(ccid)); 151 REPORTER_ASSERT(reporter, image != nullptr); 152 153 if (ctx2 && info.grContext()->caps()->crossContextTextureSupport()) { 154 // MakeFromCrossContextImageData should have imported the texture back into our 155 // cache, so we should see an uptick. (If we have crossContextTextureSupport, 156 // otherwise we're just handing around a CPU or codec-backed image, so no cache 157 // impact will occur). 158 ctx2->getResourceCacheUsage(&resourceCountAfter, &resourceBytesAfter); 159 REPORTER_ASSERT(reporter, resourceCountAfter == resourceCountBefore + 1); 160 REPORTER_ASSERT(reporter, resourceBytesAfter > resourceBytesBefore); 161 } 162 163 // JPEG encode -> decode won't round trip the image perfectly 164 assert_equal(reporter, testImage.get(), image.get(), 165 SkEncodedImageFormat::kJPEG == format ? 2 : 0); 166 } 167 } 168 } 169 170 namespace { 171 struct CrossContextImage_ThreadContext { 172 GrContext* fGrContext; 173 sk_gpu_test::TestContext* fTestContext; 174 SkSemaphore fSemaphore; 175 std::unique_ptr<SkCrossContextImageData> fCCID; 176 sk_sp<SkData> fEncoded; 177 }; 178 } 179 180 static void upload_image_thread_proc(void* data) { 181 CrossContextImage_ThreadContext* ctx = static_cast<CrossContextImage_ThreadContext*>(data); 182 ctx->fTestContext->makeCurrent(); 183 ctx->fCCID = SkCrossContextImageData::MakeFromEncoded(ctx->fGrContext, ctx->fEncoded, nullptr); 184 ctx->fSemaphore.signal(); 185 } 186 187 DEF_GPUTEST(CrossContextImage_SharedContextOtherThread, reporter, /*factory*/) { 188 sk_sp<SkImage> testImage = create_test_image(); 189 190 // Test both PNG and JPG, to exercise GPU YUV conversion 191 for (auto format : { SkEncodedImageFormat::kPNG, SkEncodedImageFormat::kJPEG }) { 192 // Use a new factory for each batch of tests. Otherwise the shared context will still be 193 // current on the upload thread when we do the second iteration, and we get undefined 194 // behavior. 195 GrContextFactory factory; 196 sk_sp<SkData> encoded = create_test_data(format); 197 198 for (int i = 0; i < GrContextFactory::kContextTypeCnt; ++i) { 199 GrContextFactory::ContextType ctxType = static_cast<GrContextFactory::ContextType>(i); 200 if (!sk_gpu_test::GrContextFactory::IsRenderingContext(ctxType)) { 201 continue; 202 } 203 204 // Create two GrContexts in a share group 205 ContextInfo info = factory.getContextInfo(ctxType); 206 if (!info.grContext()) { 207 continue; 208 } 209 ContextInfo info2 = factory.getSharedContextInfo(info.grContext()); 210 if (!info2.grContext()) { 211 continue; 212 } 213 214 // Make the first one current (on this thread) again 215 info.testContext()->makeCurrent(); 216 217 // Bundle up data for the worker thread 218 CrossContextImage_ThreadContext ctx; 219 ctx.fGrContext = info2.grContext(); 220 ctx.fTestContext = info2.testContext(); 221 ctx.fEncoded = encoded; 222 223 SkThread uploadThread(upload_image_thread_proc, &ctx); 224 SkAssertResult(uploadThread.start()); 225 226 ctx.fSemaphore.wait(); 227 auto image = SkImage::MakeFromCrossContextImageData(info.grContext(), 228 std::move(ctx.fCCID)); 229 REPORTER_ASSERT(reporter, image != nullptr); 230 231 // JPEG encode -> decode won't round trip the image perfectly 232 assert_equal(reporter, testImage.get(), image.get(), 233 SkEncodedImageFormat::kJPEG == format ? 2 : 0); 234 235 uploadThread.join(); 236 } 237 } 238 } 239 240 #endif 241