• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2019 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 "include/core/SkCanvas.h"
9 #include "include/gpu/GrDirectContext.h"
10 #include "include/gpu/GrRecordingContext.h"
11 #include "src/core/SkAutoPixmapStorage.h"
12 #include "src/core/SkCompressedDataUtils.h"
13 #include "src/core/SkMipmap.h"
14 #include "src/core/SkPaintPriv.h"
15 #include "src/gpu/GrBackendUtils.h"
16 #include "src/gpu/GrDirectContextPriv.h"
17 #include "src/image/SkImage_Base.h"
18 #include "tests/Test.h"
19 #include "tests/TestUtils.h"
20 #include "tools/ToolUtils.h"
21 
22 // Just verify that 'actual' is entirely 'expected'
check_solid_pixmap(skiatest::Reporter * reporter,const SkColor4f & expected,const SkPixmap & actual,const char * label0,const char * label1,const char * label2)23 static void check_solid_pixmap(skiatest::Reporter* reporter,
24                                const SkColor4f& expected, const SkPixmap& actual,
25                                const char* label0, const char* label1, const char* label2) {
26     const float tols[4] = { 0.01f, 0.01f, 0.01f, 0.01f };
27 
28     auto error = std::function<ComparePixmapsErrorReporter>(
29         [reporter, label0, label1, label2](int x, int y, const float diffs[4]) {
30             SkASSERT(x >= 0 && y >= 0);
31             ERRORF(reporter, "%s %s %s - mismatch at %d, %d (%f, %f, %f %f)",
32                    label0, label1, label2, x, y,
33                    diffs[0], diffs[1], diffs[2], diffs[3]);
34         });
35 
36     CheckSolidPixels(expected, actual, tols, error);
37 }
38 
39 // Create an SkImage to wrap 'backendTex'
create_image(GrDirectContext * dContext,const GrBackendTexture & backendTex)40 sk_sp<SkImage> create_image(GrDirectContext* dContext, const GrBackendTexture& backendTex) {
41     SkImage::CompressionType compression =
42             GrBackendFormatToCompressionType(backendTex.getBackendFormat());
43 
44     SkAlphaType at = SkCompressionTypeIsOpaque(compression) ? kOpaque_SkAlphaType
45                                                             : kPremul_SkAlphaType;
46 
47     return SkImage::MakeFromCompressedTexture(dContext,
48                                               backendTex,
49                                               kTopLeft_GrSurfaceOrigin,
50                                               at,
51                                               nullptr);
52 }
53 
54 // Draw the compressed backend texture (wrapped in an SkImage) into an RGBA surface, attempting
55 // to access all the mipMap levels.
check_compressed_mipmaps(GrRecordingContext * rContext,sk_sp<SkImage> img,SkImage::CompressionType compressionType,const SkColor4f expectedColors[6],GrMipmapped mipMapped,skiatest::Reporter * reporter,const char * label)56 static void check_compressed_mipmaps(GrRecordingContext* rContext, sk_sp<SkImage> img,
57                                      SkImage::CompressionType compressionType,
58                                      const SkColor4f expectedColors[6],
59                                      GrMipmapped mipMapped,
60                                      skiatest::Reporter* reporter, const char* label) {
61 
62     SkImageInfo readbackSurfaceII = SkImageInfo::Make(32, 32, kRGBA_8888_SkColorType,
63                                                       kPremul_SkAlphaType);
64 
65     sk_sp<SkSurface> surf = SkSurface::MakeRenderTarget(rContext,
66                                                         SkBudgeted::kNo,
67                                                         readbackSurfaceII, 1,
68                                                         kTopLeft_GrSurfaceOrigin,
69                                                         nullptr);
70     if (!surf) {
71         return;
72     }
73 
74     SkCanvas* canvas = surf->getCanvas();
75 
76     const SkSamplingOptions sampling(SkFilterMode::kLinear,
77                                      SkMipmapMode::kLinear);
78     SkPaint p;
79     p.setBlendMode(SkBlendMode::kSrc);
80 
81     int numMipLevels = 1;
82     if (mipMapped == GrMipmapped::kYes) {
83         numMipLevels = SkMipmap::ComputeLevelCount(32, 32)+1;
84     }
85 
86     for (int i = 0, rectSize = 32; i < numMipLevels; ++i, rectSize /= 2) {
87         SkASSERT(rectSize >= 1);
88 
89         canvas->clear(SK_ColorTRANSPARENT);
90 
91         SkRect r = SkRect::MakeWH(rectSize, rectSize);
92         canvas->drawImageRect(img, r, sampling, &p);
93 
94         SkImageInfo readbackII = SkImageInfo::Make(rectSize, rectSize,
95                                                    kRGBA_8888_SkColorType,
96                                                    kUnpremul_SkAlphaType);
97         SkAutoPixmapStorage actual2;
98         SkAssertResult(actual2.tryAlloc(readbackII));
99         actual2.erase(SkColors::kTransparent);
100 
101         bool result = surf->readPixels(actual2, 0, 0);
102         REPORTER_ASSERT(reporter, result);
103 
104         SkString str;
105         str.appendf("mip-level %d", i);
106 
107         check_solid_pixmap(reporter, expectedColors[i], actual2,
108                            GrCompressionTypeToStr(compressionType), label, str.c_str());
109     }
110 }
111 
112 // Verify that we can readback from a compressed texture
check_readback(GrDirectContext * dContext,sk_sp<SkImage> img,SkImage::CompressionType compressionType,const SkColor4f & expectedColor,skiatest::Reporter * reporter,const char * label)113 static void check_readback(GrDirectContext* dContext, sk_sp<SkImage> img,
114                            SkImage::CompressionType compressionType,
115                            const SkColor4f& expectedColor,
116                            skiatest::Reporter* reporter, const char* label) {
117 #ifdef SK_BUILD_FOR_IOS
118     // reading back ETC2 is broken on Metal/iOS (skbug.com/9839)
119     if (dContext->backend() == GrBackendApi::kMetal) {
120       return;
121     }
122 #endif
123 
124     SkAutoPixmapStorage actual;
125 
126     SkImageInfo readBackII = SkImageInfo::Make(img->width(), img->height(),
127                                                kRGBA_8888_SkColorType,
128                                                kUnpremul_SkAlphaType);
129 
130     SkAssertResult(actual.tryAlloc(readBackII));
131     actual.erase(SkColors::kTransparent);
132 
133     bool result = img->readPixels(dContext, actual, 0, 0);
134     REPORTER_ASSERT(reporter, result);
135 
136     check_solid_pixmap(reporter, expectedColor, actual,
137                        GrCompressionTypeToStr(compressionType), label, "");
138 }
139 
140 // Test initialization of compressed GrBackendTextures to a specific color
test_compressed_color_init(GrDirectContext * dContext,skiatest::Reporter * reporter,std::function<GrBackendTexture (GrDirectContext *,const SkColor4f &,GrMipmapped)> create,const SkColor4f & color,SkImage::CompressionType compression,GrMipmapped mipMapped)141 static void test_compressed_color_init(GrDirectContext* dContext,
142                                        skiatest::Reporter* reporter,
143                                        std::function<GrBackendTexture (GrDirectContext*,
144                                                                        const SkColor4f&,
145                                                                        GrMipmapped)> create,
146                                        const SkColor4f& color,
147                                        SkImage::CompressionType compression,
148                                        GrMipmapped mipMapped) {
149     GrBackendTexture backendTex = create(dContext, color, mipMapped);
150     if (!backendTex.isValid()) {
151         return;
152     }
153 
154     sk_sp<SkImage> img = create_image(dContext, backendTex);
155     if (!img) {
156         return;
157     }
158 
159     SkColor4f expectedColors[6] = { color, color, color, color, color, color };
160 
161     check_compressed_mipmaps(dContext, img, compression, expectedColors, mipMapped,
162                              reporter, "colorinit");
163     check_readback(dContext, img, compression, color, reporter, "solid readback");
164 
165     SkColor4f newColor;
166     newColor.fR = color.fB;
167     newColor.fG = color.fR;
168     newColor.fB = color.fG;
169     newColor.fA = color.fA;
170 
171     bool result = dContext->updateCompressedBackendTexture(backendTex, newColor, nullptr, nullptr);
172     // Since we were able to create the compressed texture we should be able to update it.
173     REPORTER_ASSERT(reporter, result);
174 
175     SkColor4f expectedNewColors[6] = {newColor, newColor, newColor, newColor, newColor, newColor};
176 
177     check_compressed_mipmaps(dContext, img, compression, expectedNewColors, mipMapped, reporter,
178                              "colorinit");
179     check_readback(dContext, std::move(img), compression, newColor, reporter, "solid readback");
180 
181     dContext->deleteBackendTexture(backendTex);
182 }
183 
184 // Create compressed data pulling the color for each mipmap level from 'levelColors'.
make_compressed_data(SkImage::CompressionType compression,SkColor4f levelColors[6],GrMipmapped mipMapped)185 static std::unique_ptr<const char[]> make_compressed_data(SkImage::CompressionType compression,
186                                                           SkColor4f levelColors[6],
187                                                           GrMipmapped mipMapped) {
188     SkISize dimensions { 32, 32 };
189 
190     int numMipLevels = 1;
191     if (mipMapped == GrMipmapped::kYes) {
192         numMipLevels = SkMipmap::ComputeLevelCount(dimensions.width(), dimensions.height()) + 1;
193     }
194 
195     SkTArray<size_t> mipMapOffsets(numMipLevels);
196 
197     size_t dataSize = SkCompressedDataSize(compression, dimensions, &mipMapOffsets,
198                                            mipMapped == GrMipmapped::kYes);
199     char* data = new char[dataSize];
200 
201     for (int level = 0; level < numMipLevels; ++level) {
202         // We have to do this a level at a time bc we might have a different color for
203         // each level
204         GrFillInCompressedData(compression, dimensions,
205                                GrMipmapped::kNo, &data[mipMapOffsets[level]], levelColors[level]);
206 
207         dimensions = {std::max(1, dimensions.width() /2), std::max(1, dimensions.height()/2)};
208     }
209 
210     return std::unique_ptr<const char[]>(data);
211 }
212 
213 // Verify that we can initialize a compressed backend texture with data (esp.
214 // the mipmap levels).
test_compressed_data_init(GrDirectContext * dContext,skiatest::Reporter * reporter,std::function<GrBackendTexture (GrDirectContext *,const char * data,size_t dataSize,GrMipmapped)> create,SkImage::CompressionType compression,GrMipmapped mipMapped)215 static void test_compressed_data_init(GrDirectContext* dContext,
216                                       skiatest::Reporter* reporter,
217                                       std::function<GrBackendTexture (GrDirectContext*,
218                                                                       const char* data,
219                                                                       size_t dataSize,
220                                                                       GrMipmapped)> create,
221                                       SkImage::CompressionType compression,
222                                       GrMipmapped mipMapped) {
223 
224     SkColor4f expectedColors[6] = {
225         { 1.0f, 0.0f, 0.0f, 1.0f }, // R
226         { 0.0f, 1.0f, 0.0f, 1.0f }, // G
227         { 0.0f, 0.0f, 1.0f, 1.0f }, // B
228         { 0.0f, 1.0f, 1.0f, 1.0f }, // C
229         { 1.0f, 0.0f, 1.0f, 1.0f }, // M
230         { 1.0f, 1.0f, 0.0f, 1.0f }, // Y
231     };
232 
233     std::unique_ptr<const char[]> data(make_compressed_data(compression, expectedColors,
234                                                             mipMapped));
235     size_t dataSize = SkCompressedDataSize(compression, { 32, 32 }, nullptr,
236                                            mipMapped == GrMipmapped::kYes);
237 
238     GrBackendTexture backendTex = create(dContext, data.get(), dataSize, mipMapped);
239     if (!backendTex.isValid()) {
240         return;
241     }
242 
243     sk_sp<SkImage> img = create_image(dContext, backendTex);
244     if (!img) {
245         return;
246     }
247 
248     check_compressed_mipmaps(dContext, img, compression, expectedColors,
249                              mipMapped, reporter, "pixmap");
250     check_readback(dContext, img, compression, expectedColors[0], reporter, "data readback");
251 
252     SkColor4f expectedColorsNew[6] = {
253         {1.0f, 1.0f, 0.0f, 1.0f},  // Y
254         {1.0f, 0.0f, 0.0f, 1.0f},  // R
255         {0.0f, 1.0f, 0.0f, 1.0f},  // G
256         {0.0f, 0.0f, 1.0f, 1.0f},  // B
257         {0.0f, 1.0f, 1.0f, 1.0f},  // C
258         {1.0f, 0.0f, 1.0f, 1.0f},  // M
259     };
260 
261     std::unique_ptr<const char[]> dataNew(
262             make_compressed_data(compression, expectedColorsNew, mipMapped));
263     size_t dataNewSize =
264             SkCompressedDataSize(compression, {32, 32}, nullptr, mipMapped == GrMipMapped::kYes);
265 
266     bool result = dContext->updateCompressedBackendTexture(backendTex, dataNew.get(), dataNewSize,
267                                                            nullptr, nullptr);
268     // Since we were able to create the compressed texture we should be able to update it.
269     REPORTER_ASSERT(reporter, result);
270 
271     check_compressed_mipmaps(dContext, img, compression, expectedColorsNew, mipMapped, reporter,
272                              "pixmap");
273     check_readback(dContext, std::move(img), compression, expectedColorsNew[0], reporter,
274                    "data readback");
275 
276     dContext->deleteBackendTexture(backendTex);
277 }
278 
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(CompressedBackendAllocationTest,reporter,ctxInfo)279 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(CompressedBackendAllocationTest, reporter, ctxInfo) {
280     auto dContext = ctxInfo.directContext();
281     const GrCaps* caps = dContext->priv().caps();
282 
283     struct {
284         SkImage::CompressionType fCompression;
285         SkColor4f                fColor;
286     } combinations[] = {
287         { SkImage::CompressionType::kETC2_RGB8_UNORM, SkColors::kRed },
288         { SkImage::CompressionType::kBC1_RGB8_UNORM,  SkColors::kBlue },
289         { SkImage::CompressionType::kBC1_RGBA8_UNORM, SkColors::kTransparent },
290     };
291 
292     for (auto combo : combinations) {
293         GrBackendFormat format = dContext->compressedBackendFormat(combo.fCompression);
294         if (!format.isValid()) {
295             continue;
296         }
297 
298         if (!caps->isFormatTexturable(format, GrTextureType::k2D)) {
299             continue;
300         }
301 
302         for (auto mipMapped : { GrMipmapped::kNo, GrMipmapped::kYes }) {
303             if (GrMipmapped::kYes == mipMapped && !caps->mipmapSupport()) {
304                 continue;
305             }
306 
307             // color initialized
308             {
309                 auto createWithColorMtd = [format](GrDirectContext* dContext,
310                                                    const SkColor4f& color,
311                                                    GrMipmapped mipMapped) {
312                     return dContext->createCompressedBackendTexture(32, 32, format, color,
313                                                                     mipMapped, GrProtected::kNo);
314                 };
315 
316                 test_compressed_color_init(dContext, reporter, createWithColorMtd,
317                                            combo.fColor, combo.fCompression, mipMapped);
318             }
319 
320             // data initialized
321             {
322                 auto createWithDataMtd = [format](GrDirectContext* dContext,
323                                                   const char* data, size_t dataSize,
324                                                   GrMipmapped mipMapped) {
325                     return dContext->createCompressedBackendTexture(32, 32, format, data, dataSize,
326                                                                     mipMapped, GrProtected::kNo);
327                 };
328 
329                 test_compressed_data_init(dContext, reporter, createWithDataMtd,
330                                           combo.fCompression, mipMapped);
331             }
332 
333         }
334     }
335 }
336