blob: 5c8b21e9b6b7d1780abf3c58e6d4303cd995d583 [file] [log] [blame]
Brian Osman2c2bc112017-02-28 10:02:49 -05001/*
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
23using namespace sk_gpu_test;
24
25static SkImageInfo read_pixels_info(SkImage* image) {
26 return SkImageInfo::MakeN32(image->width(), image->height(), image->alphaType());
27}
28
29static 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
35static 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
66static 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
73static 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
82static 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
87DEF_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
120DEF_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());
Brian Osman766fcbb2017-03-13 09:33:09 -0400143 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));
Brian Osman2c2bc112017-02-28 10:02:49 -0500151 REPORTER_ASSERT(reporter, image != nullptr);
152
Brian Osman766fcbb2017-03-13 09:33:09 -0400153 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
Brian Osman2c2bc112017-02-28 10:02:49 -0500163 // 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
170namespace {
171struct 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
180static 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
187DEF_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