GrTextureOp.cpp 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731
  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. #include "src/gpu/ops/GrTextureOp.h"
  8. #include <new>
  9. #include "include/core/SkPoint.h"
  10. #include "include/core/SkPoint3.h"
  11. #include "include/gpu/GrTexture.h"
  12. #include "include/private/GrRecordingContext.h"
  13. #include "include/private/SkFloatingPoint.h"
  14. #include "include/private/SkTo.h"
  15. #include "src/core/SkMathPriv.h"
  16. #include "src/core/SkMatrixPriv.h"
  17. #include "src/core/SkRectPriv.h"
  18. #include "src/gpu/GrAppliedClip.h"
  19. #include "src/gpu/GrCaps.h"
  20. #include "src/gpu/GrDrawOpTest.h"
  21. #include "src/gpu/GrGeometryProcessor.h"
  22. #include "src/gpu/GrGpu.h"
  23. #include "src/gpu/GrMemoryPool.h"
  24. #include "src/gpu/GrOpFlushState.h"
  25. #include "src/gpu/GrRecordingContextPriv.h"
  26. #include "src/gpu/GrResourceProvider.h"
  27. #include "src/gpu/GrResourceProviderPriv.h"
  28. #include "src/gpu/GrShaderCaps.h"
  29. #include "src/gpu/GrTexturePriv.h"
  30. #include "src/gpu/GrTextureProxy.h"
  31. #include "src/gpu/SkGr.h"
  32. #include "src/gpu/effects/GrTextureDomain.h"
  33. #include "src/gpu/geometry/GrQuad.h"
  34. #include "src/gpu/geometry/GrQuadBuffer.h"
  35. #include "src/gpu/geometry/GrQuadUtils.h"
  36. #include "src/gpu/glsl/GrGLSLVarying.h"
  37. #include "src/gpu/ops/GrFillRectOp.h"
  38. #include "src/gpu/ops/GrMeshDrawOp.h"
  39. #include "src/gpu/ops/GrQuadPerEdgeAA.h"
  40. namespace {
  41. using Domain = GrQuadPerEdgeAA::Domain;
  42. using VertexSpec = GrQuadPerEdgeAA::VertexSpec;
  43. using ColorType = GrQuadPerEdgeAA::ColorType;
  44. // Extracts lengths of vertical and horizontal edges of axis-aligned quad. "width" is the edge
  45. // between v0 and v2 (or v1 and v3), "height" is the edge between v0 and v1 (or v2 and v3).
  46. static SkSize axis_aligned_quad_size(const GrQuad& quad) {
  47. SkASSERT(quad.quadType() == GrQuad::Type::kAxisAligned);
  48. // Simplification of regular edge length equation, since it's axis aligned and can avoid sqrt
  49. float dw = sk_float_abs(quad.x(2) - quad.x(0)) + sk_float_abs(quad.y(2) - quad.y(0));
  50. float dh = sk_float_abs(quad.x(1) - quad.x(0)) + sk_float_abs(quad.y(1) - quad.y(0));
  51. return {dw, dh};
  52. }
  53. static bool filter_has_effect(const GrQuad& srcQuad, const GrQuad& dstQuad) {
  54. // If not axis-aligned in src or dst, then always say it has an effect
  55. if (srcQuad.quadType() != GrQuad::Type::kAxisAligned ||
  56. dstQuad.quadType() != GrQuad::Type::kAxisAligned) {
  57. return true;
  58. }
  59. SkRect srcRect;
  60. SkRect dstRect;
  61. if (srcQuad.asRect(&srcRect) && dstQuad.asRect(&dstRect)) {
  62. // Disable filtering when there is no scaling (width and height are the same), and the
  63. // top-left corners have the same fraction (so src and dst snap to the pixel grid
  64. // identically).
  65. SkASSERT(srcRect.isSorted());
  66. return srcRect.width() != dstRect.width() || srcRect.height() != dstRect.height() ||
  67. SkScalarFraction(srcRect.fLeft) != SkScalarFraction(dstRect.fLeft) ||
  68. SkScalarFraction(srcRect.fTop) != SkScalarFraction(dstRect.fTop);
  69. } else {
  70. // Although the quads are axis-aligned, the local coordinate system is transformed such
  71. // that fractionally-aligned sample centers will not align with the device coordinate system
  72. // So disable filtering when edges are the same length and both srcQuad and dstQuad
  73. // 0th vertex is integer aligned.
  74. if (SkScalarIsInt(srcQuad.x(0)) && SkScalarIsInt(srcQuad.y(0)) &&
  75. SkScalarIsInt(dstQuad.x(0)) && SkScalarIsInt(dstQuad.y(0))) {
  76. // Extract edge lengths
  77. SkSize srcSize = axis_aligned_quad_size(srcQuad);
  78. SkSize dstSize = axis_aligned_quad_size(dstQuad);
  79. return srcSize.fWidth != dstSize.fWidth || srcSize.fHeight != dstSize.fHeight;
  80. } else {
  81. return true;
  82. }
  83. }
  84. }
  85. // if normalizing the domain then pass 1/width, 1/height, 1 for iw, ih, h. Otherwise pass
  86. // 1, 1, and height.
  87. static void compute_domain(Domain domain, GrSamplerState::Filter filter, GrSurfaceOrigin origin,
  88. const SkRect& domainRect, float iw, float ih, float h, SkRect* out) {
  89. static constexpr SkRect kLargeRect = {-100000, -100000, 1000000, 1000000};
  90. if (domain == Domain::kNo) {
  91. // Either the quad has no domain constraint and is batched with a domain constrained op
  92. // (in which case we want a domain that doesn't restrict normalized tex coords), or the
  93. // entire op doesn't use the domain, in which case the returned value is ignored.
  94. *out = kLargeRect;
  95. return;
  96. }
  97. auto ltrb = Sk4f::Load(&domainRect);
  98. if (filter == GrSamplerState::Filter::kBilerp) {
  99. auto rblt = SkNx_shuffle<2, 3, 0, 1>(ltrb);
  100. auto whwh = (rblt - ltrb).abs();
  101. auto c = (rblt + ltrb) * 0.5f;
  102. static const Sk4f kOffsets = {0.5f, 0.5f, -0.5f, -0.5f};
  103. ltrb = (whwh < 1.f).thenElse(c, ltrb + kOffsets);
  104. }
  105. ltrb *= Sk4f(iw, ih, iw, ih);
  106. if (origin == kBottomLeft_GrSurfaceOrigin) {
  107. static const Sk4f kMul = {1.f, -1.f, 1.f, -1.f};
  108. const Sk4f kAdd = {0.f, h, 0.f, h};
  109. ltrb = SkNx_shuffle<0, 3, 2, 1>(kMul * ltrb + kAdd);
  110. }
  111. ltrb.store(out);
  112. }
  113. // Normalizes logical src coords and corrects for origin
  114. static void compute_src_quad(GrSurfaceOrigin origin, const GrQuad& srcQuad,
  115. float iw, float ih, float h, GrQuad* out) {
  116. // The src quad should not have any perspective
  117. SkASSERT(!srcQuad.hasPerspective() && !out->hasPerspective());
  118. skvx::Vec<4, float> xs = srcQuad.x4f() * iw;
  119. skvx::Vec<4, float> ys = srcQuad.y4f() * ih;
  120. if (origin == kBottomLeft_GrSurfaceOrigin) {
  121. ys = h - ys;
  122. }
  123. xs.store(out->xs());
  124. ys.store(out->ys());
  125. out->setQuadType(srcQuad.quadType());
  126. }
  127. /**
  128. * Op that implements GrTextureOp::Make. It draws textured quads. Each quad can modulate against a
  129. * the texture by color. The blend with the destination is always src-over. The edges are non-AA.
  130. */
  131. class TextureOp final : public GrMeshDrawOp {
  132. public:
  133. static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
  134. sk_sp<GrTextureProxy> proxy,
  135. sk_sp<GrColorSpaceXform> textureXform,
  136. GrSamplerState::Filter filter,
  137. const SkPMColor4f& color,
  138. GrAAType aaType,
  139. GrQuadAAFlags aaFlags,
  140. const GrQuad& deviceQuad,
  141. const GrQuad& localQuad,
  142. const SkRect* domain) {
  143. GrOpMemoryPool* pool = context->priv().opMemoryPool();
  144. return pool->allocate<TextureOp>(
  145. std::move(proxy), std::move(textureXform), filter, color, aaType, aaFlags,
  146. deviceQuad, localQuad, domain);
  147. }
  148. static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
  149. const GrRenderTargetContext::TextureSetEntry set[],
  150. int cnt, GrSamplerState::Filter filter, GrAAType aaType,
  151. SkCanvas::SrcRectConstraint constraint,
  152. const SkMatrix& viewMatrix,
  153. sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
  154. size_t size = sizeof(TextureOp) + sizeof(Proxy) * (cnt - 1);
  155. GrOpMemoryPool* pool = context->priv().opMemoryPool();
  156. void* mem = pool->allocate(size);
  157. return std::unique_ptr<GrDrawOp>(new (mem) TextureOp(
  158. set, cnt, filter, aaType, constraint, viewMatrix,
  159. std::move(textureColorSpaceXform)));
  160. }
  161. ~TextureOp() override {
  162. for (unsigned p = 0; p < fProxyCnt; ++p) {
  163. fProxies[p].fProxy->unref();
  164. }
  165. }
  166. const char* name() const override { return "TextureOp"; }
  167. void visitProxies(const VisitProxyFunc& func) const override {
  168. for (unsigned p = 0; p < fProxyCnt; ++p) {
  169. bool mipped = (GrSamplerState::Filter::kMipMap == this->filter());
  170. func(fProxies[p].fProxy, GrMipMapped(mipped));
  171. }
  172. }
  173. #ifdef SK_DEBUG
  174. SkString dumpInfo() const override {
  175. SkString str;
  176. str.appendf("# draws: %d\n", fQuads.count());
  177. auto iter = fQuads.iterator();
  178. for (unsigned p = 0; p < fProxyCnt; ++p) {
  179. str.appendf("Proxy ID: %d, Filter: %d\n", fProxies[p].fProxy->uniqueID().asUInt(),
  180. static_cast<int>(fFilter));
  181. int i = 0;
  182. while(i < fProxies[p].fQuadCnt && iter.next()) {
  183. const GrQuad& quad = iter.deviceQuad();
  184. const GrQuad& uv = iter.localQuad();
  185. const ColorDomainAndAA& info = iter.metadata();
  186. str.appendf(
  187. "%d: Color: 0x%08x, Domain(%d): [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n"
  188. " UVs [(%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f)]\n"
  189. " Quad [(%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f)]\n",
  190. i, info.fColor.toBytes_RGBA(), info.fHasDomain, info.fDomainRect.fLeft,
  191. info.fDomainRect.fTop, info.fDomainRect.fRight, info.fDomainRect.fBottom,
  192. quad.point(0).fX, quad.point(0).fY, quad.point(1).fX, quad.point(1).fY,
  193. quad.point(2).fX, quad.point(2).fY, quad.point(3).fX, quad.point(3).fY,
  194. uv.point(0).fX, uv.point(0).fY, uv.point(1).fX, uv.point(1).fY,
  195. uv.point(2).fX, uv.point(2).fY, uv.point(3).fX, uv.point(3).fY);
  196. i++;
  197. }
  198. }
  199. str += INHERITED::dumpInfo();
  200. return str;
  201. }
  202. #endif
  203. GrProcessorSet::Analysis finalize(
  204. const GrCaps& caps, const GrAppliedClip*, bool hasMixedSampledCoverage,
  205. GrClampType clampType) override {
  206. fColorType = static_cast<unsigned>(ColorType::kNone);
  207. auto iter = fQuads.metadata();
  208. while(iter.next()) {
  209. auto colorType = GrQuadPerEdgeAA::MinColorType(iter->fColor, clampType, caps);
  210. fColorType = SkTMax(fColorType, static_cast<unsigned>(colorType));
  211. }
  212. return GrProcessorSet::EmptySetAnalysis();
  213. }
  214. FixedFunctionFlags fixedFunctionFlags() const override {
  215. return this->aaType() == GrAAType::kMSAA ? FixedFunctionFlags::kUsesHWAA
  216. : FixedFunctionFlags::kNone;
  217. }
  218. DEFINE_OP_CLASS_ID
  219. private:
  220. friend class ::GrOpMemoryPool;
  221. struct ColorDomainAndAA {
  222. ColorDomainAndAA(const SkPMColor4f& color, const SkRect* domainRect, GrQuadAAFlags aaFlags)
  223. : fColor(color)
  224. , fDomainRect(domainRect ? *domainRect : SkRect::MakeEmpty())
  225. , fHasDomain(static_cast<unsigned>(domainRect ? Domain::kYes : Domain::kNo))
  226. , fAAFlags(static_cast<unsigned>(aaFlags)) {
  227. SkASSERT(fAAFlags == static_cast<unsigned>(aaFlags));
  228. }
  229. SkPMColor4f fColor;
  230. SkRect fDomainRect;
  231. unsigned fHasDomain : 1;
  232. unsigned fAAFlags : 4;
  233. Domain domain() const { return Domain(fHasDomain); }
  234. GrQuadAAFlags aaFlags() const { return static_cast<GrQuadAAFlags>(fAAFlags); }
  235. };
  236. struct Proxy {
  237. GrTextureProxy* fProxy;
  238. int fQuadCnt;
  239. };
  240. // dstQuad should be the geometry transformed by the view matrix. If domainRect
  241. // is not null it will be used to apply the strict src rect constraint.
  242. TextureOp(sk_sp<GrTextureProxy> proxy, sk_sp<GrColorSpaceXform> textureColorSpaceXform,
  243. GrSamplerState::Filter filter, const SkPMColor4f& color,
  244. GrAAType aaType, GrQuadAAFlags aaFlags,
  245. const GrQuad& dstQuad, const GrQuad& srcQuad, const SkRect* domainRect)
  246. : INHERITED(ClassID())
  247. , fQuads(1, true /* includes locals */)
  248. , fTextureColorSpaceXform(std::move(textureColorSpaceXform))
  249. , fFilter(static_cast<unsigned>(filter)) {
  250. // Clean up disparities between the overall aa type and edge configuration and apply
  251. // optimizations based on the rect and matrix when appropriate
  252. GrQuadUtils::ResolveAAType(aaType, aaFlags, dstQuad, &aaType, &aaFlags);
  253. fAAType = static_cast<unsigned>(aaType);
  254. // We expect our caller to have already caught this optimization.
  255. SkASSERT(!domainRect || !domainRect->contains(proxy->getWorstCaseBoundsRect()));
  256. // We may have had a strict constraint with nearest filter solely due to possible AA bloat.
  257. // If we don't have (or determined we don't need) coverage AA then we can skip using a
  258. // domain.
  259. if (domainRect && this->filter() == GrSamplerState::Filter::kNearest &&
  260. aaType != GrAAType::kCoverage) {
  261. domainRect = nullptr;
  262. }
  263. fQuads.append(dstQuad, {color, domainRect, aaFlags}, &srcQuad);
  264. fProxyCnt = 1;
  265. fProxies[0] = {proxy.release(), 1};
  266. this->setBounds(dstQuad.bounds(), HasAABloat(aaType == GrAAType::kCoverage),
  267. IsZeroArea::kNo);
  268. fDomain = static_cast<unsigned>(domainRect != nullptr);
  269. }
  270. TextureOp(const GrRenderTargetContext::TextureSetEntry set[], int cnt,
  271. GrSamplerState::Filter filter, GrAAType aaType,
  272. SkCanvas::SrcRectConstraint constraint, const SkMatrix& viewMatrix,
  273. sk_sp<GrColorSpaceXform> textureColorSpaceXform)
  274. : INHERITED(ClassID())
  275. , fQuads(cnt, true /* includes locals */)
  276. , fTextureColorSpaceXform(std::move(textureColorSpaceXform))
  277. , fFilter(static_cast<unsigned>(filter)) {
  278. fProxyCnt = SkToUInt(cnt);
  279. SkRect bounds = SkRectPriv::MakeLargestInverted();
  280. GrAAType overallAAType = GrAAType::kNone; // aa type maximally compatible with all dst rects
  281. bool mustFilter = false;
  282. bool allOpaque = true;
  283. Domain netDomain = Domain::kNo;
  284. for (unsigned p = 0; p < fProxyCnt; ++p) {
  285. fProxies[p].fProxy = SkRef(set[p].fProxy.get());
  286. fProxies[p].fQuadCnt = 1;
  287. SkASSERT(fProxies[p].fProxy->textureType() == fProxies[0].fProxy->textureType());
  288. SkASSERT(fProxies[p].fProxy->config() == fProxies[0].fProxy->config());
  289. SkMatrix ctm = viewMatrix;
  290. if (set[p].fPreViewMatrix) {
  291. ctm.preConcat(*set[p].fPreViewMatrix);
  292. }
  293. // Use dstRect/srcRect unless dstClip is provided, in which case derive new source
  294. // coordinates by mapping dstClipQuad by the dstRect to srcRect transform.
  295. GrQuad quad, srcQuad;
  296. if (set[p].fDstClipQuad) {
  297. quad = GrQuad::MakeFromSkQuad(set[p].fDstClipQuad, ctm);
  298. SkPoint srcPts[4];
  299. GrMapRectPoints(set[p].fDstRect, set[p].fSrcRect, set[p].fDstClipQuad, srcPts, 4);
  300. srcQuad = GrQuad::MakeFromSkQuad(srcPts, SkMatrix::I());
  301. } else {
  302. quad = GrQuad::MakeFromRect(set[p].fDstRect, ctm);
  303. srcQuad = GrQuad(set[p].fSrcRect);
  304. }
  305. if (!mustFilter && this->filter() != GrSamplerState::Filter::kNearest) {
  306. mustFilter = filter_has_effect(srcQuad, quad);
  307. }
  308. bounds.joinPossiblyEmptyRect(quad.bounds());
  309. GrQuadAAFlags aaFlags;
  310. // Don't update the overall aaType, might be inappropriate for some of the quads
  311. GrAAType aaForQuad;
  312. GrQuadUtils::ResolveAAType(aaType, set[p].fAAFlags, quad, &aaForQuad, &aaFlags);
  313. // Resolve sets aaForQuad to aaType or None, there is never a change between aa methods
  314. SkASSERT(aaForQuad == GrAAType::kNone || aaForQuad == aaType);
  315. if (overallAAType == GrAAType::kNone && aaForQuad != GrAAType::kNone) {
  316. overallAAType = aaType;
  317. }
  318. // Calculate metadata for the entry
  319. const SkRect* domainForQuad = nullptr;
  320. if (constraint == SkCanvas::kStrict_SrcRectConstraint) {
  321. // Check (briefly) if the strict constraint is needed for this set entry
  322. if (!set[p].fSrcRect.contains(fProxies[p].fProxy->getWorstCaseBoundsRect()) &&
  323. (mustFilter || aaForQuad == GrAAType::kCoverage)) {
  324. // Can't rely on hardware clamping and the draw will access outer texels
  325. // for AA and/or bilerp
  326. netDomain = Domain::kYes;
  327. domainForQuad = &set[p].fSrcRect;
  328. }
  329. }
  330. float alpha = SkTPin(set[p].fAlpha, 0.f, 1.f);
  331. allOpaque &= (1.f == alpha);
  332. SkPMColor4f color{alpha, alpha, alpha, alpha};
  333. fQuads.append(quad, {color, domainForQuad, aaFlags}, &srcQuad);
  334. }
  335. fAAType = static_cast<unsigned>(overallAAType);
  336. if (!mustFilter) {
  337. fFilter = static_cast<unsigned>(GrSamplerState::Filter::kNearest);
  338. }
  339. this->setBounds(bounds, HasAABloat(this->aaType() == GrAAType::kCoverage), IsZeroArea::kNo);
  340. fDomain = static_cast<unsigned>(netDomain);
  341. }
  342. void tess(void* v, const VertexSpec& spec, const GrTextureProxy* proxy,
  343. GrQuadBuffer<ColorDomainAndAA>::Iter* iter, int cnt) const {
  344. TRACE_EVENT0("skia.gpu", TRACE_FUNC);
  345. auto origin = proxy->origin();
  346. const auto* texture = proxy->peekTexture();
  347. float iw, ih, h;
  348. if (proxy->textureType() == GrTextureType::kRectangle) {
  349. iw = ih = 1.f;
  350. h = texture->height();
  351. } else {
  352. iw = 1.f / texture->width();
  353. ih = 1.f / texture->height();
  354. h = 1.f;
  355. }
  356. int i = 0;
  357. // Explicit ctor ensures ws are 1s, which compute_src_quad requires
  358. GrQuad srcQuad(SkRect::MakeEmpty());
  359. SkRect domain;
  360. while(i < cnt && iter->next()) {
  361. SkASSERT(iter->isLocalValid());
  362. const ColorDomainAndAA& info = iter->metadata();
  363. // Must correct the texture coordinates and domain now that the real texture size
  364. // is known
  365. compute_src_quad(origin, iter->localQuad(), iw, ih, h, &srcQuad);
  366. compute_domain(info.domain(), this->filter(), origin, info.fDomainRect, iw, ih, h,
  367. &domain);
  368. v = GrQuadPerEdgeAA::Tessellate(v, spec, iter->deviceQuad(), info.fColor, srcQuad,
  369. domain, info.aaFlags());
  370. i++;
  371. }
  372. }
  373. void onPrepareDraws(Target* target) override {
  374. TRACE_EVENT0("skia.gpu", TRACE_FUNC);
  375. GrQuad::Type quadType = GrQuad::Type::kAxisAligned;
  376. GrQuad::Type srcQuadType = GrQuad::Type::kAxisAligned;
  377. Domain domain = Domain::kNo;
  378. ColorType colorType = ColorType::kNone;
  379. int numProxies = 0;
  380. int numTotalQuads = 0;
  381. auto textureType = fProxies[0].fProxy->textureType();
  382. auto config = fProxies[0].fProxy->config();
  383. const GrSwizzle& swizzle = fProxies[0].fProxy->textureSwizzle();
  384. GrAAType aaType = this->aaType();
  385. for (const auto& op : ChainRange<TextureOp>(this)) {
  386. if (op.fQuads.deviceQuadType() > quadType) {
  387. quadType = op.fQuads.deviceQuadType();
  388. }
  389. if (op.fQuads.localQuadType() > srcQuadType) {
  390. srcQuadType = op.fQuads.localQuadType();
  391. }
  392. if (op.fDomain) {
  393. domain = Domain::kYes;
  394. }
  395. colorType = SkTMax(colorType, static_cast<ColorType>(op.fColorType));
  396. numProxies += op.fProxyCnt;
  397. for (unsigned p = 0; p < op.fProxyCnt; ++p) {
  398. numTotalQuads += op.fProxies[p].fQuadCnt;
  399. auto* proxy = op.fProxies[p].fProxy;
  400. if (!proxy->isInstantiated()) {
  401. return;
  402. }
  403. SkASSERT(proxy->config() == config);
  404. SkASSERT(proxy->textureType() == textureType);
  405. SkASSERT(proxy->textureSwizzle() == swizzle);
  406. }
  407. if (op.aaType() == GrAAType::kCoverage) {
  408. SkASSERT(aaType == GrAAType::kCoverage || aaType == GrAAType::kNone);
  409. aaType = GrAAType::kCoverage;
  410. }
  411. }
  412. VertexSpec vertexSpec(quadType, colorType, srcQuadType, /* hasLocal */ true, domain, aaType,
  413. /* alpha as coverage */ true);
  414. GrSamplerState samplerState = GrSamplerState(GrSamplerState::WrapMode::kClamp,
  415. this->filter());
  416. GrGpu* gpu = target->resourceProvider()->priv().gpu();
  417. uint32_t extraSamplerKey = gpu->getExtraSamplerKeyForProgram(
  418. samplerState, fProxies[0].fProxy->backendFormat());
  419. sk_sp<GrGeometryProcessor> gp = GrQuadPerEdgeAA::MakeTexturedProcessor(
  420. vertexSpec, *target->caps().shaderCaps(),
  421. textureType, config, samplerState, swizzle, extraSamplerKey,
  422. std::move(fTextureColorSpaceXform));
  423. // We'll use a dynamic state array for the GP textures when there are multiple ops.
  424. // Otherwise, we use fixed dynamic state to specify the single op's proxy.
  425. GrPipeline::DynamicStateArrays* dynamicStateArrays = nullptr;
  426. GrPipeline::FixedDynamicState* fixedDynamicState;
  427. if (numProxies > 1) {
  428. dynamicStateArrays = target->allocDynamicStateArrays(numProxies, 1, false);
  429. fixedDynamicState = target->makeFixedDynamicState(0);
  430. } else {
  431. fixedDynamicState = target->makeFixedDynamicState(1);
  432. fixedDynamicState->fPrimitiveProcessorTextures[0] = fProxies[0].fProxy;
  433. }
  434. size_t vertexSize = gp->vertexStride();
  435. GrMesh* meshes = target->allocMeshes(numProxies);
  436. sk_sp<const GrBuffer> vbuffer;
  437. int vertexOffsetInBuffer = 0;
  438. int numQuadVerticesLeft = numTotalQuads * vertexSpec.verticesPerQuad();
  439. int numAllocatedVertices = 0;
  440. void* vdata = nullptr;
  441. int m = 0;
  442. for (const auto& op : ChainRange<TextureOp>(this)) {
  443. auto iter = op.fQuads.iterator();
  444. for (unsigned p = 0; p < op.fProxyCnt; ++p) {
  445. int quadCnt = op.fProxies[p].fQuadCnt;
  446. auto* proxy = op.fProxies[p].fProxy;
  447. int meshVertexCnt = quadCnt * vertexSpec.verticesPerQuad();
  448. if (numAllocatedVertices < meshVertexCnt) {
  449. vdata = target->makeVertexSpaceAtLeast(
  450. vertexSize, meshVertexCnt, numQuadVerticesLeft, &vbuffer,
  451. &vertexOffsetInBuffer, &numAllocatedVertices);
  452. SkASSERT(numAllocatedVertices <= numQuadVerticesLeft);
  453. if (!vdata) {
  454. SkDebugf("Could not allocate vertices\n");
  455. return;
  456. }
  457. }
  458. SkASSERT(numAllocatedVertices >= meshVertexCnt);
  459. op.tess(vdata, vertexSpec, proxy, &iter, quadCnt);
  460. if (!GrQuadPerEdgeAA::ConfigureMeshIndices(target, &(meshes[m]), vertexSpec,
  461. quadCnt)) {
  462. SkDebugf("Could not allocate indices");
  463. return;
  464. }
  465. meshes[m].setVertexData(vbuffer, vertexOffsetInBuffer);
  466. if (dynamicStateArrays) {
  467. dynamicStateArrays->fPrimitiveProcessorTextures[m] = proxy;
  468. }
  469. ++m;
  470. numAllocatedVertices -= meshVertexCnt;
  471. numQuadVerticesLeft -= meshVertexCnt;
  472. vertexOffsetInBuffer += meshVertexCnt;
  473. vdata = reinterpret_cast<char*>(vdata) + vertexSize * meshVertexCnt;
  474. }
  475. // If quad counts per proxy were calculated correctly, the entire iterator should have
  476. // been consumed.
  477. SkASSERT(!iter.next());
  478. }
  479. SkASSERT(!numQuadVerticesLeft);
  480. SkASSERT(!numAllocatedVertices);
  481. target->recordDraw(
  482. std::move(gp), meshes, numProxies, fixedDynamicState, dynamicStateArrays);
  483. }
  484. void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
  485. auto pipelineFlags = (GrAAType::kMSAA == this->aaType())
  486. ? GrPipeline::InputFlags::kHWAntialias
  487. : GrPipeline::InputFlags::kNone;
  488. flushState->executeDrawsAndUploadsForMeshDrawOp(
  489. this, chainBounds, GrProcessorSet::MakeEmptySet(), pipelineFlags);
  490. }
  491. CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
  492. TRACE_EVENT0("skia.gpu", TRACE_FUNC);
  493. const auto* that = t->cast<TextureOp>();
  494. if (fDomain != that->fDomain) {
  495. // It is technically possible to combine operations across domain modes, but performance
  496. // testing suggests it's better to make more draw calls where some take advantage of
  497. // the more optimal shader path without coordinate clamping.
  498. return CombineResult::kCannotCombine;
  499. }
  500. if (!GrColorSpaceXform::Equals(fTextureColorSpaceXform.get(),
  501. that->fTextureColorSpaceXform.get())) {
  502. return CombineResult::kCannotCombine;
  503. }
  504. bool upgradeToCoverageAAOnMerge = false;
  505. if (this->aaType() != that->aaType()) {
  506. if (!((this->aaType() == GrAAType::kCoverage && that->aaType() == GrAAType::kNone) ||
  507. (that->aaType() == GrAAType::kCoverage && this->aaType() == GrAAType::kNone))) {
  508. return CombineResult::kCannotCombine;
  509. }
  510. upgradeToCoverageAAOnMerge = true;
  511. }
  512. if (fFilter != that->fFilter) {
  513. return CombineResult::kCannotCombine;
  514. }
  515. auto thisProxy = fProxies[0].fProxy;
  516. auto thatProxy = that->fProxies[0].fProxy;
  517. if (fProxyCnt > 1 || that->fProxyCnt > 1 ||
  518. thisProxy->uniqueID() != thatProxy->uniqueID()) {
  519. // We can't merge across different proxies. Check if 'this' can be chained with 'that'.
  520. if (GrTextureProxy::ProxiesAreCompatibleAsDynamicState(thisProxy, thatProxy) &&
  521. caps.dynamicStateArrayGeometryProcessorTextureSupport()) {
  522. return CombineResult::kMayChain;
  523. }
  524. return CombineResult::kCannotCombine;
  525. }
  526. fDomain |= that->fDomain;
  527. fColorType = SkTMax(fColorType, that->fColorType);
  528. if (upgradeToCoverageAAOnMerge) {
  529. fAAType = static_cast<unsigned>(GrAAType::kCoverage);
  530. }
  531. // Concatenate quad lists together
  532. fQuads.concat(that->fQuads);
  533. fProxies[0].fQuadCnt += that->fQuads.count();
  534. return CombineResult::kMerged;
  535. }
  536. GrAAType aaType() const { return static_cast<GrAAType>(fAAType); }
  537. GrSamplerState::Filter filter() const { return static_cast<GrSamplerState::Filter>(fFilter); }
  538. GrQuadBuffer<ColorDomainAndAA> fQuads;
  539. sk_sp<GrColorSpaceXform> fTextureColorSpaceXform;
  540. unsigned fFilter : 2;
  541. unsigned fAAType : 2;
  542. unsigned fDomain : 1;
  543. unsigned fColorType : 2;
  544. GR_STATIC_ASSERT(GrQuadPerEdgeAA::kColorTypeCount <= 4);
  545. unsigned fProxyCnt : 32 - 7;
  546. Proxy fProxies[1];
  547. static_assert(GrQuad::kTypeCount <= 4, "GrQuad::Type does not fit in 2 bits");
  548. typedef GrMeshDrawOp INHERITED;
  549. };
  550. } // anonymous namespace
  551. namespace GrTextureOp {
  552. std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
  553. sk_sp<GrTextureProxy> proxy,
  554. sk_sp<GrColorSpaceXform> textureXform,
  555. GrSamplerState::Filter filter,
  556. const SkPMColor4f& color,
  557. SkBlendMode blendMode,
  558. GrAAType aaType,
  559. GrQuadAAFlags aaFlags,
  560. const GrQuad& deviceQuad,
  561. const GrQuad& localQuad,
  562. const SkRect* domain) {
  563. // Apply optimizations that are valid whether or not using GrTextureOp or GrFillRectOp
  564. if (domain && domain->contains(proxy->getWorstCaseBoundsRect())) {
  565. // No need for a shader-based domain if hardware clamping achieves the same effect
  566. domain = nullptr;
  567. }
  568. if (filter != GrSamplerState::Filter::kNearest && !filter_has_effect(localQuad, deviceQuad)) {
  569. filter = GrSamplerState::Filter::kNearest;
  570. }
  571. if (blendMode == SkBlendMode::kSrcOver) {
  572. return TextureOp::Make(context, std::move(proxy), std::move(textureXform), filter, color,
  573. aaType, aaFlags, deviceQuad, localQuad, domain);
  574. } else {
  575. // Emulate complex blending using GrFillRectOp
  576. GrPaint paint;
  577. paint.setColor4f(color);
  578. paint.setXPFactory(SkBlendMode_AsXPFactory(blendMode));
  579. std::unique_ptr<GrFragmentProcessor> fp;
  580. if (domain) {
  581. // Update domain to match what GrTextureOp computes during tessellation, using top-left
  582. // as the origin so that it doesn't depend on final texture size (which the FP handles
  583. // later, as well as accounting for the true origin).
  584. SkRect correctedDomain;
  585. compute_domain(Domain::kYes, filter, kTopLeft_GrSurfaceOrigin, *domain,
  586. 1.f, 1.f, proxy->height(), &correctedDomain);
  587. fp = GrTextureDomainEffect::Make(std::move(proxy), SkMatrix::I(), correctedDomain,
  588. GrTextureDomain::kClamp_Mode, filter);
  589. } else {
  590. fp = GrSimpleTextureEffect::Make(std::move(proxy), SkMatrix::I(), filter);
  591. }
  592. fp = GrColorSpaceXformEffect::Make(std::move(fp), std::move(textureXform));
  593. paint.addColorFragmentProcessor(std::move(fp));
  594. return GrFillRectOp::Make(context, std::move(paint), aaType, aaFlags,
  595. deviceQuad, localQuad);
  596. }
  597. }
  598. std::unique_ptr<GrDrawOp> MakeSet(GrRecordingContext* context,
  599. const GrRenderTargetContext::TextureSetEntry set[],
  600. int cnt,
  601. GrSamplerState::Filter filter,
  602. GrAAType aaType,
  603. SkCanvas::SrcRectConstraint constraint,
  604. const SkMatrix& viewMatrix,
  605. sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
  606. return TextureOp::Make(context, set, cnt, filter, aaType, constraint, viewMatrix,
  607. std::move(textureColorSpaceXform));
  608. }
  609. } // namespace GrTextureOp
  610. #if GR_TEST_UTILS
  611. #include "include/private/GrRecordingContext.h"
  612. #include "src/gpu/GrProxyProvider.h"
  613. #include "src/gpu/GrRecordingContextPriv.h"
  614. GR_DRAW_OP_TEST_DEFINE(TextureOp) {
  615. GrSurfaceDesc desc;
  616. desc.fConfig = kRGBA_8888_GrPixelConfig;
  617. desc.fHeight = random->nextULessThan(90) + 10;
  618. desc.fWidth = random->nextULessThan(90) + 10;
  619. auto origin = random->nextBool() ? kTopLeft_GrSurfaceOrigin : kBottomLeft_GrSurfaceOrigin;
  620. GrMipMapped mipMapped = random->nextBool() ? GrMipMapped::kYes : GrMipMapped::kNo;
  621. SkBackingFit fit = SkBackingFit::kExact;
  622. if (mipMapped == GrMipMapped::kNo) {
  623. fit = random->nextBool() ? SkBackingFit::kApprox : SkBackingFit::kExact;
  624. }
  625. const GrBackendFormat format =
  626. context->priv().caps()->getBackendFormatFromColorType(GrColorType::kRGBA_8888);
  627. GrProxyProvider* proxyProvider = context->priv().proxyProvider();
  628. sk_sp<GrTextureProxy> proxy = proxyProvider->createProxy(
  629. format, desc, GrRenderable::kNo, 1, origin, mipMapped, fit, SkBudgeted::kNo,
  630. GrProtected::kNo, GrInternalSurfaceFlags::kNone);
  631. SkRect rect = GrTest::TestRect(random);
  632. SkRect srcRect;
  633. srcRect.fLeft = random->nextRangeScalar(0.f, proxy->width() / 2.f);
  634. srcRect.fRight = random->nextRangeScalar(0.f, proxy->width()) + proxy->width() / 2.f;
  635. srcRect.fTop = random->nextRangeScalar(0.f, proxy->height() / 2.f);
  636. srcRect.fBottom = random->nextRangeScalar(0.f, proxy->height()) + proxy->height() / 2.f;
  637. SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random);
  638. SkPMColor4f color = SkPMColor4f::FromBytes_RGBA(SkColorToPremulGrColor(random->nextU()));
  639. GrSamplerState::Filter filter = (GrSamplerState::Filter)random->nextULessThan(
  640. static_cast<uint32_t>(GrSamplerState::Filter::kMipMap) + 1);
  641. while (mipMapped == GrMipMapped::kNo && filter == GrSamplerState::Filter::kMipMap) {
  642. filter = (GrSamplerState::Filter)random->nextULessThan(
  643. static_cast<uint32_t>(GrSamplerState::Filter::kMipMap) + 1);
  644. }
  645. auto texXform = GrTest::TestColorXform(random);
  646. GrAAType aaType = GrAAType::kNone;
  647. if (random->nextBool()) {
  648. aaType = (numSamples > 1) ? GrAAType::kMSAA : GrAAType::kCoverage;
  649. }
  650. GrQuadAAFlags aaFlags = GrQuadAAFlags::kNone;
  651. aaFlags |= random->nextBool() ? GrQuadAAFlags::kLeft : GrQuadAAFlags::kNone;
  652. aaFlags |= random->nextBool() ? GrQuadAAFlags::kTop : GrQuadAAFlags::kNone;
  653. aaFlags |= random->nextBool() ? GrQuadAAFlags::kRight : GrQuadAAFlags::kNone;
  654. aaFlags |= random->nextBool() ? GrQuadAAFlags::kBottom : GrQuadAAFlags::kNone;
  655. bool useDomain = random->nextBool();
  656. return GrTextureOp::Make(context, std::move(proxy), std::move(texXform), filter, color,
  657. SkBlendMode::kSrcOver, aaType, aaFlags,
  658. GrQuad::MakeFromRect(rect, viewMatrix), GrQuad(srcRect),
  659. useDomain ? &srcRect : nullptr);
  660. }
  661. #endif