blob: 153b38aada526c0261aae1986a17f24f0c67a375 [file] [log] [blame]
Robert Phillips3e5e2f22019-12-19 11:19:16 -05001/*
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
Robert Phillipse19babf2020-04-06 13:57:30 -04008#include "include/core/SkCanvas.h"
Robert Phillips6d344c32020-07-06 10:56:46 -04009#include "include/gpu/GrDirectContext.h"
Robert Phillips3e5e2f22019-12-19 11:19:16 -050010#include "src/core/SkAutoPixmapStorage.h"
Robert Phillips99dead92020-01-27 16:11:57 -050011#include "src/core/SkCompressedDataUtils.h"
Mike Reed13711eb2020-07-14 17:16:32 -040012#include "src/core/SkMipmap.h"
Greg Daniel01f278c2020-06-12 16:58:17 -040013#include "src/gpu/GrBackendUtils.h"
Robert Phillips3e5e2f22019-12-19 11:19:16 -050014#include "src/gpu/GrContextPriv.h"
15#include "src/image/SkImage_Base.h"
16#include "tests/Test.h"
17#include "tests/TestUtils.h"
18#include "tools/ToolUtils.h"
19
Robert Phillips07f0e412020-01-17 15:20:00 -050020// Just verify that 'actual' is entirely 'expected'
Robert Phillips3e5e2f22019-12-19 11:19:16 -050021static void check_solid_pixmap(skiatest::Reporter* reporter,
22 const SkColor4f& expected, const SkPixmap& actual,
23 const char* label0, const char* label1, const char* label2) {
24 const float tols[4] = { 0.01f, 0.01f, 0.01f, 0.01f };
25
26 auto error = std::function<ComparePixmapsErrorReporter>(
27 [reporter, label0, label1, label2](int x, int y, const float diffs[4]) {
28 SkASSERT(x >= 0 && y >= 0);
29 ERRORF(reporter, "%s %s %s - mismatch at %d, %d (%f, %f, %f %f)",
30 label0, label1, label2, x, y,
31 diffs[0], diffs[1], diffs[2], diffs[3]);
32 });
33
34 CheckSolidPixels(expected, actual, tols, error);
35}
36
Robert Phillips07f0e412020-01-17 15:20:00 -050037// Create an SkImage to wrap 'backendTex'
38sk_sp<SkImage> create_image(GrContext* context, const GrBackendTexture& backendTex) {
Greg Daniel01f278c2020-06-12 16:58:17 -040039 SkImage::CompressionType compression =
40 GrBackendFormatToCompressionType(backendTex.getBackendFormat());
Robert Phillips3e5e2f22019-12-19 11:19:16 -050041
Robert Phillips99dead92020-01-27 16:11:57 -050042 SkAlphaType at = SkCompressionTypeIsOpaque(compression) ? kOpaque_SkAlphaType
Robert Phillipsb0855272020-01-15 12:56:52 -050043 : kPremul_SkAlphaType;
Robert Phillips3e5e2f22019-12-19 11:19:16 -050044
Robert Phillips07f0e412020-01-17 15:20:00 -050045 return SkImage::MakeFromCompressedTexture(context,
46 backendTex,
47 kTopLeft_GrSurfaceOrigin,
48 at,
49 nullptr);
50}
51
52// Draw the compressed backend texture (wrapped in an SkImage) into an RGBA surface, attempting
53// to access all the mipMap levels.
54static void check_compressed_mipmaps(GrContext* context, sk_sp<SkImage> img,
55 SkImage::CompressionType compressionType,
56 const SkColor4f expectedColors[6],
57 GrMipMapped mipMapped,
58 skiatest::Reporter* reporter, const char* label) {
Robert Phillips3e5e2f22019-12-19 11:19:16 -050059
60 SkImageInfo readbackSurfaceII = SkImageInfo::Make(32, 32, kRGBA_8888_SkColorType,
61 kPremul_SkAlphaType);
62
63 sk_sp<SkSurface> surf = SkSurface::MakeRenderTarget(context,
64 SkBudgeted::kNo,
65 readbackSurfaceII, 1,
66 kTopLeft_GrSurfaceOrigin,
67 nullptr);
68 if (!surf) {
69 return;
70 }
71
72 SkCanvas* canvas = surf->getCanvas();
73
74 SkPaint p;
Robert Phillipsb0855272020-01-15 12:56:52 -050075 p.setFilterQuality(kHigh_SkFilterQuality); // to force mipMapping
76 p.setBlendMode(SkBlendMode::kSrc);
Robert Phillips3e5e2f22019-12-19 11:19:16 -050077
78 int numMipLevels = 1;
79 if (mipMapped == GrMipMapped::kYes) {
Mike Reed13711eb2020-07-14 17:16:32 -040080 numMipLevels = SkMipmap::ComputeLevelCount(32, 32)+1;
Robert Phillips3e5e2f22019-12-19 11:19:16 -050081 }
82
83 for (int i = 0, rectSize = 32; i < numMipLevels; ++i, rectSize /= 2) {
84 SkASSERT(rectSize >= 1);
85
86 canvas->clear(SK_ColorTRANSPARENT);
87
88 SkRect r = SkRect::MakeWH(rectSize, rectSize);
89 canvas->drawImageRect(img, r, &p);
90
91 SkImageInfo readbackII = SkImageInfo::Make(rectSize, rectSize,
92 kRGBA_8888_SkColorType,
93 kUnpremul_SkAlphaType);
94 SkAutoPixmapStorage actual2;
95 SkAssertResult(actual2.tryAlloc(readbackII));
96 actual2.erase(SkColors::kTransparent);
97
98 bool result = surf->readPixels(actual2, 0, 0);
99 REPORTER_ASSERT(reporter, result);
100
101 SkString str;
102 str.appendf("mip-level %d", i);
103
104 check_solid_pixmap(reporter, expectedColors[i], actual2,
Robert Phillips07f0e412020-01-17 15:20:00 -0500105 GrCompressionTypeToStr(compressionType), label, str.c_str());
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500106 }
107}
108
Robert Phillips07f0e412020-01-17 15:20:00 -0500109// Verify that we can readback from a compressed texture
110static void check_readback(GrContext* context, sk_sp<SkImage> img,
111 SkImage::CompressionType compressionType,
112 const SkColor4f& expectedColor,
113 skiatest::Reporter* reporter, const char* label) {
Robert Phillips314524e2020-01-30 08:38:40 -0500114#ifdef SK_BUILD_FOR_IOS
115 // reading back ETC2 is broken on Metal/iOS (skbug.com/9839)
116 if (context->backend() == GrBackendApi::kMetal) {
117 return;
118 }
119#endif
120
Robert Phillips07f0e412020-01-17 15:20:00 -0500121 SkAutoPixmapStorage actual;
122
123 SkImageInfo readBackII = SkImageInfo::Make(img->width(), img->height(),
124 kRGBA_8888_SkColorType,
125 kUnpremul_SkAlphaType);
126
127 SkAssertResult(actual.tryAlloc(readBackII));
128 actual.erase(SkColors::kTransparent);
129
130 bool result = img->readPixels(actual, 0, 0);
131 REPORTER_ASSERT(reporter, result);
132
133 check_solid_pixmap(reporter, expectedColor, actual,
134 GrCompressionTypeToStr(compressionType), label, "");
135}
136
137// Test initialization of compressed GrBackendTextures to a specific color
138static void test_compressed_color_init(GrContext* context,
139 skiatest::Reporter* reporter,
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500140 std::function<GrBackendTexture (GrContext*,
141 const SkColor4f&,
142 GrMipMapped)> create,
Robert Phillips07f0e412020-01-17 15:20:00 -0500143 const SkColor4f& color,
144 SkImage::CompressionType compression,
145 GrMipMapped mipMapped) {
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500146 GrBackendTexture backendTex = create(context, color, mipMapped);
147 if (!backendTex.isValid()) {
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500148 return;
149 }
150
Robert Phillips07f0e412020-01-17 15:20:00 -0500151 sk_sp<SkImage> img = create_image(context, backendTex);
152 if (!img) {
153 return;
154 }
155
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500156 SkColor4f expectedColors[6] = { color, color, color, color, color, color };
157
Robert Phillips07f0e412020-01-17 15:20:00 -0500158 check_compressed_mipmaps(context, img, compression, expectedColors, mipMapped,
159 reporter, "colorinit");
Greg Danielceebe422020-07-14 15:05:42 +0000160 check_readback(context, std::move(img), compression, color, reporter,
161 "solid readback");
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500162
163 context->deleteBackendTexture(backendTex);
164}
165
Robert Phillips07f0e412020-01-17 15:20:00 -0500166// Create compressed data pulling the color for each mipmap level from 'levelColors'.
Robert Phillipsca0f8372019-12-20 09:57:41 -0500167static std::unique_ptr<const char[]> make_compressed_data(SkImage::CompressionType compression,
168 SkColor4f levelColors[6],
169 GrMipMapped mipMapped) {
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500170 SkISize dimensions { 32, 32 };
171
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500172 int numMipLevels = 1;
173 if (mipMapped == GrMipMapped::kYes) {
Mike Reed13711eb2020-07-14 17:16:32 -0400174 numMipLevels = SkMipmap::ComputeLevelCount(dimensions.width(), dimensions.height()) + 1;
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500175 }
176
Robert Phillipsca0f8372019-12-20 09:57:41 -0500177 SkTArray<size_t> mipMapOffsets(numMipLevels);
178
Robert Phillips99dead92020-01-27 16:11:57 -0500179 size_t dataSize = SkCompressedDataSize(compression, dimensions, &mipMapOffsets,
180 mipMapped == GrMipMapped::kYes);
Robert Phillipsca0f8372019-12-20 09:57:41 -0500181 char* data = new char[dataSize];
182
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500183 for (int level = 0; level < numMipLevels; ++level) {
Robert Phillipsca0f8372019-12-20 09:57:41 -0500184 // We have to do this a level at a time bc we might have a different color for
185 // each level
186 GrFillInCompressedData(compression, dimensions,
187 GrMipMapped::kNo, &data[mipMapOffsets[level]], levelColors[level]);
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500188
Brian Osman788b9162020-02-07 10:36:46 -0500189 dimensions = {std::max(1, dimensions.width() /2), std::max(1, dimensions.height()/2)};
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500190 }
191
192 return std::unique_ptr<const char[]>(data);
193}
194
Robert Phillips07f0e412020-01-17 15:20:00 -0500195// Verify that we can initialize a compressed backend texture with data (esp.
196// the mipmap levels).
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500197static void test_compressed_data_init(GrContext* context,
198 skiatest::Reporter* reporter,
199 std::function<GrBackendTexture (GrContext*,
200 const char* data,
201 size_t dataSize,
202 GrMipMapped)> create,
Robert Phillipsca0f8372019-12-20 09:57:41 -0500203 SkImage::CompressionType compression,
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500204 GrMipMapped mipMapped) {
205
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500206 SkColor4f expectedColors[6] = {
207 { 1.0f, 0.0f, 0.0f, 1.0f }, // R
208 { 0.0f, 1.0f, 0.0f, 1.0f }, // G
209 { 0.0f, 0.0f, 1.0f, 1.0f }, // B
210 { 0.0f, 1.0f, 1.0f, 1.0f }, // C
211 { 1.0f, 0.0f, 1.0f, 1.0f }, // M
212 { 1.0f, 1.0f, 0.0f, 1.0f }, // Y
213 };
214
Robert Phillipsca0f8372019-12-20 09:57:41 -0500215 std::unique_ptr<const char[]> data(make_compressed_data(compression, expectedColors,
216 mipMapped));
Robert Phillips99dead92020-01-27 16:11:57 -0500217 size_t dataSize = SkCompressedDataSize(compression, { 32, 32 }, nullptr,
218 mipMapped == GrMipMapped::kYes);
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500219
220 GrBackendTexture backendTex = create(context, data.get(), dataSize, mipMapped);
221 if (!backendTex.isValid()) {
222 return;
223 }
224
Robert Phillips07f0e412020-01-17 15:20:00 -0500225 sk_sp<SkImage> img = create_image(context, backendTex);
226 if (!img) {
227 return;
228 }
229
230 check_compressed_mipmaps(context, img, compression, expectedColors,
231 mipMapped, reporter, "pixmap");
Greg Danielceebe422020-07-14 15:05:42 +0000232 check_readback(context, std::move(img), compression, expectedColors[0], reporter,
Robert Phillips07f0e412020-01-17 15:20:00 -0500233 "data readback");
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500234
235 context->deleteBackendTexture(backendTex);
236}
237
238DEF_GPUTEST_FOR_RENDERING_CONTEXTS(CompressedBackendAllocationTest, reporter, ctxInfo) {
Robert Phillips6d344c32020-07-06 10:56:46 -0400239 auto context = ctxInfo.directContext();
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500240 const GrCaps* caps = context->priv().caps();
241
242 struct {
243 SkImage::CompressionType fCompression;
244 SkColor4f fColor;
245 } combinations[] = {
Robert Phillipsc558f722020-01-13 13:02:26 -0500246 { SkImage::CompressionType::kETC2_RGB8_UNORM, SkColors::kRed },
Robert Phillips07f0e412020-01-17 15:20:00 -0500247 { SkImage::CompressionType::kBC1_RGB8_UNORM, SkColors::kBlue },
Robert Phillipsb0855272020-01-15 12:56:52 -0500248 { SkImage::CompressionType::kBC1_RGBA8_UNORM, SkColors::kTransparent },
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500249 };
250
251 for (auto combo : combinations) {
252 GrBackendFormat format = context->compressedBackendFormat(combo.fCompression);
253 if (!format.isValid()) {
254 continue;
255 }
256
257 if (!caps->isFormatTexturable(format)) {
258 continue;
259 }
260
261 for (auto mipMapped : { GrMipMapped::kNo, GrMipMapped::kYes }) {
262 if (GrMipMapped::kYes == mipMapped && !caps->mipMapSupport()) {
263 continue;
264 }
265
266 // color initialized
267 {
268 auto createWithColorMtd = [format](GrContext* context,
269 const SkColor4f& color,
270 GrMipMapped mipMapped) {
271 return context->createCompressedBackendTexture(32, 32, format, color,
272 mipMapped, GrProtected::kNo);
273 };
274
275 test_compressed_color_init(context, reporter, createWithColorMtd,
Robert Phillips07f0e412020-01-17 15:20:00 -0500276 combo.fColor, combo.fCompression, mipMapped);
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500277 }
278
279 // data initialized
280 {
281 auto createWithDataMtd = [format](GrContext* context,
282 const char* data, size_t dataSize,
283 GrMipMapped mipMapped) {
284 return context->createCompressedBackendTexture(32, 32, format, data, dataSize,
285 mipMapped, GrProtected::kNo);
286 };
287
Robert Phillipsca0f8372019-12-20 09:57:41 -0500288 test_compressed_data_init(context, reporter, createWithDataMtd,
289 combo.fCompression, mipMapped);
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500290 }
291
292 }
293 }
294}