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