gfx/skia/trunk/src/gpu/GrContext.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1
michael@0 2 /*
michael@0 3 * Copyright 2011 Google Inc.
michael@0 4 *
michael@0 5 * Use of this source code is governed by a BSD-style license that can be
michael@0 6 * found in the LICENSE file.
michael@0 7 */
michael@0 8
michael@0 9
michael@0 10 #include "GrContext.h"
michael@0 11
michael@0 12 #include "effects/GrSingleTextureEffect.h"
michael@0 13 #include "effects/GrConfigConversionEffect.h"
michael@0 14
michael@0 15 #include "GrAARectRenderer.h"
michael@0 16 #include "GrBufferAllocPool.h"
michael@0 17 #include "GrGpu.h"
michael@0 18 #include "GrDrawTargetCaps.h"
michael@0 19 #include "GrIndexBuffer.h"
michael@0 20 #include "GrInOrderDrawBuffer.h"
michael@0 21 #include "GrOvalRenderer.h"
michael@0 22 #include "GrPathRenderer.h"
michael@0 23 #include "GrPathUtils.h"
michael@0 24 #include "GrResourceCache.h"
michael@0 25 #include "GrSoftwarePathRenderer.h"
michael@0 26 #include "GrStencilBuffer.h"
michael@0 27 #include "GrTextStrike.h"
michael@0 28 #include "SkRTConf.h"
michael@0 29 #include "SkRRect.h"
michael@0 30 #include "SkStrokeRec.h"
michael@0 31 #include "SkTLazy.h"
michael@0 32 #include "SkTLS.h"
michael@0 33 #include "SkTrace.h"
michael@0 34
michael@0 35 // It can be useful to set this to false to test whether a bug is caused by using the
michael@0 36 // InOrderDrawBuffer, to compare performance of using/not using InOrderDrawBuffer, or to make
michael@0 37 // debugging simpler.
michael@0 38 SK_CONF_DECLARE(bool, c_Defer, "gpu.deferContext", true,
michael@0 39 "Defers rendering in GrContext via GrInOrderDrawBuffer.");
michael@0 40
michael@0 41 #define BUFFERED_DRAW (c_Defer ? kYes_BufferedDraw : kNo_BufferedDraw)
michael@0 42
michael@0 43 #ifdef SK_DEBUG
michael@0 44 // change this to a 1 to see notifications when partial coverage fails
michael@0 45 #define GR_DEBUG_PARTIAL_COVERAGE_CHECK 0
michael@0 46 #else
michael@0 47 #define GR_DEBUG_PARTIAL_COVERAGE_CHECK 0
michael@0 48 #endif
michael@0 49
michael@0 50 static const size_t MAX_RESOURCE_CACHE_COUNT = GR_DEFAULT_RESOURCE_CACHE_COUNT_LIMIT;
michael@0 51 static const size_t MAX_RESOURCE_CACHE_BYTES = GR_DEFAULT_RESOURCE_CACHE_MB_LIMIT * 1024 * 1024;
michael@0 52
michael@0 53 static const size_t DRAW_BUFFER_VBPOOL_BUFFER_SIZE = 1 << 15;
michael@0 54 static const int DRAW_BUFFER_VBPOOL_PREALLOC_BUFFERS = 4;
michael@0 55
michael@0 56 static const size_t DRAW_BUFFER_IBPOOL_BUFFER_SIZE = 1 << 11;
michael@0 57 static const int DRAW_BUFFER_IBPOOL_PREALLOC_BUFFERS = 4;
michael@0 58
michael@0 59 #define ASSERT_OWNED_RESOURCE(R) SkASSERT(!(R) || (R)->getContext() == this)
michael@0 60
michael@0 61 // Glorified typedef to avoid including GrDrawState.h in GrContext.h
michael@0 62 class GrContext::AutoRestoreEffects : public GrDrawState::AutoRestoreEffects {};
michael@0 63
michael@0 64 class GrContext::AutoCheckFlush {
michael@0 65 public:
michael@0 66 AutoCheckFlush(GrContext* context) : fContext(context) { SkASSERT(NULL != context); }
michael@0 67
michael@0 68 ~AutoCheckFlush() {
michael@0 69 if (fContext->fFlushToReduceCacheSize) {
michael@0 70 fContext->flush();
michael@0 71 }
michael@0 72 }
michael@0 73
michael@0 74 private:
michael@0 75 GrContext* fContext;
michael@0 76 };
michael@0 77
michael@0 78 GrContext* GrContext::Create(GrBackend backend, GrBackendContext backendContext) {
michael@0 79 GrContext* context = SkNEW(GrContext);
michael@0 80 if (context->init(backend, backendContext)) {
michael@0 81 return context;
michael@0 82 } else {
michael@0 83 context->unref();
michael@0 84 return NULL;
michael@0 85 }
michael@0 86 }
michael@0 87
michael@0 88 GrContext::GrContext() {
michael@0 89 fDrawState = NULL;
michael@0 90 fGpu = NULL;
michael@0 91 fClip = NULL;
michael@0 92 fPathRendererChain = NULL;
michael@0 93 fSoftwarePathRenderer = NULL;
michael@0 94 fTextureCache = NULL;
michael@0 95 fFontCache = NULL;
michael@0 96 fDrawBuffer = NULL;
michael@0 97 fDrawBufferVBAllocPool = NULL;
michael@0 98 fDrawBufferIBAllocPool = NULL;
michael@0 99 fFlushToReduceCacheSize = false;
michael@0 100 fAARectRenderer = NULL;
michael@0 101 fOvalRenderer = NULL;
michael@0 102 fViewMatrix.reset();
michael@0 103 fMaxTextureSizeOverride = 1 << 20;
michael@0 104 }
michael@0 105
michael@0 106 bool GrContext::init(GrBackend backend, GrBackendContext backendContext) {
michael@0 107 SkASSERT(NULL == fGpu);
michael@0 108
michael@0 109 fGpu = GrGpu::Create(backend, backendContext, this);
michael@0 110 if (NULL == fGpu) {
michael@0 111 return false;
michael@0 112 }
michael@0 113
michael@0 114 fDrawState = SkNEW(GrDrawState);
michael@0 115 fGpu->setDrawState(fDrawState);
michael@0 116
michael@0 117 fTextureCache = SkNEW_ARGS(GrResourceCache,
michael@0 118 (MAX_RESOURCE_CACHE_COUNT,
michael@0 119 MAX_RESOURCE_CACHE_BYTES));
michael@0 120 fTextureCache->setOverbudgetCallback(OverbudgetCB, this);
michael@0 121
michael@0 122 fFontCache = SkNEW_ARGS(GrFontCache, (fGpu));
michael@0 123
michael@0 124 fLastDrawWasBuffered = kNo_BufferedDraw;
michael@0 125
michael@0 126 fAARectRenderer = SkNEW(GrAARectRenderer);
michael@0 127 fOvalRenderer = SkNEW(GrOvalRenderer);
michael@0 128
michael@0 129 fDidTestPMConversions = false;
michael@0 130
michael@0 131 this->setupDrawBuffer();
michael@0 132
michael@0 133 return true;
michael@0 134 }
michael@0 135
michael@0 136 GrContext::~GrContext() {
michael@0 137 if (NULL == fGpu) {
michael@0 138 return;
michael@0 139 }
michael@0 140
michael@0 141 this->flush();
michael@0 142
michael@0 143 for (int i = 0; i < fCleanUpData.count(); ++i) {
michael@0 144 (*fCleanUpData[i].fFunc)(this, fCleanUpData[i].fInfo);
michael@0 145 }
michael@0 146
michael@0 147 // Since the gpu can hold scratch textures, give it a chance to let go
michael@0 148 // of them before freeing the texture cache
michael@0 149 fGpu->purgeResources();
michael@0 150
michael@0 151 delete fTextureCache;
michael@0 152 fTextureCache = NULL;
michael@0 153 delete fFontCache;
michael@0 154 delete fDrawBuffer;
michael@0 155 delete fDrawBufferVBAllocPool;
michael@0 156 delete fDrawBufferIBAllocPool;
michael@0 157
michael@0 158 fAARectRenderer->unref();
michael@0 159 fOvalRenderer->unref();
michael@0 160
michael@0 161 fGpu->unref();
michael@0 162 SkSafeUnref(fPathRendererChain);
michael@0 163 SkSafeUnref(fSoftwarePathRenderer);
michael@0 164 fDrawState->unref();
michael@0 165 }
michael@0 166
michael@0 167 void GrContext::contextLost() {
michael@0 168 this->contextDestroyed();
michael@0 169 this->setupDrawBuffer();
michael@0 170 }
michael@0 171
michael@0 172 void GrContext::contextDestroyed() {
michael@0 173 // abandon first to so destructors
michael@0 174 // don't try to free the resources in the API.
michael@0 175 fGpu->abandonResources();
michael@0 176
michael@0 177 // a path renderer may be holding onto resources that
michael@0 178 // are now unusable
michael@0 179 SkSafeSetNull(fPathRendererChain);
michael@0 180 SkSafeSetNull(fSoftwarePathRenderer);
michael@0 181
michael@0 182 delete fDrawBuffer;
michael@0 183 fDrawBuffer = NULL;
michael@0 184
michael@0 185 delete fDrawBufferVBAllocPool;
michael@0 186 fDrawBufferVBAllocPool = NULL;
michael@0 187
michael@0 188 delete fDrawBufferIBAllocPool;
michael@0 189 fDrawBufferIBAllocPool = NULL;
michael@0 190
michael@0 191 fAARectRenderer->reset();
michael@0 192 fOvalRenderer->reset();
michael@0 193
michael@0 194 fTextureCache->purgeAllUnlocked();
michael@0 195
michael@0 196 fFontCache->freeAll();
michael@0 197 fGpu->markContextDirty();
michael@0 198 }
michael@0 199
michael@0 200 void GrContext::resetContext(uint32_t state) {
michael@0 201 fGpu->markContextDirty(state);
michael@0 202 }
michael@0 203
michael@0 204 void GrContext::freeGpuResources() {
michael@0 205 this->flush();
michael@0 206
michael@0 207 fGpu->purgeResources();
michael@0 208
michael@0 209 fAARectRenderer->reset();
michael@0 210 fOvalRenderer->reset();
michael@0 211
michael@0 212 fTextureCache->purgeAllUnlocked();
michael@0 213 fFontCache->freeAll();
michael@0 214 // a path renderer may be holding onto resources
michael@0 215 SkSafeSetNull(fPathRendererChain);
michael@0 216 SkSafeSetNull(fSoftwarePathRenderer);
michael@0 217 }
michael@0 218
michael@0 219 size_t GrContext::getGpuTextureCacheBytes() const {
michael@0 220 return fTextureCache->getCachedResourceBytes();
michael@0 221 }
michael@0 222
michael@0 223 ////////////////////////////////////////////////////////////////////////////////
michael@0 224
michael@0 225 GrTexture* GrContext::findAndRefTexture(const GrTextureDesc& desc,
michael@0 226 const GrCacheID& cacheID,
michael@0 227 const GrTextureParams* params) {
michael@0 228 GrResourceKey resourceKey = GrTexture::ComputeKey(fGpu, params, desc, cacheID);
michael@0 229 GrResource* resource = fTextureCache->find(resourceKey);
michael@0 230 SkSafeRef(resource);
michael@0 231 return static_cast<GrTexture*>(resource);
michael@0 232 }
michael@0 233
michael@0 234 bool GrContext::isTextureInCache(const GrTextureDesc& desc,
michael@0 235 const GrCacheID& cacheID,
michael@0 236 const GrTextureParams* params) const {
michael@0 237 GrResourceKey resourceKey = GrTexture::ComputeKey(fGpu, params, desc, cacheID);
michael@0 238 return fTextureCache->hasKey(resourceKey);
michael@0 239 }
michael@0 240
michael@0 241 void GrContext::addStencilBuffer(GrStencilBuffer* sb) {
michael@0 242 ASSERT_OWNED_RESOURCE(sb);
michael@0 243
michael@0 244 GrResourceKey resourceKey = GrStencilBuffer::ComputeKey(sb->width(),
michael@0 245 sb->height(),
michael@0 246 sb->numSamples());
michael@0 247 fTextureCache->addResource(resourceKey, sb);
michael@0 248 }
michael@0 249
michael@0 250 GrStencilBuffer* GrContext::findStencilBuffer(int width, int height,
michael@0 251 int sampleCnt) {
michael@0 252 GrResourceKey resourceKey = GrStencilBuffer::ComputeKey(width,
michael@0 253 height,
michael@0 254 sampleCnt);
michael@0 255 GrResource* resource = fTextureCache->find(resourceKey);
michael@0 256 return static_cast<GrStencilBuffer*>(resource);
michael@0 257 }
michael@0 258
michael@0 259 static void stretchImage(void* dst,
michael@0 260 int dstW,
michael@0 261 int dstH,
michael@0 262 void* src,
michael@0 263 int srcW,
michael@0 264 int srcH,
michael@0 265 size_t bpp) {
michael@0 266 GrFixed dx = (srcW << 16) / dstW;
michael@0 267 GrFixed dy = (srcH << 16) / dstH;
michael@0 268
michael@0 269 GrFixed y = dy >> 1;
michael@0 270
michael@0 271 size_t dstXLimit = dstW*bpp;
michael@0 272 for (int j = 0; j < dstH; ++j) {
michael@0 273 GrFixed x = dx >> 1;
michael@0 274 void* srcRow = (uint8_t*)src + (y>>16)*srcW*bpp;
michael@0 275 void* dstRow = (uint8_t*)dst + j*dstW*bpp;
michael@0 276 for (size_t i = 0; i < dstXLimit; i += bpp) {
michael@0 277 memcpy((uint8_t*) dstRow + i,
michael@0 278 (uint8_t*) srcRow + (x>>16)*bpp,
michael@0 279 bpp);
michael@0 280 x += dx;
michael@0 281 }
michael@0 282 y += dy;
michael@0 283 }
michael@0 284 }
michael@0 285
michael@0 286 namespace {
michael@0 287
michael@0 288 // position + local coordinate
michael@0 289 extern const GrVertexAttrib gVertexAttribs[] = {
michael@0 290 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
michael@0 291 {kVec2f_GrVertexAttribType, sizeof(GrPoint), kLocalCoord_GrVertexAttribBinding}
michael@0 292 };
michael@0 293
michael@0 294 };
michael@0 295
michael@0 296 // The desired texture is NPOT and tiled but that isn't supported by
michael@0 297 // the current hardware. Resize the texture to be a POT
michael@0 298 GrTexture* GrContext::createResizedTexture(const GrTextureDesc& desc,
michael@0 299 const GrCacheID& cacheID,
michael@0 300 void* srcData,
michael@0 301 size_t rowBytes,
michael@0 302 bool filter) {
michael@0 303 SkAutoTUnref<GrTexture> clampedTexture(this->findAndRefTexture(desc, cacheID, NULL));
michael@0 304 if (NULL == clampedTexture) {
michael@0 305 clampedTexture.reset(this->createTexture(NULL, desc, cacheID, srcData, rowBytes));
michael@0 306
michael@0 307 if (NULL == clampedTexture) {
michael@0 308 return NULL;
michael@0 309 }
michael@0 310 }
michael@0 311
michael@0 312 GrTextureDesc rtDesc = desc;
michael@0 313 rtDesc.fFlags = rtDesc.fFlags |
michael@0 314 kRenderTarget_GrTextureFlagBit |
michael@0 315 kNoStencil_GrTextureFlagBit;
michael@0 316 rtDesc.fWidth = GrNextPow2(desc.fWidth);
michael@0 317 rtDesc.fHeight = GrNextPow2(desc.fHeight);
michael@0 318
michael@0 319 GrTexture* texture = fGpu->createTexture(rtDesc, NULL, 0);
michael@0 320
michael@0 321 if (NULL != texture) {
michael@0 322 GrDrawTarget::AutoStateRestore asr(fGpu, GrDrawTarget::kReset_ASRInit);
michael@0 323 GrDrawState* drawState = fGpu->drawState();
michael@0 324 drawState->setRenderTarget(texture->asRenderTarget());
michael@0 325
michael@0 326 // if filtering is not desired then we want to ensure all
michael@0 327 // texels in the resampled image are copies of texels from
michael@0 328 // the original.
michael@0 329 GrTextureParams params(SkShader::kClamp_TileMode, filter ? GrTextureParams::kBilerp_FilterMode :
michael@0 330 GrTextureParams::kNone_FilterMode);
michael@0 331 drawState->addColorTextureEffect(clampedTexture, SkMatrix::I(), params);
michael@0 332
michael@0 333 drawState->setVertexAttribs<gVertexAttribs>(SK_ARRAY_COUNT(gVertexAttribs));
michael@0 334
michael@0 335 GrDrawTarget::AutoReleaseGeometry arg(fGpu, 4, 0);
michael@0 336
michael@0 337 if (arg.succeeded()) {
michael@0 338 GrPoint* verts = (GrPoint*) arg.vertices();
michael@0 339 verts[0].setIRectFan(0, 0, texture->width(), texture->height(), 2 * sizeof(GrPoint));
michael@0 340 verts[1].setIRectFan(0, 0, 1, 1, 2 * sizeof(GrPoint));
michael@0 341 fGpu->drawNonIndexed(kTriangleFan_GrPrimitiveType, 0, 4);
michael@0 342 }
michael@0 343 } else {
michael@0 344 // TODO: Our CPU stretch doesn't filter. But we create separate
michael@0 345 // stretched textures when the texture params is either filtered or
michael@0 346 // not. Either implement filtered stretch blit on CPU or just create
michael@0 347 // one when FBO case fails.
michael@0 348
michael@0 349 rtDesc.fFlags = kNone_GrTextureFlags;
michael@0 350 // no longer need to clamp at min RT size.
michael@0 351 rtDesc.fWidth = GrNextPow2(desc.fWidth);
michael@0 352 rtDesc.fHeight = GrNextPow2(desc.fHeight);
michael@0 353 size_t bpp = GrBytesPerPixel(desc.fConfig);
michael@0 354 SkAutoSMalloc<128*128*4> stretchedPixels(bpp * rtDesc.fWidth * rtDesc.fHeight);
michael@0 355 stretchImage(stretchedPixels.get(), rtDesc.fWidth, rtDesc.fHeight,
michael@0 356 srcData, desc.fWidth, desc.fHeight, bpp);
michael@0 357
michael@0 358 size_t stretchedRowBytes = rtDesc.fWidth * bpp;
michael@0 359
michael@0 360 SkDEBUGCODE(GrTexture* texture = )fGpu->createTexture(rtDesc, stretchedPixels.get(),
michael@0 361 stretchedRowBytes);
michael@0 362 SkASSERT(NULL != texture);
michael@0 363 }
michael@0 364
michael@0 365 return texture;
michael@0 366 }
michael@0 367
michael@0 368 GrTexture* GrContext::createTexture(const GrTextureParams* params,
michael@0 369 const GrTextureDesc& desc,
michael@0 370 const GrCacheID& cacheID,
michael@0 371 void* srcData,
michael@0 372 size_t rowBytes,
michael@0 373 GrResourceKey* cacheKey) {
michael@0 374 SK_TRACE_EVENT0("GrContext::createTexture");
michael@0 375
michael@0 376 GrResourceKey resourceKey = GrTexture::ComputeKey(fGpu, params, desc, cacheID);
michael@0 377
michael@0 378 GrTexture* texture;
michael@0 379 if (GrTexture::NeedsResizing(resourceKey)) {
michael@0 380 texture = this->createResizedTexture(desc, cacheID,
michael@0 381 srcData, rowBytes,
michael@0 382 GrTexture::NeedsBilerp(resourceKey));
michael@0 383 } else {
michael@0 384 texture= fGpu->createTexture(desc, srcData, rowBytes);
michael@0 385 }
michael@0 386
michael@0 387 if (NULL != texture) {
michael@0 388 // Adding a resource could put us overbudget. Try to free up the
michael@0 389 // necessary space before adding it.
michael@0 390 fTextureCache->purgeAsNeeded(1, texture->sizeInBytes());
michael@0 391 fTextureCache->addResource(resourceKey, texture);
michael@0 392
michael@0 393 if (NULL != cacheKey) {
michael@0 394 *cacheKey = resourceKey;
michael@0 395 }
michael@0 396 }
michael@0 397
michael@0 398 return texture;
michael@0 399 }
michael@0 400
michael@0 401 static GrTexture* create_scratch_texture(GrGpu* gpu,
michael@0 402 GrResourceCache* textureCache,
michael@0 403 const GrTextureDesc& desc) {
michael@0 404 GrTexture* texture = gpu->createTexture(desc, NULL, 0);
michael@0 405 if (NULL != texture) {
michael@0 406 GrResourceKey key = GrTexture::ComputeScratchKey(texture->desc());
michael@0 407 // Adding a resource could put us overbudget. Try to free up the
michael@0 408 // necessary space before adding it.
michael@0 409 textureCache->purgeAsNeeded(1, texture->sizeInBytes());
michael@0 410 // Make the resource exclusive so future 'find' calls don't return it
michael@0 411 textureCache->addResource(key, texture, GrResourceCache::kHide_OwnershipFlag);
michael@0 412 }
michael@0 413 return texture;
michael@0 414 }
michael@0 415
michael@0 416 GrTexture* GrContext::lockAndRefScratchTexture(const GrTextureDesc& inDesc, ScratchTexMatch match) {
michael@0 417
michael@0 418 SkASSERT((inDesc.fFlags & kRenderTarget_GrTextureFlagBit) ||
michael@0 419 !(inDesc.fFlags & kNoStencil_GrTextureFlagBit));
michael@0 420
michael@0 421 // Renderable A8 targets are not universally supported (e.g., not on ANGLE)
michael@0 422 SkASSERT(this->isConfigRenderable(kAlpha_8_GrPixelConfig, inDesc.fSampleCnt > 0) ||
michael@0 423 !(inDesc.fFlags & kRenderTarget_GrTextureFlagBit) ||
michael@0 424 (inDesc.fConfig != kAlpha_8_GrPixelConfig));
michael@0 425
michael@0 426 if (!fGpu->caps()->reuseScratchTextures() &&
michael@0 427 !(inDesc.fFlags & kRenderTarget_GrTextureFlagBit)) {
michael@0 428 // If we're never recycling this texture we can always make it the right size
michael@0 429 return create_scratch_texture(fGpu, fTextureCache, inDesc);
michael@0 430 }
michael@0 431
michael@0 432 GrTextureDesc desc = inDesc;
michael@0 433
michael@0 434 if (kApprox_ScratchTexMatch == match) {
michael@0 435 // bin by pow2 with a reasonable min
michael@0 436 static const int MIN_SIZE = 16;
michael@0 437 desc.fWidth = GrMax(MIN_SIZE, GrNextPow2(desc.fWidth));
michael@0 438 desc.fHeight = GrMax(MIN_SIZE, GrNextPow2(desc.fHeight));
michael@0 439 }
michael@0 440
michael@0 441 GrResource* resource = NULL;
michael@0 442 int origWidth = desc.fWidth;
michael@0 443 int origHeight = desc.fHeight;
michael@0 444
michael@0 445 do {
michael@0 446 GrResourceKey key = GrTexture::ComputeScratchKey(desc);
michael@0 447 // Ensure we have exclusive access to the texture so future 'find' calls don't return it
michael@0 448 resource = fTextureCache->find(key, GrResourceCache::kHide_OwnershipFlag);
michael@0 449 if (NULL != resource) {
michael@0 450 resource->ref();
michael@0 451 break;
michael@0 452 }
michael@0 453 if (kExact_ScratchTexMatch == match) {
michael@0 454 break;
michael@0 455 }
michael@0 456 // We had a cache miss and we are in approx mode, relax the fit of the flags.
michael@0 457
michael@0 458 // We no longer try to reuse textures that were previously used as render targets in
michael@0 459 // situations where no RT is needed; doing otherwise can confuse the video driver and
michael@0 460 // cause significant performance problems in some cases.
michael@0 461 if (desc.fFlags & kNoStencil_GrTextureFlagBit) {
michael@0 462 desc.fFlags = desc.fFlags & ~kNoStencil_GrTextureFlagBit;
michael@0 463 } else {
michael@0 464 break;
michael@0 465 }
michael@0 466
michael@0 467 } while (true);
michael@0 468
michael@0 469 if (NULL == resource) {
michael@0 470 desc.fFlags = inDesc.fFlags;
michael@0 471 desc.fWidth = origWidth;
michael@0 472 desc.fHeight = origHeight;
michael@0 473 resource = create_scratch_texture(fGpu, fTextureCache, desc);
michael@0 474 }
michael@0 475
michael@0 476 return static_cast<GrTexture*>(resource);
michael@0 477 }
michael@0 478
michael@0 479 void GrContext::addExistingTextureToCache(GrTexture* texture) {
michael@0 480
michael@0 481 if (NULL == texture) {
michael@0 482 return;
michael@0 483 }
michael@0 484
michael@0 485 // This texture should already have a cache entry since it was once
michael@0 486 // attached
michael@0 487 SkASSERT(NULL != texture->getCacheEntry());
michael@0 488
michael@0 489 // Conceptually, the cache entry is going to assume responsibility
michael@0 490 // for the creation ref. Assert refcnt == 1.
michael@0 491 SkASSERT(texture->unique());
michael@0 492
michael@0 493 if (fGpu->caps()->reuseScratchTextures() || NULL != texture->asRenderTarget()) {
michael@0 494 // Since this texture came from an AutoScratchTexture it should
michael@0 495 // still be in the exclusive pile. Recycle it.
michael@0 496 fTextureCache->makeNonExclusive(texture->getCacheEntry());
michael@0 497 this->purgeCache();
michael@0 498 } else if (texture->getDeferredRefCount() <= 0) {
michael@0 499 // When we aren't reusing textures we know this scratch texture
michael@0 500 // will never be reused and would be just wasting time in the cache
michael@0 501 fTextureCache->makeNonExclusive(texture->getCacheEntry());
michael@0 502 fTextureCache->deleteResource(texture->getCacheEntry());
michael@0 503 } else {
michael@0 504 // In this case (fDeferredRefCount > 0) but the cache is the only
michael@0 505 // one holding a real ref. Mark the object so when the deferred
michael@0 506 // ref count goes to 0 the texture will be deleted (remember
michael@0 507 // in this code path scratch textures aren't getting reused).
michael@0 508 texture->setNeedsDeferredUnref();
michael@0 509 }
michael@0 510 }
michael@0 511
michael@0 512
michael@0 513 void GrContext::unlockScratchTexture(GrTexture* texture) {
michael@0 514 ASSERT_OWNED_RESOURCE(texture);
michael@0 515 SkASSERT(NULL != texture->getCacheEntry());
michael@0 516
michael@0 517 // If this is a scratch texture we detached it from the cache
michael@0 518 // while it was locked (to avoid two callers simultaneously getting
michael@0 519 // the same texture).
michael@0 520 if (texture->getCacheEntry()->key().isScratch()) {
michael@0 521 if (fGpu->caps()->reuseScratchTextures() || NULL != texture->asRenderTarget()) {
michael@0 522 fTextureCache->makeNonExclusive(texture->getCacheEntry());
michael@0 523 this->purgeCache();
michael@0 524 } else if (texture->unique() && texture->getDeferredRefCount() <= 0) {
michael@0 525 // Only the cache now knows about this texture. Since we're never
michael@0 526 // reusing scratch textures (in this code path) it would just be
michael@0 527 // wasting time sitting in the cache.
michael@0 528 fTextureCache->makeNonExclusive(texture->getCacheEntry());
michael@0 529 fTextureCache->deleteResource(texture->getCacheEntry());
michael@0 530 } else {
michael@0 531 // In this case (fRefCnt > 1 || defRefCnt > 0) but we don't really
michael@0 532 // want to readd it to the cache (since it will never be reused).
michael@0 533 // Instead, give up the cache's ref and leave the decision up to
michael@0 534 // addExistingTextureToCache once its ref count reaches 0. For
michael@0 535 // this to work we need to leave it in the exclusive list.
michael@0 536 texture->setFlag((GrTextureFlags) GrTexture::kReturnToCache_FlagBit);
michael@0 537 // Give up the cache's ref to the texture
michael@0 538 texture->unref();
michael@0 539 }
michael@0 540 }
michael@0 541 }
michael@0 542
michael@0 543 void GrContext::purgeCache() {
michael@0 544 if (NULL != fTextureCache) {
michael@0 545 fTextureCache->purgeAsNeeded();
michael@0 546 }
michael@0 547 }
michael@0 548
michael@0 549 bool GrContext::OverbudgetCB(void* data) {
michael@0 550 SkASSERT(NULL != data);
michael@0 551
michael@0 552 GrContext* context = reinterpret_cast<GrContext*>(data);
michael@0 553
michael@0 554 // Flush the InOrderDrawBuffer to possibly free up some textures
michael@0 555 context->fFlushToReduceCacheSize = true;
michael@0 556
michael@0 557 return true;
michael@0 558 }
michael@0 559
michael@0 560
michael@0 561 GrTexture* GrContext::createUncachedTexture(const GrTextureDesc& descIn,
michael@0 562 void* srcData,
michael@0 563 size_t rowBytes) {
michael@0 564 GrTextureDesc descCopy = descIn;
michael@0 565 return fGpu->createTexture(descCopy, srcData, rowBytes);
michael@0 566 }
michael@0 567
michael@0 568 void GrContext::getTextureCacheLimits(int* maxTextures,
michael@0 569 size_t* maxTextureBytes) const {
michael@0 570 fTextureCache->getLimits(maxTextures, maxTextureBytes);
michael@0 571 }
michael@0 572
michael@0 573 void GrContext::setTextureCacheLimits(int maxTextures, size_t maxTextureBytes) {
michael@0 574 fTextureCache->setLimits(maxTextures, maxTextureBytes);
michael@0 575 }
michael@0 576
michael@0 577 int GrContext::getMaxTextureSize() const {
michael@0 578 return GrMin(fGpu->caps()->maxTextureSize(), fMaxTextureSizeOverride);
michael@0 579 }
michael@0 580
michael@0 581 int GrContext::getMaxRenderTargetSize() const {
michael@0 582 return fGpu->caps()->maxRenderTargetSize();
michael@0 583 }
michael@0 584
michael@0 585 int GrContext::getMaxSampleCount() const {
michael@0 586 return fGpu->caps()->maxSampleCount();
michael@0 587 }
michael@0 588
michael@0 589 ///////////////////////////////////////////////////////////////////////////////
michael@0 590
michael@0 591 GrTexture* GrContext::wrapBackendTexture(const GrBackendTextureDesc& desc) {
michael@0 592 return fGpu->wrapBackendTexture(desc);
michael@0 593 }
michael@0 594
michael@0 595 GrRenderTarget* GrContext::wrapBackendRenderTarget(const GrBackendRenderTargetDesc& desc) {
michael@0 596 return fGpu->wrapBackendRenderTarget(desc);
michael@0 597 }
michael@0 598
michael@0 599 ///////////////////////////////////////////////////////////////////////////////
michael@0 600
michael@0 601 bool GrContext::supportsIndex8PixelConfig(const GrTextureParams* params,
michael@0 602 int width, int height) const {
michael@0 603 const GrDrawTargetCaps* caps = fGpu->caps();
michael@0 604 if (!caps->eightBitPaletteSupport()) {
michael@0 605 return false;
michael@0 606 }
michael@0 607
michael@0 608 bool isPow2 = GrIsPow2(width) && GrIsPow2(height);
michael@0 609
michael@0 610 if (!isPow2) {
michael@0 611 bool tiled = NULL != params && params->isTiled();
michael@0 612 if (tiled && !caps->npotTextureTileSupport()) {
michael@0 613 return false;
michael@0 614 }
michael@0 615 }
michael@0 616 return true;
michael@0 617 }
michael@0 618
michael@0 619
michael@0 620 ////////////////////////////////////////////////////////////////////////////////
michael@0 621
michael@0 622 void GrContext::clear(const SkIRect* rect,
michael@0 623 const GrColor color,
michael@0 624 bool canIgnoreRect,
michael@0 625 GrRenderTarget* target) {
michael@0 626 AutoRestoreEffects are;
michael@0 627 AutoCheckFlush acf(this);
michael@0 628 this->prepareToDraw(NULL, BUFFERED_DRAW, &are, &acf)->clear(rect, color,
michael@0 629 canIgnoreRect, target);
michael@0 630 }
michael@0 631
michael@0 632 void GrContext::drawPaint(const GrPaint& origPaint) {
michael@0 633 // set rect to be big enough to fill the space, but not super-huge, so we
michael@0 634 // don't overflow fixed-point implementations
michael@0 635 SkRect r;
michael@0 636 r.setLTRB(0, 0,
michael@0 637 SkIntToScalar(getRenderTarget()->width()),
michael@0 638 SkIntToScalar(getRenderTarget()->height()));
michael@0 639 SkMatrix inverse;
michael@0 640 SkTCopyOnFirstWrite<GrPaint> paint(origPaint);
michael@0 641 AutoMatrix am;
michael@0 642
michael@0 643 // We attempt to map r by the inverse matrix and draw that. mapRect will
michael@0 644 // map the four corners and bound them with a new rect. This will not
michael@0 645 // produce a correct result for some perspective matrices.
michael@0 646 if (!this->getMatrix().hasPerspective()) {
michael@0 647 if (!fViewMatrix.invert(&inverse)) {
michael@0 648 GrPrintf("Could not invert matrix\n");
michael@0 649 return;
michael@0 650 }
michael@0 651 inverse.mapRect(&r);
michael@0 652 } else {
michael@0 653 if (!am.setIdentity(this, paint.writable())) {
michael@0 654 GrPrintf("Could not invert matrix\n");
michael@0 655 return;
michael@0 656 }
michael@0 657 }
michael@0 658 // by definition this fills the entire clip, no need for AA
michael@0 659 if (paint->isAntiAlias()) {
michael@0 660 paint.writable()->setAntiAlias(false);
michael@0 661 }
michael@0 662 this->drawRect(*paint, r);
michael@0 663 }
michael@0 664
michael@0 665 #ifdef SK_DEVELOPER
michael@0 666 void GrContext::dumpFontCache() const {
michael@0 667 fFontCache->dump();
michael@0 668 }
michael@0 669 #endif
michael@0 670
michael@0 671 ////////////////////////////////////////////////////////////////////////////////
michael@0 672
michael@0 673 /* create a triangle strip that strokes the specified triangle. There are 8
michael@0 674 unique vertices, but we repreat the last 2 to close up. Alternatively we
michael@0 675 could use an indices array, and then only send 8 verts, but not sure that
michael@0 676 would be faster.
michael@0 677 */
michael@0 678 static void setStrokeRectStrip(GrPoint verts[10], SkRect rect,
michael@0 679 SkScalar width) {
michael@0 680 const SkScalar rad = SkScalarHalf(width);
michael@0 681 rect.sort();
michael@0 682
michael@0 683 verts[0].set(rect.fLeft + rad, rect.fTop + rad);
michael@0 684 verts[1].set(rect.fLeft - rad, rect.fTop - rad);
michael@0 685 verts[2].set(rect.fRight - rad, rect.fTop + rad);
michael@0 686 verts[3].set(rect.fRight + rad, rect.fTop - rad);
michael@0 687 verts[4].set(rect.fRight - rad, rect.fBottom - rad);
michael@0 688 verts[5].set(rect.fRight + rad, rect.fBottom + rad);
michael@0 689 verts[6].set(rect.fLeft + rad, rect.fBottom - rad);
michael@0 690 verts[7].set(rect.fLeft - rad, rect.fBottom + rad);
michael@0 691 verts[8] = verts[0];
michael@0 692 verts[9] = verts[1];
michael@0 693 }
michael@0 694
michael@0 695 static bool isIRect(const SkRect& r) {
michael@0 696 return SkScalarIsInt(r.fLeft) && SkScalarIsInt(r.fTop) &&
michael@0 697 SkScalarIsInt(r.fRight) && SkScalarIsInt(r.fBottom);
michael@0 698 }
michael@0 699
michael@0 700 static bool apply_aa_to_rect(GrDrawTarget* target,
michael@0 701 const SkRect& rect,
michael@0 702 SkScalar strokeWidth,
michael@0 703 const SkMatrix& combinedMatrix,
michael@0 704 SkRect* devBoundRect,
michael@0 705 bool* useVertexCoverage) {
michael@0 706 // we use a simple coverage ramp to do aa on axis-aligned rects
michael@0 707 // we check if the rect will be axis-aligned, and the rect won't land on
michael@0 708 // integer coords.
michael@0 709
michael@0 710 // we are keeping around the "tweak the alpha" trick because
michael@0 711 // it is our only hope for the fixed-pipe implementation.
michael@0 712 // In a shader implementation we can give a separate coverage input
michael@0 713 // TODO: remove this ugliness when we drop the fixed-pipe impl
michael@0 714 *useVertexCoverage = false;
michael@0 715 if (!target->getDrawState().canTweakAlphaForCoverage()) {
michael@0 716 if (target->shouldDisableCoverageAAForBlend()) {
michael@0 717 #ifdef SK_DEBUG
michael@0 718 //GrPrintf("Turning off AA to correctly apply blend.\n");
michael@0 719 #endif
michael@0 720 return false;
michael@0 721 } else {
michael@0 722 *useVertexCoverage = true;
michael@0 723 }
michael@0 724 }
michael@0 725 const GrDrawState& drawState = target->getDrawState();
michael@0 726 if (drawState.getRenderTarget()->isMultisampled()) {
michael@0 727 return false;
michael@0 728 }
michael@0 729
michael@0 730 if (0 == strokeWidth && target->willUseHWAALines()) {
michael@0 731 return false;
michael@0 732 }
michael@0 733
michael@0 734 #if defined(SHADER_AA_FILL_RECT) || !defined(IGNORE_ROT_AA_RECT_OPT)
michael@0 735 if (strokeWidth >= 0) {
michael@0 736 #endif
michael@0 737 if (!combinedMatrix.preservesAxisAlignment()) {
michael@0 738 return false;
michael@0 739 }
michael@0 740
michael@0 741 #if defined(SHADER_AA_FILL_RECT) || !defined(IGNORE_ROT_AA_RECT_OPT)
michael@0 742 } else {
michael@0 743 if (!combinedMatrix.preservesRightAngles()) {
michael@0 744 return false;
michael@0 745 }
michael@0 746 }
michael@0 747 #endif
michael@0 748
michael@0 749 combinedMatrix.mapRect(devBoundRect, rect);
michael@0 750
michael@0 751 if (strokeWidth < 0) {
michael@0 752 return !isIRect(*devBoundRect);
michael@0 753 } else {
michael@0 754 return true;
michael@0 755 }
michael@0 756 }
michael@0 757
michael@0 758 static inline bool rect_contains_inclusive(const SkRect& rect, const SkPoint& point) {
michael@0 759 return point.fX >= rect.fLeft && point.fX <= rect.fRight &&
michael@0 760 point.fY >= rect.fTop && point.fY <= rect.fBottom;
michael@0 761 }
michael@0 762
michael@0 763 void GrContext::drawRect(const GrPaint& paint,
michael@0 764 const SkRect& rect,
michael@0 765 const SkStrokeRec* stroke,
michael@0 766 const SkMatrix* matrix) {
michael@0 767 SK_TRACE_EVENT0("GrContext::drawRect");
michael@0 768
michael@0 769 AutoRestoreEffects are;
michael@0 770 AutoCheckFlush acf(this);
michael@0 771 GrDrawTarget* target = this->prepareToDraw(&paint, BUFFERED_DRAW, &are, &acf);
michael@0 772
michael@0 773 SkScalar width = stroke == NULL ? -1 : stroke->getWidth();
michael@0 774 SkMatrix combinedMatrix = target->drawState()->getViewMatrix();
michael@0 775 if (NULL != matrix) {
michael@0 776 combinedMatrix.preConcat(*matrix);
michael@0 777 }
michael@0 778
michael@0 779 // Check if this is a full RT draw and can be replaced with a clear. We don't bother checking
michael@0 780 // cases where the RT is fully inside a stroke.
michael@0 781 if (width < 0) {
michael@0 782 SkRect rtRect;
michael@0 783 target->getDrawState().getRenderTarget()->getBoundsRect(&rtRect);
michael@0 784 SkRect clipSpaceRTRect = rtRect;
michael@0 785 bool checkClip = false;
michael@0 786 if (NULL != this->getClip()) {
michael@0 787 checkClip = true;
michael@0 788 clipSpaceRTRect.offset(SkIntToScalar(this->getClip()->fOrigin.fX),
michael@0 789 SkIntToScalar(this->getClip()->fOrigin.fY));
michael@0 790 }
michael@0 791 // Does the clip contain the entire RT?
michael@0 792 if (!checkClip || target->getClip()->fClipStack->quickContains(clipSpaceRTRect)) {
michael@0 793 SkMatrix invM;
michael@0 794 if (!combinedMatrix.invert(&invM)) {
michael@0 795 return;
michael@0 796 }
michael@0 797 // Does the rect bound the RT?
michael@0 798 SkPoint srcSpaceRTQuad[4];
michael@0 799 invM.mapRectToQuad(srcSpaceRTQuad, rtRect);
michael@0 800 if (rect_contains_inclusive(rect, srcSpaceRTQuad[0]) &&
michael@0 801 rect_contains_inclusive(rect, srcSpaceRTQuad[1]) &&
michael@0 802 rect_contains_inclusive(rect, srcSpaceRTQuad[2]) &&
michael@0 803 rect_contains_inclusive(rect, srcSpaceRTQuad[3])) {
michael@0 804 // Will it blend?
michael@0 805 GrColor clearColor;
michael@0 806 if (paint.isOpaqueAndConstantColor(&clearColor)) {
michael@0 807 target->clear(NULL, clearColor, true);
michael@0 808 return;
michael@0 809 }
michael@0 810 }
michael@0 811 }
michael@0 812 }
michael@0 813
michael@0 814 SkRect devBoundRect;
michael@0 815 bool useVertexCoverage;
michael@0 816 bool needAA = paint.isAntiAlias() &&
michael@0 817 !target->getDrawState().getRenderTarget()->isMultisampled();
michael@0 818 bool doAA = needAA && apply_aa_to_rect(target, rect, width, combinedMatrix, &devBoundRect,
michael@0 819 &useVertexCoverage);
michael@0 820 if (doAA) {
michael@0 821 GrDrawState::AutoViewMatrixRestore avmr;
michael@0 822 if (!avmr.setIdentity(target->drawState())) {
michael@0 823 return;
michael@0 824 }
michael@0 825 if (width >= 0) {
michael@0 826 fAARectRenderer->strokeAARect(this->getGpu(), target, rect,
michael@0 827 combinedMatrix, devBoundRect,
michael@0 828 stroke, useVertexCoverage);
michael@0 829 } else {
michael@0 830 // filled AA rect
michael@0 831 fAARectRenderer->fillAARect(this->getGpu(), target,
michael@0 832 rect, combinedMatrix, devBoundRect,
michael@0 833 useVertexCoverage);
michael@0 834 }
michael@0 835 return;
michael@0 836 }
michael@0 837
michael@0 838 if (width >= 0) {
michael@0 839 // TODO: consider making static vertex buffers for these cases.
michael@0 840 // Hairline could be done by just adding closing vertex to
michael@0 841 // unitSquareVertexBuffer()
michael@0 842
michael@0 843 static const int worstCaseVertCount = 10;
michael@0 844 target->drawState()->setDefaultVertexAttribs();
michael@0 845 GrDrawTarget::AutoReleaseGeometry geo(target, worstCaseVertCount, 0);
michael@0 846
michael@0 847 if (!geo.succeeded()) {
michael@0 848 GrPrintf("Failed to get space for vertices!\n");
michael@0 849 return;
michael@0 850 }
michael@0 851
michael@0 852 GrPrimitiveType primType;
michael@0 853 int vertCount;
michael@0 854 GrPoint* vertex = geo.positions();
michael@0 855
michael@0 856 if (width > 0) {
michael@0 857 vertCount = 10;
michael@0 858 primType = kTriangleStrip_GrPrimitiveType;
michael@0 859 setStrokeRectStrip(vertex, rect, width);
michael@0 860 } else {
michael@0 861 // hairline
michael@0 862 vertCount = 5;
michael@0 863 primType = kLineStrip_GrPrimitiveType;
michael@0 864 vertex[0].set(rect.fLeft, rect.fTop);
michael@0 865 vertex[1].set(rect.fRight, rect.fTop);
michael@0 866 vertex[2].set(rect.fRight, rect.fBottom);
michael@0 867 vertex[3].set(rect.fLeft, rect.fBottom);
michael@0 868 vertex[4].set(rect.fLeft, rect.fTop);
michael@0 869 }
michael@0 870
michael@0 871 GrDrawState::AutoViewMatrixRestore avmr;
michael@0 872 if (NULL != matrix) {
michael@0 873 GrDrawState* drawState = target->drawState();
michael@0 874 avmr.set(drawState, *matrix);
michael@0 875 }
michael@0 876
michael@0 877 target->drawNonIndexed(primType, 0, vertCount);
michael@0 878 } else {
michael@0 879 // filled BW rect
michael@0 880 target->drawSimpleRect(rect, matrix);
michael@0 881 }
michael@0 882 }
michael@0 883
michael@0 884 void GrContext::drawRectToRect(const GrPaint& paint,
michael@0 885 const SkRect& dstRect,
michael@0 886 const SkRect& localRect,
michael@0 887 const SkMatrix* dstMatrix,
michael@0 888 const SkMatrix* localMatrix) {
michael@0 889 SK_TRACE_EVENT0("GrContext::drawRectToRect");
michael@0 890 AutoRestoreEffects are;
michael@0 891 AutoCheckFlush acf(this);
michael@0 892 GrDrawTarget* target = this->prepareToDraw(&paint, BUFFERED_DRAW, &are, &acf);
michael@0 893
michael@0 894 target->drawRect(dstRect, dstMatrix, &localRect, localMatrix);
michael@0 895 }
michael@0 896
michael@0 897 namespace {
michael@0 898
michael@0 899 extern const GrVertexAttrib gPosUVColorAttribs[] = {
michael@0 900 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding },
michael@0 901 {kVec2f_GrVertexAttribType, sizeof(GrPoint), kLocalCoord_GrVertexAttribBinding },
michael@0 902 {kVec4ub_GrVertexAttribType, 2*sizeof(GrPoint), kColor_GrVertexAttribBinding}
michael@0 903 };
michael@0 904
michael@0 905 extern const GrVertexAttrib gPosColorAttribs[] = {
michael@0 906 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
michael@0 907 {kVec4ub_GrVertexAttribType, sizeof(GrPoint), kColor_GrVertexAttribBinding},
michael@0 908 };
michael@0 909
michael@0 910 static void set_vertex_attributes(GrDrawState* drawState,
michael@0 911 const GrPoint* texCoords,
michael@0 912 const GrColor* colors,
michael@0 913 int* colorOffset,
michael@0 914 int* texOffset) {
michael@0 915 *texOffset = -1;
michael@0 916 *colorOffset = -1;
michael@0 917
michael@0 918 if (NULL != texCoords && NULL != colors) {
michael@0 919 *texOffset = sizeof(GrPoint);
michael@0 920 *colorOffset = 2*sizeof(GrPoint);
michael@0 921 drawState->setVertexAttribs<gPosUVColorAttribs>(3);
michael@0 922 } else if (NULL != texCoords) {
michael@0 923 *texOffset = sizeof(GrPoint);
michael@0 924 drawState->setVertexAttribs<gPosUVColorAttribs>(2);
michael@0 925 } else if (NULL != colors) {
michael@0 926 *colorOffset = sizeof(GrPoint);
michael@0 927 drawState->setVertexAttribs<gPosColorAttribs>(2);
michael@0 928 } else {
michael@0 929 drawState->setVertexAttribs<gPosColorAttribs>(1);
michael@0 930 }
michael@0 931 }
michael@0 932
michael@0 933 };
michael@0 934
michael@0 935 void GrContext::drawVertices(const GrPaint& paint,
michael@0 936 GrPrimitiveType primitiveType,
michael@0 937 int vertexCount,
michael@0 938 const GrPoint positions[],
michael@0 939 const GrPoint texCoords[],
michael@0 940 const GrColor colors[],
michael@0 941 const uint16_t indices[],
michael@0 942 int indexCount) {
michael@0 943 SK_TRACE_EVENT0("GrContext::drawVertices");
michael@0 944
michael@0 945 AutoRestoreEffects are;
michael@0 946 AutoCheckFlush acf(this);
michael@0 947 GrDrawTarget::AutoReleaseGeometry geo; // must be inside AutoCheckFlush scope
michael@0 948
michael@0 949 GrDrawTarget* target = this->prepareToDraw(&paint, BUFFERED_DRAW, &are, &acf);
michael@0 950
michael@0 951 GrDrawState* drawState = target->drawState();
michael@0 952
michael@0 953 int colorOffset = -1, texOffset = -1;
michael@0 954 set_vertex_attributes(drawState, texCoords, colors, &colorOffset, &texOffset);
michael@0 955
michael@0 956 size_t vertexSize = drawState->getVertexSize();
michael@0 957 if (sizeof(GrPoint) != vertexSize) {
michael@0 958 if (!geo.set(target, vertexCount, 0)) {
michael@0 959 GrPrintf("Failed to get space for vertices!\n");
michael@0 960 return;
michael@0 961 }
michael@0 962 void* curVertex = geo.vertices();
michael@0 963
michael@0 964 for (int i = 0; i < vertexCount; ++i) {
michael@0 965 *((GrPoint*)curVertex) = positions[i];
michael@0 966
michael@0 967 if (texOffset >= 0) {
michael@0 968 *(GrPoint*)((intptr_t)curVertex + texOffset) = texCoords[i];
michael@0 969 }
michael@0 970 if (colorOffset >= 0) {
michael@0 971 *(GrColor*)((intptr_t)curVertex + colorOffset) = colors[i];
michael@0 972 }
michael@0 973 curVertex = (void*)((intptr_t)curVertex + vertexSize);
michael@0 974 }
michael@0 975 } else {
michael@0 976 target->setVertexSourceToArray(positions, vertexCount);
michael@0 977 }
michael@0 978
michael@0 979 // we don't currently apply offscreen AA to this path. Need improved
michael@0 980 // management of GrDrawTarget's geometry to avoid copying points per-tile.
michael@0 981
michael@0 982 if (NULL != indices) {
michael@0 983 target->setIndexSourceToArray(indices, indexCount);
michael@0 984 target->drawIndexed(primitiveType, 0, 0, vertexCount, indexCount);
michael@0 985 target->resetIndexSource();
michael@0 986 } else {
michael@0 987 target->drawNonIndexed(primitiveType, 0, vertexCount);
michael@0 988 }
michael@0 989 }
michael@0 990
michael@0 991 ///////////////////////////////////////////////////////////////////////////////
michael@0 992
michael@0 993 void GrContext::drawRRect(const GrPaint& paint,
michael@0 994 const SkRRect& rect,
michael@0 995 const SkStrokeRec& stroke) {
michael@0 996 if (rect.isEmpty()) {
michael@0 997 return;
michael@0 998 }
michael@0 999
michael@0 1000 AutoRestoreEffects are;
michael@0 1001 AutoCheckFlush acf(this);
michael@0 1002 GrDrawTarget* target = this->prepareToDraw(&paint, BUFFERED_DRAW, &are, &acf);
michael@0 1003
michael@0 1004 if (!fOvalRenderer->drawSimpleRRect(target, this, paint.isAntiAlias(), rect, stroke)) {
michael@0 1005 SkPath path;
michael@0 1006 path.addRRect(rect);
michael@0 1007 this->internalDrawPath(target, paint.isAntiAlias(), path, stroke);
michael@0 1008 }
michael@0 1009 }
michael@0 1010
michael@0 1011 ///////////////////////////////////////////////////////////////////////////////
michael@0 1012
michael@0 1013 void GrContext::drawOval(const GrPaint& paint,
michael@0 1014 const SkRect& oval,
michael@0 1015 const SkStrokeRec& stroke) {
michael@0 1016 if (oval.isEmpty()) {
michael@0 1017 return;
michael@0 1018 }
michael@0 1019
michael@0 1020 AutoRestoreEffects are;
michael@0 1021 AutoCheckFlush acf(this);
michael@0 1022 GrDrawTarget* target = this->prepareToDraw(&paint, BUFFERED_DRAW, &are, &acf);
michael@0 1023
michael@0 1024 if (!fOvalRenderer->drawOval(target, this, paint.isAntiAlias(), oval, stroke)) {
michael@0 1025 SkPath path;
michael@0 1026 path.addOval(oval);
michael@0 1027 this->internalDrawPath(target, paint.isAntiAlias(), path, stroke);
michael@0 1028 }
michael@0 1029 }
michael@0 1030
michael@0 1031 // Can 'path' be drawn as a pair of filled nested rectangles?
michael@0 1032 static bool is_nested_rects(GrDrawTarget* target,
michael@0 1033 const SkPath& path,
michael@0 1034 const SkStrokeRec& stroke,
michael@0 1035 SkRect rects[2],
michael@0 1036 bool* useVertexCoverage) {
michael@0 1037 SkASSERT(stroke.isFillStyle());
michael@0 1038
michael@0 1039 if (path.isInverseFillType()) {
michael@0 1040 return false;
michael@0 1041 }
michael@0 1042
michael@0 1043 const GrDrawState& drawState = target->getDrawState();
michael@0 1044
michael@0 1045 // TODO: this restriction could be lifted if we were willing to apply
michael@0 1046 // the matrix to all the points individually rather than just to the rect
michael@0 1047 if (!drawState.getViewMatrix().preservesAxisAlignment()) {
michael@0 1048 return false;
michael@0 1049 }
michael@0 1050
michael@0 1051 *useVertexCoverage = false;
michael@0 1052 if (!target->getDrawState().canTweakAlphaForCoverage()) {
michael@0 1053 if (target->shouldDisableCoverageAAForBlend()) {
michael@0 1054 return false;
michael@0 1055 } else {
michael@0 1056 *useVertexCoverage = true;
michael@0 1057 }
michael@0 1058 }
michael@0 1059
michael@0 1060 SkPath::Direction dirs[2];
michael@0 1061 if (!path.isNestedRects(rects, dirs)) {
michael@0 1062 return false;
michael@0 1063 }
michael@0 1064
michael@0 1065 if (SkPath::kWinding_FillType == path.getFillType() && dirs[0] == dirs[1]) {
michael@0 1066 // The two rects need to be wound opposite to each other
michael@0 1067 return false;
michael@0 1068 }
michael@0 1069
michael@0 1070 // Right now, nested rects where the margin is not the same width
michael@0 1071 // all around do not render correctly
michael@0 1072 const SkScalar* outer = rects[0].asScalars();
michael@0 1073 const SkScalar* inner = rects[1].asScalars();
michael@0 1074
michael@0 1075 SkScalar margin = SkScalarAbs(outer[0] - inner[0]);
michael@0 1076 for (int i = 1; i < 4; ++i) {
michael@0 1077 SkScalar temp = SkScalarAbs(outer[i] - inner[i]);
michael@0 1078 if (!SkScalarNearlyEqual(margin, temp)) {
michael@0 1079 return false;
michael@0 1080 }
michael@0 1081 }
michael@0 1082
michael@0 1083 return true;
michael@0 1084 }
michael@0 1085
michael@0 1086 void GrContext::drawPath(const GrPaint& paint, const SkPath& path, const SkStrokeRec& stroke) {
michael@0 1087
michael@0 1088 if (path.isEmpty()) {
michael@0 1089 if (path.isInverseFillType()) {
michael@0 1090 this->drawPaint(paint);
michael@0 1091 }
michael@0 1092 return;
michael@0 1093 }
michael@0 1094
michael@0 1095 // Note that internalDrawPath may sw-rasterize the path into a scratch texture.
michael@0 1096 // Scratch textures can be recycled after they are returned to the texture
michael@0 1097 // cache. This presents a potential hazard for buffered drawing. However,
michael@0 1098 // the writePixels that uploads to the scratch will perform a flush so we're
michael@0 1099 // OK.
michael@0 1100 AutoRestoreEffects are;
michael@0 1101 AutoCheckFlush acf(this);
michael@0 1102 GrDrawTarget* target = this->prepareToDraw(&paint, BUFFERED_DRAW, &are, &acf);
michael@0 1103 GrDrawState* drawState = target->drawState();
michael@0 1104
michael@0 1105 bool useCoverageAA = paint.isAntiAlias() && !drawState->getRenderTarget()->isMultisampled();
michael@0 1106
michael@0 1107 if (useCoverageAA && stroke.getWidth() < 0 && !path.isConvex()) {
michael@0 1108 // Concave AA paths are expensive - try to avoid them for special cases
michael@0 1109 bool useVertexCoverage;
michael@0 1110 SkRect rects[2];
michael@0 1111
michael@0 1112 if (is_nested_rects(target, path, stroke, rects, &useVertexCoverage)) {
michael@0 1113 SkMatrix origViewMatrix = drawState->getViewMatrix();
michael@0 1114 GrDrawState::AutoViewMatrixRestore avmr;
michael@0 1115 if (!avmr.setIdentity(target->drawState())) {
michael@0 1116 return;
michael@0 1117 }
michael@0 1118
michael@0 1119 fAARectRenderer->fillAANestedRects(this->getGpu(), target,
michael@0 1120 rects,
michael@0 1121 origViewMatrix,
michael@0 1122 useVertexCoverage);
michael@0 1123 return;
michael@0 1124 }
michael@0 1125 }
michael@0 1126
michael@0 1127 SkRect ovalRect;
michael@0 1128 bool isOval = path.isOval(&ovalRect);
michael@0 1129
michael@0 1130 if (!isOval || path.isInverseFillType()
michael@0 1131 || !fOvalRenderer->drawOval(target, this, paint.isAntiAlias(), ovalRect, stroke)) {
michael@0 1132 this->internalDrawPath(target, paint.isAntiAlias(), path, stroke);
michael@0 1133 }
michael@0 1134 }
michael@0 1135
michael@0 1136 void GrContext::internalDrawPath(GrDrawTarget* target, bool useAA, const SkPath& path,
michael@0 1137 const SkStrokeRec& origStroke) {
michael@0 1138 SkASSERT(!path.isEmpty());
michael@0 1139
michael@0 1140 // An Assumption here is that path renderer would use some form of tweaking
michael@0 1141 // the src color (either the input alpha or in the frag shader) to implement
michael@0 1142 // aa. If we have some future driver-mojo path AA that can do the right
michael@0 1143 // thing WRT to the blend then we'll need some query on the PR.
michael@0 1144 bool useCoverageAA = useAA &&
michael@0 1145 !target->getDrawState().getRenderTarget()->isMultisampled() &&
michael@0 1146 !target->shouldDisableCoverageAAForBlend();
michael@0 1147
michael@0 1148
michael@0 1149 GrPathRendererChain::DrawType type =
michael@0 1150 useCoverageAA ? GrPathRendererChain::kColorAntiAlias_DrawType :
michael@0 1151 GrPathRendererChain::kColor_DrawType;
michael@0 1152
michael@0 1153 const SkPath* pathPtr = &path;
michael@0 1154 SkTLazy<SkPath> tmpPath;
michael@0 1155 SkTCopyOnFirstWrite<SkStrokeRec> stroke(origStroke);
michael@0 1156
michael@0 1157 // Try a 1st time without stroking the path and without allowing the SW renderer
michael@0 1158 GrPathRenderer* pr = this->getPathRenderer(*pathPtr, *stroke, target, false, type);
michael@0 1159
michael@0 1160 if (NULL == pr) {
michael@0 1161 if (!GrPathRenderer::IsStrokeHairlineOrEquivalent(*stroke, this->getMatrix(), NULL)) {
michael@0 1162 // It didn't work the 1st time, so try again with the stroked path
michael@0 1163 if (stroke->applyToPath(tmpPath.init(), *pathPtr)) {
michael@0 1164 pathPtr = tmpPath.get();
michael@0 1165 stroke.writable()->setFillStyle();
michael@0 1166 if (pathPtr->isEmpty()) {
michael@0 1167 return;
michael@0 1168 }
michael@0 1169 }
michael@0 1170 }
michael@0 1171
michael@0 1172 // This time, allow SW renderer
michael@0 1173 pr = this->getPathRenderer(*pathPtr, *stroke, target, true, type);
michael@0 1174 }
michael@0 1175
michael@0 1176 if (NULL == pr) {
michael@0 1177 #ifdef SK_DEBUG
michael@0 1178 GrPrintf("Unable to find path renderer compatible with path.\n");
michael@0 1179 #endif
michael@0 1180 return;
michael@0 1181 }
michael@0 1182
michael@0 1183 pr->drawPath(*pathPtr, *stroke, target, useCoverageAA);
michael@0 1184 }
michael@0 1185
michael@0 1186 ////////////////////////////////////////////////////////////////////////////////
michael@0 1187
michael@0 1188 void GrContext::flush(int flagsBitfield) {
michael@0 1189 if (NULL == fDrawBuffer) {
michael@0 1190 return;
michael@0 1191 }
michael@0 1192
michael@0 1193 if (kDiscard_FlushBit & flagsBitfield) {
michael@0 1194 fDrawBuffer->reset();
michael@0 1195 } else {
michael@0 1196 fDrawBuffer->flush();
michael@0 1197 }
michael@0 1198 fFlushToReduceCacheSize = false;
michael@0 1199 }
michael@0 1200
michael@0 1201 bool GrContext::writeTexturePixels(GrTexture* texture,
michael@0 1202 int left, int top, int width, int height,
michael@0 1203 GrPixelConfig config, const void* buffer, size_t rowBytes,
michael@0 1204 uint32_t flags) {
michael@0 1205 SK_TRACE_EVENT0("GrContext::writeTexturePixels");
michael@0 1206 ASSERT_OWNED_RESOURCE(texture);
michael@0 1207
michael@0 1208 if ((kUnpremul_PixelOpsFlag & flags) || !fGpu->canWriteTexturePixels(texture, config)) {
michael@0 1209 if (NULL != texture->asRenderTarget()) {
michael@0 1210 return this->writeRenderTargetPixels(texture->asRenderTarget(),
michael@0 1211 left, top, width, height,
michael@0 1212 config, buffer, rowBytes, flags);
michael@0 1213 } else {
michael@0 1214 return false;
michael@0 1215 }
michael@0 1216 }
michael@0 1217
michael@0 1218 if (!(kDontFlush_PixelOpsFlag & flags)) {
michael@0 1219 this->flush();
michael@0 1220 }
michael@0 1221
michael@0 1222 return fGpu->writeTexturePixels(texture, left, top, width, height,
michael@0 1223 config, buffer, rowBytes);
michael@0 1224 }
michael@0 1225
michael@0 1226 bool GrContext::readTexturePixels(GrTexture* texture,
michael@0 1227 int left, int top, int width, int height,
michael@0 1228 GrPixelConfig config, void* buffer, size_t rowBytes,
michael@0 1229 uint32_t flags) {
michael@0 1230 SK_TRACE_EVENT0("GrContext::readTexturePixels");
michael@0 1231 ASSERT_OWNED_RESOURCE(texture);
michael@0 1232
michael@0 1233 GrRenderTarget* target = texture->asRenderTarget();
michael@0 1234 if (NULL != target) {
michael@0 1235 return this->readRenderTargetPixels(target,
michael@0 1236 left, top, width, height,
michael@0 1237 config, buffer, rowBytes,
michael@0 1238 flags);
michael@0 1239 } else {
michael@0 1240 // TODO: make this more efficient for cases where we're reading the entire
michael@0 1241 // texture, i.e., use GetTexImage() instead
michael@0 1242
michael@0 1243 // create scratch rendertarget and read from that
michael@0 1244 GrAutoScratchTexture ast;
michael@0 1245 GrTextureDesc desc;
michael@0 1246 desc.fFlags = kRenderTarget_GrTextureFlagBit;
michael@0 1247 desc.fWidth = width;
michael@0 1248 desc.fHeight = height;
michael@0 1249 desc.fConfig = config;
michael@0 1250 desc.fOrigin = kTopLeft_GrSurfaceOrigin;
michael@0 1251 ast.set(this, desc, kExact_ScratchTexMatch);
michael@0 1252 GrTexture* dst = ast.texture();
michael@0 1253 if (NULL != dst && NULL != (target = dst->asRenderTarget())) {
michael@0 1254 this->copyTexture(texture, target, NULL);
michael@0 1255 return this->readRenderTargetPixels(target,
michael@0 1256 left, top, width, height,
michael@0 1257 config, buffer, rowBytes,
michael@0 1258 flags);
michael@0 1259 }
michael@0 1260
michael@0 1261 return false;
michael@0 1262 }
michael@0 1263 }
michael@0 1264
michael@0 1265 #include "SkConfig8888.h"
michael@0 1266
michael@0 1267 namespace {
michael@0 1268 /**
michael@0 1269 * Converts a GrPixelConfig to a SkCanvas::Config8888. Only byte-per-channel
michael@0 1270 * formats are representable as Config8888 and so the function returns false
michael@0 1271 * if the GrPixelConfig has no equivalent Config8888.
michael@0 1272 */
michael@0 1273 bool grconfig_to_config8888(GrPixelConfig config,
michael@0 1274 bool unpremul,
michael@0 1275 SkCanvas::Config8888* config8888) {
michael@0 1276 switch (config) {
michael@0 1277 case kRGBA_8888_GrPixelConfig:
michael@0 1278 if (unpremul) {
michael@0 1279 *config8888 = SkCanvas::kRGBA_Unpremul_Config8888;
michael@0 1280 } else {
michael@0 1281 *config8888 = SkCanvas::kRGBA_Premul_Config8888;
michael@0 1282 }
michael@0 1283 return true;
michael@0 1284 case kBGRA_8888_GrPixelConfig:
michael@0 1285 if (unpremul) {
michael@0 1286 *config8888 = SkCanvas::kBGRA_Unpremul_Config8888;
michael@0 1287 } else {
michael@0 1288 *config8888 = SkCanvas::kBGRA_Premul_Config8888;
michael@0 1289 }
michael@0 1290 return true;
michael@0 1291 default:
michael@0 1292 return false;
michael@0 1293 }
michael@0 1294 }
michael@0 1295
michael@0 1296 // It returns a configuration with where the byte position of the R & B components are swapped in
michael@0 1297 // relation to the input config. This should only be called with the result of
michael@0 1298 // grconfig_to_config8888 as it will fail for other configs.
michael@0 1299 SkCanvas::Config8888 swap_config8888_red_and_blue(SkCanvas::Config8888 config8888) {
michael@0 1300 switch (config8888) {
michael@0 1301 case SkCanvas::kBGRA_Premul_Config8888:
michael@0 1302 return SkCanvas::kRGBA_Premul_Config8888;
michael@0 1303 case SkCanvas::kBGRA_Unpremul_Config8888:
michael@0 1304 return SkCanvas::kRGBA_Unpremul_Config8888;
michael@0 1305 case SkCanvas::kRGBA_Premul_Config8888:
michael@0 1306 return SkCanvas::kBGRA_Premul_Config8888;
michael@0 1307 case SkCanvas::kRGBA_Unpremul_Config8888:
michael@0 1308 return SkCanvas::kBGRA_Unpremul_Config8888;
michael@0 1309 default:
michael@0 1310 GrCrash("Unexpected input");
michael@0 1311 return SkCanvas::kBGRA_Unpremul_Config8888;;
michael@0 1312 }
michael@0 1313 }
michael@0 1314 }
michael@0 1315
michael@0 1316 bool GrContext::readRenderTargetPixels(GrRenderTarget* target,
michael@0 1317 int left, int top, int width, int height,
michael@0 1318 GrPixelConfig dstConfig, void* buffer, size_t rowBytes,
michael@0 1319 uint32_t flags) {
michael@0 1320 SK_TRACE_EVENT0("GrContext::readRenderTargetPixels");
michael@0 1321 ASSERT_OWNED_RESOURCE(target);
michael@0 1322
michael@0 1323 if (NULL == target) {
michael@0 1324 target = fRenderTarget.get();
michael@0 1325 if (NULL == target) {
michael@0 1326 return false;
michael@0 1327 }
michael@0 1328 }
michael@0 1329
michael@0 1330 if (!(kDontFlush_PixelOpsFlag & flags)) {
michael@0 1331 this->flush();
michael@0 1332 }
michael@0 1333
michael@0 1334 // Determine which conversions have to be applied: flipY, swapRAnd, and/or unpremul.
michael@0 1335
michael@0 1336 // If fGpu->readPixels would incur a y-flip cost then we will read the pixels upside down. We'll
michael@0 1337 // either do the flipY by drawing into a scratch with a matrix or on the cpu after the read.
michael@0 1338 bool flipY = fGpu->readPixelsWillPayForYFlip(target, left, top,
michael@0 1339 width, height, dstConfig,
michael@0 1340 rowBytes);
michael@0 1341 // We ignore the preferred config if it is different than our config unless it is an R/B swap.
michael@0 1342 // In that case we'll perform an R and B swap while drawing to a scratch texture of the swapped
michael@0 1343 // config. Then we will call readPixels on the scratch with the swapped config. The swaps during
michael@0 1344 // the draw cancels out the fact that we call readPixels with a config that is R/B swapped from
michael@0 1345 // dstConfig.
michael@0 1346 GrPixelConfig readConfig = dstConfig;
michael@0 1347 bool swapRAndB = false;
michael@0 1348 if (GrPixelConfigSwapRAndB(dstConfig) ==
michael@0 1349 fGpu->preferredReadPixelsConfig(dstConfig, target->config())) {
michael@0 1350 readConfig = GrPixelConfigSwapRAndB(readConfig);
michael@0 1351 swapRAndB = true;
michael@0 1352 }
michael@0 1353
michael@0 1354 bool unpremul = SkToBool(kUnpremul_PixelOpsFlag & flags);
michael@0 1355
michael@0 1356 if (unpremul && !GrPixelConfigIs8888(dstConfig)) {
michael@0 1357 // The unpremul flag is only allowed for these two configs.
michael@0 1358 return false;
michael@0 1359 }
michael@0 1360
michael@0 1361 // If the src is a texture and we would have to do conversions after read pixels, we instead
michael@0 1362 // do the conversions by drawing the src to a scratch texture. If we handle any of the
michael@0 1363 // conversions in the draw we set the corresponding bool to false so that we don't reapply it
michael@0 1364 // on the read back pixels.
michael@0 1365 GrTexture* src = target->asTexture();
michael@0 1366 GrAutoScratchTexture ast;
michael@0 1367 if (NULL != src && (swapRAndB || unpremul || flipY)) {
michael@0 1368 // Make the scratch a render target because we don't have a robust readTexturePixels as of
michael@0 1369 // yet. It calls this function.
michael@0 1370 GrTextureDesc desc;
michael@0 1371 desc.fFlags = kRenderTarget_GrTextureFlagBit;
michael@0 1372 desc.fWidth = width;
michael@0 1373 desc.fHeight = height;
michael@0 1374 desc.fConfig = readConfig;
michael@0 1375 desc.fOrigin = kTopLeft_GrSurfaceOrigin;
michael@0 1376
michael@0 1377 // When a full read back is faster than a partial we could always make the scratch exactly
michael@0 1378 // match the passed rect. However, if we see many different size rectangles we will trash
michael@0 1379 // our texture cache and pay the cost of creating and destroying many textures. So, we only
michael@0 1380 // request an exact match when the caller is reading an entire RT.
michael@0 1381 ScratchTexMatch match = kApprox_ScratchTexMatch;
michael@0 1382 if (0 == left &&
michael@0 1383 0 == top &&
michael@0 1384 target->width() == width &&
michael@0 1385 target->height() == height &&
michael@0 1386 fGpu->fullReadPixelsIsFasterThanPartial()) {
michael@0 1387 match = kExact_ScratchTexMatch;
michael@0 1388 }
michael@0 1389 ast.set(this, desc, match);
michael@0 1390 GrTexture* texture = ast.texture();
michael@0 1391 if (texture) {
michael@0 1392 // compute a matrix to perform the draw
michael@0 1393 SkMatrix textureMatrix;
michael@0 1394 textureMatrix.setTranslate(SK_Scalar1 *left, SK_Scalar1 *top);
michael@0 1395 textureMatrix.postIDiv(src->width(), src->height());
michael@0 1396
michael@0 1397 SkAutoTUnref<const GrEffectRef> effect;
michael@0 1398 if (unpremul) {
michael@0 1399 effect.reset(this->createPMToUPMEffect(src, swapRAndB, textureMatrix));
michael@0 1400 if (NULL != effect) {
michael@0 1401 unpremul = false; // we no longer need to do this on CPU after the read back.
michael@0 1402 }
michael@0 1403 }
michael@0 1404 // If we failed to create a PM->UPM effect and have no other conversions to perform then
michael@0 1405 // there is no longer any point to using the scratch.
michael@0 1406 if (NULL != effect || flipY || swapRAndB) {
michael@0 1407 if (!effect) {
michael@0 1408 effect.reset(GrConfigConversionEffect::Create(
michael@0 1409 src,
michael@0 1410 swapRAndB,
michael@0 1411 GrConfigConversionEffect::kNone_PMConversion,
michael@0 1412 textureMatrix));
michael@0 1413 }
michael@0 1414 swapRAndB = false; // we will handle the swap in the draw.
michael@0 1415
michael@0 1416 // We protect the existing geometry here since it may not be
michael@0 1417 // clear to the caller that a draw operation (i.e., drawSimpleRect)
michael@0 1418 // can be invoked in this method
michael@0 1419 GrDrawTarget::AutoGeometryAndStatePush agasp(fGpu, GrDrawTarget::kReset_ASRInit);
michael@0 1420 GrDrawState* drawState = fGpu->drawState();
michael@0 1421 SkASSERT(effect);
michael@0 1422 drawState->addColorEffect(effect);
michael@0 1423
michael@0 1424 drawState->setRenderTarget(texture->asRenderTarget());
michael@0 1425 SkRect rect = SkRect::MakeWH(SkIntToScalar(width), SkIntToScalar(height));
michael@0 1426 fGpu->drawSimpleRect(rect, NULL);
michael@0 1427 // we want to read back from the scratch's origin
michael@0 1428 left = 0;
michael@0 1429 top = 0;
michael@0 1430 target = texture->asRenderTarget();
michael@0 1431 }
michael@0 1432 }
michael@0 1433 }
michael@0 1434 if (!fGpu->readPixels(target,
michael@0 1435 left, top, width, height,
michael@0 1436 readConfig, buffer, rowBytes)) {
michael@0 1437 return false;
michael@0 1438 }
michael@0 1439 // Perform any conversions we weren't able to perform using a scratch texture.
michael@0 1440 if (unpremul || swapRAndB) {
michael@0 1441 // These are initialized to suppress a warning
michael@0 1442 SkCanvas::Config8888 srcC8888 = SkCanvas::kNative_Premul_Config8888;
michael@0 1443 SkCanvas::Config8888 dstC8888 = SkCanvas::kNative_Premul_Config8888;
michael@0 1444
michael@0 1445 SkDEBUGCODE(bool c8888IsValid =) grconfig_to_config8888(dstConfig, false, &srcC8888);
michael@0 1446 grconfig_to_config8888(dstConfig, unpremul, &dstC8888);
michael@0 1447
michael@0 1448 if (swapRAndB) {
michael@0 1449 SkASSERT(c8888IsValid); // we should only do r/b swap on 8888 configs
michael@0 1450 srcC8888 = swap_config8888_red_and_blue(srcC8888);
michael@0 1451 }
michael@0 1452 SkASSERT(c8888IsValid);
michael@0 1453 uint32_t* b32 = reinterpret_cast<uint32_t*>(buffer);
michael@0 1454 SkConvertConfig8888Pixels(b32, rowBytes, dstC8888,
michael@0 1455 b32, rowBytes, srcC8888,
michael@0 1456 width, height);
michael@0 1457 }
michael@0 1458 return true;
michael@0 1459 }
michael@0 1460
michael@0 1461 void GrContext::resolveRenderTarget(GrRenderTarget* target) {
michael@0 1462 SkASSERT(target);
michael@0 1463 ASSERT_OWNED_RESOURCE(target);
michael@0 1464 // In the future we may track whether there are any pending draws to this
michael@0 1465 // target. We don't today so we always perform a flush. We don't promise
michael@0 1466 // this to our clients, though.
michael@0 1467 this->flush();
michael@0 1468 fGpu->resolveRenderTarget(target);
michael@0 1469 }
michael@0 1470
michael@0 1471 void GrContext::copyTexture(GrTexture* src, GrRenderTarget* dst, const SkIPoint* topLeft) {
michael@0 1472 if (NULL == src || NULL == dst) {
michael@0 1473 return;
michael@0 1474 }
michael@0 1475 ASSERT_OWNED_RESOURCE(src);
michael@0 1476
michael@0 1477 // Writes pending to the source texture are not tracked, so a flush
michael@0 1478 // is required to ensure that the copy captures the most recent contents
michael@0 1479 // of the source texture. See similar behavior in
michael@0 1480 // GrContext::resolveRenderTarget.
michael@0 1481 this->flush();
michael@0 1482
michael@0 1483 GrDrawTarget::AutoStateRestore asr(fGpu, GrDrawTarget::kReset_ASRInit);
michael@0 1484 GrDrawState* drawState = fGpu->drawState();
michael@0 1485 drawState->setRenderTarget(dst);
michael@0 1486 SkMatrix sampleM;
michael@0 1487 sampleM.setIDiv(src->width(), src->height());
michael@0 1488 SkIRect srcRect = SkIRect::MakeWH(dst->width(), dst->height());
michael@0 1489 if (NULL != topLeft) {
michael@0 1490 srcRect.offset(*topLeft);
michael@0 1491 }
michael@0 1492 SkIRect srcBounds = SkIRect::MakeWH(src->width(), src->height());
michael@0 1493 if (!srcRect.intersect(srcBounds)) {
michael@0 1494 return;
michael@0 1495 }
michael@0 1496 sampleM.preTranslate(SkIntToScalar(srcRect.fLeft), SkIntToScalar(srcRect.fTop));
michael@0 1497 drawState->addColorTextureEffect(src, sampleM);
michael@0 1498 SkRect dstR = SkRect::MakeWH(SkIntToScalar(srcRect.width()), SkIntToScalar(srcRect.height()));
michael@0 1499 fGpu->drawSimpleRect(dstR, NULL);
michael@0 1500 }
michael@0 1501
michael@0 1502 bool GrContext::writeRenderTargetPixels(GrRenderTarget* target,
michael@0 1503 int left, int top, int width, int height,
michael@0 1504 GrPixelConfig srcConfig,
michael@0 1505 const void* buffer,
michael@0 1506 size_t rowBytes,
michael@0 1507 uint32_t flags) {
michael@0 1508 SK_TRACE_EVENT0("GrContext::writeRenderTargetPixels");
michael@0 1509 ASSERT_OWNED_RESOURCE(target);
michael@0 1510
michael@0 1511 if (NULL == target) {
michael@0 1512 target = fRenderTarget.get();
michael@0 1513 if (NULL == target) {
michael@0 1514 return false;
michael@0 1515 }
michael@0 1516 }
michael@0 1517
michael@0 1518 // TODO: when underlying api has a direct way to do this we should use it (e.g. glDrawPixels on
michael@0 1519 // desktop GL).
michael@0 1520
michael@0 1521 // We will always call some form of writeTexturePixels and we will pass our flags on to it.
michael@0 1522 // Thus, we don't perform a flush here since that call will do it (if the kNoFlush flag isn't
michael@0 1523 // set.)
michael@0 1524
michael@0 1525 // If the RT is also a texture and we don't have to premultiply then take the texture path.
michael@0 1526 // We expect to be at least as fast or faster since it doesn't use an intermediate texture as
michael@0 1527 // we do below.
michael@0 1528
michael@0 1529 #if !defined(SK_BUILD_FOR_MAC)
michael@0 1530 // At least some drivers on the Mac get confused when glTexImage2D is called on a texture
michael@0 1531 // attached to an FBO. The FBO still sees the old image. TODO: determine what OS versions and/or
michael@0 1532 // HW is affected.
michael@0 1533 if (NULL != target->asTexture() && !(kUnpremul_PixelOpsFlag & flags) &&
michael@0 1534 fGpu->canWriteTexturePixels(target->asTexture(), srcConfig)) {
michael@0 1535 return this->writeTexturePixels(target->asTexture(),
michael@0 1536 left, top, width, height,
michael@0 1537 srcConfig, buffer, rowBytes, flags);
michael@0 1538 }
michael@0 1539 #endif
michael@0 1540
michael@0 1541 // We ignore the preferred config unless it is a R/B swap of the src config. In that case
michael@0 1542 // we will upload the original src data to a scratch texture but we will spoof it as the swapped
michael@0 1543 // config. This scratch will then have R and B swapped. We correct for this by swapping again
michael@0 1544 // when drawing the scratch to the dst using a conversion effect.
michael@0 1545 bool swapRAndB = false;
michael@0 1546 GrPixelConfig writeConfig = srcConfig;
michael@0 1547 if (GrPixelConfigSwapRAndB(srcConfig) ==
michael@0 1548 fGpu->preferredWritePixelsConfig(srcConfig, target->config())) {
michael@0 1549 writeConfig = GrPixelConfigSwapRAndB(srcConfig);
michael@0 1550 swapRAndB = true;
michael@0 1551 }
michael@0 1552
michael@0 1553 GrTextureDesc desc;
michael@0 1554 desc.fWidth = width;
michael@0 1555 desc.fHeight = height;
michael@0 1556 desc.fConfig = writeConfig;
michael@0 1557 GrAutoScratchTexture ast(this, desc);
michael@0 1558 GrTexture* texture = ast.texture();
michael@0 1559 if (NULL == texture) {
michael@0 1560 return false;
michael@0 1561 }
michael@0 1562
michael@0 1563 SkAutoTUnref<const GrEffectRef> effect;
michael@0 1564 SkMatrix textureMatrix;
michael@0 1565 textureMatrix.setIDiv(texture->width(), texture->height());
michael@0 1566
michael@0 1567 // allocate a tmp buffer and sw convert the pixels to premul
michael@0 1568 SkAutoSTMalloc<128 * 128, uint32_t> tmpPixels(0);
michael@0 1569
michael@0 1570 if (kUnpremul_PixelOpsFlag & flags) {
michael@0 1571 if (!GrPixelConfigIs8888(srcConfig)) {
michael@0 1572 return false;
michael@0 1573 }
michael@0 1574 effect.reset(this->createUPMToPMEffect(texture, swapRAndB, textureMatrix));
michael@0 1575 // handle the unpremul step on the CPU if we couldn't create an effect to do it.
michael@0 1576 if (NULL == effect) {
michael@0 1577 SkCanvas::Config8888 srcConfig8888, dstConfig8888;
michael@0 1578 SkDEBUGCODE(bool success = )
michael@0 1579 grconfig_to_config8888(srcConfig, true, &srcConfig8888);
michael@0 1580 SkASSERT(success);
michael@0 1581 SkDEBUGCODE(success = )
michael@0 1582 grconfig_to_config8888(srcConfig, false, &dstConfig8888);
michael@0 1583 SkASSERT(success);
michael@0 1584 const uint32_t* src = reinterpret_cast<const uint32_t*>(buffer);
michael@0 1585 tmpPixels.reset(width * height);
michael@0 1586 SkConvertConfig8888Pixels(tmpPixels.get(), 4 * width, dstConfig8888,
michael@0 1587 src, rowBytes, srcConfig8888,
michael@0 1588 width, height);
michael@0 1589 buffer = tmpPixels.get();
michael@0 1590 rowBytes = 4 * width;
michael@0 1591 }
michael@0 1592 }
michael@0 1593 if (NULL == effect) {
michael@0 1594 effect.reset(GrConfigConversionEffect::Create(texture,
michael@0 1595 swapRAndB,
michael@0 1596 GrConfigConversionEffect::kNone_PMConversion,
michael@0 1597 textureMatrix));
michael@0 1598 }
michael@0 1599
michael@0 1600 if (!this->writeTexturePixels(texture,
michael@0 1601 0, 0, width, height,
michael@0 1602 writeConfig, buffer, rowBytes,
michael@0 1603 flags & ~kUnpremul_PixelOpsFlag)) {
michael@0 1604 return false;
michael@0 1605 }
michael@0 1606
michael@0 1607 // writeRenderTargetPixels can be called in the midst of drawing another
michael@0 1608 // object (e.g., when uploading a SW path rendering to the gpu while
michael@0 1609 // drawing a rect) so preserve the current geometry.
michael@0 1610 SkMatrix matrix;
michael@0 1611 matrix.setTranslate(SkIntToScalar(left), SkIntToScalar(top));
michael@0 1612 GrDrawTarget::AutoGeometryAndStatePush agasp(fGpu, GrDrawTarget::kReset_ASRInit, &matrix);
michael@0 1613 GrDrawState* drawState = fGpu->drawState();
michael@0 1614 SkASSERT(effect);
michael@0 1615 drawState->addColorEffect(effect);
michael@0 1616
michael@0 1617 drawState->setRenderTarget(target);
michael@0 1618
michael@0 1619 fGpu->drawSimpleRect(SkRect::MakeWH(SkIntToScalar(width), SkIntToScalar(height)), NULL);
michael@0 1620 return true;
michael@0 1621 }
michael@0 1622 ////////////////////////////////////////////////////////////////////////////////
michael@0 1623
michael@0 1624 GrDrawTarget* GrContext::prepareToDraw(const GrPaint* paint,
michael@0 1625 BufferedDraw buffered,
michael@0 1626 AutoRestoreEffects* are,
michael@0 1627 AutoCheckFlush* acf) {
michael@0 1628 // All users of this draw state should be freeing up all effects when they're done.
michael@0 1629 // Otherwise effects that own resources may keep those resources alive indefinitely.
michael@0 1630 SkASSERT(0 == fDrawState->numColorStages() && 0 == fDrawState->numCoverageStages());
michael@0 1631
michael@0 1632 if (kNo_BufferedDraw == buffered && kYes_BufferedDraw == fLastDrawWasBuffered) {
michael@0 1633 fDrawBuffer->flush();
michael@0 1634 fLastDrawWasBuffered = kNo_BufferedDraw;
michael@0 1635 }
michael@0 1636 ASSERT_OWNED_RESOURCE(fRenderTarget.get());
michael@0 1637 if (NULL != paint) {
michael@0 1638 SkASSERT(NULL != are);
michael@0 1639 SkASSERT(NULL != acf);
michael@0 1640 are->set(fDrawState);
michael@0 1641 fDrawState->setFromPaint(*paint, fViewMatrix, fRenderTarget.get());
michael@0 1642 #if GR_DEBUG_PARTIAL_COVERAGE_CHECK
michael@0 1643 if ((paint->hasMask() || 0xff != paint->fCoverage) &&
michael@0 1644 !fGpu->canApplyCoverage()) {
michael@0 1645 GrPrintf("Partial pixel coverage will be incorrectly blended.\n");
michael@0 1646 }
michael@0 1647 #endif
michael@0 1648 } else {
michael@0 1649 fDrawState->reset(fViewMatrix);
michael@0 1650 fDrawState->setRenderTarget(fRenderTarget.get());
michael@0 1651 }
michael@0 1652 GrDrawTarget* target;
michael@0 1653 if (kYes_BufferedDraw == buffered) {
michael@0 1654 fLastDrawWasBuffered = kYes_BufferedDraw;
michael@0 1655 target = fDrawBuffer;
michael@0 1656 } else {
michael@0 1657 SkASSERT(kNo_BufferedDraw == buffered);
michael@0 1658 fLastDrawWasBuffered = kNo_BufferedDraw;
michael@0 1659 target = fGpu;
michael@0 1660 }
michael@0 1661 fDrawState->setState(GrDrawState::kClip_StateBit, NULL != fClip &&
michael@0 1662 !fClip->fClipStack->isWideOpen());
michael@0 1663 target->setClip(fClip);
michael@0 1664 SkASSERT(fDrawState == target->drawState());
michael@0 1665 return target;
michael@0 1666 }
michael@0 1667
michael@0 1668 /*
michael@0 1669 * This method finds a path renderer that can draw the specified path on
michael@0 1670 * the provided target.
michael@0 1671 * Due to its expense, the software path renderer has split out so it can
michael@0 1672 * can be individually allowed/disallowed via the "allowSW" boolean.
michael@0 1673 */
michael@0 1674 GrPathRenderer* GrContext::getPathRenderer(const SkPath& path,
michael@0 1675 const SkStrokeRec& stroke,
michael@0 1676 const GrDrawTarget* target,
michael@0 1677 bool allowSW,
michael@0 1678 GrPathRendererChain::DrawType drawType,
michael@0 1679 GrPathRendererChain::StencilSupport* stencilSupport) {
michael@0 1680
michael@0 1681 if (NULL == fPathRendererChain) {
michael@0 1682 fPathRendererChain = SkNEW_ARGS(GrPathRendererChain, (this));
michael@0 1683 }
michael@0 1684
michael@0 1685 GrPathRenderer* pr = fPathRendererChain->getPathRenderer(path,
michael@0 1686 stroke,
michael@0 1687 target,
michael@0 1688 drawType,
michael@0 1689 stencilSupport);
michael@0 1690
michael@0 1691 if (NULL == pr && allowSW) {
michael@0 1692 if (NULL == fSoftwarePathRenderer) {
michael@0 1693 fSoftwarePathRenderer = SkNEW_ARGS(GrSoftwarePathRenderer, (this));
michael@0 1694 }
michael@0 1695 pr = fSoftwarePathRenderer;
michael@0 1696 }
michael@0 1697
michael@0 1698 return pr;
michael@0 1699 }
michael@0 1700
michael@0 1701 ////////////////////////////////////////////////////////////////////////////////
michael@0 1702 bool GrContext::isConfigRenderable(GrPixelConfig config, bool withMSAA) const {
michael@0 1703 return fGpu->caps()->isConfigRenderable(config, withMSAA);
michael@0 1704 }
michael@0 1705
michael@0 1706 int GrContext::getRecommendedSampleCount(GrPixelConfig config,
michael@0 1707 SkScalar dpi) const {
michael@0 1708 if (!this->isConfigRenderable(config, true)) {
michael@0 1709 return 0;
michael@0 1710 }
michael@0 1711 int chosenSampleCount = 0;
michael@0 1712 if (fGpu->caps()->pathRenderingSupport()) {
michael@0 1713 if (dpi >= 250.0f) {
michael@0 1714 chosenSampleCount = 4;
michael@0 1715 } else {
michael@0 1716 chosenSampleCount = 16;
michael@0 1717 }
michael@0 1718 }
michael@0 1719 return chosenSampleCount <= fGpu->caps()->maxSampleCount() ?
michael@0 1720 chosenSampleCount : 0;
michael@0 1721 }
michael@0 1722
michael@0 1723 void GrContext::setupDrawBuffer() {
michael@0 1724 SkASSERT(NULL == fDrawBuffer);
michael@0 1725 SkASSERT(NULL == fDrawBufferVBAllocPool);
michael@0 1726 SkASSERT(NULL == fDrawBufferIBAllocPool);
michael@0 1727
michael@0 1728 fDrawBufferVBAllocPool =
michael@0 1729 SkNEW_ARGS(GrVertexBufferAllocPool, (fGpu, false,
michael@0 1730 DRAW_BUFFER_VBPOOL_BUFFER_SIZE,
michael@0 1731 DRAW_BUFFER_VBPOOL_PREALLOC_BUFFERS));
michael@0 1732 fDrawBufferIBAllocPool =
michael@0 1733 SkNEW_ARGS(GrIndexBufferAllocPool, (fGpu, false,
michael@0 1734 DRAW_BUFFER_IBPOOL_BUFFER_SIZE,
michael@0 1735 DRAW_BUFFER_IBPOOL_PREALLOC_BUFFERS));
michael@0 1736
michael@0 1737 fDrawBuffer = SkNEW_ARGS(GrInOrderDrawBuffer, (fGpu,
michael@0 1738 fDrawBufferVBAllocPool,
michael@0 1739 fDrawBufferIBAllocPool));
michael@0 1740
michael@0 1741 fDrawBuffer->setDrawState(fDrawState);
michael@0 1742 }
michael@0 1743
michael@0 1744 GrDrawTarget* GrContext::getTextTarget() {
michael@0 1745 return this->prepareToDraw(NULL, BUFFERED_DRAW, NULL, NULL);
michael@0 1746 }
michael@0 1747
michael@0 1748 const GrIndexBuffer* GrContext::getQuadIndexBuffer() const {
michael@0 1749 return fGpu->getQuadIndexBuffer();
michael@0 1750 }
michael@0 1751
michael@0 1752 namespace {
michael@0 1753 void test_pm_conversions(GrContext* ctx, int* pmToUPMValue, int* upmToPMValue) {
michael@0 1754 GrConfigConversionEffect::PMConversion pmToUPM;
michael@0 1755 GrConfigConversionEffect::PMConversion upmToPM;
michael@0 1756 GrConfigConversionEffect::TestForPreservingPMConversions(ctx, &pmToUPM, &upmToPM);
michael@0 1757 *pmToUPMValue = pmToUPM;
michael@0 1758 *upmToPMValue = upmToPM;
michael@0 1759 }
michael@0 1760 }
michael@0 1761
michael@0 1762 const GrEffectRef* GrContext::createPMToUPMEffect(GrTexture* texture,
michael@0 1763 bool swapRAndB,
michael@0 1764 const SkMatrix& matrix) {
michael@0 1765 if (!fDidTestPMConversions) {
michael@0 1766 test_pm_conversions(this, &fPMToUPMConversion, &fUPMToPMConversion);
michael@0 1767 fDidTestPMConversions = true;
michael@0 1768 }
michael@0 1769 GrConfigConversionEffect::PMConversion pmToUPM =
michael@0 1770 static_cast<GrConfigConversionEffect::PMConversion>(fPMToUPMConversion);
michael@0 1771 if (GrConfigConversionEffect::kNone_PMConversion != pmToUPM) {
michael@0 1772 return GrConfigConversionEffect::Create(texture, swapRAndB, pmToUPM, matrix);
michael@0 1773 } else {
michael@0 1774 return NULL;
michael@0 1775 }
michael@0 1776 }
michael@0 1777
michael@0 1778 const GrEffectRef* GrContext::createUPMToPMEffect(GrTexture* texture,
michael@0 1779 bool swapRAndB,
michael@0 1780 const SkMatrix& matrix) {
michael@0 1781 if (!fDidTestPMConversions) {
michael@0 1782 test_pm_conversions(this, &fPMToUPMConversion, &fUPMToPMConversion);
michael@0 1783 fDidTestPMConversions = true;
michael@0 1784 }
michael@0 1785 GrConfigConversionEffect::PMConversion upmToPM =
michael@0 1786 static_cast<GrConfigConversionEffect::PMConversion>(fUPMToPMConversion);
michael@0 1787 if (GrConfigConversionEffect::kNone_PMConversion != upmToPM) {
michael@0 1788 return GrConfigConversionEffect::Create(texture, swapRAndB, upmToPM, matrix);
michael@0 1789 } else {
michael@0 1790 return NULL;
michael@0 1791 }
michael@0 1792 }
michael@0 1793
michael@0 1794 GrPath* GrContext::createPath(const SkPath& inPath, const SkStrokeRec& stroke) {
michael@0 1795 SkASSERT(fGpu->caps()->pathRenderingSupport());
michael@0 1796
michael@0 1797 // TODO: now we add to fTextureCache. This should change to fResourceCache.
michael@0 1798 GrResourceKey resourceKey = GrPath::ComputeKey(inPath, stroke);
michael@0 1799 GrPath* path = static_cast<GrPath*>(fTextureCache->find(resourceKey));
michael@0 1800 if (NULL != path && path->isEqualTo(inPath, stroke)) {
michael@0 1801 path->ref();
michael@0 1802 } else {
michael@0 1803 path = fGpu->createPath(inPath, stroke);
michael@0 1804 fTextureCache->purgeAsNeeded(1, path->sizeInBytes());
michael@0 1805 fTextureCache->addResource(resourceKey, path);
michael@0 1806 }
michael@0 1807 return path;
michael@0 1808 }
michael@0 1809
michael@0 1810 ///////////////////////////////////////////////////////////////////////////////
michael@0 1811 #if GR_CACHE_STATS
michael@0 1812 void GrContext::printCacheStats() const {
michael@0 1813 fTextureCache->printStats();
michael@0 1814 }
michael@0 1815 #endif

mercurial