blob: 9fbe9963ad022528760e6fc3cacb2a1e9a6f536a [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 Phillipsc8ae4942020-07-20 10:56:01 -040010#include "include/gpu/GrRecordingContext.h"
Robert Phillips3e5e2f22019-12-19 11:19:16 -050011#include "src/core/SkAutoPixmapStorage.h"
Robert Phillips99dead92020-01-27 16:11:57 -050012#include "src/core/SkCompressedDataUtils.h"
Mike Reed13711eb2020-07-14 17:16:32 -040013#include "src/core/SkMipmap.h"
Mike Reed839eef32020-12-23 11:18:24 -050014#include "src/core/SkPaintPriv.h"
Greg Daniel01f278c2020-06-12 16:58:17 -040015#include "src/gpu/GrBackendUtils.h"
Adlai Hollera0693042020-10-14 11:23:11 -040016#include "src/gpu/GrDirectContextPriv.h"
Robert Phillips3e5e2f22019-12-19 11:19:16 -050017#include "src/image/SkImage_Base.h"
18#include "tests/Test.h"
19#include "tests/TestUtils.h"
20#include "tools/ToolUtils.h"
21
Robert Phillips07f0e412020-01-17 15:20:00 -050022// Just verify that 'actual' is entirely 'expected'
Robert Phillips3e5e2f22019-12-19 11:19:16 -050023static 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
Robert Phillips07f0e412020-01-17 15:20:00 -050039// Create an SkImage to wrap 'backendTex'
Adlai Holler14dc7912020-08-11 15:48:49 +000040sk_sp<SkImage> create_image(GrDirectContext* dContext, const GrBackendTexture& backendTex) {
Greg Daniel01f278c2020-06-12 16:58:17 -040041 SkImage::CompressionType compression =
42 GrBackendFormatToCompressionType(backendTex.getBackendFormat());
Robert Phillips3e5e2f22019-12-19 11:19:16 -050043
Robert Phillips99dead92020-01-27 16:11:57 -050044 SkAlphaType at = SkCompressionTypeIsOpaque(compression) ? kOpaque_SkAlphaType
Robert Phillipsb0855272020-01-15 12:56:52 -050045 : kPremul_SkAlphaType;
Robert Phillips3e5e2f22019-12-19 11:19:16 -050046
Adlai Holler14dc7912020-08-11 15:48:49 +000047 return SkImage::MakeFromCompressedTexture(dContext,
Robert Phillips07f0e412020-01-17 15:20:00 -050048 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.
Robert Phillipsc8ae4942020-07-20 10:56:01 -040056static void check_compressed_mipmaps(GrRecordingContext* rContext, sk_sp<SkImage> img,
Robert Phillips07f0e412020-01-17 15:20:00 -050057 SkImage::CompressionType compressionType,
58 const SkColor4f expectedColors[6],
Brian Salomon7e67dca2020-07-21 09:27:25 -040059 GrMipmapped mipMapped,
Robert Phillips07f0e412020-01-17 15:20:00 -050060 skiatest::Reporter* reporter, const char* label) {
Robert Phillips3e5e2f22019-12-19 11:19:16 -050061
62 SkImageInfo readbackSurfaceII = SkImageInfo::Make(32, 32, kRGBA_8888_SkColorType,
63 kPremul_SkAlphaType);
64
Robert Phillipsc8ae4942020-07-20 10:56:01 -040065 sk_sp<SkSurface> surf = SkSurface::MakeRenderTarget(rContext,
Robert Phillips3e5e2f22019-12-19 11:19:16 -050066 SkBudgeted::kNo,
67 readbackSurfaceII, 1,
68 kTopLeft_GrSurfaceOrigin,
69 nullptr);
70 if (!surf) {
71 return;
72 }
73
74 SkCanvas* canvas = surf->getCanvas();
75
Mike Reed34c56a52021-01-22 15:26:41 -050076 const SkSamplingOptions sampling(SkFilterMode::kLinear,
77 SkMipmapMode::kLinear);
Robert Phillips3e5e2f22019-12-19 11:19:16 -050078 SkPaint p;
Mike Reed839eef32020-12-23 11:18:24 -050079 SkPaintPriv::SetFQ(&p, kMedium_SkFilterQuality); // to force mipMapping
Robert Phillipsb0855272020-01-15 12:56:52 -050080 p.setBlendMode(SkBlendMode::kSrc);
Robert Phillips3e5e2f22019-12-19 11:19:16 -050081
82 int numMipLevels = 1;
Brian Salomon7e67dca2020-07-21 09:27:25 -040083 if (mipMapped == GrMipmapped::kYes) {
Mike Reed13711eb2020-07-14 17:16:32 -040084 numMipLevels = SkMipmap::ComputeLevelCount(32, 32)+1;
Robert Phillips3e5e2f22019-12-19 11:19:16 -050085 }
86
87 for (int i = 0, rectSize = 32; i < numMipLevels; ++i, rectSize /= 2) {
88 SkASSERT(rectSize >= 1);
89
90 canvas->clear(SK_ColorTRANSPARENT);
91
92 SkRect r = SkRect::MakeWH(rectSize, rectSize);
Mike Reed34c56a52021-01-22 15:26:41 -050093 canvas->drawImageRect(img, r, sampling, &p);
Robert Phillips3e5e2f22019-12-19 11:19:16 -050094
95 SkImageInfo readbackII = SkImageInfo::Make(rectSize, rectSize,
96 kRGBA_8888_SkColorType,
97 kUnpremul_SkAlphaType);
98 SkAutoPixmapStorage actual2;
99 SkAssertResult(actual2.tryAlloc(readbackII));
100 actual2.erase(SkColors::kTransparent);
101
102 bool result = surf->readPixels(actual2, 0, 0);
103 REPORTER_ASSERT(reporter, result);
104
105 SkString str;
106 str.appendf("mip-level %d", i);
107
108 check_solid_pixmap(reporter, expectedColors[i], actual2,
Robert Phillips07f0e412020-01-17 15:20:00 -0500109 GrCompressionTypeToStr(compressionType), label, str.c_str());
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500110 }
111}
112
Robert Phillips07f0e412020-01-17 15:20:00 -0500113// Verify that we can readback from a compressed texture
Robert Phillipsc8ae4942020-07-20 10:56:01 -0400114static void check_readback(GrDirectContext* dContext, sk_sp<SkImage> img,
Robert Phillips07f0e412020-01-17 15:20:00 -0500115 SkImage::CompressionType compressionType,
116 const SkColor4f& expectedColor,
117 skiatest::Reporter* reporter, const char* label) {
Robert Phillips314524e2020-01-30 08:38:40 -0500118#ifdef SK_BUILD_FOR_IOS
119 // reading back ETC2 is broken on Metal/iOS (skbug.com/9839)
Robert Phillipsc8ae4942020-07-20 10:56:01 -0400120 if (dContext->backend() == GrBackendApi::kMetal) {
Robert Phillips314524e2020-01-30 08:38:40 -0500121 return;
122 }
123#endif
124
Robert Phillips07f0e412020-01-17 15:20:00 -0500125 SkAutoPixmapStorage actual;
126
127 SkImageInfo readBackII = SkImageInfo::Make(img->width(), img->height(),
128 kRGBA_8888_SkColorType,
129 kUnpremul_SkAlphaType);
130
131 SkAssertResult(actual.tryAlloc(readBackII));
132 actual.erase(SkColors::kTransparent);
133
Adlai Hollerbcfc5542020-08-27 12:44:07 -0400134 bool result = img->readPixels(dContext, actual, 0, 0);
Robert Phillips07f0e412020-01-17 15:20:00 -0500135 REPORTER_ASSERT(reporter, result);
136
137 check_solid_pixmap(reporter, expectedColor, actual,
138 GrCompressionTypeToStr(compressionType), label, "");
139}
140
141// Test initialization of compressed GrBackendTextures to a specific color
Robert Phillipsc8ae4942020-07-20 10:56:01 -0400142static void test_compressed_color_init(GrDirectContext* dContext,
Robert Phillips07f0e412020-01-17 15:20:00 -0500143 skiatest::Reporter* reporter,
Robert Phillipsc8ae4942020-07-20 10:56:01 -0400144 std::function<GrBackendTexture (GrDirectContext*,
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500145 const SkColor4f&,
Brian Salomon7e67dca2020-07-21 09:27:25 -0400146 GrMipmapped)> create,
Robert Phillips07f0e412020-01-17 15:20:00 -0500147 const SkColor4f& color,
148 SkImage::CompressionType compression,
Brian Salomon7e67dca2020-07-21 09:27:25 -0400149 GrMipmapped mipMapped) {
Robert Phillipsc8ae4942020-07-20 10:56:01 -0400150 GrBackendTexture backendTex = create(dContext, color, mipMapped);
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500151 if (!backendTex.isValid()) {
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500152 return;
153 }
154
Robert Phillipsc8ae4942020-07-20 10:56:01 -0400155 sk_sp<SkImage> img = create_image(dContext, backendTex);
Robert Phillips07f0e412020-01-17 15:20:00 -0500156 if (!img) {
157 return;
158 }
159
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500160 SkColor4f expectedColors[6] = { color, color, color, color, color, color };
161
Robert Phillipsc8ae4942020-07-20 10:56:01 -0400162 check_compressed_mipmaps(dContext, img, compression, expectedColors, mipMapped,
Robert Phillips07f0e412020-01-17 15:20:00 -0500163 reporter, "colorinit");
Greg Daniel95afafb2020-07-22 12:09:26 -0400164 check_readback(dContext, img, compression, color, reporter, "solid readback");
165
166 SkColor4f newColor;
167 newColor.fR = color.fB;
168 newColor.fG = color.fR;
169 newColor.fB = color.fG;
170 newColor.fA = color.fA;
171
172 bool result = dContext->updateCompressedBackendTexture(backendTex, newColor, nullptr, nullptr);
173 // Since we were able to create the compressed texture we should be able to update it.
174 REPORTER_ASSERT(reporter, result);
175
176 SkColor4f expectedNewColors[6] = {newColor, newColor, newColor, newColor, newColor, newColor};
177
178 check_compressed_mipmaps(dContext, img, compression, expectedNewColors, mipMapped, reporter,
179 "colorinit");
180 check_readback(dContext, std::move(img), compression, newColor, reporter, "solid readback");
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500181
Robert Phillipsc8ae4942020-07-20 10:56:01 -0400182 dContext->deleteBackendTexture(backendTex);
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500183}
184
Robert Phillips07f0e412020-01-17 15:20:00 -0500185// Create compressed data pulling the color for each mipmap level from 'levelColors'.
Robert Phillipsca0f8372019-12-20 09:57:41 -0500186static std::unique_ptr<const char[]> make_compressed_data(SkImage::CompressionType compression,
187 SkColor4f levelColors[6],
Brian Salomon7e67dca2020-07-21 09:27:25 -0400188 GrMipmapped mipMapped) {
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500189 SkISize dimensions { 32, 32 };
190
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500191 int numMipLevels = 1;
Brian Salomon7e67dca2020-07-21 09:27:25 -0400192 if (mipMapped == GrMipmapped::kYes) {
Mike Reed13711eb2020-07-14 17:16:32 -0400193 numMipLevels = SkMipmap::ComputeLevelCount(dimensions.width(), dimensions.height()) + 1;
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500194 }
195
Robert Phillipsca0f8372019-12-20 09:57:41 -0500196 SkTArray<size_t> mipMapOffsets(numMipLevels);
197
Robert Phillips99dead92020-01-27 16:11:57 -0500198 size_t dataSize = SkCompressedDataSize(compression, dimensions, &mipMapOffsets,
Brian Salomon7e67dca2020-07-21 09:27:25 -0400199 mipMapped == GrMipmapped::kYes);
Robert Phillipsca0f8372019-12-20 09:57:41 -0500200 char* data = new char[dataSize];
201
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500202 for (int level = 0; level < numMipLevels; ++level) {
Robert Phillipsca0f8372019-12-20 09:57:41 -0500203 // We have to do this a level at a time bc we might have a different color for
204 // each level
205 GrFillInCompressedData(compression, dimensions,
Brian Salomon7e67dca2020-07-21 09:27:25 -0400206 GrMipmapped::kNo, &data[mipMapOffsets[level]], levelColors[level]);
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500207
Brian Osman788b9162020-02-07 10:36:46 -0500208 dimensions = {std::max(1, dimensions.width() /2), std::max(1, dimensions.height()/2)};
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500209 }
210
211 return std::unique_ptr<const char[]>(data);
212}
213
Robert Phillips07f0e412020-01-17 15:20:00 -0500214// Verify that we can initialize a compressed backend texture with data (esp.
215// the mipmap levels).
Robert Phillipsc8ae4942020-07-20 10:56:01 -0400216static void test_compressed_data_init(GrDirectContext* dContext,
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500217 skiatest::Reporter* reporter,
Robert Phillipsc8ae4942020-07-20 10:56:01 -0400218 std::function<GrBackendTexture (GrDirectContext*,
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500219 const char* data,
220 size_t dataSize,
Brian Salomon7e67dca2020-07-21 09:27:25 -0400221 GrMipmapped)> create,
Robert Phillipsca0f8372019-12-20 09:57:41 -0500222 SkImage::CompressionType compression,
Brian Salomon7e67dca2020-07-21 09:27:25 -0400223 GrMipmapped mipMapped) {
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500224
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500225 SkColor4f expectedColors[6] = {
226 { 1.0f, 0.0f, 0.0f, 1.0f }, // R
227 { 0.0f, 1.0f, 0.0f, 1.0f }, // G
228 { 0.0f, 0.0f, 1.0f, 1.0f }, // B
229 { 0.0f, 1.0f, 1.0f, 1.0f }, // C
230 { 1.0f, 0.0f, 1.0f, 1.0f }, // M
231 { 1.0f, 1.0f, 0.0f, 1.0f }, // Y
232 };
233
Robert Phillipsca0f8372019-12-20 09:57:41 -0500234 std::unique_ptr<const char[]> data(make_compressed_data(compression, expectedColors,
235 mipMapped));
Robert Phillips99dead92020-01-27 16:11:57 -0500236 size_t dataSize = SkCompressedDataSize(compression, { 32, 32 }, nullptr,
Brian Salomon7e67dca2020-07-21 09:27:25 -0400237 mipMapped == GrMipmapped::kYes);
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500238
Robert Phillipsc8ae4942020-07-20 10:56:01 -0400239 GrBackendTexture backendTex = create(dContext, data.get(), dataSize, mipMapped);
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500240 if (!backendTex.isValid()) {
241 return;
242 }
243
Robert Phillipsc8ae4942020-07-20 10:56:01 -0400244 sk_sp<SkImage> img = create_image(dContext, backendTex);
Robert Phillips07f0e412020-01-17 15:20:00 -0500245 if (!img) {
246 return;
247 }
248
Robert Phillipsc8ae4942020-07-20 10:56:01 -0400249 check_compressed_mipmaps(dContext, img, compression, expectedColors,
Robert Phillips07f0e412020-01-17 15:20:00 -0500250 mipMapped, reporter, "pixmap");
Greg Daniel95afafb2020-07-22 12:09:26 -0400251 check_readback(dContext, img, compression, expectedColors[0], reporter, "data readback");
252
253 SkColor4f expectedColorsNew[6] = {
254 {1.0f, 1.0f, 0.0f, 1.0f}, // Y
255 {1.0f, 0.0f, 0.0f, 1.0f}, // R
256 {0.0f, 1.0f, 0.0f, 1.0f}, // G
257 {0.0f, 0.0f, 1.0f, 1.0f}, // B
258 {0.0f, 1.0f, 1.0f, 1.0f}, // C
259 {1.0f, 0.0f, 1.0f, 1.0f}, // M
260 };
261
262 std::unique_ptr<const char[]> dataNew(
263 make_compressed_data(compression, expectedColorsNew, mipMapped));
264 size_t dataNewSize =
265 SkCompressedDataSize(compression, {32, 32}, nullptr, mipMapped == GrMipMapped::kYes);
266
267 bool result = dContext->updateCompressedBackendTexture(backendTex, dataNew.get(), dataNewSize,
268 nullptr, nullptr);
269 // Since we were able to create the compressed texture we should be able to update it.
270 REPORTER_ASSERT(reporter, result);
271
272 check_compressed_mipmaps(dContext, img, compression, expectedColorsNew, mipMapped, reporter,
273 "pixmap");
274 check_readback(dContext, std::move(img), compression, expectedColorsNew[0], reporter,
Robert Phillips07f0e412020-01-17 15:20:00 -0500275 "data readback");
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500276
Robert Phillipsc8ae4942020-07-20 10:56:01 -0400277 dContext->deleteBackendTexture(backendTex);
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500278}
279
280DEF_GPUTEST_FOR_RENDERING_CONTEXTS(CompressedBackendAllocationTest, reporter, ctxInfo) {
Robert Phillipsc8ae4942020-07-20 10:56:01 -0400281 auto dContext = ctxInfo.directContext();
282 const GrCaps* caps = dContext->priv().caps();
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500283
284 struct {
285 SkImage::CompressionType fCompression;
286 SkColor4f fColor;
287 } combinations[] = {
Robert Phillipsc558f722020-01-13 13:02:26 -0500288 { SkImage::CompressionType::kETC2_RGB8_UNORM, SkColors::kRed },
Robert Phillips07f0e412020-01-17 15:20:00 -0500289 { SkImage::CompressionType::kBC1_RGB8_UNORM, SkColors::kBlue },
Robert Phillipsb0855272020-01-15 12:56:52 -0500290 { SkImage::CompressionType::kBC1_RGBA8_UNORM, SkColors::kTransparent },
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500291 };
292
293 for (auto combo : combinations) {
Robert Phillipsc8ae4942020-07-20 10:56:01 -0400294 GrBackendFormat format = dContext->compressedBackendFormat(combo.fCompression);
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500295 if (!format.isValid()) {
296 continue;
297 }
298
299 if (!caps->isFormatTexturable(format)) {
300 continue;
301 }
302
Brian Salomon7e67dca2020-07-21 09:27:25 -0400303 for (auto mipMapped : { GrMipmapped::kNo, GrMipmapped::kYes }) {
Brian Salomon69100f02020-07-21 10:49:25 -0400304 if (GrMipmapped::kYes == mipMapped && !caps->mipmapSupport()) {
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500305 continue;
306 }
307
308 // color initialized
309 {
Robert Phillipsc8ae4942020-07-20 10:56:01 -0400310 auto createWithColorMtd = [format](GrDirectContext* dContext,
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500311 const SkColor4f& color,
Brian Salomon7e67dca2020-07-21 09:27:25 -0400312 GrMipmapped mipMapped) {
Robert Phillipsc8ae4942020-07-20 10:56:01 -0400313 return dContext->createCompressedBackendTexture(32, 32, format, color,
314 mipMapped, GrProtected::kNo);
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500315 };
316
Robert Phillipsc8ae4942020-07-20 10:56:01 -0400317 test_compressed_color_init(dContext, reporter, createWithColorMtd,
Robert Phillips07f0e412020-01-17 15:20:00 -0500318 combo.fColor, combo.fCompression, mipMapped);
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500319 }
320
321 // data initialized
322 {
Robert Phillipsc8ae4942020-07-20 10:56:01 -0400323 auto createWithDataMtd = [format](GrDirectContext* dContext,
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500324 const char* data, size_t dataSize,
Brian Salomon7e67dca2020-07-21 09:27:25 -0400325 GrMipmapped mipMapped) {
Robert Phillipsc8ae4942020-07-20 10:56:01 -0400326 return dContext->createCompressedBackendTexture(32, 32, format, data, dataSize,
327 mipMapped, GrProtected::kNo);
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500328 };
329
Robert Phillipsc8ae4942020-07-20 10:56:01 -0400330 test_compressed_data_init(dContext, reporter, createWithDataMtd,
Robert Phillipsca0f8372019-12-20 09:57:41 -0500331 combo.fCompression, mipMapped);
Robert Phillips3e5e2f22019-12-19 11:19:16 -0500332 }
333
334 }
335 }
336}