2 * Copyright 2019 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "include/core/SkCanvas.h"
9 #include "include/core/SkColorSpace.h"
10 #include "include/gpu/GrDirectContext.h"
11 #include "include/gpu/GrRecordingContext.h"
12 #include "src/core/SkAutoPixmapStorage.h"
13 #include "src/core/SkCompressedDataUtils.h"
14 #include "src/core/SkMipmap.h"
15 #include "src/core/SkPaintPriv.h"
16 #include "src/gpu/ganesh/GrBackendUtils.h"
17 #include "src/gpu/ganesh/GrDirectContextPriv.h"
18 #include "src/image/SkImage_Base.h"
19 #include "tests/Test.h"
20 #include "tests/TestUtils.h"
21 #include "tools/ToolUtils.h"
23 // Just verify that 'actual' is entirely 'expected'
24 static void check_solid_pixmap(skiatest::Reporter* reporter,
25 const SkColor4f& expected, const SkPixmap& actual,
26 const char* label0, const char* label1, const char* label2) {
27 const float tols[4] = { 0.01f, 0.01f, 0.01f, 0.01f };
29 auto error = std::function<ComparePixmapsErrorReporter>(
30 [reporter, label0, label1, label2](int x, int y, const float diffs[4]) {
31 SkASSERT(x >= 0 && y >= 0);
32 ERRORF(reporter, "%s %s %s - mismatch at %d, %d (%f, %f, %f %f)",
33 label0, label1, label2, x, y,
34 diffs[0], diffs[1], diffs[2], diffs[3]);
37 CheckSolidPixels(expected, actual, tols, error);
40 // Create an SkImage to wrap 'backendTex'
41 sk_sp<SkImage> create_image(GrDirectContext* dContext, const GrBackendTexture& backendTex) {
42 SkImage::CompressionType compression =
43 GrBackendFormatToCompressionType(backendTex.getBackendFormat());
45 SkAlphaType at = SkCompressionTypeIsOpaque(compression) ? kOpaque_SkAlphaType
46 : kPremul_SkAlphaType;
48 return SkImage::MakeFromCompressedTexture(dContext,
50 kTopLeft_GrSurfaceOrigin,
55 // Draw the compressed backend texture (wrapped in an SkImage) into an RGBA surface, attempting
56 // to access all the mipMap levels.
57 static void check_compressed_mipmaps(GrRecordingContext* rContext, sk_sp<SkImage> img,
58 SkImage::CompressionType compressionType,
59 const SkColor4f expectedColors[6],
60 GrMipmapped mipmapped,
61 skiatest::Reporter* reporter, const char* label) {
63 SkImageInfo readbackSurfaceII = SkImageInfo::Make(32, 32, kRGBA_8888_SkColorType,
66 sk_sp<SkSurface> surf = SkSurface::MakeRenderTarget(rContext,
69 kTopLeft_GrSurfaceOrigin,
75 SkCanvas* canvas = surf->getCanvas();
77 // Given that we bias LOD selection with MIP maps, hitting a level exactly using
78 // SkMipmap::kLinear is difficult so we use kNearest.
79 const SkSamplingOptions sampling(SkFilterMode::kLinear,
80 SkMipmapMode::kNearest);
82 p.setBlendMode(SkBlendMode::kSrc);
85 if (mipmapped == GrMipmapped::kYes) {
86 numMipLevels = SkMipmap::ComputeLevelCount(32, 32)+1;
89 for (int i = 0, rectSize = 32; i < numMipLevels; ++i, rectSize /= 2) {
90 SkASSERT(rectSize >= 1);
92 canvas->clear(SK_ColorTRANSPARENT);
94 SkRect r = SkRect::MakeWH(rectSize, rectSize);
95 canvas->drawImageRect(img, r, sampling, &p);
97 SkImageInfo readbackII = SkImageInfo::Make(rectSize, rectSize,
98 kRGBA_8888_SkColorType,
99 kUnpremul_SkAlphaType);
100 SkAutoPixmapStorage actual2;
101 SkAssertResult(actual2.tryAlloc(readbackII));
102 actual2.erase(SkColors::kTransparent);
104 bool result = surf->readPixels(actual2, 0, 0);
105 REPORTER_ASSERT(reporter, result);
108 str.appendf("mip-level %d", i);
110 check_solid_pixmap(reporter, expectedColors[i], actual2,
111 GrCompressionTypeToStr(compressionType), label, str.c_str());
115 // Verify that we can readback from a compressed texture
116 static void check_readback(GrDirectContext* dContext, sk_sp<SkImage> img,
117 SkImage::CompressionType compressionType,
118 const SkColor4f& expectedColor,
119 skiatest::Reporter* reporter, const char* label) {
120 #ifdef SK_BUILD_FOR_IOS
121 // reading back ETC2 is broken on Metal/iOS (skbug.com/9839)
122 if (dContext->backend() == GrBackendApi::kMetal) {
127 SkAutoPixmapStorage actual;
129 SkImageInfo readBackII = SkImageInfo::Make(img->width(), img->height(),
130 kRGBA_8888_SkColorType,
131 kUnpremul_SkAlphaType);
133 SkAssertResult(actual.tryAlloc(readBackII));
134 actual.erase(SkColors::kTransparent);
136 bool result = img->readPixels(dContext, actual, 0, 0);
137 REPORTER_ASSERT(reporter, result);
139 check_solid_pixmap(reporter, expectedColor, actual,
140 GrCompressionTypeToStr(compressionType), label, "");
143 // Test initialization of compressed GrBackendTextures to a specific color
144 static void test_compressed_color_init(GrDirectContext* dContext,
145 skiatest::Reporter* reporter,
146 std::function<GrBackendTexture (GrDirectContext*,
148 GrMipmapped)> create,
149 const SkColor4f& color,
150 SkImage::CompressionType compression,
151 GrMipmapped mipmapped) {
152 GrBackendTexture backendTex = create(dContext, color, mipmapped);
153 if (!backendTex.isValid()) {
157 sk_sp<SkImage> img = create_image(dContext, backendTex);
162 SkColor4f expectedColors[6] = { color, color, color, color, color, color };
164 check_compressed_mipmaps(dContext, img, compression, expectedColors, mipmapped,
165 reporter, "colorinit");
166 check_readback(dContext, img, compression, color, reporter, "solid readback");
169 newColor.fR = color.fB;
170 newColor.fG = color.fR;
171 newColor.fB = color.fG;
172 newColor.fA = color.fA;
174 bool result = dContext->updateCompressedBackendTexture(backendTex, newColor, nullptr, nullptr);
175 // Since we were able to create the compressed texture we should be able to update it.
176 REPORTER_ASSERT(reporter, result);
178 SkColor4f expectedNewColors[6] = {newColor, newColor, newColor, newColor, newColor, newColor};
180 check_compressed_mipmaps(dContext, img, compression, expectedNewColors, mipmapped, reporter,
182 check_readback(dContext, std::move(img), compression, newColor, reporter, "solid readback");
184 dContext->deleteBackendTexture(backendTex);
187 // Create compressed data pulling the color for each mipmap level from 'levelColors'.
188 static std::unique_ptr<const char[]> make_compressed_data(SkImage::CompressionType compression,
189 SkColor4f levelColors[6],
190 GrMipmapped mipmapped) {
191 SkISize dimensions { 32, 32 };
193 int numMipLevels = 1;
194 if (mipmapped == GrMipmapped::kYes) {
195 numMipLevels = SkMipmap::ComputeLevelCount(dimensions.width(), dimensions.height()) + 1;
198 SkTArray<size_t> mipMapOffsets(numMipLevels);
200 size_t dataSize = SkCompressedDataSize(compression, dimensions, &mipMapOffsets,
201 mipmapped == GrMipmapped::kYes);
202 char* data = new char[dataSize];
204 for (int level = 0; level < numMipLevels; ++level) {
205 // We have to do this a level at a time bc we might have a different color for
207 GrFillInCompressedData(compression, dimensions,
208 GrMipmapped::kNo, &data[mipMapOffsets[level]], levelColors[level]);
210 dimensions = {std::max(1, dimensions.width() /2), std::max(1, dimensions.height()/2)};
213 return std::unique_ptr<const char[]>(data);
216 // Verify that we can initialize a compressed backend texture with data (esp.
217 // the mipmap levels).
218 static void test_compressed_data_init(GrDirectContext* dContext,
219 skiatest::Reporter* reporter,
220 std::function<GrBackendTexture (GrDirectContext*,
223 GrMipmapped)> create,
224 SkImage::CompressionType compression,
225 GrMipmapped mipmapped) {
227 SkColor4f expectedColors[6] = {
228 { 1.0f, 0.0f, 0.0f, 1.0f }, // R
229 { 0.0f, 1.0f, 0.0f, 1.0f }, // G
230 { 0.0f, 0.0f, 1.0f, 1.0f }, // B
231 { 0.0f, 1.0f, 1.0f, 1.0f }, // C
232 { 1.0f, 0.0f, 1.0f, 1.0f }, // M
233 { 1.0f, 1.0f, 0.0f, 1.0f }, // Y
236 std::unique_ptr<const char[]> data(make_compressed_data(compression, expectedColors,
238 size_t dataSize = SkCompressedDataSize(compression, { 32, 32 }, nullptr,
239 mipmapped == GrMipmapped::kYes);
241 GrBackendTexture backendTex = create(dContext, data.get(), dataSize, mipmapped);
242 if (!backendTex.isValid()) {
246 sk_sp<SkImage> img = create_image(dContext, backendTex);
251 check_compressed_mipmaps(dContext, img, compression, expectedColors,
252 mipmapped, reporter, "pixmap");
253 check_readback(dContext, img, compression, expectedColors[0], reporter, "data readback");
255 SkColor4f expectedColorsNew[6] = {
256 {1.0f, 1.0f, 0.0f, 1.0f}, // Y
257 {1.0f, 0.0f, 0.0f, 1.0f}, // R
258 {0.0f, 1.0f, 0.0f, 1.0f}, // G
259 {0.0f, 0.0f, 1.0f, 1.0f}, // B
260 {0.0f, 1.0f, 1.0f, 1.0f}, // C
261 {1.0f, 0.0f, 1.0f, 1.0f}, // M
264 std::unique_ptr<const char[]> dataNew(
265 make_compressed_data(compression, expectedColorsNew, mipmapped));
267 SkCompressedDataSize(compression, {32, 32}, nullptr, mipmapped == GrMipmapped::kYes);
269 bool result = dContext->updateCompressedBackendTexture(backendTex, dataNew.get(), dataNewSize,
271 // Since we were able to create the compressed texture we should be able to update it.
272 REPORTER_ASSERT(reporter, result);
274 check_compressed_mipmaps(dContext, img, compression, expectedColorsNew, mipmapped, reporter,
276 check_readback(dContext, std::move(img), compression, expectedColorsNew[0], reporter,
279 dContext->deleteBackendTexture(backendTex);
282 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(CompressedBackendAllocationTest, reporter, ctxInfo) {
283 auto dContext = ctxInfo.directContext();
284 const GrCaps* caps = dContext->priv().caps();
287 SkImage::CompressionType fCompression;
290 { SkImage::CompressionType::kETC2_RGB8_UNORM, SkColors::kRed },
291 { SkImage::CompressionType::kBC1_RGB8_UNORM, SkColors::kBlue },
292 { SkImage::CompressionType::kBC1_RGBA8_UNORM, SkColors::kTransparent },
295 for (auto combo : combinations) {
296 GrBackendFormat format = dContext->compressedBackendFormat(combo.fCompression);
297 if (!format.isValid()) {
301 if (!caps->isFormatTexturable(format, GrTextureType::k2D)) {
305 for (auto mipmapped : { GrMipmapped::kNo, GrMipmapped::kYes }) {
306 if (GrMipmapped::kYes == mipmapped && !caps->mipmapSupport()) {
312 auto createWithColorMtd = [format](GrDirectContext* dContext,
313 const SkColor4f& color,
314 GrMipmapped mipmapped) {
315 return dContext->createCompressedBackendTexture(32, 32, format, color,
316 mipmapped, GrProtected::kNo);
319 test_compressed_color_init(dContext, reporter, createWithColorMtd,
320 combo.fColor, combo.fCompression, mipmapped);
325 auto createWithDataMtd = [format](GrDirectContext* dContext,
326 const char* data, size_t dataSize,
327 GrMipmapped mipmapped) {
328 return dContext->createCompressedBackendTexture(32, 32, format, data, dataSize,
329 mipmapped, GrProtected::kNo);
332 test_compressed_data_init(dContext, reporter, createWithDataMtd,
333 combo.fCompression, mipmapped);