1 2 /* 3 * Copyright 2011 Google Inc. 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 9 #include "GrContext.h" 10 11 #include "GrAARectRenderer.h" 12 #include "GrAtlasTextContext.h" 13 #include "GrBatch.h" 14 #include "GrBatchFontCache.h" 15 #include "GrBatchTarget.h" 16 #include "GrBatchTest.h" 17 #include "GrDefaultGeoProcFactory.h" 18 #include "GrGpuResource.h" 19 #include "GrGpuResourcePriv.h" 20 #include "GrDrawTargetCaps.h" 21 #include "GrGpu.h" 22 #include "GrIndexBuffer.h" 23 #include "GrInOrderDrawBuffer.h" 24 #include "GrLayerCache.h" 25 #include "GrOvalRenderer.h" 26 #include "GrPathRenderer.h" 27 #include "GrPathUtils.h" 28 #include "GrRenderTargetPriv.h" 29 #include "GrResourceCache.h" 30 #include "GrResourceProvider.h" 31 #include "GrSoftwarePathRenderer.h" 32 #include "GrStencilAndCoverTextContext.h" 33 #include "GrStrokeInfo.h" 34 #include "GrSurfacePriv.h" 35 #include "GrTextBlobCache.h" 36 #include "GrTexturePriv.h" 37 #include "GrTraceMarker.h" 38 #include "GrTracing.h" 39 #include "GrVertices.h" 40 #include "SkDashPathPriv.h" 41 #include "SkConfig8888.h" 42 #include "SkGr.h" 43 #include "SkRRect.h" 44 #include "SkStrokeRec.h" 45 #include "SkTLazy.h" 46 #include "SkTLS.h" 47 #include "SkTraceEvent.h" 48 49 #include "effects/GrConfigConversionEffect.h" 50 #include "effects/GrDashingEffect.h" 51 #include "effects/GrSingleTextureEffect.h" 52 53 #define ASSERT_OWNED_RESOURCE(R) SkASSERT(!(R) || (R)->getContext() == this) 54 #define RETURN_IF_ABANDONED if (!fDrawBuffer) { return; } 55 #define RETURN_FALSE_IF_ABANDONED if (!fDrawBuffer) { return false; } 56 #define RETURN_NULL_IF_ABANDONED if (!fDrawBuffer) { return NULL; } 57 58 class GrContext::AutoCheckFlush { 59 public: 60 AutoCheckFlush(GrContext* context) : fContext(context) { SkASSERT(context); } 61 62 ~AutoCheckFlush() { 63 if (fContext->fFlushToReduceCacheSize) { 64 fContext->flush(); 65 } 66 } 67 68 private: 69 GrContext* fContext; 70 }; 71 72 GrContext* GrContext::Create(GrBackend backend, GrBackendContext backendContext, 73 const Options* opts) { 74 GrContext* context; 75 if (NULL == opts) { 76 context = SkNEW_ARGS(GrContext, (Options())); 77 } else { 78 context = SkNEW_ARGS(GrContext, (*opts)); 79 } 80 81 if (context->init(backend, backendContext)) { 82 return context; 83 } else { 84 context->unref(); 85 return NULL; 86 } 87 } 88 89 static int32_t gNextID = 1; 90 static int32_t next_id() { 91 int32_t id; 92 do { 93 id = sk_atomic_inc(&gNextID); 94 } while (id == SK_InvalidGenID); 95 return id; 96 } 97 98 GrContext::GrContext(const Options& opts) : fOptions(opts), fUniqueID(next_id()) { 99 fGpu = NULL; 100 fResourceCache = NULL; 101 fResourceProvider = NULL; 102 fPathRendererChain = NULL; 103 fSoftwarePathRenderer = NULL; 104 fBatchFontCache = NULL; 105 fDrawBuffer = NULL; 106 fFlushToReduceCacheSize = false; 107 fAARectRenderer = NULL; 108 fOvalRenderer = NULL; 109 fMaxTextureSizeOverride = 1 << 20; 110 } 111 112 bool GrContext::init(GrBackend backend, GrBackendContext backendContext) { 113 SkASSERT(NULL == fGpu); 114 115 fGpu = GrGpu::Create(backend, backendContext, this); 116 if (NULL == fGpu) { 117 return false; 118 } 119 this->initCommon(); 120 return true; 121 } 122 123 void GrContext::initCommon() { 124 fResourceCache = SkNEW(GrResourceCache); 125 fResourceCache->setOverBudgetCallback(OverBudgetCB, this); 126 fResourceProvider = SkNEW_ARGS(GrResourceProvider, (fGpu, fResourceCache)); 127 128 fLayerCache.reset(SkNEW_ARGS(GrLayerCache, (this))); 129 130 fAARectRenderer = SkNEW(GrAARectRenderer); 131 fOvalRenderer = SkNEW(GrOvalRenderer); 132 133 fDidTestPMConversions = false; 134 135 fDrawBuffer = SkNEW_ARGS(GrInOrderDrawBuffer, (this)); 136 137 // GrBatchFontCache will eventually replace GrFontCache 138 fBatchFontCache = SkNEW_ARGS(GrBatchFontCache, (this)); 139 140 fTextBlobCache.reset(SkNEW_ARGS(GrTextBlobCache, (TextBlobCacheOverBudgetCB, this))); 141 } 142 143 GrContext::~GrContext() { 144 if (NULL == fGpu) { 145 return; 146 } 147 148 this->flush(); 149 150 for (int i = 0; i < fCleanUpData.count(); ++i) { 151 (*fCleanUpData[i].fFunc)(this, fCleanUpData[i].fInfo); 152 } 153 154 SkDELETE(fResourceProvider); 155 SkDELETE(fResourceCache); 156 SkDELETE(fBatchFontCache); 157 SkDELETE(fDrawBuffer); 158 159 fAARectRenderer->unref(); 160 fOvalRenderer->unref(); 161 162 fGpu->unref(); 163 SkSafeUnref(fPathRendererChain); 164 SkSafeUnref(fSoftwarePathRenderer); 165 } 166 167 void GrContext::abandonContext() { 168 fResourceProvider->abandon(); 169 // abandon first to so destructors 170 // don't try to free the resources in the API. 171 fResourceCache->abandonAll(); 172 173 fGpu->contextAbandoned(); 174 175 // a path renderer may be holding onto resources that 176 // are now unusable 177 SkSafeSetNull(fPathRendererChain); 178 SkSafeSetNull(fSoftwarePathRenderer); 179 180 SkDELETE(fDrawBuffer); 181 fDrawBuffer = NULL; 182 183 fBatchFontCache->freeAll(); 184 fLayerCache->freeAll(); 185 fTextBlobCache->freeAll(); 186 } 187 188 void GrContext::resetContext(uint32_t state) { 189 fGpu->markContextDirty(state); 190 } 191 192 void GrContext::freeGpuResources() { 193 this->flush(); 194 195 if (fDrawBuffer) { 196 fDrawBuffer->purgeResources(); 197 } 198 199 fBatchFontCache->freeAll(); 200 fLayerCache->freeAll(); 201 // a path renderer may be holding onto resources 202 SkSafeSetNull(fPathRendererChain); 203 SkSafeSetNull(fSoftwarePathRenderer); 204 205 fResourceCache->purgeAllUnlocked(); 206 } 207 208 void GrContext::getResourceCacheUsage(int* resourceCount, size_t* resourceBytes) const { 209 if (resourceCount) { 210 *resourceCount = fResourceCache->getBudgetedResourceCount(); 211 } 212 if (resourceBytes) { 213 *resourceBytes = fResourceCache->getBudgetedResourceBytes(); 214 } 215 } 216 217 GrTextContext* GrContext::createTextContext(GrRenderTarget* renderTarget, 218 SkGpuDevice* gpuDevice, 219 const SkDeviceProperties& 220 leakyProperties, 221 bool enableDistanceFieldFonts) { 222 if (fGpu->caps()->shaderCaps()->pathRenderingSupport() && renderTarget->isMultisampled()) { 223 GrStencilAttachment* sb = renderTarget->renderTargetPriv().attachStencilAttachment(); 224 if (sb) { 225 return GrStencilAndCoverTextContext::Create(this, gpuDevice, leakyProperties); 226 } 227 } 228 229 return GrAtlasTextContext::Create(this, gpuDevice, leakyProperties, enableDistanceFieldFonts); 230 } 231 232 //////////////////////////////////////////////////////////////////////////////// 233 234 bool GrContext::isConfigTexturable(GrPixelConfig config) const { 235 return fGpu->caps()->isConfigTexturable(config); 236 } 237 238 bool GrContext::npotTextureTileSupport() const { 239 return fGpu->caps()->npotTextureTileSupport(); 240 } 241 242 void GrContext::OverBudgetCB(void* data) { 243 SkASSERT(data); 244 245 GrContext* context = reinterpret_cast<GrContext*>(data); 246 247 // Flush the InOrderDrawBuffer to possibly free up some textures 248 context->fFlushToReduceCacheSize = true; 249 } 250 251 void GrContext::TextBlobCacheOverBudgetCB(void* data) { 252 SkASSERT(data); 253 254 // Unlike the GrResourceCache, TextBlobs are drawn at the SkGpuDevice level, therefore they 255 // cannot use fFlushTorReduceCacheSize because it uses AutoCheckFlush. The solution is to move 256 // drawText calls to below the GrContext level, but this is not trivial because they call 257 // drawPath on SkGpuDevice 258 GrContext* context = reinterpret_cast<GrContext*>(data); 259 context->flush(); 260 } 261 262 int GrContext::getMaxTextureSize() const { 263 return SkTMin(fGpu->caps()->maxTextureSize(), fMaxTextureSizeOverride); 264 } 265 266 int GrContext::getMaxRenderTargetSize() const { 267 return fGpu->caps()->maxRenderTargetSize(); 268 } 269 270 int GrContext::getMaxSampleCount() const { 271 return fGpu->caps()->maxSampleCount(); 272 } 273 274 /////////////////////////////////////////////////////////////////////////////// 275 276 void GrContext::clear(const SkIRect* rect, 277 const GrColor color, 278 bool canIgnoreRect, 279 GrRenderTarget* renderTarget) { 280 RETURN_IF_ABANDONED 281 ASSERT_OWNED_RESOURCE(renderTarget); 282 SkASSERT(renderTarget); 283 284 AutoCheckFlush acf(this); 285 GR_CREATE_TRACE_MARKER_CONTEXT("GrContext::clear", this); 286 GrDrawTarget* target = this->prepareToDraw(); 287 if (NULL == target) { 288 return; 289 } 290 target->clear(rect, color, canIgnoreRect, renderTarget); 291 } 292 293 void GrContext::drawPaint(GrRenderTarget* rt, 294 const GrClip& clip, 295 const GrPaint& origPaint, 296 const SkMatrix& viewMatrix) { 297 RETURN_IF_ABANDONED 298 // set rect to be big enough to fill the space, but not super-huge, so we 299 // don't overflow fixed-point implementations 300 SkRect r; 301 r.setLTRB(0, 0, 302 SkIntToScalar(rt->width()), 303 SkIntToScalar(rt->height())); 304 SkTCopyOnFirstWrite<GrPaint> paint(origPaint); 305 306 // by definition this fills the entire clip, no need for AA 307 if (paint->isAntiAlias()) { 308 paint.writable()->setAntiAlias(false); 309 } 310 311 bool isPerspective = viewMatrix.hasPerspective(); 312 313 // We attempt to map r by the inverse matrix and draw that. mapRect will 314 // map the four corners and bound them with a new rect. This will not 315 // produce a correct result for some perspective matrices. 316 if (!isPerspective) { 317 SkMatrix inverse; 318 if (!viewMatrix.invert(&inverse)) { 319 SkDebugf("Could not invert matrix\n"); 320 return; 321 } 322 inverse.mapRect(&r); 323 this->drawRect(rt, clip, *paint, viewMatrix, r); 324 } else { 325 SkMatrix localMatrix; 326 if (!viewMatrix.invert(&localMatrix)) { 327 SkDebugf("Could not invert matrix\n"); 328 return; 329 } 330 331 AutoCheckFlush acf(this); 332 GrPipelineBuilder pipelineBuilder; 333 GrDrawTarget* target = this->prepareToDraw(&pipelineBuilder, rt, clip, paint, &acf); 334 if (NULL == target) { 335 return; 336 } 337 338 GR_CREATE_TRACE_MARKER("GrContext::drawPaintWithPerspective", target); 339 target->drawRect(&pipelineBuilder, 340 paint->getColor(), 341 SkMatrix::I(), 342 r, 343 NULL, 344 &localMatrix); 345 } 346 } 347 348 //////////////////////////////////////////////////////////////////////////////// 349 350 static inline bool is_irect(const SkRect& r) { 351 return SkScalarIsInt(r.fLeft) && SkScalarIsInt(r.fTop) && 352 SkScalarIsInt(r.fRight) && SkScalarIsInt(r.fBottom); 353 } 354 355 static bool apply_aa_to_rect(GrDrawTarget* target, 356 GrPipelineBuilder* pipelineBuilder, 357 SkRect* devBoundRect, 358 const SkRect& rect, 359 SkScalar strokeWidth, 360 const SkMatrix& combinedMatrix, 361 GrColor color) { 362 if (pipelineBuilder->getRenderTarget()->isMultisampled()) { 363 return false; 364 } 365 366 #if defined(SHADER_AA_FILL_RECT) || !defined(IGNORE_ROT_AA_RECT_OPT) 367 if (strokeWidth >= 0) { 368 #endif 369 if (!combinedMatrix.preservesAxisAlignment()) { 370 return false; 371 } 372 373 #if defined(SHADER_AA_FILL_RECT) || !defined(IGNORE_ROT_AA_RECT_OPT) 374 } else { 375 if (!combinedMatrix.preservesRightAngles()) { 376 return false; 377 } 378 } 379 #endif 380 381 combinedMatrix.mapRect(devBoundRect, rect); 382 if (!combinedMatrix.rectStaysRect()) { 383 return true; 384 } 385 386 if (strokeWidth < 0) { 387 return !is_irect(*devBoundRect); 388 } 389 390 return true; 391 } 392 393 static inline bool rect_contains_inclusive(const SkRect& rect, const SkPoint& point) { 394 return point.fX >= rect.fLeft && point.fX <= rect.fRight && 395 point.fY >= rect.fTop && point.fY <= rect.fBottom; 396 } 397 398 class StrokeRectBatch : public GrBatch { 399 public: 400 struct Geometry { 401 GrColor fColor; 402 SkMatrix fViewMatrix; 403 SkRect fRect; 404 SkScalar fStrokeWidth; 405 }; 406 407 static GrBatch* Create(const Geometry& geometry, bool snapToPixelCenters) { 408 return SkNEW_ARGS(StrokeRectBatch, (geometry, snapToPixelCenters)); 409 } 410 411 const char* name() const override { return "StrokeRectBatch"; } 412 413 void getInvariantOutputColor(GrInitInvariantOutput* out) const override { 414 // When this is called on a batch, there is only one geometry bundle 415 out->setKnownFourComponents(fGeoData[0].fColor); 416 } 417 418 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override { 419 out->setKnownSingleComponent(0xff); 420 } 421 422 void initBatchTracker(const GrPipelineInfo& init) override { 423 // Handle any color overrides 424 if (init.fColorIgnored) { 425 fGeoData[0].fColor = GrColor_ILLEGAL; 426 } else if (GrColor_ILLEGAL != init.fOverrideColor) { 427 fGeoData[0].fColor = init.fOverrideColor; 428 } 429 430 // setup batch properties 431 fBatch.fColorIgnored = init.fColorIgnored; 432 fBatch.fColor = fGeoData[0].fColor; 433 fBatch.fUsesLocalCoords = init.fUsesLocalCoords; 434 fBatch.fCoverageIgnored = init.fCoverageIgnored; 435 } 436 437 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override { 438 SkAutoTUnref<const GrGeometryProcessor> gp( 439 GrDefaultGeoProcFactory::Create(GrDefaultGeoProcFactory::kPosition_GPType, 440 this->color(), 441 this->viewMatrix(), 442 SkMatrix::I())); 443 444 batchTarget->initDraw(gp, pipeline); 445 446 // TODO this is hacky, but the only way we have to initialize the GP is to use the 447 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch 448 // everywhere we can remove this nastiness 449 GrPipelineInfo init; 450 init.fColorIgnored = fBatch.fColorIgnored; 451 init.fOverrideColor = GrColor_ILLEGAL; 452 init.fCoverageIgnored = fBatch.fCoverageIgnored; 453 init.fUsesLocalCoords = this->usesLocalCoords(); 454 gp->initBatchTracker(batchTarget->currentBatchTracker(), init); 455 456 size_t vertexStride = gp->getVertexStride(); 457 458 SkASSERT(vertexStride == sizeof(GrDefaultGeoProcFactory::PositionAttr)); 459 460 Geometry& args = fGeoData[0]; 461 462 int vertexCount = kVertsPerHairlineRect; 463 if (args.fStrokeWidth > 0) { 464 vertexCount = kVertsPerStrokeRect; 465 } 466 467 const GrVertexBuffer* vertexBuffer; 468 int firstVertex; 469 470 void* verts = batchTarget->makeVertSpace(vertexStride, vertexCount, 471 &vertexBuffer, &firstVertex); 472 473 if (!verts) { 474 SkDebugf("Could not allocate vertices\n"); 475 return; 476 } 477 478 SkPoint* vertex = reinterpret_cast<SkPoint*>(verts); 479 480 GrPrimitiveType primType; 481 482 if (args.fStrokeWidth > 0) {; 483 primType = kTriangleStrip_GrPrimitiveType; 484 args.fRect.sort(); 485 this->setStrokeRectStrip(vertex, args.fRect, args.fStrokeWidth); 486 } else { 487 // hairline 488 primType = kLineStrip_GrPrimitiveType; 489 vertex[0].set(args.fRect.fLeft, args.fRect.fTop); 490 vertex[1].set(args.fRect.fRight, args.fRect.fTop); 491 vertex[2].set(args.fRect.fRight, args.fRect.fBottom); 492 vertex[3].set(args.fRect.fLeft, args.fRect.fBottom); 493 vertex[4].set(args.fRect.fLeft, args.fRect.fTop); 494 } 495 496 GrVertices vertices; 497 vertices.init(primType, vertexBuffer, firstVertex, vertexCount); 498 batchTarget->draw(vertices); 499 } 500 501 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; } 502 503 private: 504 StrokeRectBatch(const Geometry& geometry, bool snapToPixelCenters) { 505 this->initClassID<StrokeRectBatch>(); 506 507 fBatch.fHairline = geometry.fStrokeWidth == 0; 508 509 fGeoData.push_back(geometry); 510 511 // setup bounds 512 fBounds = geometry.fRect; 513 SkScalar rad = SkScalarHalf(geometry.fStrokeWidth); 514 fBounds.outset(rad, rad); 515 geometry.fViewMatrix.mapRect(&fBounds); 516 517 // If our caller snaps to pixel centers then we have to round out the bounds 518 if (snapToPixelCenters) { 519 fBounds.roundOut(); 520 } 521 } 522 523 /* create a triangle strip that strokes the specified rect. There are 8 524 unique vertices, but we repeat the last 2 to close up. Alternatively we 525 could use an indices array, and then only send 8 verts, but not sure that 526 would be faster. 527 */ 528 void setStrokeRectStrip(SkPoint verts[10], const SkRect& rect, SkScalar width) { 529 const SkScalar rad = SkScalarHalf(width); 530 // TODO we should be able to enable this assert, but we'd have to filter these draws 531 // this is a bug 532 //SkASSERT(rad < rect.width() / 2 && rad < rect.height() / 2); 533 534 verts[0].set(rect.fLeft + rad, rect.fTop + rad); 535 verts[1].set(rect.fLeft - rad, rect.fTop - rad); 536 verts[2].set(rect.fRight - rad, rect.fTop + rad); 537 verts[3].set(rect.fRight + rad, rect.fTop - rad); 538 verts[4].set(rect.fRight - rad, rect.fBottom - rad); 539 verts[5].set(rect.fRight + rad, rect.fBottom + rad); 540 verts[6].set(rect.fLeft + rad, rect.fBottom - rad); 541 verts[7].set(rect.fLeft - rad, rect.fBottom + rad); 542 verts[8] = verts[0]; 543 verts[9] = verts[1]; 544 } 545 546 547 GrColor color() const { return fBatch.fColor; } 548 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; } 549 bool colorIgnored() const { return fBatch.fColorIgnored; } 550 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; } 551 bool hairline() const { return fBatch.fHairline; } 552 553 bool onCombineIfPossible(GrBatch* t) override { 554 // StrokeRectBatch* that = t->cast<StrokeRectBatch>(); 555 556 // NonAA stroke rects cannot batch right now 557 // TODO make these batchable 558 return false; 559 } 560 561 struct BatchTracker { 562 GrColor fColor; 563 bool fUsesLocalCoords; 564 bool fColorIgnored; 565 bool fCoverageIgnored; 566 bool fHairline; 567 }; 568 569 const static int kVertsPerHairlineRect = 5; 570 const static int kVertsPerStrokeRect = 10; 571 572 BatchTracker fBatch; 573 SkSTArray<1, Geometry, true> fGeoData; 574 }; 575 576 void GrContext::drawRect(GrRenderTarget* rt, 577 const GrClip& clip, 578 const GrPaint& paint, 579 const SkMatrix& viewMatrix, 580 const SkRect& rect, 581 const GrStrokeInfo* strokeInfo) { 582 RETURN_IF_ABANDONED 583 if (strokeInfo && strokeInfo->isDashed()) { 584 SkPath path; 585 path.addRect(rect); 586 this->drawPath(rt, clip, paint, viewMatrix, path, *strokeInfo); 587 return; 588 } 589 590 AutoCheckFlush acf(this); 591 GrPipelineBuilder pipelineBuilder; 592 GrDrawTarget* target = this->prepareToDraw(&pipelineBuilder, rt, clip, &paint, &acf); 593 if (NULL == target) { 594 return; 595 } 596 597 GR_CREATE_TRACE_MARKER("GrContext::drawRect", target); 598 SkScalar width = NULL == strokeInfo ? -1 : strokeInfo->getStrokeRec().getWidth(); 599 600 // Check if this is a full RT draw and can be replaced with a clear. We don't bother checking 601 // cases where the RT is fully inside a stroke. 602 if (width < 0) { 603 SkRect rtRect; 604 pipelineBuilder.getRenderTarget()->getBoundsRect(&rtRect); 605 SkRect clipSpaceRTRect = rtRect; 606 bool checkClip = GrClip::kWideOpen_ClipType != clip.clipType(); 607 if (checkClip) { 608 clipSpaceRTRect.offset(SkIntToScalar(clip.origin().fX), 609 SkIntToScalar(clip.origin().fY)); 610 } 611 // Does the clip contain the entire RT? 612 if (!checkClip || clip.quickContains(clipSpaceRTRect)) { 613 SkMatrix invM; 614 if (!viewMatrix.invert(&invM)) { 615 return; 616 } 617 // Does the rect bound the RT? 618 SkPoint srcSpaceRTQuad[4]; 619 invM.mapRectToQuad(srcSpaceRTQuad, rtRect); 620 if (rect_contains_inclusive(rect, srcSpaceRTQuad[0]) && 621 rect_contains_inclusive(rect, srcSpaceRTQuad[1]) && 622 rect_contains_inclusive(rect, srcSpaceRTQuad[2]) && 623 rect_contains_inclusive(rect, srcSpaceRTQuad[3])) { 624 // Will it blend? 625 GrColor clearColor; 626 if (paint.isOpaqueAndConstantColor(&clearColor)) { 627 target->clear(NULL, clearColor, true, rt); 628 return; 629 } 630 } 631 } 632 } 633 634 GrColor color = paint.getColor(); 635 SkRect devBoundRect; 636 bool needAA = paint.isAntiAlias() && !pipelineBuilder.getRenderTarget()->isMultisampled(); 637 bool doAA = needAA && apply_aa_to_rect(target, &pipelineBuilder, &devBoundRect, rect, width, 638 viewMatrix, color); 639 640 if (doAA) { 641 if (width >= 0) { 642 const SkStrokeRec& strokeRec = strokeInfo->getStrokeRec(); 643 fAARectRenderer->strokeAARect(target, 644 &pipelineBuilder, 645 color, 646 viewMatrix, 647 rect, 648 devBoundRect, 649 strokeRec); 650 } else { 651 // filled AA rect 652 fAARectRenderer->fillAARect(target, 653 &pipelineBuilder, 654 color, 655 viewMatrix, 656 rect, 657 devBoundRect); 658 } 659 return; 660 } 661 662 if (width >= 0) { 663 StrokeRectBatch::Geometry geometry; 664 geometry.fViewMatrix = viewMatrix; 665 geometry.fColor = color; 666 geometry.fRect = rect; 667 geometry.fStrokeWidth = width; 668 669 // Non-AA hairlines are snapped to pixel centers to make which pixels are hit deterministic 670 bool snapToPixelCenters = (0 == width && !rt->isMultisampled()); 671 SkAutoTUnref<GrBatch> batch(StrokeRectBatch::Create(geometry, snapToPixelCenters)); 672 673 // Depending on sub-pixel coordinates and the particular GPU, we may lose a corner of 674 // hairline rects. We jam all the vertices to pixel centers to avoid this, but not when MSAA 675 // is enabled because it can cause ugly artifacts. 676 pipelineBuilder.setState(GrPipelineBuilder::kSnapVerticesToPixelCenters_Flag, 677 snapToPixelCenters); 678 target->drawBatch(&pipelineBuilder, batch); 679 } else { 680 // filled BW rect 681 target->drawSimpleRect(&pipelineBuilder, color, viewMatrix, rect); 682 } 683 } 684 685 void GrContext::drawNonAARectToRect(GrRenderTarget* rt, 686 const GrClip& clip, 687 const GrPaint& paint, 688 const SkMatrix& viewMatrix, 689 const SkRect& rectToDraw, 690 const SkRect& localRect, 691 const SkMatrix* localMatrix) { 692 RETURN_IF_ABANDONED 693 AutoCheckFlush acf(this); 694 GrPipelineBuilder pipelineBuilder; 695 GrDrawTarget* target = this->prepareToDraw(&pipelineBuilder, rt, clip, &paint, &acf); 696 if (NULL == target) { 697 return; 698 } 699 700 GR_CREATE_TRACE_MARKER("GrContext::drawRectToRect", target); 701 702 target->drawRect(&pipelineBuilder, 703 paint.getColor(), 704 viewMatrix, 705 rectToDraw, 706 &localRect, 707 localMatrix); 708 } 709 710 static const GrGeometryProcessor* set_vertex_attributes(bool hasLocalCoords, 711 bool hasColors, 712 int* colorOffset, 713 int* texOffset, 714 GrColor color, 715 const SkMatrix& viewMatrix) { 716 *texOffset = -1; 717 *colorOffset = -1; 718 uint32_t flags = GrDefaultGeoProcFactory::kPosition_GPType; 719 if (hasLocalCoords && hasColors) { 720 *colorOffset = sizeof(SkPoint); 721 *texOffset = sizeof(SkPoint) + sizeof(GrColor); 722 flags |= GrDefaultGeoProcFactory::kColor_GPType | 723 GrDefaultGeoProcFactory::kLocalCoord_GPType; 724 } else if (hasLocalCoords) { 725 *texOffset = sizeof(SkPoint); 726 flags |= GrDefaultGeoProcFactory::kLocalCoord_GPType; 727 } else if (hasColors) { 728 *colorOffset = sizeof(SkPoint); 729 flags |= GrDefaultGeoProcFactory::kColor_GPType; 730 } 731 return GrDefaultGeoProcFactory::Create(flags, color, viewMatrix, SkMatrix::I()); 732 } 733 734 class DrawVerticesBatch : public GrBatch { 735 public: 736 struct Geometry { 737 GrColor fColor; 738 SkTDArray<SkPoint> fPositions; 739 SkTDArray<uint16_t> fIndices; 740 SkTDArray<GrColor> fColors; 741 SkTDArray<SkPoint> fLocalCoords; 742 }; 743 744 static GrBatch* Create(const Geometry& geometry, GrPrimitiveType primitiveType, 745 const SkMatrix& viewMatrix, 746 const SkPoint* positions, int vertexCount, 747 const uint16_t* indices, int indexCount, 748 const GrColor* colors, const SkPoint* localCoords, 749 const SkRect& bounds) { 750 return SkNEW_ARGS(DrawVerticesBatch, (geometry, primitiveType, viewMatrix, positions, 751 vertexCount, indices, indexCount, colors, 752 localCoords, bounds)); 753 } 754 755 const char* name() const override { return "DrawVerticesBatch"; } 756 757 void getInvariantOutputColor(GrInitInvariantOutput* out) const override { 758 // When this is called on a batch, there is only one geometry bundle 759 if (this->hasColors()) { 760 out->setUnknownFourComponents(); 761 } else { 762 out->setKnownFourComponents(fGeoData[0].fColor); 763 } 764 } 765 766 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override { 767 out->setKnownSingleComponent(0xff); 768 } 769 770 void initBatchTracker(const GrPipelineInfo& init) override { 771 // Handle any color overrides 772 if (init.fColorIgnored) { 773 fGeoData[0].fColor = GrColor_ILLEGAL; 774 } else if (GrColor_ILLEGAL != init.fOverrideColor) { 775 fGeoData[0].fColor = init.fOverrideColor; 776 } 777 778 // setup batch properties 779 fBatch.fColorIgnored = init.fColorIgnored; 780 fBatch.fColor = fGeoData[0].fColor; 781 fBatch.fUsesLocalCoords = init.fUsesLocalCoords; 782 fBatch.fCoverageIgnored = init.fCoverageIgnored; 783 } 784 785 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override { 786 int colorOffset = -1, texOffset = -1; 787 SkAutoTUnref<const GrGeometryProcessor> gp( 788 set_vertex_attributes(this->hasLocalCoords(), this->hasColors(), &colorOffset, 789 &texOffset, this->color(), this->viewMatrix())); 790 791 batchTarget->initDraw(gp, pipeline); 792 793 // TODO this is hacky, but the only way we have to initialize the GP is to use the 794 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch 795 // everywhere we can remove this nastiness 796 GrPipelineInfo init; 797 init.fColorIgnored = fBatch.fColorIgnored; 798 init.fOverrideColor = GrColor_ILLEGAL; 799 init.fCoverageIgnored = fBatch.fCoverageIgnored; 800 init.fUsesLocalCoords = this->usesLocalCoords(); 801 gp->initBatchTracker(batchTarget->currentBatchTracker(), init); 802 803 size_t vertexStride = gp->getVertexStride(); 804 805 SkASSERT(vertexStride == sizeof(SkPoint) + (this->hasLocalCoords() ? sizeof(SkPoint) : 0) 806 + (this->hasColors() ? sizeof(GrColor) : 0)); 807 808 int instanceCount = fGeoData.count(); 809 810 const GrVertexBuffer* vertexBuffer; 811 int firstVertex; 812 813 void* verts = batchTarget->makeVertSpace(vertexStride, this->vertexCount(), 814 &vertexBuffer, &firstVertex); 815 816 if (!verts) { 817 SkDebugf("Could not allocate vertices\n"); 818 return; 819 } 820 821 const GrIndexBuffer* indexBuffer = NULL; 822 int firstIndex = 0; 823 824 uint16_t* indices = NULL; 825 if (this->hasIndices()) { 826 indices = batchTarget->makeIndexSpace(this->indexCount(), &indexBuffer, &firstIndex); 827 828 if (!indices) { 829 SkDebugf("Could not allocate indices\n"); 830 return; 831 } 832 } 833 834 int indexOffset = 0; 835 int vertexOffset = 0; 836 for (int i = 0; i < instanceCount; i++) { 837 const Geometry& args = fGeoData[i]; 838 839 // TODO we can actually cache this interleaved and then just memcopy 840 if (this->hasIndices()) { 841 for (int j = 0; j < args.fIndices.count(); ++j, ++indexOffset) { 842 *(indices + indexOffset) = args.fIndices[j] + vertexOffset; 843 } 844 } 845 846 for (int j = 0; j < args.fPositions.count(); ++j) { 847 *((SkPoint*)verts) = args.fPositions[j]; 848 if (this->hasColors()) { 849 *(GrColor*)((intptr_t)verts + colorOffset) = args.fColors[j]; 850 } 851 if (this->hasLocalCoords()) { 852 *(SkPoint*)((intptr_t)verts + texOffset) = args.fLocalCoords[j]; 853 } 854 verts = (void*)((intptr_t)verts + vertexStride); 855 vertexOffset++; 856 } 857 } 858 859 GrVertices vertices; 860 if (this->hasIndices()) { 861 vertices.initIndexed(this->primitiveType(), vertexBuffer, indexBuffer, firstVertex, 862 firstIndex, this->vertexCount(), this->indexCount()); 863 864 } else { 865 vertices.init(this->primitiveType(), vertexBuffer, firstVertex, this->vertexCount()); 866 } 867 batchTarget->draw(vertices); 868 } 869 870 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; } 871 872 private: 873 DrawVerticesBatch(const Geometry& geometry, GrPrimitiveType primitiveType, 874 const SkMatrix& viewMatrix, 875 const SkPoint* positions, int vertexCount, 876 const uint16_t* indices, int indexCount, 877 const GrColor* colors, const SkPoint* localCoords, const SkRect& bounds) { 878 this->initClassID<DrawVerticesBatch>(); 879 SkASSERT(positions); 880 881 fBatch.fViewMatrix = viewMatrix; 882 Geometry& installedGeo = fGeoData.push_back(geometry); 883 884 installedGeo.fPositions.append(vertexCount, positions); 885 if (indices) { 886 installedGeo.fIndices.append(indexCount, indices); 887 fBatch.fHasIndices = true; 888 } else { 889 fBatch.fHasIndices = false; 890 } 891 892 if (colors) { 893 installedGeo.fColors.append(vertexCount, colors); 894 fBatch.fHasColors = true; 895 } else { 896 fBatch.fHasColors = false; 897 } 898 899 if (localCoords) { 900 installedGeo.fLocalCoords.append(vertexCount, localCoords); 901 fBatch.fHasLocalCoords = true; 902 } else { 903 fBatch.fHasLocalCoords = false; 904 } 905 fBatch.fVertexCount = vertexCount; 906 fBatch.fIndexCount = indexCount; 907 fBatch.fPrimitiveType = primitiveType; 908 909 this->setBounds(bounds); 910 } 911 912 GrPrimitiveType primitiveType() const { return fBatch.fPrimitiveType; } 913 bool batchablePrimitiveType() const { 914 return kTriangles_GrPrimitiveType == fBatch.fPrimitiveType || 915 kLines_GrPrimitiveType == fBatch.fPrimitiveType || 916 kPoints_GrPrimitiveType == fBatch.fPrimitiveType; 917 } 918 GrColor color() const { return fBatch.fColor; } 919 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; } 920 bool colorIgnored() const { return fBatch.fColorIgnored; } 921 const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; } 922 bool hasColors() const { return fBatch.fHasColors; } 923 bool hasIndices() const { return fBatch.fHasIndices; } 924 bool hasLocalCoords() const { return fBatch.fHasLocalCoords; } 925 int vertexCount() const { return fBatch.fVertexCount; } 926 int indexCount() const { return fBatch.fIndexCount; } 927 928 bool onCombineIfPossible(GrBatch* t) override { 929 DrawVerticesBatch* that = t->cast<DrawVerticesBatch>(); 930 931 if (!this->batchablePrimitiveType() || this->primitiveType() != that->primitiveType()) { 932 return false; 933 } 934 935 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords()); 936 937 // We currently use a uniform viewmatrix for this batch 938 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) { 939 return false; 940 } 941 942 if (this->hasColors() != that->hasColors()) { 943 return false; 944 } 945 946 if (this->hasIndices() != that->hasIndices()) { 947 return false; 948 } 949 950 if (this->hasLocalCoords() != that->hasLocalCoords()) { 951 return false; 952 } 953 954 if (!this->hasColors() && this->color() != that->color()) { 955 return false; 956 } 957 958 if (this->color() != that->color()) { 959 fBatch.fColor = GrColor_ILLEGAL; 960 } 961 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin()); 962 fBatch.fVertexCount += that->vertexCount(); 963 fBatch.fIndexCount += that->indexCount(); 964 965 this->joinBounds(that->bounds()); 966 return true; 967 } 968 969 struct BatchTracker { 970 GrPrimitiveType fPrimitiveType; 971 SkMatrix fViewMatrix; 972 GrColor fColor; 973 bool fUsesLocalCoords; 974 bool fColorIgnored; 975 bool fCoverageIgnored; 976 bool fHasColors; 977 bool fHasIndices; 978 bool fHasLocalCoords; 979 int fVertexCount; 980 int fIndexCount; 981 }; 982 983 BatchTracker fBatch; 984 SkSTArray<1, Geometry, true> fGeoData; 985 }; 986 987 void GrContext::drawVertices(GrRenderTarget* rt, 988 const GrClip& clip, 989 const GrPaint& paint, 990 const SkMatrix& viewMatrix, 991 GrPrimitiveType primitiveType, 992 int vertexCount, 993 const SkPoint positions[], 994 const SkPoint texCoords[], 995 const GrColor colors[], 996 const uint16_t indices[], 997 int indexCount) { 998 RETURN_IF_ABANDONED 999 AutoCheckFlush acf(this); 1000 GrPipelineBuilder pipelineBuilder; 1001 1002 GrDrawTarget* target = this->prepareToDraw(&pipelineBuilder, rt, clip, &paint, &acf); 1003 if (NULL == target) { 1004 return; 1005 } 1006 1007 GR_CREATE_TRACE_MARKER("GrContext::drawVertices", target); 1008 1009 // TODO clients should give us bounds 1010 SkRect bounds; 1011 if (!bounds.setBoundsCheck(positions, vertexCount)) { 1012 SkDebugf("drawVertices call empty bounds\n"); 1013 return; 1014 } 1015 1016 viewMatrix.mapRect(&bounds); 1017 1018 // If we don't have AA then we outset for a half pixel in each direction to account for 1019 // snapping 1020 if (!paint.isAntiAlias()) { 1021 bounds.outset(0.5f, 0.5f); 1022 } 1023 1024 DrawVerticesBatch::Geometry geometry; 1025 geometry.fColor = paint.getColor(); 1026 SkAutoTUnref<GrBatch> batch(DrawVerticesBatch::Create(geometry, primitiveType, viewMatrix, 1027 positions, vertexCount, indices, 1028 indexCount, colors, texCoords, 1029 bounds)); 1030 1031 target->drawBatch(&pipelineBuilder, batch); 1032 } 1033 1034 /////////////////////////////////////////////////////////////////////////////// 1035 1036 void GrContext::drawRRect(GrRenderTarget*rt, 1037 const GrClip& clip, 1038 const GrPaint& paint, 1039 const SkMatrix& viewMatrix, 1040 const SkRRect& rrect, 1041 const GrStrokeInfo& strokeInfo) { 1042 RETURN_IF_ABANDONED 1043 if (rrect.isEmpty()) { 1044 return; 1045 } 1046 1047 if (strokeInfo.isDashed()) { 1048 SkPath path; 1049 path.addRRect(rrect); 1050 this->drawPath(rt, clip, paint, viewMatrix, path, strokeInfo); 1051 return; 1052 } 1053 1054 AutoCheckFlush acf(this); 1055 GrPipelineBuilder pipelineBuilder; 1056 GrDrawTarget* target = this->prepareToDraw(&pipelineBuilder, rt, clip, &paint, &acf); 1057 if (NULL == target) { 1058 return; 1059 } 1060 1061 GR_CREATE_TRACE_MARKER("GrContext::drawRRect", target); 1062 1063 const SkStrokeRec& strokeRec = strokeInfo.getStrokeRec(); 1064 1065 GrColor color = paint.getColor(); 1066 if (!fOvalRenderer->drawRRect(target, 1067 &pipelineBuilder, 1068 color, 1069 viewMatrix, 1070 paint.isAntiAlias(), 1071 rrect, 1072 strokeRec)) { 1073 SkPath path; 1074 path.addRRect(rrect); 1075 this->internalDrawPath(target, &pipelineBuilder, viewMatrix, color, paint.isAntiAlias(), 1076 path, strokeInfo); 1077 } 1078 } 1079 1080 /////////////////////////////////////////////////////////////////////////////// 1081 1082 void GrContext::drawDRRect(GrRenderTarget* rt, 1083 const GrClip& clip, 1084 const GrPaint& paint, 1085 const SkMatrix& viewMatrix, 1086 const SkRRect& outer, 1087 const SkRRect& inner) { 1088 RETURN_IF_ABANDONED 1089 if (outer.isEmpty()) { 1090 return; 1091 } 1092 1093 AutoCheckFlush acf(this); 1094 GrPipelineBuilder pipelineBuilder; 1095 GrDrawTarget* target = this->prepareToDraw(&pipelineBuilder, rt, clip, &paint, &acf); 1096 1097 GR_CREATE_TRACE_MARKER("GrContext::drawDRRect", target); 1098 1099 GrColor color = paint.getColor(); 1100 if (!fOvalRenderer->drawDRRect(target, 1101 &pipelineBuilder, 1102 color, 1103 viewMatrix, 1104 paint.isAntiAlias(), 1105 outer, 1106 inner)) { 1107 SkPath path; 1108 path.addRRect(inner); 1109 path.addRRect(outer); 1110 path.setFillType(SkPath::kEvenOdd_FillType); 1111 1112 GrStrokeInfo fillRec(SkStrokeRec::kFill_InitStyle); 1113 this->internalDrawPath(target, &pipelineBuilder, viewMatrix, color, paint.isAntiAlias(), 1114 path, fillRec); 1115 } 1116 } 1117 1118 /////////////////////////////////////////////////////////////////////////////// 1119 1120 void GrContext::drawOval(GrRenderTarget* rt, 1121 const GrClip& clip, 1122 const GrPaint& paint, 1123 const SkMatrix& viewMatrix, 1124 const SkRect& oval, 1125 const GrStrokeInfo& strokeInfo) { 1126 RETURN_IF_ABANDONED 1127 if (oval.isEmpty()) { 1128 return; 1129 } 1130 1131 if (strokeInfo.isDashed()) { 1132 SkPath path; 1133 path.addOval(oval); 1134 this->drawPath(rt, clip, paint, viewMatrix, path, strokeInfo); 1135 return; 1136 } 1137 1138 AutoCheckFlush acf(this); 1139 GrPipelineBuilder pipelineBuilder; 1140 GrDrawTarget* target = this->prepareToDraw(&pipelineBuilder, rt, clip, &paint, &acf); 1141 if (NULL == target) { 1142 return; 1143 } 1144 1145 GR_CREATE_TRACE_MARKER("GrContext::drawOval", target); 1146 1147 const SkStrokeRec& strokeRec = strokeInfo.getStrokeRec(); 1148 1149 GrColor color = paint.getColor(); 1150 if (!fOvalRenderer->drawOval(target, 1151 &pipelineBuilder, 1152 color, 1153 viewMatrix, 1154 paint.isAntiAlias(), 1155 oval, 1156 strokeRec)) { 1157 SkPath path; 1158 path.addOval(oval); 1159 this->internalDrawPath(target, &pipelineBuilder, viewMatrix, color, paint.isAntiAlias(), 1160 path, strokeInfo); 1161 } 1162 } 1163 1164 // Can 'path' be drawn as a pair of filled nested rectangles? 1165 static bool is_nested_rects(GrDrawTarget* target, 1166 GrPipelineBuilder* pipelineBuilder, 1167 GrColor color, 1168 const SkMatrix& viewMatrix, 1169 const SkPath& path, 1170 const SkStrokeRec& stroke, 1171 SkRect rects[2]) { 1172 SkASSERT(stroke.isFillStyle()); 1173 1174 if (path.isInverseFillType()) { 1175 return false; 1176 } 1177 1178 // TODO: this restriction could be lifted if we were willing to apply 1179 // the matrix to all the points individually rather than just to the rect 1180 if (!viewMatrix.preservesAxisAlignment()) { 1181 return false; 1182 } 1183 1184 SkPath::Direction dirs[2]; 1185 if (!path.isNestedFillRects(rects, dirs)) { 1186 return false; 1187 } 1188 1189 if (SkPath::kWinding_FillType == path.getFillType() && dirs[0] == dirs[1]) { 1190 // The two rects need to be wound opposite to each other 1191 return false; 1192 } 1193 1194 // Right now, nested rects where the margin is not the same width 1195 // all around do not render correctly 1196 const SkScalar* outer = rects[0].asScalars(); 1197 const SkScalar* inner = rects[1].asScalars(); 1198 1199 bool allEq = true; 1200 1201 SkScalar margin = SkScalarAbs(outer[0] - inner[0]); 1202 bool allGoE1 = margin >= SK_Scalar1; 1203 1204 for (int i = 1; i < 4; ++i) { 1205 SkScalar temp = SkScalarAbs(outer[i] - inner[i]); 1206 if (temp < SK_Scalar1) { 1207 allGoE1 = false; 1208 } 1209 if (!SkScalarNearlyEqual(margin, temp)) { 1210 allEq = false; 1211 } 1212 } 1213 1214 return allEq || allGoE1; 1215 } 1216 1217 void GrContext::drawPath(GrRenderTarget* rt, 1218 const GrClip& clip, 1219 const GrPaint& paint, 1220 const SkMatrix& viewMatrix, 1221 const SkPath& path, 1222 const GrStrokeInfo& strokeInfo) { 1223 RETURN_IF_ABANDONED 1224 if (path.isEmpty()) { 1225 if (path.isInverseFillType()) { 1226 this->drawPaint(rt, clip, paint, viewMatrix); 1227 } 1228 return; 1229 } 1230 1231 GrColor color = paint.getColor(); 1232 1233 // Note that internalDrawPath may sw-rasterize the path into a scratch texture. 1234 // Scratch textures can be recycled after they are returned to the texture 1235 // cache. This presents a potential hazard for buffered drawing. However, 1236 // the writePixels that uploads to the scratch will perform a flush so we're 1237 // OK. 1238 AutoCheckFlush acf(this); 1239 GrPipelineBuilder pipelineBuilder; 1240 GrDrawTarget* target = this->prepareToDraw(&pipelineBuilder, rt, clip, &paint, &acf); 1241 if (NULL == target) { 1242 return; 1243 } 1244 1245 GR_CREATE_TRACE_MARKER1("GrContext::drawPath", target, "Is Convex", path.isConvex()); 1246 1247 if (!strokeInfo.isDashed()) { 1248 const SkStrokeRec& strokeRec = strokeInfo.getStrokeRec(); 1249 bool useCoverageAA = paint.isAntiAlias() && 1250 !pipelineBuilder.getRenderTarget()->isMultisampled(); 1251 1252 if (useCoverageAA && strokeRec.getWidth() < 0 && !path.isConvex()) { 1253 // Concave AA paths are expensive - try to avoid them for special cases 1254 SkRect rects[2]; 1255 1256 if (is_nested_rects(target, &pipelineBuilder, color, viewMatrix, path, strokeRec, 1257 rects)) { 1258 fAARectRenderer->fillAANestedRects(target, &pipelineBuilder, color, viewMatrix, 1259 rects); 1260 return; 1261 } 1262 } 1263 SkRect ovalRect; 1264 bool isOval = path.isOval(&ovalRect); 1265 1266 if (isOval && !path.isInverseFillType()) { 1267 if (fOvalRenderer->drawOval(target, 1268 &pipelineBuilder, 1269 color, 1270 viewMatrix, 1271 paint.isAntiAlias(), 1272 ovalRect, 1273 strokeRec)) { 1274 return; 1275 } 1276 } 1277 } 1278 this->internalDrawPath(target, &pipelineBuilder, viewMatrix, color, paint.isAntiAlias(), 1279 path, strokeInfo); 1280 } 1281 1282 void GrContext::internalDrawPath(GrDrawTarget* target, 1283 GrPipelineBuilder* pipelineBuilder, 1284 const SkMatrix& viewMatrix, 1285 GrColor color, 1286 bool useAA, 1287 const SkPath& path, 1288 const GrStrokeInfo& strokeInfo) { 1289 RETURN_IF_ABANDONED 1290 SkASSERT(!path.isEmpty()); 1291 1292 GR_CREATE_TRACE_MARKER("GrContext::internalDrawPath", target); 1293 1294 1295 // An Assumption here is that path renderer would use some form of tweaking 1296 // the src color (either the input alpha or in the frag shader) to implement 1297 // aa. If we have some future driver-mojo path AA that can do the right 1298 // thing WRT to the blend then we'll need some query on the PR. 1299 bool useCoverageAA = useAA && 1300 !pipelineBuilder->getRenderTarget()->isMultisampled(); 1301 1302 1303 GrPathRendererChain::DrawType type = 1304 useCoverageAA ? GrPathRendererChain::kColorAntiAlias_DrawType : 1305 GrPathRendererChain::kColor_DrawType; 1306 1307 const SkPath* pathPtr = &path; 1308 SkTLazy<SkPath> tmpPath; 1309 const GrStrokeInfo* strokeInfoPtr = &strokeInfo; 1310 1311 // Try a 1st time without stroking the path and without allowing the SW renderer 1312 GrPathRenderer* pr = this->getPathRenderer(target, pipelineBuilder, viewMatrix, *pathPtr, 1313 *strokeInfoPtr, false, type); 1314 1315 GrStrokeInfo dashlessStrokeInfo(strokeInfo, false); 1316 if (NULL == pr && strokeInfo.isDashed()) { 1317 // It didn't work above, so try again with dashed stroke converted to a dashless stroke. 1318 if (!strokeInfo.applyDash(tmpPath.init(), &dashlessStrokeInfo, *pathPtr)) { 1319 return; 1320 } 1321 pathPtr = tmpPath.get(); 1322 if (pathPtr->isEmpty()) { 1323 return; 1324 } 1325 strokeInfoPtr = &dashlessStrokeInfo; 1326 pr = this->getPathRenderer(target, pipelineBuilder, viewMatrix, *pathPtr, *strokeInfoPtr, 1327 false, type); 1328 } 1329 1330 if (NULL == pr) { 1331 if (!GrPathRenderer::IsStrokeHairlineOrEquivalent(*strokeInfoPtr, viewMatrix, NULL) && 1332 !strokeInfoPtr->isFillStyle()) { 1333 // It didn't work above, so try again with stroke converted to a fill. 1334 if (!tmpPath.isValid()) { 1335 tmpPath.init(); 1336 } 1337 SkStrokeRec* strokeRec = dashlessStrokeInfo.getStrokeRecPtr(); 1338 strokeRec->setResScale(SkScalarAbs(viewMatrix.getMaxScale())); 1339 if (!strokeRec->applyToPath(tmpPath.get(), *pathPtr)) { 1340 return; 1341 } 1342 pathPtr = tmpPath.get(); 1343 if (pathPtr->isEmpty()) { 1344 return; 1345 } 1346 strokeRec->setFillStyle(); 1347 strokeInfoPtr = &dashlessStrokeInfo; 1348 } 1349 1350 // This time, allow SW renderer 1351 pr = this->getPathRenderer(target, pipelineBuilder, viewMatrix, *pathPtr, *strokeInfoPtr, 1352 true, type); 1353 } 1354 1355 if (NULL == pr) { 1356 #ifdef SK_DEBUG 1357 SkDebugf("Unable to find path renderer compatible with path.\n"); 1358 #endif 1359 return; 1360 } 1361 1362 pr->drawPath(target, pipelineBuilder, color, viewMatrix, *pathPtr, *strokeInfoPtr, useCoverageAA); 1363 } 1364 1365 //////////////////////////////////////////////////////////////////////////////// 1366 1367 void GrContext::flush(int flagsBitfield) { 1368 if (NULL == fDrawBuffer) { 1369 return; 1370 } 1371 1372 if (kDiscard_FlushBit & flagsBitfield) { 1373 fDrawBuffer->reset(); 1374 } else { 1375 fDrawBuffer->flush(); 1376 } 1377 fResourceCache->notifyFlushOccurred(); 1378 fFlushToReduceCacheSize = false; 1379 } 1380 1381 bool sw_convert_to_premul(GrPixelConfig srcConfig, int width, int height, size_t inRowBytes, 1382 const void* inPixels, size_t outRowBytes, void* outPixels) { 1383 SkSrcPixelInfo srcPI; 1384 if (!GrPixelConfig2ColorAndProfileType(srcConfig, &srcPI.fColorType, NULL)) { 1385 return false; 1386 } 1387 srcPI.fAlphaType = kUnpremul_SkAlphaType; 1388 srcPI.fPixels = inPixels; 1389 srcPI.fRowBytes = inRowBytes; 1390 1391 SkDstPixelInfo dstPI; 1392 dstPI.fColorType = srcPI.fColorType; 1393 dstPI.fAlphaType = kPremul_SkAlphaType; 1394 dstPI.fPixels = outPixels; 1395 dstPI.fRowBytes = outRowBytes; 1396 1397 return srcPI.convertPixelsTo(&dstPI, width, height); 1398 } 1399 1400 bool GrContext::writeSurfacePixels(GrSurface* surface, 1401 int left, int top, int width, int height, 1402 GrPixelConfig srcConfig, const void* buffer, size_t rowBytes, 1403 uint32_t pixelOpsFlags) { 1404 RETURN_FALSE_IF_ABANDONED 1405 { 1406 GrTexture* texture = NULL; 1407 if (!(kUnpremul_PixelOpsFlag & pixelOpsFlags) && (texture = surface->asTexture()) && 1408 fGpu->canWriteTexturePixels(texture, srcConfig)) { 1409 1410 if (!(kDontFlush_PixelOpsFlag & pixelOpsFlags) && 1411 surface->surfacePriv().hasPendingIO()) { 1412 this->flush(); 1413 } 1414 return fGpu->writeTexturePixels(texture, left, top, width, height, 1415 srcConfig, buffer, rowBytes); 1416 // Don't need to check kFlushWrites_PixelOp here, we just did a direct write so the 1417 // upload is already flushed. 1418 } 1419 } 1420 1421 // If we didn't do a direct texture write then we upload the pixels to a texture and draw. 1422 GrRenderTarget* renderTarget = surface->asRenderTarget(); 1423 if (NULL == renderTarget) { 1424 return false; 1425 } 1426 1427 // We ignore the preferred config unless it is a R/B swap of the src config. In that case 1428 // we will upload the original src data to a scratch texture but we will spoof it as the swapped 1429 // config. This scratch will then have R and B swapped. We correct for this by swapping again 1430 // when drawing the scratch to the dst using a conversion effect. 1431 bool swapRAndB = false; 1432 GrPixelConfig writeConfig = srcConfig; 1433 if (GrPixelConfigSwapRAndB(srcConfig) == 1434 fGpu->preferredWritePixelsConfig(srcConfig, renderTarget->config())) { 1435 writeConfig = GrPixelConfigSwapRAndB(srcConfig); 1436 swapRAndB = true; 1437 } 1438 1439 GrSurfaceDesc desc; 1440 desc.fWidth = width; 1441 desc.fHeight = height; 1442 desc.fConfig = writeConfig; 1443 SkAutoTUnref<GrTexture> texture(this->textureProvider()->refScratchTexture(desc, 1444 GrTextureProvider::kApprox_ScratchTexMatch)); 1445 if (!texture) { 1446 return false; 1447 } 1448 1449 SkAutoTUnref<const GrFragmentProcessor> fp; 1450 SkMatrix textureMatrix; 1451 textureMatrix.setIDiv(texture->width(), texture->height()); 1452 1453 // allocate a tmp buffer and sw convert the pixels to premul 1454 SkAutoSTMalloc<128 * 128, uint32_t> tmpPixels(0); 1455 1456 if (kUnpremul_PixelOpsFlag & pixelOpsFlags) { 1457 if (!GrPixelConfigIs8888(srcConfig)) { 1458 return false; 1459 } 1460 fp.reset(this->createUPMToPMEffect(texture, swapRAndB, textureMatrix)); 1461 // handle the unpremul step on the CPU if we couldn't create an effect to do it. 1462 if (NULL == fp) { 1463 size_t tmpRowBytes = 4 * width; 1464 tmpPixels.reset(width * height); 1465 if (!sw_convert_to_premul(srcConfig, width, height, rowBytes, buffer, tmpRowBytes, 1466 tmpPixels.get())) { 1467 return false; 1468 } 1469 rowBytes = tmpRowBytes; 1470 buffer = tmpPixels.get(); 1471 } 1472 } 1473 if (NULL == fp) { 1474 fp.reset(GrConfigConversionEffect::Create(texture, 1475 swapRAndB, 1476 GrConfigConversionEffect::kNone_PMConversion, 1477 textureMatrix)); 1478 } 1479 1480 // Even if the client told us not to flush, we still flush here. The client may have known that 1481 // writes to the original surface caused no data hazards, but they can't know that the scratch 1482 // we just got is safe. 1483 if (texture->surfacePriv().hasPendingIO()) { 1484 this->flush(); 1485 } 1486 if (!fGpu->writeTexturePixels(texture, 0, 0, width, height, 1487 writeConfig, buffer, rowBytes)) { 1488 return false; 1489 } 1490 1491 SkMatrix matrix; 1492 matrix.setTranslate(SkIntToScalar(left), SkIntToScalar(top)); 1493 1494 // This function can be called in the midst of drawing another object (e.g., when uploading a 1495 // SW-rasterized clip while issuing a draw). So we push the current geometry state before 1496 // drawing a rect to the render target. 1497 // The bracket ensures we pop the stack if we wind up flushing below. 1498 { 1499 GrDrawTarget* drawTarget = this->prepareToDraw(); 1500 if (!drawTarget) { 1501 return false; 1502 } 1503 1504 GrPipelineBuilder pipelineBuilder; 1505 pipelineBuilder.addColorProcessor(fp); 1506 pipelineBuilder.setRenderTarget(renderTarget); 1507 drawTarget->drawSimpleRect(&pipelineBuilder, 1508 GrColor_WHITE, 1509 matrix, 1510 SkRect::MakeWH(SkIntToScalar(width), SkIntToScalar(height))); 1511 } 1512 1513 if (kFlushWrites_PixelOp & pixelOpsFlags) { 1514 this->flushSurfaceWrites(surface); 1515 } 1516 1517 return true; 1518 } 1519 1520 // toggles between RGBA and BGRA 1521 static SkColorType toggle_colortype32(SkColorType ct) { 1522 if (kRGBA_8888_SkColorType == ct) { 1523 return kBGRA_8888_SkColorType; 1524 } else { 1525 SkASSERT(kBGRA_8888_SkColorType == ct); 1526 return kRGBA_8888_SkColorType; 1527 } 1528 } 1529 1530 bool GrContext::readRenderTargetPixels(GrRenderTarget* target, 1531 int left, int top, int width, int height, 1532 GrPixelConfig dstConfig, void* buffer, size_t rowBytes, 1533 uint32_t flags) { 1534 RETURN_FALSE_IF_ABANDONED 1535 ASSERT_OWNED_RESOURCE(target); 1536 SkASSERT(target); 1537 1538 if (!(kDontFlush_PixelOpsFlag & flags) && target->surfacePriv().hasPendingWrite()) { 1539 this->flush(); 1540 } 1541 1542 // Determine which conversions have to be applied: flipY, swapRAnd, and/or unpremul. 1543 1544 // If fGpu->readPixels would incur a y-flip cost then we will read the pixels upside down. We'll 1545 // either do the flipY by drawing into a scratch with a matrix or on the cpu after the read. 1546 bool flipY = fGpu->readPixelsWillPayForYFlip(target, left, top, 1547 width, height, dstConfig, 1548 rowBytes); 1549 // We ignore the preferred config if it is different than our config unless it is an R/B swap. 1550 // In that case we'll perform an R and B swap while drawing to a scratch texture of the swapped 1551 // config. Then we will call readPixels on the scratch with the swapped config. The swaps during 1552 // the draw cancels out the fact that we call readPixels with a config that is R/B swapped from 1553 // dstConfig. 1554 GrPixelConfig readConfig = dstConfig; 1555 bool swapRAndB = false; 1556 if (GrPixelConfigSwapRAndB(dstConfig) == 1557 fGpu->preferredReadPixelsConfig(dstConfig, target->config())) { 1558 readConfig = GrPixelConfigSwapRAndB(readConfig); 1559 swapRAndB = true; 1560 } 1561 1562 bool unpremul = SkToBool(kUnpremul_PixelOpsFlag & flags); 1563 1564 if (unpremul && !GrPixelConfigIs8888(dstConfig)) { 1565 // The unpremul flag is only allowed for these two configs. 1566 return false; 1567 } 1568 1569 SkAutoTUnref<GrTexture> tempTexture; 1570 1571 // If the src is a texture and we would have to do conversions after read pixels, we instead 1572 // do the conversions by drawing the src to a scratch texture. If we handle any of the 1573 // conversions in the draw we set the corresponding bool to false so that we don't reapply it 1574 // on the read back pixels. 1575 GrTexture* src = target->asTexture(); 1576 if (src && (swapRAndB || unpremul || flipY)) { 1577 // Make the scratch a render so we can read its pixels. 1578 GrSurfaceDesc desc; 1579 desc.fFlags = kRenderTarget_GrSurfaceFlag; 1580 desc.fWidth = width; 1581 desc.fHeight = height; 1582 desc.fConfig = readConfig; 1583 desc.fOrigin = kTopLeft_GrSurfaceOrigin; 1584 1585 // When a full read back is faster than a partial we could always make the scratch exactly 1586 // match the passed rect. However, if we see many different size rectangles we will trash 1587 // our texture cache and pay the cost of creating and destroying many textures. So, we only 1588 // request an exact match when the caller is reading an entire RT. 1589 GrTextureProvider::ScratchTexMatch match = GrTextureProvider::kApprox_ScratchTexMatch; 1590 if (0 == left && 1591 0 == top && 1592 target->width() == width && 1593 target->height() == height && 1594 fGpu->fullReadPixelsIsFasterThanPartial()) { 1595 match = GrTextureProvider::kExact_ScratchTexMatch; 1596 } 1597 tempTexture.reset(this->textureProvider()->refScratchTexture(desc, match)); 1598 if (tempTexture) { 1599 // compute a matrix to perform the draw 1600 SkMatrix textureMatrix; 1601 textureMatrix.setTranslate(SK_Scalar1 *left, SK_Scalar1 *top); 1602 textureMatrix.postIDiv(src->width(), src->height()); 1603 1604 SkAutoTUnref<const GrFragmentProcessor> fp; 1605 if (unpremul) { 1606 fp.reset(this->createPMToUPMEffect(src, swapRAndB, textureMatrix)); 1607 if (fp) { 1608 unpremul = false; // we no longer need to do this on CPU after the read back. 1609 } 1610 } 1611 // If we failed to create a PM->UPM effect and have no other conversions to perform then 1612 // there is no longer any point to using the scratch. 1613 if (fp || flipY || swapRAndB) { 1614 if (!fp) { 1615 fp.reset(GrConfigConversionEffect::Create( 1616 src, swapRAndB, GrConfigConversionEffect::kNone_PMConversion, 1617 textureMatrix)); 1618 } 1619 swapRAndB = false; // we will handle the swap in the draw. 1620 1621 // We protect the existing geometry here since it may not be 1622 // clear to the caller that a draw operation (i.e., drawSimpleRect) 1623 // can be invoked in this method 1624 { 1625 GrPipelineBuilder pipelineBuilder; 1626 SkASSERT(fp); 1627 pipelineBuilder.addColorProcessor(fp); 1628 1629 pipelineBuilder.setRenderTarget(tempTexture->asRenderTarget()); 1630 SkRect rect = SkRect::MakeWH(SkIntToScalar(width), SkIntToScalar(height)); 1631 fDrawBuffer->drawSimpleRect(&pipelineBuilder, 1632 GrColor_WHITE, 1633 SkMatrix::I(), 1634 rect); 1635 // we want to read back from the scratch's origin 1636 left = 0; 1637 top = 0; 1638 target = tempTexture->asRenderTarget(); 1639 } 1640 this->flushSurfaceWrites(target); 1641 } 1642 } 1643 } 1644 1645 if (!fGpu->readPixels(target, 1646 left, top, width, height, 1647 readConfig, buffer, rowBytes)) { 1648 return false; 1649 } 1650 // Perform any conversions we weren't able to perform using a scratch texture. 1651 if (unpremul || swapRAndB) { 1652 SkDstPixelInfo dstPI; 1653 if (!GrPixelConfig2ColorAndProfileType(dstConfig, &dstPI.fColorType, NULL)) { 1654 return false; 1655 } 1656 dstPI.fAlphaType = kUnpremul_SkAlphaType; 1657 dstPI.fPixels = buffer; 1658 dstPI.fRowBytes = rowBytes; 1659 1660 SkSrcPixelInfo srcPI; 1661 srcPI.fColorType = swapRAndB ? toggle_colortype32(dstPI.fColorType) : dstPI.fColorType; 1662 srcPI.fAlphaType = kPremul_SkAlphaType; 1663 srcPI.fPixels = buffer; 1664 srcPI.fRowBytes = rowBytes; 1665 1666 return srcPI.convertPixelsTo(&dstPI, width, height); 1667 } 1668 return true; 1669 } 1670 1671 void GrContext::prepareSurfaceForExternalRead(GrSurface* surface) { 1672 RETURN_IF_ABANDONED 1673 SkASSERT(surface); 1674 ASSERT_OWNED_RESOURCE(surface); 1675 if (surface->surfacePriv().hasPendingIO()) { 1676 this->flush(); 1677 } 1678 GrRenderTarget* rt = surface->asRenderTarget(); 1679 if (fGpu && rt) { 1680 fGpu->resolveRenderTarget(rt); 1681 } 1682 } 1683 1684 void GrContext::discardRenderTarget(GrRenderTarget* renderTarget) { 1685 RETURN_IF_ABANDONED 1686 SkASSERT(renderTarget); 1687 ASSERT_OWNED_RESOURCE(renderTarget); 1688 AutoCheckFlush acf(this); 1689 GrDrawTarget* target = this->prepareToDraw(); 1690 if (NULL == target) { 1691 return; 1692 } 1693 target->discard(renderTarget); 1694 } 1695 1696 void GrContext::copySurface(GrSurface* dst, GrSurface* src, const SkIRect& srcRect, 1697 const SkIPoint& dstPoint, uint32_t pixelOpsFlags) { 1698 RETURN_IF_ABANDONED 1699 if (NULL == src || NULL == dst) { 1700 return; 1701 } 1702 ASSERT_OWNED_RESOURCE(src); 1703 ASSERT_OWNED_RESOURCE(dst); 1704 1705 // Since we're going to the draw target and not GPU, no need to check kNoFlush 1706 // here. 1707 1708 GrDrawTarget* target = this->prepareToDraw(); 1709 if (NULL == target) { 1710 return; 1711 } 1712 target->copySurface(dst, src, srcRect, dstPoint); 1713 1714 if (kFlushWrites_PixelOp & pixelOpsFlags) { 1715 this->flush(); 1716 } 1717 } 1718 1719 void GrContext::flushSurfaceWrites(GrSurface* surface) { 1720 RETURN_IF_ABANDONED 1721 if (surface->surfacePriv().hasPendingWrite()) { 1722 this->flush(); 1723 } 1724 } 1725 1726 GrDrawTarget* GrContext::prepareToDraw(GrPipelineBuilder* pipelineBuilder, 1727 GrRenderTarget* rt, 1728 const GrClip& clip, 1729 const GrPaint* paint, 1730 const AutoCheckFlush* acf) { 1731 if (NULL == fGpu || NULL == fDrawBuffer) { 1732 return NULL; 1733 } 1734 1735 ASSERT_OWNED_RESOURCE(rt); 1736 SkASSERT(rt && paint && acf); 1737 pipelineBuilder->setFromPaint(*paint, rt, clip); 1738 return fDrawBuffer; 1739 } 1740 1741 GrDrawTarget* GrContext::prepareToDraw() { 1742 if (NULL == fGpu) { 1743 return NULL; 1744 } 1745 return fDrawBuffer; 1746 } 1747 1748 /* 1749 * This method finds a path renderer that can draw the specified path on 1750 * the provided target. 1751 * Due to its expense, the software path renderer has split out so it can 1752 * can be individually allowed/disallowed via the "allowSW" boolean. 1753 */ 1754 GrPathRenderer* GrContext::getPathRenderer(const GrDrawTarget* target, 1755 const GrPipelineBuilder* pipelineBuilder, 1756 const SkMatrix& viewMatrix, 1757 const SkPath& path, 1758 const GrStrokeInfo& stroke, 1759 bool allowSW, 1760 GrPathRendererChain::DrawType drawType, 1761 GrPathRendererChain::StencilSupport* stencilSupport) { 1762 1763 if (NULL == fPathRendererChain) { 1764 fPathRendererChain = SkNEW_ARGS(GrPathRendererChain, (this)); 1765 } 1766 1767 GrPathRenderer* pr = fPathRendererChain->getPathRenderer(target, 1768 pipelineBuilder, 1769 viewMatrix, 1770 path, 1771 stroke, 1772 drawType, 1773 stencilSupport); 1774 1775 if (NULL == pr && allowSW) { 1776 if (NULL == fSoftwarePathRenderer) { 1777 fSoftwarePathRenderer = SkNEW_ARGS(GrSoftwarePathRenderer, (this)); 1778 } 1779 pr = fSoftwarePathRenderer; 1780 } 1781 1782 return pr; 1783 } 1784 1785 //////////////////////////////////////////////////////////////////////////////// 1786 bool GrContext::isConfigRenderable(GrPixelConfig config, bool withMSAA) const { 1787 return fGpu->caps()->isConfigRenderable(config, withMSAA); 1788 } 1789 1790 int GrContext::getRecommendedSampleCount(GrPixelConfig config, 1791 SkScalar dpi) const { 1792 if (!this->isConfigRenderable(config, true)) { 1793 return 0; 1794 } 1795 int chosenSampleCount = 0; 1796 if (fGpu->caps()->shaderCaps()->pathRenderingSupport()) { 1797 if (dpi >= 250.0f) { 1798 chosenSampleCount = 4; 1799 } else { 1800 chosenSampleCount = 16; 1801 } 1802 } 1803 return chosenSampleCount <= fGpu->caps()->maxSampleCount() ? 1804 chosenSampleCount : 0; 1805 } 1806 1807 GrDrawTarget* GrContext::getTextTarget() { 1808 return this->prepareToDraw(); 1809 } 1810 1811 namespace { 1812 void test_pm_conversions(GrContext* ctx, int* pmToUPMValue, int* upmToPMValue) { 1813 GrConfigConversionEffect::PMConversion pmToUPM; 1814 GrConfigConversionEffect::PMConversion upmToPM; 1815 GrConfigConversionEffect::TestForPreservingPMConversions(ctx, &pmToUPM, &upmToPM); 1816 *pmToUPMValue = pmToUPM; 1817 *upmToPMValue = upmToPM; 1818 } 1819 } 1820 1821 const GrFragmentProcessor* GrContext::createPMToUPMEffect(GrTexture* texture, 1822 bool swapRAndB, 1823 const SkMatrix& matrix) { 1824 if (!fDidTestPMConversions) { 1825 test_pm_conversions(this, &fPMToUPMConversion, &fUPMToPMConversion); 1826 fDidTestPMConversions = true; 1827 } 1828 GrConfigConversionEffect::PMConversion pmToUPM = 1829 static_cast<GrConfigConversionEffect::PMConversion>(fPMToUPMConversion); 1830 if (GrConfigConversionEffect::kNone_PMConversion != pmToUPM) { 1831 return GrConfigConversionEffect::Create(texture, swapRAndB, pmToUPM, matrix); 1832 } else { 1833 return NULL; 1834 } 1835 } 1836 1837 const GrFragmentProcessor* GrContext::createUPMToPMEffect(GrTexture* texture, 1838 bool swapRAndB, 1839 const SkMatrix& matrix) { 1840 if (!fDidTestPMConversions) { 1841 test_pm_conversions(this, &fPMToUPMConversion, &fUPMToPMConversion); 1842 fDidTestPMConversions = true; 1843 } 1844 GrConfigConversionEffect::PMConversion upmToPM = 1845 static_cast<GrConfigConversionEffect::PMConversion>(fUPMToPMConversion); 1846 if (GrConfigConversionEffect::kNone_PMConversion != upmToPM) { 1847 return GrConfigConversionEffect::Create(texture, swapRAndB, upmToPM, matrix); 1848 } else { 1849 return NULL; 1850 } 1851 } 1852 1853 ////////////////////////////////////////////////////////////////////////////// 1854 1855 void GrContext::getResourceCacheLimits(int* maxTextures, size_t* maxTextureBytes) const { 1856 if (maxTextures) { 1857 *maxTextures = fResourceCache->getMaxResourceCount(); 1858 } 1859 if (maxTextureBytes) { 1860 *maxTextureBytes = fResourceCache->getMaxResourceBytes(); 1861 } 1862 } 1863 1864 void GrContext::setResourceCacheLimits(int maxTextures, size_t maxTextureBytes) { 1865 fResourceCache->setLimits(maxTextures, maxTextureBytes); 1866 } 1867 1868 ////////////////////////////////////////////////////////////////////////////// 1869 1870 void GrContext::addGpuTraceMarker(const GrGpuTraceMarker* marker) { 1871 fGpu->addGpuTraceMarker(marker); 1872 if (fDrawBuffer) { 1873 fDrawBuffer->addGpuTraceMarker(marker); 1874 } 1875 } 1876 1877 void GrContext::removeGpuTraceMarker(const GrGpuTraceMarker* marker) { 1878 fGpu->removeGpuTraceMarker(marker); 1879 if (fDrawBuffer) { 1880 fDrawBuffer->removeGpuTraceMarker(marker); 1881 } 1882 } 1883 1884 /////////////////////////////////////////////////////////////////////////////////////////////////// 1885 1886 #ifdef GR_TEST_UTILS 1887 1888 BATCH_TEST_DEFINE(StrokeRectBatch) { 1889 StrokeRectBatch::Geometry geometry; 1890 geometry.fViewMatrix = GrTest::TestMatrix(random); 1891 geometry.fColor = GrRandomColor(random); 1892 geometry.fRect = GrTest::TestRect(random); 1893 geometry.fStrokeWidth = random->nextBool() ? 0.0f : 1.0f; 1894 1895 return StrokeRectBatch::Create(geometry, random->nextBool()); 1896 } 1897 1898 static uint32_t seed_vertices(GrPrimitiveType type) { 1899 switch (type) { 1900 case kTriangles_GrPrimitiveType: 1901 case kTriangleStrip_GrPrimitiveType: 1902 case kTriangleFan_GrPrimitiveType: 1903 return 3; 1904 case kPoints_GrPrimitiveType: 1905 return 1; 1906 case kLines_GrPrimitiveType: 1907 case kLineStrip_GrPrimitiveType: 1908 return 2; 1909 } 1910 SkFAIL("Incomplete switch\n"); 1911 return 0; 1912 } 1913 1914 static uint32_t primitive_vertices(GrPrimitiveType type) { 1915 switch (type) { 1916 case kTriangles_GrPrimitiveType: 1917 return 3; 1918 case kLines_GrPrimitiveType: 1919 return 2; 1920 case kTriangleStrip_GrPrimitiveType: 1921 case kTriangleFan_GrPrimitiveType: 1922 case kPoints_GrPrimitiveType: 1923 case kLineStrip_GrPrimitiveType: 1924 return 1; 1925 } 1926 SkFAIL("Incomplete switch\n"); 1927 return 0; 1928 } 1929 1930 static SkPoint random_point(SkRandom* random, SkScalar min, SkScalar max) { 1931 SkPoint p; 1932 p.fX = random->nextRangeScalar(min, max); 1933 p.fY = random->nextRangeScalar(min, max); 1934 return p; 1935 } 1936 1937 static void randomize_params(size_t count, size_t maxVertex, SkScalar min, SkScalar max, 1938 SkRandom* random, 1939 SkTArray<SkPoint>* positions, 1940 SkTArray<SkPoint>* texCoords, bool hasTexCoords, 1941 SkTArray<GrColor>* colors, bool hasColors, 1942 SkTArray<uint16_t>* indices, bool hasIndices) { 1943 for (uint32_t v = 0; v < count; v++) { 1944 positions->push_back(random_point(random, min, max)); 1945 if (hasTexCoords) { 1946 texCoords->push_back(random_point(random, min, max)); 1947 } 1948 if (hasColors) { 1949 colors->push_back(GrRandomColor(random)); 1950 } 1951 if (hasIndices) { 1952 SkASSERT(maxVertex <= SK_MaxU16); 1953 indices->push_back(random->nextULessThan((uint16_t)maxVertex)); 1954 } 1955 } 1956 } 1957 1958 BATCH_TEST_DEFINE(VerticesBatch) { 1959 GrPrimitiveType type = GrPrimitiveType(random->nextULessThan(kLast_GrPrimitiveType + 1)); 1960 uint32_t primitiveCount = random->nextRangeU(1, 100); 1961 1962 // TODO make 'sensible' indexbuffers 1963 SkTArray<SkPoint> positions; 1964 SkTArray<SkPoint> texCoords; 1965 SkTArray<GrColor> colors; 1966 SkTArray<uint16_t> indices; 1967 1968 bool hasTexCoords = random->nextBool(); 1969 bool hasIndices = random->nextBool(); 1970 bool hasColors = random->nextBool(); 1971 1972 uint32_t vertexCount = seed_vertices(type) + (primitiveCount - 1) * primitive_vertices(type); 1973 1974 static const SkScalar kMinVertExtent = -100.f; 1975 static const SkScalar kMaxVertExtent = 100.f; 1976 randomize_params(seed_vertices(type), vertexCount, kMinVertExtent, kMaxVertExtent, 1977 random, 1978 &positions, 1979 &texCoords, hasTexCoords, 1980 &colors, hasColors, 1981 &indices, hasIndices); 1982 1983 for (uint32_t i = 1; i < primitiveCount; i++) { 1984 randomize_params(primitive_vertices(type), vertexCount, kMinVertExtent, kMaxVertExtent, 1985 random, 1986 &positions, 1987 &texCoords, hasTexCoords, 1988 &colors, hasColors, 1989 &indices, hasIndices); 1990 } 1991 1992 SkMatrix viewMatrix = GrTest::TestMatrix(random); 1993 SkRect bounds; 1994 SkDEBUGCODE(bool result = ) bounds.setBoundsCheck(positions.begin(), vertexCount); 1995 SkASSERT(result); 1996 1997 viewMatrix.mapRect(&bounds); 1998 1999 DrawVerticesBatch::Geometry geometry; 2000 geometry.fColor = GrRandomColor(random); 2001 return DrawVerticesBatch::Create(geometry, type, viewMatrix, 2002 positions.begin(), vertexCount, 2003 indices.begin(), hasIndices ? vertexCount : 0, 2004 colors.begin(), 2005 texCoords.begin(), 2006 bounds); 2007 } 2008 2009 #endif 2010