Home | History | Annotate | Download | only in tests
      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