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