SurfaceTest.cpp revision 9594da111dc1c36c1912eb61207aaa54c17ea550
1/* 2 * Copyright 2013 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 "SkCanvas.h" 9#include "SkData.h" 10#include "SkImageEncoder.h" 11#include "SkRRect.h" 12#include "SkSurface.h" 13#include "SkUtils.h" 14#include "Test.h" 15 16#if SK_SUPPORT_GPU 17#include "GrContextFactory.h" 18#else 19class GrContextFactory; 20class GrContext; 21#endif 22 23enum SurfaceType { 24 kRaster_SurfaceType, 25 kRasterDirect_SurfaceType, 26 kGpu_SurfaceType, 27 kGpuScratch_SurfaceType, 28}; 29 30static void release_storage(void* pixels, void* context) { 31 SkASSERT(pixels == context); 32 sk_free(pixels); 33} 34 35static SkSurface* createSurface(SurfaceType surfaceType, GrContext* context, 36 SkImageInfo* requestedInfo = NULL) { 37 static const SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10); 38 39 if (requestedInfo) { 40 *requestedInfo = info; 41 } 42 43 switch (surfaceType) { 44 case kRaster_SurfaceType: 45 return SkSurface::NewRaster(info); 46 case kRasterDirect_SurfaceType: { 47 const size_t rowBytes = info.minRowBytes(); 48 void* storage = sk_malloc_throw(info.getSafeSize(rowBytes)); 49 return SkSurface::NewRasterDirectReleaseProc(info, storage, rowBytes, 50 release_storage, storage); 51 } 52 case kGpu_SurfaceType: 53#if SK_SUPPORT_GPU 54 return context ? SkSurface::NewRenderTarget(context, info) : NULL; 55#endif 56 break; 57 case kGpuScratch_SurfaceType: 58#if SK_SUPPORT_GPU 59 return context ? SkSurface::NewScratchRenderTarget(context, info) : NULL; 60#endif 61 break; 62 } 63 return NULL; 64} 65 66enum ImageType { 67 kRasterCopy_ImageType, 68 kRasterData_ImageType, 69 kGpu_ImageType, 70 kCodec_ImageType, 71}; 72 73static void test_image(skiatest::Reporter* reporter) { 74 SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1); 75 size_t rowBytes = info.minRowBytes(); 76 size_t size = info.getSafeSize(rowBytes); 77 SkData* data = SkData::NewUninitialized(size); 78 79 REPORTER_ASSERT(reporter, 1 == data->getRefCnt()); 80 SkImage* image = SkImage::NewRasterData(info, data, rowBytes); 81 REPORTER_ASSERT(reporter, 2 == data->getRefCnt()); 82 image->unref(); 83 REPORTER_ASSERT(reporter, 1 == data->getRefCnt()); 84 data->unref(); 85} 86 87static SkImage* createImage(ImageType imageType, GrContext* context, 88 SkColor color) { 89 const SkPMColor pmcolor = SkPreMultiplyColor(color); 90 const SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10); 91 const size_t rowBytes = info.minRowBytes(); 92 const size_t size = rowBytes * info.height(); 93 94 SkAutoTUnref<SkData> data(SkData::NewUninitialized(size)); 95 void* addr = data->writable_data(); 96 sk_memset32((SkPMColor*)addr, pmcolor, SkToInt(size >> 2)); 97 98 switch (imageType) { 99 case kRasterCopy_ImageType: 100 return SkImage::NewRasterCopy(info, addr, rowBytes); 101 case kRasterData_ImageType: 102 return SkImage::NewRasterData(info, data, rowBytes); 103 case kGpu_ImageType: 104 return NULL; // TODO 105 case kCodec_ImageType: { 106 SkBitmap bitmap; 107 bitmap.installPixels(info, addr, rowBytes); 108 SkAutoTUnref<SkData> src( 109 SkImageEncoder::EncodeData(bitmap, SkImageEncoder::kPNG_Type, 110 100)); 111 return SkImage::NewEncodedData(src); 112 } 113 } 114 SkASSERT(false); 115 return NULL; 116} 117 118static void test_imagepeek(skiatest::Reporter* reporter) { 119 static const struct { 120 ImageType fType; 121 bool fPeekShouldSucceed; 122 } gRec[] = { 123 { kRasterCopy_ImageType, true }, 124 { kRasterData_ImageType, true }, 125 { kGpu_ImageType, false }, 126 { kCodec_ImageType, false }, 127 }; 128 129 const SkColor color = SK_ColorRED; 130 const SkPMColor pmcolor = SkPreMultiplyColor(color); 131 132 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) { 133 SkImageInfo info; 134 size_t rowBytes; 135 136 SkAutoTUnref<SkImage> image(createImage(gRec[i].fType, NULL, color)); 137 if (!image.get()) { 138 continue; // gpu may not be enabled 139 } 140 const void* addr = image->peekPixels(&info, &rowBytes); 141 bool success = SkToBool(addr); 142 REPORTER_ASSERT(reporter, gRec[i].fPeekShouldSucceed == success); 143 if (success) { 144 REPORTER_ASSERT(reporter, 10 == info.width()); 145 REPORTER_ASSERT(reporter, 10 == info.height()); 146 REPORTER_ASSERT(reporter, kN32_SkColorType == info.colorType()); 147 REPORTER_ASSERT(reporter, kPremul_SkAlphaType == info.alphaType() || 148 kOpaque_SkAlphaType == info.alphaType()); 149 REPORTER_ASSERT(reporter, info.minRowBytes() <= rowBytes); 150 REPORTER_ASSERT(reporter, pmcolor == *(const SkPMColor*)addr); 151 } 152 } 153} 154 155static void test_canvaspeek(skiatest::Reporter* reporter, 156 GrContextFactory* factory) { 157 static const struct { 158 SurfaceType fType; 159 bool fPeekShouldSucceed; 160 } gRec[] = { 161 { kRaster_SurfaceType, true }, 162 { kRasterDirect_SurfaceType, true }, 163#if SK_SUPPORT_GPU 164 { kGpu_SurfaceType, false }, 165 { kGpuScratch_SurfaceType, false }, 166#endif 167 }; 168 169 const SkColor color = SK_ColorRED; 170 const SkPMColor pmcolor = SkPreMultiplyColor(color); 171 172 int cnt; 173#if SK_SUPPORT_GPU 174 cnt = GrContextFactory::kGLContextTypeCnt; 175#else 176 cnt = 1; 177#endif 178 179 for (int i= 0; i < cnt; ++i) { 180 GrContext* context = NULL; 181#if SK_SUPPORT_GPU 182 GrContextFactory::GLContextType glCtxType = (GrContextFactory::GLContextType) i; 183 if (!GrContextFactory::IsRenderingGLContext(glCtxType)) { 184 continue; 185 } 186 context = factory->get(glCtxType); 187 188 if (NULL == context) { 189 continue; 190 } 191#endif 192 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) { 193 SkImageInfo info, requestInfo; 194 size_t rowBytes; 195 196 SkAutoTUnref<SkSurface> surface(createSurface(gRec[i].fType, context, 197 &requestInfo)); 198 surface->getCanvas()->clear(color); 199 200 const void* addr = surface->getCanvas()->peekPixels(&info, &rowBytes); 201 bool success = SkToBool(addr); 202 REPORTER_ASSERT(reporter, gRec[i].fPeekShouldSucceed == success); 203 204 SkImageInfo info2; 205 size_t rb2; 206 const void* addr2 = surface->peekPixels(&info2, &rb2); 207 208 if (success) { 209 REPORTER_ASSERT(reporter, requestInfo == info); 210 REPORTER_ASSERT(reporter, requestInfo.minRowBytes() <= rowBytes); 211 REPORTER_ASSERT(reporter, pmcolor == *(const SkPMColor*)addr); 212 213 REPORTER_ASSERT(reporter, addr2 == addr); 214 REPORTER_ASSERT(reporter, info2 == info); 215 REPORTER_ASSERT(reporter, rb2 == rowBytes); 216 } else { 217 REPORTER_ASSERT(reporter, NULL == addr2); 218 } 219 } 220 } 221} 222 223static void TestSurfaceCopyOnWrite(skiatest::Reporter* reporter, SurfaceType surfaceType, 224 GrContext* context) { 225 // Verify that the right canvas commands trigger a copy on write 226 SkSurface* surface = createSurface(surfaceType, context); 227 SkAutoTUnref<SkSurface> aur_surface(surface); 228 SkCanvas* canvas = surface->getCanvas(); 229 230 const SkRect testRect = 231 SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(0), 232 SkIntToScalar(4), SkIntToScalar(5)); 233 SkMatrix testMatrix; 234 testMatrix.reset(); 235 testMatrix.setScale(SkIntToScalar(2), SkIntToScalar(3)); 236 237 SkPath testPath; 238 testPath.addRect(SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(0), 239 SkIntToScalar(2), SkIntToScalar(1))); 240 241 const SkIRect testIRect = SkIRect::MakeXYWH(0, 0, 2, 1); 242 243 SkRegion testRegion; 244 testRegion.setRect(testIRect); 245 246 247 const SkColor testColor = 0x01020304; 248 const SkPaint testPaint; 249 const SkPoint testPoints[3] = { 250 {SkIntToScalar(0), SkIntToScalar(0)}, 251 {SkIntToScalar(2), SkIntToScalar(1)}, 252 {SkIntToScalar(0), SkIntToScalar(2)} 253 }; 254 const size_t testPointCount = 3; 255 256 SkBitmap testBitmap; 257 testBitmap.allocN32Pixels(10, 10); 258 testBitmap.eraseColor(0); 259 260 SkRRect testRRect; 261 testRRect.setRectXY(testRect, SK_Scalar1, SK_Scalar1); 262 263 SkString testText("Hello World"); 264 const SkPoint testPoints2[] = { 265 { SkIntToScalar(0), SkIntToScalar(1) }, 266 { SkIntToScalar(1), SkIntToScalar(1) }, 267 { SkIntToScalar(2), SkIntToScalar(1) }, 268 { SkIntToScalar(3), SkIntToScalar(1) }, 269 { SkIntToScalar(4), SkIntToScalar(1) }, 270 { SkIntToScalar(5), SkIntToScalar(1) }, 271 { SkIntToScalar(6), SkIntToScalar(1) }, 272 { SkIntToScalar(7), SkIntToScalar(1) }, 273 { SkIntToScalar(8), SkIntToScalar(1) }, 274 { SkIntToScalar(9), SkIntToScalar(1) }, 275 { SkIntToScalar(10), SkIntToScalar(1) }, 276 }; 277 278#define EXPECT_COPY_ON_WRITE(command) \ 279 { \ 280 SkImage* imageBefore = surface->newImageSnapshot(); \ 281 SkAutoTUnref<SkImage> aur_before(imageBefore); \ 282 canvas-> command ; \ 283 SkImage* imageAfter = surface->newImageSnapshot(); \ 284 SkAutoTUnref<SkImage> aur_after(imageAfter); \ 285 REPORTER_ASSERT(reporter, imageBefore != imageAfter); \ 286 } 287 288 EXPECT_COPY_ON_WRITE(clear(testColor)) 289 EXPECT_COPY_ON_WRITE(drawPaint(testPaint)) 290 EXPECT_COPY_ON_WRITE(drawPoints(SkCanvas::kPoints_PointMode, testPointCount, testPoints, \ 291 testPaint)) 292 EXPECT_COPY_ON_WRITE(drawOval(testRect, testPaint)) 293 EXPECT_COPY_ON_WRITE(drawRect(testRect, testPaint)) 294 EXPECT_COPY_ON_WRITE(drawRRect(testRRect, testPaint)) 295 EXPECT_COPY_ON_WRITE(drawPath(testPath, testPaint)) 296 EXPECT_COPY_ON_WRITE(drawBitmap(testBitmap, 0, 0)) 297 EXPECT_COPY_ON_WRITE(drawBitmapRect(testBitmap, NULL, testRect)) 298 EXPECT_COPY_ON_WRITE(drawBitmapMatrix(testBitmap, testMatrix, NULL)) 299 EXPECT_COPY_ON_WRITE(drawBitmapNine(testBitmap, testIRect, testRect, NULL)) 300 EXPECT_COPY_ON_WRITE(drawSprite(testBitmap, 0, 0, NULL)) 301 EXPECT_COPY_ON_WRITE(drawText(testText.c_str(), testText.size(), 0, 1, testPaint)) 302 EXPECT_COPY_ON_WRITE(drawPosText(testText.c_str(), testText.size(), testPoints2, \ 303 testPaint)) 304 EXPECT_COPY_ON_WRITE(drawTextOnPath(testText.c_str(), testText.size(), testPath, NULL, \ 305 testPaint)) 306} 307 308static void TestSurfaceWritableAfterSnapshotRelease(skiatest::Reporter* reporter, 309 SurfaceType surfaceType, 310 GrContext* context) { 311 // This test succeeds by not triggering an assertion. 312 // The test verifies that the surface remains writable (usable) after 313 // acquiring and releasing a snapshot without triggering a copy on write. 314 SkAutoTUnref<SkSurface> surface(createSurface(surfaceType, context)); 315 SkCanvas* canvas = surface->getCanvas(); 316 canvas->clear(1); 317 surface->newImageSnapshot()->unref(); // Create and destroy SkImage 318 canvas->clear(2); // Must not assert internally 319} 320 321#if SK_SUPPORT_GPU 322static void TestSurfaceInCache(skiatest::Reporter* reporter, 323 SurfaceType surfaceType, 324 GrContext* context) { 325 context->freeGpuResources(); 326 int resourceCount; 327 328 context->getResourceCacheUsage(&resourceCount, NULL); 329 REPORTER_ASSERT(reporter, 0 == resourceCount); 330 SkAutoTUnref<SkSurface> surface(createSurface(surfaceType, context)); 331 // Note: the stencil buffer is always cached, so kGpu_SurfaceType uses 332 // one cached resource, and kGpuScratch_SurfaceType uses two. 333 int expectedCachedResources = surfaceType == kGpuScratch_SurfaceType ? 2 : 1; 334 context->getResourceCacheUsage(&resourceCount, NULL); 335 REPORTER_ASSERT(reporter, expectedCachedResources == resourceCount); 336 337 // Verify that all the cached resources are locked in cache. 338 context->freeGpuResources(); 339 context->getResourceCacheUsage(&resourceCount, NULL); 340 REPORTER_ASSERT(reporter, expectedCachedResources == resourceCount); 341 342 // Verify that all the cached resources are unlocked upon surface release 343 surface.reset(0); 344 context->freeGpuResources(); 345 context->getResourceCacheUsage(&resourceCount, NULL); 346 REPORTER_ASSERT(reporter, 0 == resourceCount); 347} 348 349static void Test_crbug263329(skiatest::Reporter* reporter, 350 SurfaceType surfaceType, 351 GrContext* context) { 352 // This is a regression test for crbug.com/263329 353 // Bug was caused by onCopyOnWrite releasing the old surface texture 354 // back to the scratch texture pool even though the texture is used 355 // by and active SkImage_Gpu. 356 SkAutoTUnref<SkSurface> surface1(createSurface(surfaceType, context)); 357 SkAutoTUnref<SkSurface> surface2(createSurface(surfaceType, context)); 358 SkCanvas* canvas1 = surface1->getCanvas(); 359 SkCanvas* canvas2 = surface2->getCanvas(); 360 canvas1->clear(1); 361 SkAutoTUnref<SkImage> image1(surface1->newImageSnapshot()); 362 // Trigger copy on write, new backing is a scratch texture 363 canvas1->clear(2); 364 SkAutoTUnref<SkImage> image2(surface1->newImageSnapshot()); 365 // Trigger copy on write, old backing should not be returned to scratch 366 // pool because it is held by image2 367 canvas1->clear(3); 368 369 canvas2->clear(4); 370 SkAutoTUnref<SkImage> image3(surface2->newImageSnapshot()); 371 // Trigger copy on write on surface2. The new backing store should not 372 // be recycling a texture that is held by an existing image. 373 canvas2->clear(5); 374 SkAutoTUnref<SkImage> image4(surface2->newImageSnapshot()); 375 REPORTER_ASSERT(reporter, image4->getTexture() != image3->getTexture()); 376 // The following assertion checks crbug.com/263329 377 REPORTER_ASSERT(reporter, image4->getTexture() != image2->getTexture()); 378 REPORTER_ASSERT(reporter, image4->getTexture() != image1->getTexture()); 379 REPORTER_ASSERT(reporter, image3->getTexture() != image2->getTexture()); 380 REPORTER_ASSERT(reporter, image3->getTexture() != image1->getTexture()); 381 REPORTER_ASSERT(reporter, image2->getTexture() != image1->getTexture()); 382} 383 384static void TestGetTexture(skiatest::Reporter* reporter, 385 SurfaceType surfaceType, 386 GrContext* context) { 387 SkAutoTUnref<SkSurface> surface(createSurface(surfaceType, context)); 388 SkAutoTUnref<SkImage> image(surface->newImageSnapshot()); 389 GrTexture* texture = image->getTexture(); 390 if (surfaceType == kGpu_SurfaceType || surfaceType == kGpuScratch_SurfaceType) { 391 REPORTER_ASSERT(reporter, texture); 392 REPORTER_ASSERT(reporter, 0 != texture->getTextureHandle()); 393 } else { 394 REPORTER_ASSERT(reporter, NULL == texture); 395 } 396 surface->notifyContentWillChange(SkSurface::kDiscard_ContentChangeMode); 397 REPORTER_ASSERT(reporter, image->getTexture() == texture); 398} 399#endif 400 401static void TestSurfaceNoCanvas(skiatest::Reporter* reporter, 402 SurfaceType surfaceType, 403 GrContext* context, 404 SkSurface::ContentChangeMode mode) { 405 // Verifies the robustness of SkSurface for handling use cases where calls 406 // are made before a canvas is created. 407 { 408 // Test passes by not asserting 409 SkSurface* surface = createSurface(surfaceType, context); 410 SkAutoTUnref<SkSurface> aur_surface(surface); 411 surface->notifyContentWillChange(mode); 412 SkDEBUGCODE(surface->validate();) 413 } 414 { 415 SkSurface* surface = createSurface(surfaceType, context); 416 SkAutoTUnref<SkSurface> aur_surface(surface); 417 SkImage* image1 = surface->newImageSnapshot(); 418 SkAutoTUnref<SkImage> aur_image1(image1); 419 SkDEBUGCODE(image1->validate();) 420 SkDEBUGCODE(surface->validate();) 421 surface->notifyContentWillChange(mode); 422 SkDEBUGCODE(image1->validate();) 423 SkDEBUGCODE(surface->validate();) 424 SkImage* image2 = surface->newImageSnapshot(); 425 SkAutoTUnref<SkImage> aur_image2(image2); 426 SkDEBUGCODE(image2->validate();) 427 SkDEBUGCODE(surface->validate();) 428 REPORTER_ASSERT(reporter, image1 != image2); 429 } 430 431} 432 433DEF_GPUTEST(Surface, reporter, factory) { 434 test_image(reporter); 435 436 TestSurfaceCopyOnWrite(reporter, kRaster_SurfaceType, NULL); 437 TestSurfaceWritableAfterSnapshotRelease(reporter, kRaster_SurfaceType, NULL); 438 TestSurfaceNoCanvas(reporter, kRaster_SurfaceType, NULL, SkSurface::kDiscard_ContentChangeMode); 439 TestSurfaceNoCanvas(reporter, kRaster_SurfaceType, NULL, SkSurface::kRetain_ContentChangeMode); 440 441 test_imagepeek(reporter); 442 test_canvaspeek(reporter, factory); 443 444#if SK_SUPPORT_GPU 445 TestGetTexture(reporter, kRaster_SurfaceType, NULL); 446 if (factory) { 447 for (int i= 0; i < GrContextFactory::kGLContextTypeCnt; ++i) { 448 GrContextFactory::GLContextType glCtxType = (GrContextFactory::GLContextType) i; 449 if (!GrContextFactory::IsRenderingGLContext(glCtxType)) { 450 continue; 451 } 452 GrContext* context = factory->get(glCtxType); 453 if (context) { 454 TestSurfaceInCache(reporter, kGpu_SurfaceType, context); 455 TestSurfaceInCache(reporter, kGpuScratch_SurfaceType, context); 456 Test_crbug263329(reporter, kGpu_SurfaceType, context); 457 Test_crbug263329(reporter, kGpuScratch_SurfaceType, context); 458 TestSurfaceCopyOnWrite(reporter, kGpu_SurfaceType, context); 459 TestSurfaceCopyOnWrite(reporter, kGpuScratch_SurfaceType, context); 460 TestSurfaceWritableAfterSnapshotRelease(reporter, kGpu_SurfaceType, context); 461 TestSurfaceWritableAfterSnapshotRelease(reporter, kGpuScratch_SurfaceType, context); 462 TestSurfaceNoCanvas(reporter, kGpu_SurfaceType, context, SkSurface::kDiscard_ContentChangeMode); 463 TestSurfaceNoCanvas(reporter, kGpuScratch_SurfaceType, context, SkSurface::kDiscard_ContentChangeMode); 464 TestSurfaceNoCanvas(reporter, kGpu_SurfaceType, context, SkSurface::kRetain_ContentChangeMode); 465 TestSurfaceNoCanvas(reporter, kGpuScratch_SurfaceType, context, SkSurface::kRetain_ContentChangeMode); 466 TestGetTexture(reporter, kGpu_SurfaceType, context); 467 TestGetTexture(reporter, kGpuScratch_SurfaceType, context); 468 } 469 } 470 } 471#endif 472} 473