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