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