• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
read_pixels_info(SkImage * image)25 static SkImageInfo read_pixels_info(SkImage* image) {
26     return SkImageInfo::MakeN32(image->width(), image->height(), image->alphaType());
27 }
28 
colors_are_close(SkColor a,SkColor b,int error)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 
assert_equal(skiatest::Reporter * reporter,SkImage * a,SkImage * b,int error)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 
draw_image_test_pattern(SkCanvas * canvas)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 
create_test_image()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 
create_test_data(SkEncodedImageFormat format)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 
upload_image_thread_proc(void * data)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