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 "GrAppliedClip.h" 10 #include "GrCaps.h" 11 #include "GrDrawOpTest.h" 12 #include "GrGeometryProcessor.h" 13 #include "GrMeshDrawOp.h" 14 #include "GrOpFlushState.h" 15 #include "GrQuad.h" 16 #include "GrResourceProvider.h" 17 #include "GrShaderCaps.h" 18 #include "GrTexture.h" 19 #include "GrTexturePriv.h" 20 #include "GrTextureProxy.h" 21 #include "SkGr.h" 22 #include "SkMathPriv.h" 23 #include "SkPoint.h" 24 #include "SkPoint3.h" 25 #include "glsl/GrGLSLColorSpaceXformHelper.h" 26 #include "glsl/GrGLSLFragmentShaderBuilder.h" 27 #include "glsl/GrGLSLGeometryProcessor.h" 28 #include "glsl/GrGLSLVarying.h" 29 #include "glsl/GrGLSLVertexGeoBuilder.h" 30 31 namespace { 32 33 /** 34 * Geometry Processor that draws a texture modulated by a vertex color (though, this is meant to be 35 * the same value across all vertices of a quad and uses flat interpolation when available). This is 36 * used by TextureOp below. 37 */ 38 class TextureGeometryProcessor : public GrGeometryProcessor { 39 public: 40 struct Vertex { 41 SkPoint fPosition; 42 SkPoint fTextureCoords; 43 GrColor fColor; 44 }; 45 struct AAVertex { 46 SkPoint fPosition; 47 SkPoint fTextureCoords; 48 SkPoint3 fEdges[4]; 49 GrColor fColor; 50 }; 51 struct MultiTextureVertex { 52 SkPoint fPosition; 53 int fTextureIdx; 54 SkPoint fTextureCoords; 55 GrColor fColor; 56 }; 57 struct AAMultiTextureVertex { 58 SkPoint fPosition; 59 int fTextureIdx; 60 SkPoint fTextureCoords; 61 SkPoint3 fEdges[4]; 62 GrColor fColor; 63 }; 64 65 // Maximum number of textures supported by this op. Must also be checked against the caps 66 // limit. These numbers were based on some limited experiments on a HP Z840 and Pixel XL 2016 67 // and could probably use more tuning. 68 #ifdef SK_BUILD_FOR_ANDROID 69 static constexpr int kMaxTextures = 4; 70 #else 71 static constexpr int kMaxTextures = 8; 72 #endif 73 74 static int SupportsMultitexture(const GrShaderCaps& caps) { 75 return caps.integerSupport() && caps.maxFragmentSamplers() > 1; 76 } 77 78 static sk_sp<GrGeometryProcessor> Make(sk_sp<GrTextureProxy> proxies[], int proxyCnt, 79 sk_sp<GrColorSpaceXform> csxf, bool coverageAA, 80 const GrSamplerState::Filter filters[], 81 const GrShaderCaps& caps) { 82 // We use placement new to avoid always allocating space for kMaxTextures TextureSampler 83 // instances. 84 int samplerCnt = NumSamplersToUse(proxyCnt, caps); 85 size_t size = sizeof(TextureGeometryProcessor) + sizeof(TextureSampler) * (samplerCnt - 1); 86 void* mem = GrGeometryProcessor::operator new(size); 87 return sk_sp<TextureGeometryProcessor>(new (mem) TextureGeometryProcessor( 88 proxies, proxyCnt, samplerCnt, std::move(csxf), coverageAA, filters, caps)); 89 } 90 91 ~TextureGeometryProcessor() override { 92 int cnt = this->numTextureSamplers(); 93 for (int i = 1; i < cnt; ++i) { 94 fSamplers[i].~TextureSampler(); 95 } 96 } 97 98 const char* name() const override { return "TextureGeometryProcessor"; } 99 100 void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override { 101 b->add32(GrColorSpaceXform::XformKey(fColorSpaceXform.get())); 102 b->add32(static_cast<uint32_t>(this->usesCoverageEdgeAA())); 103 } 104 105 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps& caps) const override { 106 class GLSLProcessor : public GrGLSLGeometryProcessor { 107 public: 108 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc, 109 FPCoordTransformIter&& transformIter) override { 110 const auto& textureGP = proc.cast<TextureGeometryProcessor>(); 111 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter); 112 if (fColorSpaceXformHelper.isValid()) { 113 fColorSpaceXformHelper.setData(pdman, textureGP.fColorSpaceXform.get()); 114 } 115 } 116 117 private: 118 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { 119 const auto& textureGP = args.fGP.cast<TextureGeometryProcessor>(); 120 fColorSpaceXformHelper.emitCode( 121 args.fUniformHandler, textureGP.fColorSpaceXform.get()); 122 args.fVaryingHandler->setNoPerspective(); 123 args.fVaryingHandler->emitAttributes(textureGP); 124 this->writeOutputPosition(args.fVertBuilder, gpArgs, textureGP.fPositions.fName); 125 this->emitTransforms(args.fVertBuilder, 126 args.fVaryingHandler, 127 args.fUniformHandler, 128 textureGP.fTextureCoords.asShaderVar(), 129 args.fFPCoordTransformHandler); 130 if (args.fShaderCaps->preferFlatInterpolation()) { 131 args.fVaryingHandler->addFlatPassThroughAttribute(&textureGP.fColors, 132 args.fOutputColor); 133 } else { 134 args.fVaryingHandler->addPassThroughAttribute(&textureGP.fColors, 135 args.fOutputColor); 136 } 137 args.fFragBuilder->codeAppend("float2 texCoord;"); 138 args.fVaryingHandler->addPassThroughAttribute(&textureGP.fTextureCoords, 139 "texCoord"); 140 if (textureGP.numTextureSamplers() > 1) { 141 SkASSERT(args.fShaderCaps->integerSupport()); 142 args.fFragBuilder->codeAppend("int texIdx;"); 143 if (args.fShaderCaps->flatInterpolationSupport()) { 144 args.fVaryingHandler->addFlatPassThroughAttribute(&textureGP.fTextureIdx, 145 "texIdx"); 146 } else { 147 args.fVaryingHandler->addPassThroughAttribute(&textureGP.fTextureIdx, 148 "texIdx"); 149 } 150 args.fFragBuilder->codeAppend("switch (texIdx) {"); 151 for (int i = 0; i < textureGP.numTextureSamplers(); ++i) { 152 args.fFragBuilder->codeAppendf("case %d: %s = ", i, args.fOutputColor); 153 args.fFragBuilder->appendTextureLookupAndModulate(args.fOutputColor, 154 args.fTexSamplers[i], 155 "texCoord", 156 kFloat2_GrSLType, 157 &fColorSpaceXformHelper); 158 args.fFragBuilder->codeAppend("; break;"); 159 } 160 args.fFragBuilder->codeAppend("}"); 161 } else { 162 args.fFragBuilder->codeAppendf("%s = ", args.fOutputColor); 163 args.fFragBuilder->appendTextureLookupAndModulate(args.fOutputColor, 164 args.fTexSamplers[0], 165 "texCoord", 166 kFloat2_GrSLType, 167 &fColorSpaceXformHelper); 168 } 169 args.fFragBuilder->codeAppend(";"); 170 if (textureGP.usesCoverageEdgeAA()) { 171 const char* aaDistName = nullptr; 172 // When interpolation is innacurate we perform the evaluation of the edge 173 // equations in the fragment shader rather than interpolating values computed 174 // in the vertex shader. 175 if (!args.fShaderCaps->interpolantsAreInaccurate()) { 176 GrGLSLVarying aaDistVarying(kFloat4_GrSLType, 177 GrGLSLVarying::Scope::kVertToFrag); 178 args.fVaryingHandler->addVarying("aaDists", &aaDistVarying); 179 args.fVertBuilder->codeAppendf( 180 R"(%s = float4(dot(aaEdge0.xy, %s.xy) + aaEdge0.z, 181 dot(aaEdge1.xy, %s.xy) + aaEdge1.z, 182 dot(aaEdge2.xy, %s.xy) + aaEdge2.z, 183 dot(aaEdge3.xy, %s.xy) + aaEdge3.z);)", 184 aaDistVarying.vsOut(), textureGP.fPositions.fName, 185 textureGP.fPositions.fName, textureGP.fPositions.fName, 186 textureGP.fPositions.fName); 187 aaDistName = aaDistVarying.fsIn(); 188 } else { 189 GrGLSLVarying aaEdgeVarying[4]{ 190 {kFloat3_GrSLType, GrGLSLVarying::Scope::kVertToFrag}, 191 {kFloat3_GrSLType, GrGLSLVarying::Scope::kVertToFrag}, 192 {kFloat3_GrSLType, GrGLSLVarying::Scope::kVertToFrag}, 193 {kFloat3_GrSLType, GrGLSLVarying::Scope::kVertToFrag} 194 }; 195 for (int i = 0; i < 4; ++i) { 196 SkString name; 197 name.printf("aaEdge%d", i); 198 args.fVaryingHandler->addVarying(name.c_str(), &aaEdgeVarying[i]); 199 args.fVertBuilder->codeAppendf( 200 "%s = aaEdge%d;", aaEdgeVarying[i].vsOut(), i); 201 } 202 args.fFragBuilder->codeAppendf( 203 R"(float4 aaDists = float4(dot(%s.xy, sk_FragCoord.xy) + %s.z, 204 dot(%s.xy, sk_FragCoord.xy) + %s.z, 205 dot(%s.xy, sk_FragCoord.xy) + %s.z, 206 dot(%s.xy, sk_FragCoord.xy) + %s.z);)", 207 aaEdgeVarying[0].fsIn(), aaEdgeVarying[0].fsIn(), 208 aaEdgeVarying[1].fsIn(), aaEdgeVarying[1].fsIn(), 209 aaEdgeVarying[2].fsIn(), aaEdgeVarying[2].fsIn(), 210 aaEdgeVarying[3].fsIn(), aaEdgeVarying[3].fsIn()); 211 aaDistName = "aaDists"; 212 } 213 args.fFragBuilder->codeAppendf( 214 "float mindist = min(min(%s.x, %s.y), min(%s.z, %s.w));", 215 aaDistName, aaDistName, aaDistName, aaDistName); 216 args.fFragBuilder->codeAppendf("%s = float4(clamp(mindist, 0, 1));", 217 args.fOutputCoverage); 218 } else { 219 args.fFragBuilder->codeAppendf("%s = float4(1);", args.fOutputCoverage); 220 } 221 } 222 GrGLSLColorSpaceXformHelper fColorSpaceXformHelper; 223 }; 224 return new GLSLProcessor; 225 } 226 227 bool usesCoverageEdgeAA() const { return SkToBool(fAAEdges[0].isInitialized()); } 228 229 private: 230 // This exists to reduce the number of shaders generated. It does some rounding of sampler 231 // counts. 232 static int NumSamplersToUse(int numRealProxies, const GrShaderCaps& caps) { 233 SkASSERT(numRealProxies > 0 && numRealProxies <= kMaxTextures && 234 numRealProxies <= caps.maxFragmentSamplers()); 235 if (1 == numRealProxies) { 236 return 1; 237 } 238 if (numRealProxies <= 4) { 239 return 4; 240 } 241 // Round to the next power of 2 and then clamp to kMaxTextures and the max allowed by caps. 242 return SkTMin(SkNextPow2(numRealProxies), SkTMin(kMaxTextures, caps.maxFragmentSamplers())); 243 } 244 245 TextureGeometryProcessor(sk_sp<GrTextureProxy> proxies[], int proxyCnt, int samplerCnt, 246 sk_sp<GrColorSpaceXform> csxf, bool coverageAA, 247 const GrSamplerState::Filter filters[], const GrShaderCaps& caps) 248 : INHERITED(kTextureGeometryProcessor_ClassID), fColorSpaceXform(std::move(csxf)) { 249 SkASSERT(proxyCnt > 0 && samplerCnt >= proxyCnt); 250 fPositions = this->addVertexAttrib("position", kFloat2_GrVertexAttribType); 251 fSamplers[0].reset(std::move(proxies[0]), filters[0]); 252 this->addTextureSampler(&fSamplers[0]); 253 for (int i = 1; i < proxyCnt; ++i) { 254 // This class has one sampler built in, the rest come from memory this processor was 255 // placement-newed into and so haven't been constructed. 256 new (&fSamplers[i]) TextureSampler(std::move(proxies[i]), filters[i]); 257 this->addTextureSampler(&fSamplers[i]); 258 } 259 if (samplerCnt > 1) { 260 // Here we initialize any extra samplers by repeating the last one samplerCnt - proxyCnt 261 // times. 262 GrTextureProxy* dupeProxy = fSamplers[proxyCnt - 1].proxy(); 263 for (int i = proxyCnt; i < samplerCnt; ++i) { 264 new (&fSamplers[i]) TextureSampler(sk_ref_sp(dupeProxy), filters[proxyCnt - 1]); 265 this->addTextureSampler(&fSamplers[i]); 266 } 267 SkASSERT(caps.integerSupport()); 268 fTextureIdx = this->addVertexAttrib("textureIdx", kInt_GrVertexAttribType); 269 } 270 271 fTextureCoords = this->addVertexAttrib("textureCoords", kFloat2_GrVertexAttribType); 272 if (coverageAA) { 273 fAAEdges[0] = this->addVertexAttrib("aaEdge0", kFloat3_GrVertexAttribType); 274 fAAEdges[1] = this->addVertexAttrib("aaEdge1", kFloat3_GrVertexAttribType); 275 fAAEdges[2] = this->addVertexAttrib("aaEdge2", kFloat3_GrVertexAttribType); 276 fAAEdges[3] = this->addVertexAttrib("aaEdge3", kFloat3_GrVertexAttribType); 277 } 278 fColors = this->addVertexAttrib("color", kUByte4_norm_GrVertexAttribType); 279 } 280 281 Attribute fPositions; 282 Attribute fTextureIdx; 283 Attribute fTextureCoords; 284 Attribute fColors; 285 Attribute fAAEdges[4]; 286 sk_sp<GrColorSpaceXform> fColorSpaceXform; 287 TextureSampler fSamplers[1]; 288 289 typedef GrGeometryProcessor INHERITED; 290 }; 291 292 namespace { 293 // This is a class soley so it can be partially specialized (functions cannot be). 294 template<GrAA, typename Vertex> class VertexAAHandler; 295 296 template<typename Vertex> class VertexAAHandler<GrAA::kNo, Vertex> { 297 public: 298 static void AssignPositionsAndTexCoords(Vertex* vertices, const GrQuad& quad, 299 const SkRect& texRect) { 300 vertices[0].fPosition = quad.point(0); 301 vertices[0].fTextureCoords = {texRect.fLeft, texRect.fTop}; 302 vertices[1].fPosition = quad.point(1); 303 vertices[1].fTextureCoords = {texRect.fLeft, texRect.fBottom}; 304 vertices[2].fPosition = quad.point(2); 305 vertices[2].fTextureCoords = {texRect.fRight, texRect.fTop}; 306 vertices[3].fPosition = quad.point(3); 307 vertices[3].fTextureCoords = {texRect.fRight, texRect.fBottom}; 308 } 309 }; 310 311 template<typename Vertex> class VertexAAHandler<GrAA::kYes, Vertex> { 312 public: 313 static void AssignPositionsAndTexCoords(Vertex* vertices, const GrQuad& quad, 314 const SkRect& texRect) { 315 // We compute the four edge equations for quad, then outset them and compute a new quad 316 // as the intersection points of the outset edges. 317 318 // GrQuad is in tristip order but we want the points to be in a fan order, so swap 2 and 3. 319 Sk4f xs(quad.point(0).fX, quad.point(1).fX, quad.point(3).fX, quad.point(2).fX); 320 Sk4f ys(quad.point(0).fY, quad.point(1).fY, quad.point(3).fY, quad.point(2).fY); 321 Sk4f xsrot = SkNx_shuffle<1, 2, 3, 0>(xs); 322 Sk4f ysrot = SkNx_shuffle<1, 2, 3, 0>(ys); 323 Sk4f normXs = ysrot - ys; 324 Sk4f normYs = xs - xsrot; 325 Sk4f ds = xsrot * ys - ysrot * xs; 326 Sk4f invNormLengths = (normXs * normXs + normYs * normYs).rsqrt(); 327 float test = normXs[0] * xs[2] + normYs[0] * ys[2] + ds[0]; 328 // Make sure the edge equations have their normals facing into the quad in device space 329 if (test < 0) { 330 invNormLengths = -invNormLengths; 331 } 332 normXs *= invNormLengths; 333 normYs *= invNormLengths; 334 ds *= invNormLengths; 335 336 // Here is the bloat. This makes our edge equations compute coverage without requiring a 337 // half pixel offset and is also used to compute the bloated quad that will cover all 338 // pixels. 339 ds += Sk4f(0.5f); 340 341 for (int i = 0; i < 4; ++i) { 342 for (int j = 0; j < 4; ++j) { 343 vertices[j].fEdges[i].fX = normXs[i]; 344 vertices[j].fEdges[i].fY = normYs[i]; 345 vertices[j].fEdges[i].fZ = ds[i]; 346 } 347 } 348 349 // Reverse the process to compute the points of the bloated quad from the edge equations. 350 // This time the inputs don't have 1s as their third coord and we want to homogenize rather 351 // than normalize the output since we need a GrQuad with 2D points. 352 xsrot = SkNx_shuffle<3, 0, 1, 2>(normXs); 353 ysrot = SkNx_shuffle<3, 0, 1, 2>(normYs); 354 Sk4f dsrot = SkNx_shuffle<3, 0, 1, 2>(ds); 355 xs = ysrot * ds - normYs * dsrot; 356 ys = normXs * dsrot - xsrot * ds; 357 ds = xsrot * normYs - ysrot * normXs; 358 ds = ds.invert(); 359 xs *= ds; 360 ys *= ds; 361 362 // Go back to tri strip order when writing out the bloated quad to vertex positions. 363 vertices[0].fPosition = {xs[0], ys[0]}; 364 vertices[1].fPosition = {xs[1], ys[1]}; 365 vertices[3].fPosition = {xs[2], ys[2]}; 366 vertices[2].fPosition = {xs[3], ys[3]}; 367 368 AssignTexCoords(vertices, quad, texRect); 369 } 370 371 private: 372 static void AssignTexCoords(Vertex* vertices, const GrQuad& quad, const SkRect& tex) { 373 SkMatrix q = SkMatrix::MakeAll(quad.point(0).fX, quad.point(1).fX, quad.point(2).fX, 374 quad.point(0).fY, quad.point(1).fY, quad.point(2).fY, 375 1.f, 1.f, 1.f); 376 SkMatrix qinv; 377 if (!q.invert(&qinv)) { 378 return; 379 } 380 SkMatrix t = SkMatrix::MakeAll(tex.fLeft, tex.fLeft, tex.fRight, 381 tex.fTop, tex.fBottom, tex.fTop, 382 1.f, 1.f, 1.f); 383 SkMatrix map; 384 map.setConcat(t, qinv); 385 SkMatrixPriv::MapPointsWithStride(map, &vertices[0].fTextureCoords, sizeof(Vertex), 386 &vertices[0].fPosition, sizeof(Vertex), 4); 387 } 388 }; 389 390 template <typename Vertex, bool IsMultiTex> struct TexIdAssigner; 391 392 template <typename Vertex> struct TexIdAssigner<Vertex, true> { 393 static void Assign(Vertex* vertices, int textureIdx) { 394 vertices[0].fTextureIdx = textureIdx; 395 vertices[1].fTextureIdx = textureIdx; 396 vertices[2].fTextureIdx = textureIdx; 397 vertices[3].fTextureIdx = textureIdx; 398 } 399 }; 400 401 template <typename Vertex> struct TexIdAssigner<Vertex, false> { 402 static void Assign(Vertex* vertices, int textureIdx) {} 403 }; 404 } // anonymous namespace 405 406 template <typename Vertex, bool IsMultiTex, GrAA AA> 407 static void tessellate_quad(const GrQuad& devQuad, const SkRect& srcRect, GrColor color, 408 GrSurfaceOrigin origin, Vertex* vertices, SkScalar iw, SkScalar ih, 409 int textureIdx) { 410 SkRect texRect = { 411 iw * srcRect.fLeft, 412 ih * srcRect.fTop, 413 iw * srcRect.fRight, 414 ih * srcRect.fBottom 415 }; 416 if (origin == kBottomLeft_GrSurfaceOrigin) { 417 texRect.fTop = 1.f - texRect.fTop; 418 texRect.fBottom = 1.f - texRect.fBottom; 419 } 420 VertexAAHandler<AA, Vertex>::AssignPositionsAndTexCoords(vertices, devQuad, texRect); 421 vertices[0].fColor = color; 422 vertices[1].fColor = color; 423 vertices[2].fColor = color; 424 vertices[3].fColor = color; 425 TexIdAssigner<Vertex, IsMultiTex>::Assign(vertices, textureIdx); 426 } 427 /** 428 * Op that implements GrTextureOp::Make. It draws textured quads. Each quad can modulate against a 429 * the texture by color. The blend with the destination is always src-over. The edges are non-AA. 430 */ 431 class TextureOp final : public GrMeshDrawOp { 432 public: 433 static std::unique_ptr<GrDrawOp> Make(sk_sp<GrTextureProxy> proxy, 434 GrSamplerState::Filter filter, GrColor color, 435 const SkRect& srcRect, const SkRect& dstRect, 436 GrAAType aaType, const SkMatrix& viewMatrix, 437 sk_sp<GrColorSpaceXform> csxf, bool allowSRBInputs) { 438 return std::unique_ptr<GrDrawOp>(new TextureOp(std::move(proxy), filter, color, srcRect, 439 dstRect, aaType, viewMatrix, std::move(csxf), 440 allowSRBInputs)); 441 } 442 443 ~TextureOp() override { 444 if (fFinalized) { 445 auto proxies = this->proxies(); 446 for (int i = 0; i < fProxyCnt; ++i) { 447 proxies[i]->completedRead(); 448 } 449 if (fProxyCnt > 1) { 450 delete[] reinterpret_cast<const char*>(proxies); 451 } 452 } else { 453 SkASSERT(1 == fProxyCnt); 454 fProxy0->unref(); 455 } 456 } 457 458 const char* name() const override { return "TextureOp"; } 459 460 void visitProxies(const VisitProxyFunc& func) const override { 461 auto proxies = this->proxies(); 462 for (int i = 0; i < fProxyCnt; ++i) { 463 func(proxies[i]); 464 } 465 } 466 467 SkString dumpInfo() const override { 468 SkString str; 469 str.appendf("AllowSRGBInputs: %d\n", fAllowSRGBInputs); 470 str.appendf("# draws: %d\n", fDraws.count()); 471 auto proxies = this->proxies(); 472 for (int i = 0; i < fProxyCnt; ++i) { 473 str.appendf("Proxy ID %d: %d, Filter: %d\n", i, proxies[i]->uniqueID().asUInt(), 474 static_cast<int>(this->filters()[i])); 475 } 476 for (int i = 0; i < fDraws.count(); ++i) { 477 const Draw& draw = fDraws[i]; 478 str.appendf( 479 "%d: Color: 0x%08x, ProxyIdx: %d, TexRect [L: %.2f, T: %.2f, R: %.2f, B: %.2f] " 480 "Quad [(%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f)]\n", 481 i, draw.fColor, draw.fTextureIdx, draw.fSrcRect.fLeft, draw.fSrcRect.fTop, 482 draw.fSrcRect.fRight, draw.fSrcRect.fBottom, draw.fQuad.points()[0].fX, 483 draw.fQuad.points()[0].fY, draw.fQuad.points()[1].fX, draw.fQuad.points()[1].fY, 484 draw.fQuad.points()[2].fX, draw.fQuad.points()[2].fY, draw.fQuad.points()[3].fX, 485 draw.fQuad.points()[3].fY); 486 } 487 str += INHERITED::dumpInfo(); 488 return str; 489 } 490 491 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip, 492 GrPixelConfigIsClamped dstIsClamped) override { 493 SkASSERT(!fFinalized); 494 SkASSERT(1 == fProxyCnt); 495 fFinalized = true; 496 fProxy0->addPendingRead(); 497 fProxy0->unref(); 498 return RequiresDstTexture::kNo; 499 } 500 501 FixedFunctionFlags fixedFunctionFlags() const override { 502 return this->aaType() == GrAAType::kMSAA ? FixedFunctionFlags::kUsesHWAA 503 : FixedFunctionFlags::kNone; 504 } 505 506 DEFINE_OP_CLASS_ID 507 508 private: 509 510 // This is used in a heursitic for choosing a code path. We don't care what happens with 511 // really large rects, infs, nans, etc. 512 #if defined(__clang__) && (__clang_major__ * 1000 + __clang_minor__) >= 3007 513 __attribute__((no_sanitize("float-cast-overflow"))) 514 #endif 515 size_t RectSizeAsSizeT(const SkRect& rect) {; 516 return static_cast<size_t>(SkTMax(rect.width(), 1.f) * SkTMax(rect.height(), 1.f)); 517 } 518 519 static constexpr int kMaxTextures = TextureGeometryProcessor::kMaxTextures; 520 521 TextureOp(sk_sp<GrTextureProxy> proxy, GrSamplerState::Filter filter, GrColor color, 522 const SkRect& srcRect, const SkRect& dstRect, GrAAType aaType, 523 const SkMatrix& viewMatrix, sk_sp<GrColorSpaceXform> csxf, bool allowSRGBInputs) 524 : INHERITED(ClassID()) 525 , fColorSpaceXform(std::move(csxf)) 526 , fProxy0(proxy.release()) 527 , fFilter0(filter) 528 , fProxyCnt(1) 529 , fAAType(static_cast<unsigned>(aaType)) 530 , fFinalized(0) 531 , fAllowSRGBInputs(allowSRGBInputs ? 1 : 0) { 532 SkASSERT(aaType != GrAAType::kMixedSamples); 533 Draw& draw = fDraws.push_back(); 534 draw.fSrcRect = srcRect; 535 draw.fTextureIdx = 0; 536 draw.fColor = color; 537 draw.fQuad.setFromMappedRect(dstRect, viewMatrix); 538 SkRect bounds; 539 bounds.setBounds(draw.fQuad.points(), 4); 540 this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo); 541 542 fMaxApproxDstPixelArea = RectSizeAsSizeT(bounds); 543 } 544 545 void onPrepareDraws(Target* target) override { 546 sk_sp<GrTextureProxy> proxiesSPs[kMaxTextures]; 547 auto proxies = this->proxies(); 548 auto filters = this->filters(); 549 for (int i = 0; i < fProxyCnt; ++i) { 550 if (!proxies[i]->instantiate(target->resourceProvider())) { 551 return; 552 } 553 proxiesSPs[i] = sk_ref_sp(proxies[i]); 554 } 555 556 bool coverageAA = GrAAType::kCoverage == this->aaType(); 557 sk_sp<GrGeometryProcessor> gp = 558 TextureGeometryProcessor::Make(proxiesSPs, fProxyCnt, std::move(fColorSpaceXform), 559 coverageAA, filters, *target->caps().shaderCaps()); 560 GrPipeline::InitArgs args; 561 args.fProxy = target->proxy(); 562 args.fCaps = &target->caps(); 563 args.fResourceProvider = target->resourceProvider(); 564 args.fFlags = 0; 565 if (fAllowSRGBInputs) { 566 args.fFlags |= GrPipeline::kAllowSRGBInputs_Flag; 567 } 568 if (GrAAType::kMSAA == this->aaType()) { 569 args.fFlags |= GrPipeline::kHWAntialias_Flag; 570 } 571 572 const GrPipeline* pipeline = target->allocPipeline(args, GrProcessorSet::MakeEmptySet(), 573 target->detachAppliedClip()); 574 int vstart; 575 const GrBuffer* vbuffer; 576 void* vdata = target->makeVertexSpace(gp->getVertexStride(), 4 * fDraws.count(), &vbuffer, 577 &vstart); 578 if (!vdata) { 579 SkDebugf("Could not allocate vertices\n"); 580 return; 581 } 582 if (1 == fProxyCnt) { 583 GrSurfaceOrigin origin = proxies[0]->origin(); 584 GrTexture* texture = proxies[0]->priv().peekTexture(); 585 float iw = 1.f / texture->width(); 586 float ih = 1.f / texture->height(); 587 if (coverageAA) { 588 SkASSERT(gp->getVertexStride() == sizeof(TextureGeometryProcessor::AAVertex)); 589 auto vertices = static_cast<TextureGeometryProcessor::AAVertex*>(vdata); 590 for (int i = 0; i < fDraws.count(); ++i) { 591 tessellate_quad<TextureGeometryProcessor::AAVertex, false, GrAA::kYes>( 592 fDraws[i].fQuad, fDraws[i].fSrcRect, fDraws[i].fColor, origin, 593 vertices + 4 * i, iw, ih, 0); 594 } 595 } else { 596 SkASSERT(gp->getVertexStride() == sizeof(TextureGeometryProcessor::Vertex)); 597 auto vertices = static_cast<TextureGeometryProcessor::Vertex*>(vdata); 598 for (int i = 0; i < fDraws.count(); ++i) { 599 tessellate_quad<TextureGeometryProcessor::Vertex, false, GrAA::kNo>( 600 fDraws[i].fQuad, fDraws[i].fSrcRect, fDraws[i].fColor, origin, 601 vertices + 4 * i, iw, ih, 0); 602 } 603 } 604 } else { 605 GrTexture* textures[kMaxTextures]; 606 float iw[kMaxTextures]; 607 float ih[kMaxTextures]; 608 for (int t = 0; t < fProxyCnt; ++t) { 609 textures[t] = proxies[t]->priv().peekTexture(); 610 iw[t] = 1.f / textures[t]->width(); 611 ih[t] = 1.f / textures[t]->height(); 612 } 613 if (coverageAA) { 614 SkASSERT(gp->getVertexStride() == 615 sizeof(TextureGeometryProcessor::AAMultiTextureVertex)); 616 auto vertices = static_cast<TextureGeometryProcessor::AAMultiTextureVertex*>(vdata); 617 for (int i = 0; i < fDraws.count(); ++i) { 618 auto tidx = fDraws[i].fTextureIdx; 619 GrSurfaceOrigin origin = proxies[tidx]->origin(); 620 tessellate_quad<TextureGeometryProcessor::AAMultiTextureVertex, true, 621 GrAA::kYes>(fDraws[i].fQuad, fDraws[i].fSrcRect, 622 fDraws[i].fColor, origin, vertices + 4 * i, 623 iw[tidx], ih[tidx], tidx); 624 } 625 } else { 626 SkASSERT(gp->getVertexStride() == 627 sizeof(TextureGeometryProcessor::MultiTextureVertex)); 628 auto vertices = static_cast<TextureGeometryProcessor::MultiTextureVertex*>(vdata); 629 for (int i = 0; i < fDraws.count(); ++i) { 630 auto tidx = fDraws[i].fTextureIdx; 631 GrSurfaceOrigin origin = proxies[tidx]->origin(); 632 tessellate_quad<TextureGeometryProcessor::MultiTextureVertex, true, GrAA::kNo>( 633 fDraws[i].fQuad, fDraws[i].fSrcRect, fDraws[i].fColor, origin, 634 vertices + 4 * i, iw[tidx], ih[tidx], tidx); 635 } 636 } 637 } 638 GrPrimitiveType primitiveType = 639 fDraws.count() > 1 ? GrPrimitiveType::kTriangles : GrPrimitiveType::kTriangleStrip; 640 GrMesh mesh(primitiveType); 641 if (fDraws.count() > 1) { 642 sk_sp<const GrBuffer> ibuffer = target->resourceProvider()->refQuadIndexBuffer(); 643 if (!ibuffer) { 644 SkDebugf("Could not allocate quad indices\n"); 645 return; 646 } 647 mesh.setIndexedPatterned(ibuffer.get(), 6, 4, fDraws.count(), 648 GrResourceProvider::QuadCountOfQuadBuffer()); 649 } else { 650 mesh.setNonIndexedNonInstanced(4); 651 } 652 mesh.setVertexData(vbuffer, vstart); 653 target->draw(gp.get(), pipeline, mesh); 654 } 655 656 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override { 657 const auto* that = t->cast<TextureOp>(); 658 const auto& shaderCaps = *caps.shaderCaps(); 659 if (!GrColorSpaceXform::Equals(fColorSpaceXform.get(), that->fColorSpaceXform.get())) { 660 return false; 661 } 662 if (this->aaType() != that->aaType()) { 663 return false; 664 } 665 // Because of an issue where GrColorSpaceXform adds the same function every time it is used 666 // in a texture lookup, we only allow multiple textures when there is no transform. 667 if (TextureGeometryProcessor::SupportsMultitexture(shaderCaps) && !fColorSpaceXform && 668 fMaxApproxDstPixelArea <= shaderCaps.disableImageMultitexturingDstRectAreaThreshold() && 669 that->fMaxApproxDstPixelArea <= 670 shaderCaps.disableImageMultitexturingDstRectAreaThreshold()) { 671 int map[kMaxTextures]; 672 int numNewProxies = this->mergeProxies(that, map, shaderCaps); 673 if (numNewProxies < 0) { 674 return false; 675 } 676 if (1 == fProxyCnt && numNewProxies) { 677 void* mem = new char[(sizeof(GrSamplerState::Filter) + sizeof(GrTextureProxy*)) * 678 kMaxTextures]; 679 auto proxies = reinterpret_cast<GrTextureProxy**>(mem); 680 auto filters = reinterpret_cast<GrSamplerState::Filter*>(proxies + kMaxTextures); 681 proxies[0] = fProxy0; 682 filters[0] = fFilter0; 683 fProxyArray = proxies; 684 } 685 fProxyCnt += numNewProxies; 686 auto thisProxies = fProxyArray; 687 auto thatProxies = that->proxies(); 688 auto thatFilters = that->filters(); 689 auto thisFilters = reinterpret_cast<GrSamplerState::Filter*>(thisProxies + 690 kMaxTextures); 691 for (int i = 0; i < that->fProxyCnt; ++i) { 692 if (map[i] < 0) { 693 thatProxies[i]->addPendingRead(); 694 695 thisProxies[-map[i]] = thatProxies[i]; 696 thisFilters[-map[i]] = thatFilters[i]; 697 map[i] = -map[i]; 698 } 699 } 700 int firstNewDraw = fDraws.count(); 701 fDraws.push_back_n(that->fDraws.count(), that->fDraws.begin()); 702 for (int i = firstNewDraw; i < fDraws.count(); ++i) { 703 fDraws[i].fTextureIdx = map[fDraws[i].fTextureIdx]; 704 } 705 } else { 706 // We can get here when one of the ops is already multitextured but the other cannot 707 // be because of the dst rect size. 708 if (fProxyCnt > 1 || that->fProxyCnt > 1) { 709 return false; 710 } 711 if (fProxy0->uniqueID() != that->fProxy0->uniqueID() || fFilter0 != that->fFilter0) { 712 return false; 713 } 714 fDraws.push_back_n(that->fDraws.count(), that->fDraws.begin()); 715 } 716 this->joinBounds(*that); 717 fMaxApproxDstPixelArea = SkTMax(that->fMaxApproxDstPixelArea, fMaxApproxDstPixelArea); 718 return true; 719 } 720 721 /** 722 * Determines a mapping of indices from that's proxy array to this's proxy array. A negative map 723 * value means that's proxy should be added to this's proxy array at the absolute value of 724 * the map entry. If it is determined that the ops shouldn't combine their proxies then a 725 * negative value is returned. Otherwise, return value indicates the number of proxies that have 726 * to be added to this op or, equivalently, the number of negative entries in map. 727 */ 728 int mergeProxies(const TextureOp* that, int map[kMaxTextures], const GrShaderCaps& caps) const { 729 std::fill_n(map, kMaxTextures, -kMaxTextures); 730 int sharedProxyCnt = 0; 731 auto thisProxies = this->proxies(); 732 auto thisFilters = this->filters(); 733 auto thatProxies = that->proxies(); 734 auto thatFilters = that->filters(); 735 for (int i = 0; i < fProxyCnt; ++i) { 736 for (int j = 0; j < that->fProxyCnt; ++j) { 737 if (thisProxies[i]->uniqueID() == thatProxies[j]->uniqueID()) { 738 if (thisFilters[i] != thatFilters[j]) { 739 // In GL we don't currently support using the same texture with different 740 // samplers. If we added support for sampler objects and a cap bit to know 741 // it's ok to use different filter modes then we could support this. 742 // Otherwise, we could also only allow a single filter mode for each op 743 // instance. 744 return -1; 745 } 746 map[j] = i; 747 ++sharedProxyCnt; 748 break; 749 } 750 } 751 } 752 int actualMaxTextures = SkTMin(caps.maxFragmentSamplers(), kMaxTextures); 753 int newProxyCnt = that->fProxyCnt - sharedProxyCnt; 754 if (newProxyCnt + fProxyCnt > actualMaxTextures) { 755 return -1; 756 } 757 GrPixelConfig config = thisProxies[0]->config(); 758 int nextSlot = fProxyCnt; 759 for (int j = 0; j < that->fProxyCnt; ++j) { 760 // We want to avoid making many shaders because of different permutations of shader 761 // based swizzle and sampler types. The approach taken here is to require the configs to 762 // be the same and to only allow already instantiated proxies that have the most 763 // common sampler type. Otherwise we don't merge. 764 if (thatProxies[j]->config() != config) { 765 return -1; 766 } 767 if (GrTexture* tex = thatProxies[j]->priv().peekTexture()) { 768 if (tex->texturePriv().samplerType() != kTexture2DSampler_GrSLType) { 769 return -1; 770 } 771 } 772 if (map[j] < 0) { 773 map[j] = -(nextSlot++); 774 } 775 } 776 return newProxyCnt; 777 } 778 779 GrAAType aaType() const { return static_cast<GrAAType>(fAAType); } 780 781 GrTextureProxy* const* proxies() const { return fProxyCnt > 1 ? fProxyArray : &fProxy0; } 782 783 const GrSamplerState::Filter* filters() const { 784 if (fProxyCnt > 1) { 785 return reinterpret_cast<const GrSamplerState::Filter*>(fProxyArray + kMaxTextures); 786 } 787 return &fFilter0; 788 } 789 790 struct Draw { 791 SkRect fSrcRect; 792 int fTextureIdx; 793 GrQuad fQuad; 794 GrColor fColor; 795 }; 796 SkSTArray<1, Draw, true> fDraws; 797 sk_sp<GrColorSpaceXform> fColorSpaceXform; 798 // Initially we store a single proxy ptr and a single filter. If we grow to have more than 799 // one proxy we instead store pointers to dynamically allocated arrays of size kMaxTextures 800 // followed by kMaxTextures filters. 801 union { 802 GrTextureProxy* fProxy0; 803 GrTextureProxy** fProxyArray; 804 }; 805 size_t fMaxApproxDstPixelArea; 806 GrSamplerState::Filter fFilter0; 807 uint8_t fProxyCnt; 808 unsigned fAAType : 2; 809 // Used to track whether fProxy is ref'ed or has a pending IO after finalize() is called. 810 unsigned fFinalized : 1; 811 unsigned fAllowSRGBInputs : 1; 812 813 typedef GrMeshDrawOp INHERITED; 814 }; 815 816 constexpr int TextureGeometryProcessor::kMaxTextures; 817 constexpr int TextureOp::kMaxTextures; 818 819 } // anonymous namespace 820 821 namespace GrTextureOp { 822 823 std::unique_ptr<GrDrawOp> Make(sk_sp<GrTextureProxy> proxy, GrSamplerState::Filter filter, 824 GrColor color, const SkRect& srcRect, const SkRect& dstRect, 825 GrAAType aaType, const SkMatrix& viewMatrix, 826 sk_sp<GrColorSpaceXform> csxf, bool allowSRGBInputs) { 827 SkASSERT(!viewMatrix.hasPerspective()); 828 return TextureOp::Make(std::move(proxy), filter, color, srcRect, dstRect, aaType, viewMatrix, 829 std::move(csxf), allowSRGBInputs); 830 } 831 832 } // namespace GrTextureOp 833 834 #if GR_TEST_UTILS 835 #include "GrContext.h" 836 #include "GrContextPriv.h" 837 #include "GrProxyProvider.h" 838 839 GR_DRAW_OP_TEST_DEFINE(TextureOp) { 840 GrSurfaceDesc desc; 841 desc.fConfig = kRGBA_8888_GrPixelConfig; 842 desc.fHeight = random->nextULessThan(90) + 10; 843 desc.fWidth = random->nextULessThan(90) + 10; 844 desc.fOrigin = random->nextBool() ? kTopLeft_GrSurfaceOrigin : kBottomLeft_GrSurfaceOrigin; 845 SkBackingFit fit = random->nextBool() ? SkBackingFit::kApprox : SkBackingFit::kExact; 846 847 GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider(); 848 sk_sp<GrTextureProxy> proxy = proxyProvider->createProxy(desc, fit, SkBudgeted::kNo); 849 850 SkRect rect = GrTest::TestRect(random); 851 SkRect srcRect; 852 srcRect.fLeft = random->nextRangeScalar(0.f, proxy->width() / 2.f); 853 srcRect.fRight = random->nextRangeScalar(0.f, proxy->width()) + proxy->width() / 2.f; 854 srcRect.fTop = random->nextRangeScalar(0.f, proxy->height() / 2.f); 855 srcRect.fBottom = random->nextRangeScalar(0.f, proxy->height()) + proxy->height() / 2.f; 856 SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random); 857 GrColor color = SkColorToPremulGrColor(random->nextU()); 858 GrSamplerState::Filter filter = (GrSamplerState::Filter)random->nextULessThan( 859 static_cast<uint32_t>(GrSamplerState::Filter::kMipMap) + 1); 860 auto csxf = GrTest::TestColorXform(random); 861 bool allowSRGBInputs = random->nextBool(); 862 GrAAType aaType = GrAAType::kNone; 863 if (random->nextBool()) { 864 aaType = (fsaaType == GrFSAAType::kUnifiedMSAA) ? GrAAType::kMSAA : GrAAType::kCoverage; 865 } 866 return GrTextureOp::Make(std::move(proxy), filter, color, srcRect, rect, aaType, viewMatrix, 867 std::move(csxf), allowSRGBInputs); 868 } 869 870 #endif 871