1 /* 2 * Copyright 2017 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #include "GrTextureOp.h" 9 #include <new> 10 #include "GrAppliedClip.h" 11 #include "GrCaps.h" 12 #include "GrContext.h" 13 #include "GrContextPriv.h" 14 #include "GrDrawOpTest.h" 15 #include "GrGeometryProcessor.h" 16 #include "GrGpu.h" 17 #include "GrMemoryPool.h" 18 #include "GrMeshDrawOp.h" 19 #include "GrOpFlushState.h" 20 #include "GrQuad.h" 21 #include "GrQuadPerEdgeAA.h" 22 #include "GrResourceProvider.h" 23 #include "GrResourceProviderPriv.h" 24 #include "GrShaderCaps.h" 25 #include "GrTexture.h" 26 #include "GrTexturePriv.h" 27 #include "GrTextureProxy.h" 28 #include "SkGr.h" 29 #include "SkMathPriv.h" 30 #include "SkMatrixPriv.h" 31 #include "SkPoint.h" 32 #include "SkPoint3.h" 33 #include "SkRectPriv.h" 34 #include "SkTo.h" 35 #include "glsl/GrGLSLVarying.h" 36 37 namespace { 38 39 using Domain = GrQuadPerEdgeAA::Domain; 40 using VertexSpec = GrQuadPerEdgeAA::VertexSpec; 41 using ColorType = GrQuadPerEdgeAA::ColorType; 42 43 // if normalizing the domain then pass 1/width, 1/height, 1 for iw, ih, h. Otherwise pass 44 // 1, 1, and height. 45 static SkRect compute_domain(Domain domain, GrSamplerState::Filter filter, GrSurfaceOrigin origin, 46 const SkRect& srcRect, float iw, float ih, float h) { 47 static constexpr SkRect kLargeRect = {-100000, -100000, 1000000, 1000000}; 48 if (domain == Domain::kNo) { 49 // Either the quad has no domain constraint and is batched with a domain constrained op 50 // (in which case we want a domain that doesn't restrict normalized tex coords), or the 51 // entire op doesn't use the domain, in which case the returned value is ignored. 52 return kLargeRect; 53 } 54 55 auto ltrb = Sk4f::Load(&srcRect); 56 if (filter == GrSamplerState::Filter::kBilerp) { 57 auto rblt = SkNx_shuffle<2, 3, 0, 1>(ltrb); 58 auto whwh = (rblt - ltrb).abs(); 59 auto c = (rblt + ltrb) * 0.5f; 60 static const Sk4f kOffsets = {0.5f, 0.5f, -0.5f, -0.5f}; 61 ltrb = (whwh < 1.f).thenElse(c, ltrb + kOffsets); 62 } 63 ltrb *= Sk4f(iw, ih, iw, ih); 64 if (origin == kBottomLeft_GrSurfaceOrigin) { 65 static const Sk4f kMul = {1.f, -1.f, 1.f, -1.f}; 66 const Sk4f kAdd = {0.f, h, 0.f, h}; 67 ltrb = SkNx_shuffle<0, 3, 2, 1>(kMul * ltrb + kAdd); 68 } 69 70 SkRect domainRect; 71 ltrb.store(&domainRect); 72 return domainRect; 73 } 74 75 // If normalizing the src quad then pass 1/width, 1/height, 1 for iw, ih, h. Otherwise pass 76 // 1, 1, and height. 77 static GrPerspQuad compute_src_quad(GrSurfaceOrigin origin, const SkRect& srcRect, float iw, 78 float ih, float h) { 79 // Convert the pixel-space src rectangle into normalized texture coordinates 80 SkRect texRect = { 81 iw * srcRect.fLeft, 82 ih * srcRect.fTop, 83 iw * srcRect.fRight, 84 ih * srcRect.fBottom 85 }; 86 if (origin == kBottomLeft_GrSurfaceOrigin) { 87 texRect.fTop = h - texRect.fTop; 88 texRect.fBottom = h - texRect.fBottom; 89 } 90 return GrPerspQuad(texRect, SkMatrix::I()); 91 } 92 93 /** 94 * Op that implements GrTextureOp::Make. It draws textured quads. Each quad can modulate against a 95 * the texture by color. The blend with the destination is always src-over. The edges are non-AA. 96 */ 97 class TextureOp final : public GrMeshDrawOp { 98 public: 99 static std::unique_ptr<GrDrawOp> Make(GrContext* context, 100 sk_sp<GrTextureProxy> proxy, 101 GrSamplerState::Filter filter, 102 const SkPMColor4f& color, 103 const SkRect& srcRect, 104 const SkRect& dstRect, 105 GrAAType aaType, 106 GrQuadAAFlags aaFlags, 107 SkCanvas::SrcRectConstraint constraint, 108 const SkMatrix& viewMatrix, 109 sk_sp<GrColorSpaceXform> textureColorSpaceXform) { 110 GrOpMemoryPool* pool = context->contextPriv().opMemoryPool(); 111 112 return pool->allocate<TextureOp>( 113 std::move(proxy), filter, color, srcRect, dstRect, aaType, aaFlags, constraint, 114 viewMatrix, std::move(textureColorSpaceXform)); 115 } 116 static std::unique_ptr<GrDrawOp> Make(GrContext* context, 117 const GrRenderTargetContext::TextureSetEntry set[], 118 int cnt, GrSamplerState::Filter filter, GrAAType aaType, 119 const SkMatrix& viewMatrix, 120 sk_sp<GrColorSpaceXform> textureColorSpaceXform) { 121 size_t size = sizeof(TextureOp) + sizeof(Proxy) * (cnt - 1); 122 GrOpMemoryPool* pool = context->contextPriv().opMemoryPool(); 123 void* mem = pool->allocate(size); 124 return std::unique_ptr<GrDrawOp>(new (mem) TextureOp(set, cnt, filter, aaType, viewMatrix, 125 std::move(textureColorSpaceXform))); 126 } 127 128 ~TextureOp() override { 129 for (unsigned p = 0; p < fProxyCnt; ++p) { 130 if (fFinalized) { 131 fProxies[p].fProxy->completedRead(); 132 } else { 133 fProxies[p].fProxy->unref(); 134 } 135 } 136 } 137 138 const char* name() const override { return "TextureOp"; } 139 140 void visitProxies(const VisitProxyFunc& func, VisitorType visitor) const override { 141 if (visitor == VisitorType::kAllocatorGather && fCanSkipAllocatorGather) { 142 return; 143 } 144 for (unsigned p = 0; p < fProxyCnt; ++p) { 145 func(fProxies[p].fProxy); 146 } 147 } 148 149 #ifdef SK_DEBUG 150 SkString dumpInfo() const override { 151 SkString str; 152 str.appendf("# draws: %d\n", fQuads.count()); 153 int q = 0; 154 for (unsigned p = 0; p < fProxyCnt; ++p) { 155 str.appendf("Proxy ID: %d, Filter: %d\n", fProxies[p].fProxy->uniqueID().asUInt(), 156 static_cast<int>(fFilter)); 157 for (int i = 0; i < fProxies[p].fQuadCnt; ++i, ++q) { 158 GrPerspQuad quad = fQuads[q]; 159 const ColorDomainAndAA& info = fQuads.metadata(i); 160 str.appendf( 161 "%d: Color: 0x%08x, TexRect [L: %.2f, T: %.2f, R: %.2f, B: %.2f] " 162 "Quad [(%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f)]\n", 163 i, info.fColor.toBytes_RGBA(), info.fSrcRect.fLeft, info.fSrcRect.fTop, 164 info.fSrcRect.fRight, info.fSrcRect.fBottom, quad.point(0).fX, 165 quad.point(0).fY, quad.point(1).fX, quad.point(1).fY, 166 quad.point(2).fX, quad.point(2).fY, quad.point(3).fX, 167 quad.point(3).fY); 168 } 169 } 170 str += INHERITED::dumpInfo(); 171 return str; 172 } 173 #endif 174 175 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip) override { 176 SkASSERT(!fFinalized); 177 fFinalized = true; 178 for (unsigned p = 0; p < fProxyCnt; ++p) { 179 fProxies[p].fProxy->addPendingRead(); 180 fProxies[p].fProxy->unref(); 181 } 182 return GrProcessorSet::EmptySetAnalysis(); 183 } 184 185 FixedFunctionFlags fixedFunctionFlags() const override { 186 return this->aaType() == GrAAType::kMSAA ? FixedFunctionFlags::kUsesHWAA 187 : FixedFunctionFlags::kNone; 188 } 189 190 DEFINE_OP_CLASS_ID 191 192 private: 193 friend class ::GrOpMemoryPool; 194 195 TextureOp(sk_sp<GrTextureProxy> proxy, GrSamplerState::Filter filter, const SkPMColor4f& color, 196 const SkRect& srcRect, const SkRect& dstRect, GrAAType aaType, GrQuadAAFlags aaFlags, 197 SkCanvas::SrcRectConstraint constraint, const SkMatrix& viewMatrix, 198 sk_sp<GrColorSpaceXform> textureColorSpaceXform) 199 : INHERITED(ClassID()) 200 , fTextureColorSpaceXform(std::move(textureColorSpaceXform)) 201 , fFilter(static_cast<unsigned>(filter)) 202 , fFinalized(0) { 203 GrQuadType quadType = GrQuadTypeForTransformedRect(viewMatrix); 204 auto quad = GrPerspQuad(dstRect, viewMatrix); 205 206 // Clean up disparities between the overall aa type and edge configuration and apply 207 // optimizations based on the rect and matrix when appropriate 208 GrResolveAATypeForQuad(aaType, aaFlags, quad, quadType, &aaType, &aaFlags); 209 fAAType = static_cast<unsigned>(aaType); 210 211 // We expect our caller to have already caught this optimization. 212 SkASSERT(!srcRect.contains(proxy->getWorstCaseBoundsRect()) || 213 constraint == SkCanvas::kFast_SrcRectConstraint); 214 if (quadType == GrQuadType::kRect) { 215 // Disable filtering if possible (note AA optimizations for rects are automatically 216 // handled above in GrResolveAATypeForQuad). 217 if (this->filter() != GrSamplerState::Filter::kNearest && 218 !GrTextureOp::GetFilterHasEffect(viewMatrix, srcRect, dstRect)) { 219 fFilter = static_cast<unsigned>(GrSamplerState::Filter::kNearest); 220 } 221 } 222 // We may have had a strict constraint with nearest filter solely due to possible AA bloat. 223 // If we don't have (or determined we don't need) coverage AA then we can skip using a 224 // domain. 225 if (constraint == SkCanvas::kStrict_SrcRectConstraint && 226 this->filter() == GrSamplerState::Filter::kNearest && 227 aaType != GrAAType::kCoverage) { 228 constraint = SkCanvas::kFast_SrcRectConstraint; 229 } 230 231 Domain domain = constraint == SkCanvas::kStrict_SrcRectConstraint ? Domain::kYes 232 : Domain::kNo; 233 fQuads.push_back(quad, quadType, {color, srcRect, domain, aaFlags}); 234 fProxyCnt = 1; 235 fProxies[0] = {proxy.release(), 1}; 236 auto bounds = quad.bounds(quadType); 237 this->setBounds(bounds, HasAABloat(aaType == GrAAType::kCoverage), IsZeroArea::kNo); 238 fDomain = static_cast<unsigned>(domain); 239 fWideColor = !SkPMColor4fFitsInBytes(color); 240 fCanSkipAllocatorGather = 241 static_cast<unsigned>(fProxies[0].fProxy->canSkipResourceAllocator()); 242 } 243 TextureOp(const GrRenderTargetContext::TextureSetEntry set[], int cnt, 244 GrSamplerState::Filter filter, GrAAType aaType, const SkMatrix& viewMatrix, 245 sk_sp<GrColorSpaceXform> textureColorSpaceXform) 246 : INHERITED(ClassID()) 247 , fTextureColorSpaceXform(std::move(textureColorSpaceXform)) 248 , fFilter(static_cast<unsigned>(filter)) 249 , fFinalized(0) { 250 fProxyCnt = SkToUInt(cnt); 251 SkRect bounds = SkRectPriv::MakeLargestInverted(); 252 GrAAType overallAAType = GrAAType::kNone; // aa type maximally compatible with all dst rects 253 bool mustFilter = false; 254 fCanSkipAllocatorGather = static_cast<unsigned>(true); 255 // All dst rects are transformed by the same view matrix, so their quad types are identical 256 GrQuadType quadType = GrQuadTypeForTransformedRect(viewMatrix); 257 fQuads.reserve(cnt, quadType); 258 259 for (unsigned p = 0; p < fProxyCnt; ++p) { 260 fProxies[p].fProxy = SkRef(set[p].fProxy.get()); 261 fProxies[p].fQuadCnt = 1; 262 SkASSERT(fProxies[p].fProxy->textureType() == fProxies[0].fProxy->textureType()); 263 SkASSERT(fProxies[p].fProxy->config() == fProxies[0].fProxy->config()); 264 if (!fProxies[p].fProxy->canSkipResourceAllocator()) { 265 fCanSkipAllocatorGather = static_cast<unsigned>(false); 266 } 267 auto quad = GrPerspQuad(set[p].fDstRect, viewMatrix); 268 bounds.joinPossiblyEmptyRect(quad.bounds(quadType)); 269 GrQuadAAFlags aaFlags; 270 // Don't update the overall aaType, might be inappropriate for some of the quads 271 GrAAType aaForQuad; 272 GrResolveAATypeForQuad(aaType, set[p].fAAFlags, quad, quadType, &aaForQuad, &aaFlags); 273 // Resolve sets aaForQuad to aaType or None, there is never a change between aa methods 274 SkASSERT(aaForQuad == GrAAType::kNone || aaForQuad == aaType); 275 if (overallAAType == GrAAType::kNone && aaForQuad != GrAAType::kNone) { 276 overallAAType = aaType; 277 } 278 if (!mustFilter && this->filter() != GrSamplerState::Filter::kNearest) { 279 mustFilter = quadType != GrQuadType::kRect || 280 GrTextureOp::GetFilterHasEffect(viewMatrix, set[p].fSrcRect, 281 set[p].fDstRect); 282 } 283 float alpha = SkTPin(set[p].fAlpha, 0.f, 1.f); 284 SkPMColor4f color{alpha, alpha, alpha, alpha}; 285 fQuads.push_back(quad, quadType, {color, set[p].fSrcRect, Domain::kNo, aaFlags}); 286 } 287 fAAType = static_cast<unsigned>(overallAAType); 288 if (!mustFilter) { 289 fFilter = static_cast<unsigned>(GrSamplerState::Filter::kNearest); 290 } 291 this->setBounds(bounds, HasAABloat(this->aaType() == GrAAType::kCoverage), IsZeroArea::kNo); 292 fDomain = static_cast<unsigned>(false); 293 fWideColor = static_cast<unsigned>(false); 294 } 295 296 void tess(void* v, const VertexSpec& spec, const GrTextureProxy* proxy, int start, 297 int cnt) const { 298 TRACE_EVENT0("skia", TRACE_FUNC); 299 auto origin = proxy->origin(); 300 const auto* texture = proxy->peekTexture(); 301 float iw, ih, h; 302 if (proxy->textureType() == GrTextureType::kRectangle) { 303 iw = ih = 1.f; 304 h = texture->height(); 305 } else { 306 iw = 1.f / texture->width(); 307 ih = 1.f / texture->height(); 308 h = 1.f; 309 } 310 311 for (int i = start; i < start + cnt; ++i) { 312 const GrPerspQuad& device = fQuads[i]; 313 const ColorDomainAndAA& info = fQuads.metadata(i); 314 315 GrPerspQuad srcQuad = compute_src_quad(origin, info.fSrcRect, iw, ih, h); 316 SkRect domain = 317 compute_domain(info.domain(), this->filter(), origin, info.fSrcRect, iw, ih, h); 318 v = GrQuadPerEdgeAA::Tessellate(v, spec, device, info.fColor, srcQuad, domain, 319 info.aaFlags()); 320 } 321 } 322 323 void onPrepareDraws(Target* target) override { 324 TRACE_EVENT0("skia", TRACE_FUNC); 325 GrQuadType quadType = GrQuadType::kRect; 326 Domain domain = Domain::kNo; 327 bool wideColor = false; 328 int numProxies = 0; 329 int numTotalQuads = 0; 330 auto textureType = fProxies[0].fProxy->textureType(); 331 auto config = fProxies[0].fProxy->config(); 332 GrAAType aaType = this->aaType(); 333 for (const auto& op : ChainRange<TextureOp>(this)) { 334 if (op.fQuads.quadType() > quadType) { 335 quadType = op.fQuads.quadType(); 336 } 337 if (op.fDomain) { 338 domain = Domain::kYes; 339 } 340 wideColor |= op.fWideColor; 341 numProxies += op.fProxyCnt; 342 for (unsigned p = 0; p < op.fProxyCnt; ++p) { 343 numTotalQuads += op.fProxies[p].fQuadCnt; 344 auto* proxy = op.fProxies[p].fProxy; 345 if (!proxy->instantiate(target->resourceProvider())) { 346 return; 347 } 348 SkASSERT(proxy->config() == config); 349 SkASSERT(proxy->textureType() == textureType); 350 } 351 if (op.aaType() == GrAAType::kCoverage) { 352 SkASSERT(aaType == GrAAType::kCoverage || aaType == GrAAType::kNone); 353 aaType = GrAAType::kCoverage; 354 } 355 } 356 357 VertexSpec vertexSpec(quadType, wideColor ? ColorType::kHalf : ColorType::kByte, 358 GrQuadType::kRect, /* hasLocal */ true, domain, aaType, 359 /* alpha as coverage */ true); 360 361 GrSamplerState samplerState = GrSamplerState(GrSamplerState::WrapMode::kClamp, 362 this->filter()); 363 GrGpu* gpu = target->resourceProvider()->priv().gpu(); 364 uint32_t extraSamplerKey = gpu->getExtraSamplerKeyForProgram( 365 samplerState, fProxies[0].fProxy->backendFormat()); 366 367 sk_sp<GrGeometryProcessor> gp = GrQuadPerEdgeAA::MakeTexturedProcessor( 368 vertexSpec, *target->caps().shaderCaps(), 369 textureType, config, samplerState, extraSamplerKey, 370 std::move(fTextureColorSpaceXform)); 371 372 GrPipeline::InitArgs args; 373 args.fCaps = &target->caps(); 374 args.fResourceProvider = target->resourceProvider(); 375 args.fFlags = 0; 376 if (aaType == GrAAType::kMSAA) { 377 args.fFlags |= GrPipeline::kHWAntialias_Flag; 378 } 379 380 auto clip = target->detachAppliedClip(); 381 // We'll use a dynamic state array for the GP textures when there are multiple ops. 382 // Otherwise, we use fixed dynamic state to specify the single op's proxy. 383 GrPipeline::DynamicStateArrays* dynamicStateArrays = nullptr; 384 GrPipeline::FixedDynamicState* fixedDynamicState; 385 if (numProxies > 1) { 386 dynamicStateArrays = target->allocDynamicStateArrays(numProxies, 1, false); 387 fixedDynamicState = target->allocFixedDynamicState(clip.scissorState().rect(), 0); 388 } else { 389 fixedDynamicState = target->allocFixedDynamicState(clip.scissorState().rect(), 1); 390 fixedDynamicState->fPrimitiveProcessorTextures[0] = fProxies[0].fProxy; 391 } 392 const auto* pipeline = 393 target->allocPipeline(args, GrProcessorSet::MakeEmptySet(), std::move(clip)); 394 395 size_t vertexSize = gp->vertexStride(); 396 397 GrMesh* meshes = target->allocMeshes(numProxies); 398 sk_sp<const GrBuffer> vbuffer; 399 int vertexOffsetInBuffer = 0; 400 int numQuadVerticesLeft = numTotalQuads * vertexSpec.verticesPerQuad(); 401 int numAllocatedVertices = 0; 402 void* vdata = nullptr; 403 404 int m = 0; 405 for (const auto& op : ChainRange<TextureOp>(this)) { 406 int q = 0; 407 for (unsigned p = 0; p < op.fProxyCnt; ++p) { 408 int quadCnt = op.fProxies[p].fQuadCnt; 409 auto* proxy = op.fProxies[p].fProxy; 410 int meshVertexCnt = quadCnt * vertexSpec.verticesPerQuad(); 411 if (numAllocatedVertices < meshVertexCnt) { 412 vdata = target->makeVertexSpaceAtLeast( 413 vertexSize, meshVertexCnt, numQuadVerticesLeft, &vbuffer, 414 &vertexOffsetInBuffer, &numAllocatedVertices); 415 SkASSERT(numAllocatedVertices <= numQuadVerticesLeft); 416 if (!vdata) { 417 SkDebugf("Could not allocate vertices\n"); 418 return; 419 } 420 } 421 SkASSERT(numAllocatedVertices >= meshVertexCnt); 422 423 op.tess(vdata, vertexSpec, proxy, q, quadCnt); 424 425 if (!GrQuadPerEdgeAA::ConfigureMeshIndices(target, &(meshes[m]), vertexSpec, 426 quadCnt)) { 427 SkDebugf("Could not allocate indices"); 428 return; 429 } 430 meshes[m].setVertexData(vbuffer, vertexOffsetInBuffer); 431 if (dynamicStateArrays) { 432 dynamicStateArrays->fPrimitiveProcessorTextures[m] = proxy; 433 } 434 ++m; 435 numAllocatedVertices -= meshVertexCnt; 436 numQuadVerticesLeft -= meshVertexCnt; 437 vertexOffsetInBuffer += meshVertexCnt; 438 vdata = reinterpret_cast<char*>(vdata) + vertexSize * meshVertexCnt; 439 q += quadCnt; 440 } 441 } 442 SkASSERT(!numQuadVerticesLeft); 443 SkASSERT(!numAllocatedVertices); 444 target->draw(std::move(gp), pipeline, fixedDynamicState, dynamicStateArrays, meshes, 445 numProxies); 446 } 447 448 CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override { 449 TRACE_EVENT0("skia", TRACE_FUNC); 450 const auto* that = t->cast<TextureOp>(); 451 if (!GrColorSpaceXform::Equals(fTextureColorSpaceXform.get(), 452 that->fTextureColorSpaceXform.get())) { 453 return CombineResult::kCannotCombine; 454 } 455 bool upgradeToCoverageAAOnMerge = false; 456 if (this->aaType() != that->aaType()) { 457 if (!((this->aaType() == GrAAType::kCoverage && that->aaType() == GrAAType::kNone) || 458 (that->aaType() == GrAAType::kCoverage && this->aaType() == GrAAType::kNone))) { 459 return CombineResult::kCannotCombine; 460 } 461 upgradeToCoverageAAOnMerge = true; 462 } 463 if (fFilter != that->fFilter) { 464 return CombineResult::kCannotCombine; 465 } 466 auto thisProxy = fProxies[0].fProxy; 467 auto thatProxy = that->fProxies[0].fProxy; 468 if (fProxyCnt > 1 || that->fProxyCnt > 1 || 469 thisProxy->uniqueID() != thatProxy->uniqueID()) { 470 // We can't merge across different proxies. Check if 'this' can be chained with 'that'. 471 if (GrTextureProxy::ProxiesAreCompatibleAsDynamicState(thisProxy, thatProxy) && 472 caps.dynamicStateArrayGeometryProcessorTextureSupport()) { 473 return CombineResult::kMayChain; 474 } 475 return CombineResult::kCannotCombine; 476 } 477 fProxies[0].fQuadCnt += that->fQuads.count(); 478 fQuads.concat(that->fQuads); 479 fDomain |= that->fDomain; 480 fWideColor |= that->fWideColor; 481 if (upgradeToCoverageAAOnMerge) { 482 fAAType = static_cast<unsigned>(GrAAType::kCoverage); 483 } 484 return CombineResult::kMerged; 485 } 486 487 GrAAType aaType() const { return static_cast<GrAAType>(fAAType); } 488 GrSamplerState::Filter filter() const { return static_cast<GrSamplerState::Filter>(fFilter); } 489 490 struct ColorDomainAndAA { 491 // Special constructor to convert enums into the packed bits, which should not delete 492 // the implicit move constructor (but it does require us to declare an empty ctor for 493 // use with the GrTQuadList). 494 ColorDomainAndAA(const SkPMColor4f& color, const SkRect& srcRect, 495 Domain hasDomain, GrQuadAAFlags aaFlags) 496 : fColor(color) 497 , fSrcRect(srcRect) 498 , fHasDomain(static_cast<unsigned>(hasDomain)) 499 , fAAFlags(static_cast<unsigned>(aaFlags)) { 500 SkASSERT(fHasDomain == static_cast<unsigned>(hasDomain)); 501 SkASSERT(fAAFlags == static_cast<unsigned>(aaFlags)); 502 } 503 ColorDomainAndAA() = default; 504 505 SkPMColor4f fColor; 506 SkRect fSrcRect; 507 unsigned fHasDomain : 1; 508 unsigned fAAFlags : 4; 509 510 Domain domain() const { return Domain(fHasDomain); } 511 GrQuadAAFlags aaFlags() const { return static_cast<GrQuadAAFlags>(fAAFlags); } 512 }; 513 struct Proxy { 514 GrTextureProxy* fProxy; 515 int fQuadCnt; 516 }; 517 GrTQuadList<ColorDomainAndAA> fQuads; 518 sk_sp<GrColorSpaceXform> fTextureColorSpaceXform; 519 unsigned fFilter : 2; 520 unsigned fAAType : 2; 521 unsigned fDomain : 1; 522 unsigned fWideColor : 1; 523 // Used to track whether fProxy is ref'ed or has a pending IO after finalize() is called. 524 unsigned fFinalized : 1; 525 unsigned fCanSkipAllocatorGather : 1; 526 unsigned fProxyCnt : 32 - 8; 527 Proxy fProxies[1]; 528 529 static_assert(kGrQuadTypeCount <= 4, "GrQuadType does not fit in 2 bits"); 530 531 typedef GrMeshDrawOp INHERITED; 532 }; 533 534 } // anonymous namespace 535 536 namespace GrTextureOp { 537 538 std::unique_ptr<GrDrawOp> Make(GrContext* context, 539 sk_sp<GrTextureProxy> proxy, 540 GrSamplerState::Filter filter, 541 const SkPMColor4f& color, 542 const SkRect& srcRect, 543 const SkRect& dstRect, 544 GrAAType aaType, 545 GrQuadAAFlags aaFlags, 546 SkCanvas::SrcRectConstraint constraint, 547 const SkMatrix& viewMatrix, 548 sk_sp<GrColorSpaceXform> textureColorSpaceXform) { 549 return TextureOp::Make(context, std::move(proxy), filter, color, srcRect, dstRect, aaType, 550 aaFlags, constraint, viewMatrix, std::move(textureColorSpaceXform)); 551 } 552 553 std::unique_ptr<GrDrawOp> Make(GrContext* context, 554 const GrRenderTargetContext::TextureSetEntry set[], 555 int cnt, 556 GrSamplerState::Filter filter, 557 GrAAType aaType, 558 const SkMatrix& viewMatrix, 559 sk_sp<GrColorSpaceXform> textureColorSpaceXform) { 560 return TextureOp::Make(context, set, cnt, filter, aaType, viewMatrix, 561 std::move(textureColorSpaceXform)); 562 } 563 564 bool GetFilterHasEffect(const SkMatrix& viewMatrix, const SkRect& srcRect, const SkRect& dstRect) { 565 // Hypothetically we could disable bilerp filtering when flipping or rotating 90 degrees, but 566 // that makes the math harder and we don't want to increase the overhead of the checks 567 if (!viewMatrix.isScaleTranslate() || 568 viewMatrix.getScaleX() < 0.0f || viewMatrix.getScaleY() < 0.0f) { 569 return true; 570 } 571 572 // Given the matrix conditions ensured above, this computes the device space coordinates for 573 // the top left corner of dstRect and its size. 574 SkScalar dw = viewMatrix.getScaleX() * dstRect.width(); 575 SkScalar dh = viewMatrix.getScaleY() * dstRect.height(); 576 SkScalar dl = viewMatrix.getScaleX() * dstRect.fLeft + viewMatrix.getTranslateX(); 577 SkScalar dt = viewMatrix.getScaleY() * dstRect.fTop + viewMatrix.getTranslateY(); 578 579 // Disable filtering when there is no scaling of the src rect and the src rect and dst rect 580 // align fractionally. If we allow inverted src rects this logic needs to consider that. 581 SkASSERT(srcRect.isSorted()); 582 return dw != srcRect.width() || dh != srcRect.height() || 583 SkScalarFraction(dl) != SkScalarFraction(srcRect.fLeft) || 584 SkScalarFraction(dt) != SkScalarFraction(srcRect.fTop); 585 } 586 587 } // namespace GrTextureOp 588 589 #if GR_TEST_UTILS 590 #include "GrContext.h" 591 #include "GrContextPriv.h" 592 #include "GrProxyProvider.h" 593 594 GR_DRAW_OP_TEST_DEFINE(TextureOp) { 595 GrSurfaceDesc desc; 596 desc.fConfig = kRGBA_8888_GrPixelConfig; 597 desc.fHeight = random->nextULessThan(90) + 10; 598 desc.fWidth = random->nextULessThan(90) + 10; 599 auto origin = random->nextBool() ? kTopLeft_GrSurfaceOrigin : kBottomLeft_GrSurfaceOrigin; 600 GrMipMapped mipMapped = random->nextBool() ? GrMipMapped::kYes : GrMipMapped::kNo; 601 SkBackingFit fit = SkBackingFit::kExact; 602 if (mipMapped == GrMipMapped::kNo) { 603 fit = random->nextBool() ? SkBackingFit::kApprox : SkBackingFit::kExact; 604 } 605 606 const GrBackendFormat format = 607 context->contextPriv().caps()->getBackendFormatFromColorType(kRGBA_8888_SkColorType); 608 609 GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider(); 610 sk_sp<GrTextureProxy> proxy = proxyProvider->createProxy(format, desc, origin, mipMapped, fit, 611 SkBudgeted::kNo, 612 GrInternalSurfaceFlags::kNone); 613 614 SkRect rect = GrTest::TestRect(random); 615 SkRect srcRect; 616 srcRect.fLeft = random->nextRangeScalar(0.f, proxy->width() / 2.f); 617 srcRect.fRight = random->nextRangeScalar(0.f, proxy->width()) + proxy->width() / 2.f; 618 srcRect.fTop = random->nextRangeScalar(0.f, proxy->height() / 2.f); 619 srcRect.fBottom = random->nextRangeScalar(0.f, proxy->height()) + proxy->height() / 2.f; 620 SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random); 621 SkPMColor4f color = SkPMColor4f::FromBytes_RGBA(SkColorToPremulGrColor(random->nextU())); 622 GrSamplerState::Filter filter = (GrSamplerState::Filter)random->nextULessThan( 623 static_cast<uint32_t>(GrSamplerState::Filter::kMipMap) + 1); 624 while (mipMapped == GrMipMapped::kNo && filter == GrSamplerState::Filter::kMipMap) { 625 filter = (GrSamplerState::Filter)random->nextULessThan( 626 static_cast<uint32_t>(GrSamplerState::Filter::kMipMap) + 1); 627 } 628 auto texXform = GrTest::TestColorXform(random); 629 GrAAType aaType = GrAAType::kNone; 630 if (random->nextBool()) { 631 aaType = (fsaaType == GrFSAAType::kUnifiedMSAA) ? GrAAType::kMSAA : GrAAType::kCoverage; 632 } 633 GrQuadAAFlags aaFlags = GrQuadAAFlags::kNone; 634 aaFlags |= random->nextBool() ? GrQuadAAFlags::kLeft : GrQuadAAFlags::kNone; 635 aaFlags |= random->nextBool() ? GrQuadAAFlags::kTop : GrQuadAAFlags::kNone; 636 aaFlags |= random->nextBool() ? GrQuadAAFlags::kRight : GrQuadAAFlags::kNone; 637 aaFlags |= random->nextBool() ? GrQuadAAFlags::kBottom : GrQuadAAFlags::kNone; 638 auto constraint = random->nextBool() ? SkCanvas::kStrict_SrcRectConstraint 639 : SkCanvas::kFast_SrcRectConstraint; 640 return GrTextureOp::Make(context, std::move(proxy), filter, color, srcRect, rect, aaType, 641 aaFlags, constraint, viewMatrix, std::move(texXform)); 642 } 643 644 #endif 645