GrTextureProducer.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. /*
  2. * Copyright 2016 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 "include/private/GrRecordingContext.h"
  8. #include "src/core/SkMipMap.h"
  9. #include "src/core/SkRectPriv.h"
  10. #include "src/gpu/GrClip.h"
  11. #include "src/gpu/GrContextPriv.h"
  12. #include "src/gpu/GrProxyProvider.h"
  13. #include "src/gpu/GrRecordingContextPriv.h"
  14. #include "src/gpu/GrRenderTargetContext.h"
  15. #include "src/gpu/GrTextureProducer.h"
  16. #include "src/gpu/GrTextureProxy.h"
  17. #include "src/gpu/SkGr.h"
  18. #include "src/gpu/effects/GrBicubicEffect.h"
  19. #include "src/gpu/effects/GrTextureDomain.h"
  20. #include "src/gpu/effects/generated/GrSimpleTextureEffect.h"
  21. sk_sp<GrTextureProxy> GrTextureProducer::CopyOnGpu(GrRecordingContext* context,
  22. sk_sp<GrTextureProxy> inputProxy,
  23. GrColorType colorType,
  24. const CopyParams& copyParams,
  25. bool dstWillRequireMipMaps) {
  26. SkASSERT(context);
  27. const SkRect dstRect = SkRect::MakeIWH(copyParams.fWidth, copyParams.fHeight);
  28. GrMipMapped mipMapped = dstWillRequireMipMaps ? GrMipMapped::kYes : GrMipMapped::kNo;
  29. SkRect localRect = SkRect::MakeWH(inputProxy->width(), inputProxy->height());
  30. bool needsDomain = false;
  31. bool resizing = false;
  32. if (copyParams.fFilter != GrSamplerState::Filter::kNearest) {
  33. bool resizing = localRect.width() != dstRect.width() ||
  34. localRect.height() != dstRect.height();
  35. needsDomain = resizing && !GrProxyProvider::IsFunctionallyExact(inputProxy.get());
  36. }
  37. if (copyParams.fFilter == GrSamplerState::Filter::kNearest && !needsDomain && !resizing &&
  38. dstWillRequireMipMaps) {
  39. sk_sp<GrTextureProxy> proxy = GrCopyBaseMipMapToTextureProxy(context, inputProxy.get());
  40. if (proxy) {
  41. return proxy;
  42. }
  43. }
  44. sk_sp<GrRenderTargetContext> copyRTC =
  45. context->priv().makeDeferredRenderTargetContextWithFallback(
  46. SkBackingFit::kExact, dstRect.width(), dstRect.height(), colorType, nullptr, 1,
  47. mipMapped, inputProxy->origin());
  48. if (!copyRTC) {
  49. return nullptr;
  50. }
  51. GrPaint paint;
  52. if (needsDomain) {
  53. const SkRect domain = localRect.makeInset(0.5f, 0.5f);
  54. // This would cause us to read values from outside the subset. Surely, the caller knows
  55. // better!
  56. SkASSERT(copyParams.fFilter != GrSamplerState::Filter::kMipMap);
  57. paint.addColorFragmentProcessor(
  58. GrTextureDomainEffect::Make(std::move(inputProxy), SkMatrix::I(), domain,
  59. GrTextureDomain::kClamp_Mode, copyParams.fFilter));
  60. } else {
  61. GrSamplerState samplerState(GrSamplerState::WrapMode::kClamp, copyParams.fFilter);
  62. paint.addColorTextureProcessor(std::move(inputProxy), SkMatrix::I(), samplerState);
  63. }
  64. paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
  65. copyRTC->fillRectToRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), dstRect,
  66. localRect);
  67. return copyRTC->asTextureProxyRef();
  68. }
  69. /** Determines whether a texture domain is necessary and if so what domain to use. There are two
  70. * rectangles to consider:
  71. * - The first is the content area specified by the texture adjuster (i.e., textureContentArea).
  72. * We can *never* allow filtering to cause bleed of pixels outside this rectangle.
  73. * - The second rectangle is the constraint rectangle (i.e., constraintRect), which is known to
  74. * be contained by the content area. The filterConstraint specifies whether we are allowed to
  75. * bleed across this rect.
  76. *
  77. * We want to avoid using a domain if possible. We consider the above rectangles, the filter type,
  78. * and whether the coords generated by the draw would all fall within the constraint rect. If the
  79. * latter is true we only need to consider whether the filter would extend beyond the rects.
  80. */
  81. GrTextureProducer::DomainMode GrTextureProducer::DetermineDomainMode(
  82. const SkRect& constraintRect,
  83. FilterConstraint filterConstraint,
  84. bool coordsLimitedToConstraintRect,
  85. GrTextureProxy* proxy,
  86. const GrSamplerState::Filter* filterModeOrNullForBicubic,
  87. SkRect* domainRect) {
  88. const SkIRect proxyBounds = SkIRect::MakeWH(proxy->width(), proxy->height());
  89. SkASSERT(proxyBounds.contains(constraintRect));
  90. const bool proxyIsExact = GrProxyProvider::IsFunctionallyExact(proxy);
  91. // If the constraint rectangle contains the whole proxy then no need for a domain.
  92. if (constraintRect.contains(proxyBounds) && proxyIsExact) {
  93. return kNoDomain_DomainMode;
  94. }
  95. bool restrictFilterToRect = (filterConstraint == GrTextureProducer::kYes_FilterConstraint);
  96. // If we can filter outside the constraint rect, and there is no non-content area of the
  97. // proxy, and we aren't going to generate sample coords outside the constraint rect then we
  98. // don't need a domain.
  99. if (!restrictFilterToRect && proxyIsExact && coordsLimitedToConstraintRect) {
  100. return kNoDomain_DomainMode;
  101. }
  102. // Get the domain inset based on sampling mode (or bail if mipped)
  103. SkScalar filterHalfWidth = 0.f;
  104. if (filterModeOrNullForBicubic) {
  105. switch (*filterModeOrNullForBicubic) {
  106. case GrSamplerState::Filter::kNearest:
  107. if (coordsLimitedToConstraintRect) {
  108. return kNoDomain_DomainMode;
  109. } else {
  110. filterHalfWidth = 0.f;
  111. }
  112. break;
  113. case GrSamplerState::Filter::kBilerp:
  114. filterHalfWidth = .5f;
  115. break;
  116. case GrSamplerState::Filter::kMipMap:
  117. if (restrictFilterToRect || !proxyIsExact) {
  118. // No domain can save us here.
  119. return kTightCopy_DomainMode;
  120. }
  121. return kNoDomain_DomainMode;
  122. }
  123. } else {
  124. // bicubic does nearest filtering internally.
  125. filterHalfWidth = 1.5f;
  126. }
  127. // Both bilerp and bicubic use bilinear filtering and so need to be clamped to the center
  128. // of the edge texel. Pinning to the texel center has no impact on nearest mode and MIP-maps
  129. static const SkScalar kDomainInset = 0.5f;
  130. // Figure out the limits of pixels we're allowed to sample from.
  131. // Unless we know the amount of outset and the texture matrix we have to conservatively enforce
  132. // the domain.
  133. if (restrictFilterToRect) {
  134. *domainRect = constraintRect.makeInset(kDomainInset, kDomainInset);
  135. } else if (!proxyIsExact) {
  136. // If we got here then: proxy is not exact, the coords are limited to the
  137. // constraint rect, and we're allowed to filter across the constraint rect boundary. So
  138. // we check whether the filter would reach across the edge of the proxy.
  139. // We will only set the sides that are required.
  140. *domainRect = SkRectPriv::MakeLargest();
  141. if (coordsLimitedToConstraintRect) {
  142. // We may be able to use the fact that the texture coords are limited to the constraint
  143. // rect in order to avoid having to add a domain.
  144. bool needContentAreaConstraint = false;
  145. if (proxyBounds.fRight - filterHalfWidth < constraintRect.fRight) {
  146. domainRect->fRight = proxyBounds.fRight - kDomainInset;
  147. needContentAreaConstraint = true;
  148. }
  149. if (proxyBounds.fBottom - filterHalfWidth < constraintRect.fBottom) {
  150. domainRect->fBottom = proxyBounds.fBottom - kDomainInset;
  151. needContentAreaConstraint = true;
  152. }
  153. if (!needContentAreaConstraint) {
  154. return kNoDomain_DomainMode;
  155. }
  156. } else {
  157. // Our sample coords for the texture are allowed to be outside the constraintRect so we
  158. // don't consider it when computing the domain.
  159. domainRect->fRight = proxyBounds.fRight - kDomainInset;
  160. domainRect->fBottom = proxyBounds.fBottom - kDomainInset;
  161. }
  162. } else {
  163. return kNoDomain_DomainMode;
  164. }
  165. if (domainRect->fLeft > domainRect->fRight) {
  166. domainRect->fLeft = domainRect->fRight = SkScalarAve(domainRect->fLeft, domainRect->fRight);
  167. }
  168. if (domainRect->fTop > domainRect->fBottom) {
  169. domainRect->fTop = domainRect->fBottom = SkScalarAve(domainRect->fTop, domainRect->fBottom);
  170. }
  171. return kDomain_DomainMode;
  172. }
  173. std::unique_ptr<GrFragmentProcessor> GrTextureProducer::createFragmentProcessorForDomainAndFilter(
  174. sk_sp<GrTextureProxy> proxy,
  175. const SkMatrix& textureMatrix,
  176. DomainMode domainMode,
  177. const SkRect& domain,
  178. const GrSamplerState::Filter* filterOrNullForBicubic) {
  179. SkASSERT(kTightCopy_DomainMode != domainMode);
  180. bool clampToBorderSupport = fContext->priv().caps()->clampToBorderSupport();
  181. if (filterOrNullForBicubic) {
  182. if (kDomain_DomainMode == domainMode || (fDomainNeedsDecal && !clampToBorderSupport)) {
  183. GrTextureDomain::Mode wrapMode = fDomainNeedsDecal ? GrTextureDomain::kDecal_Mode
  184. : GrTextureDomain::kClamp_Mode;
  185. return GrTextureDomainEffect::Make(std::move(proxy), textureMatrix, domain,
  186. wrapMode, *filterOrNullForBicubic);
  187. } else {
  188. GrSamplerState::WrapMode wrapMode =
  189. fDomainNeedsDecal ? GrSamplerState::WrapMode::kClampToBorder
  190. : GrSamplerState::WrapMode::kClamp;
  191. GrSamplerState samplerState(wrapMode, *filterOrNullForBicubic);
  192. return GrSimpleTextureEffect::Make(std::move(proxy), textureMatrix, samplerState);
  193. }
  194. } else {
  195. static const GrSamplerState::WrapMode kClampClamp[] = {
  196. GrSamplerState::WrapMode::kClamp, GrSamplerState::WrapMode::kClamp};
  197. static const GrSamplerState::WrapMode kDecalDecal[] = {
  198. GrSamplerState::WrapMode::kClampToBorder, GrSamplerState::WrapMode::kClampToBorder};
  199. static constexpr auto kDir = GrBicubicEffect::Direction::kXY;
  200. if (kDomain_DomainMode == domainMode || (fDomainNeedsDecal && !clampToBorderSupport)) {
  201. GrTextureDomain::Mode wrapMode = fDomainNeedsDecal ? GrTextureDomain::kDecal_Mode
  202. : GrTextureDomain::kClamp_Mode;
  203. return GrBicubicEffect::Make(std::move(proxy), textureMatrix, kClampClamp, wrapMode,
  204. wrapMode, kDir, this->alphaType(),
  205. kDomain_DomainMode == domainMode ? &domain : nullptr);
  206. } else {
  207. return GrBicubicEffect::Make(std::move(proxy), textureMatrix,
  208. fDomainNeedsDecal ? kDecalDecal : kClampClamp, kDir,
  209. this->alphaType());
  210. }
  211. }
  212. }
  213. sk_sp<GrTextureProxy> GrTextureProducer::refTextureProxyForParams(
  214. const GrSamplerState::Filter* filterOrNullForBicubic,
  215. SkScalar scaleAdjust[2]) {
  216. GrSamplerState sampler; // Default is nearest + clamp
  217. if (filterOrNullForBicubic) {
  218. sampler.setFilterMode(*filterOrNullForBicubic);
  219. }
  220. if (fDomainNeedsDecal) {
  221. // Assuming hardware support, switch to clamp-to-border instead of clamp
  222. if (fContext->priv().caps()->clampToBorderSupport()) {
  223. sampler.setWrapModeX(GrSamplerState::WrapMode::kClampToBorder);
  224. sampler.setWrapModeY(GrSamplerState::WrapMode::kClampToBorder);
  225. }
  226. }
  227. return this->refTextureProxyForParams(sampler, scaleAdjust);
  228. }
  229. sk_sp<GrTextureProxy> GrTextureProducer::refTextureProxyForParams(
  230. const GrSamplerState& sampler,
  231. SkScalar scaleAdjust[2]) {
  232. // Check that the caller pre-initialized scaleAdjust
  233. SkASSERT(!scaleAdjust || (scaleAdjust[0] == 1 && scaleAdjust[1] == 1));
  234. // Check that if the caller passed nullptr for scaleAdjust that we're in the case where there
  235. // can be no scaling.
  236. SkDEBUGCODE(bool expectNoScale = (sampler.filter() != GrSamplerState::Filter::kMipMap &&
  237. !sampler.isRepeated()));
  238. SkASSERT(scaleAdjust || expectNoScale);
  239. int mipCount = SkMipMap::ComputeLevelCount(this->width(), this->height());
  240. bool willBeMipped = GrSamplerState::Filter::kMipMap == sampler.filter() && mipCount &&
  241. this->context()->priv().caps()->mipMapSupport();
  242. auto result = this->onRefTextureProxyForParams(sampler, willBeMipped, scaleAdjust);
  243. // Check to make sure that if we say the texture willBeMipped that the returned texture has mip
  244. // maps, unless the config is not copyable.
  245. SkASSERT(!result || !willBeMipped || result->mipMapped() == GrMipMapped::kYes ||
  246. !this->context()->priv().caps()->isConfigCopyable(result->config()));
  247. // Check that the "no scaling expected" case always returns a proxy of the same size as the
  248. // producer.
  249. SkASSERT(!result || !expectNoScale ||
  250. (result->width() == this->width() && result->height() == this->height()));
  251. return result;
  252. }
  253. sk_sp<GrTextureProxy> GrTextureProducer::refTextureProxy(GrMipMapped willNeedMips) {
  254. GrSamplerState::Filter filter =
  255. GrMipMapped::kNo == willNeedMips ? GrSamplerState::Filter::kNearest
  256. : GrSamplerState::Filter::kMipMap;
  257. GrSamplerState sampler(GrSamplerState::WrapMode::kClamp, filter);
  258. int mipCount = SkMipMap::ComputeLevelCount(this->width(), this->height());
  259. bool willBeMipped = GrSamplerState::Filter::kMipMap == sampler.filter() && mipCount &&
  260. this->context()->priv().caps()->mipMapSupport();
  261. auto result = this->onRefTextureProxyForParams(sampler, willBeMipped, nullptr);
  262. // Check to make sure that if we say the texture willBeMipped that the returned texture has mip
  263. // maps, unless the config is not copyable.
  264. SkASSERT(!result || !willBeMipped || result->mipMapped() == GrMipMapped::kYes ||
  265. !this->context()->priv().caps()->isConfigCopyable(result->config()));
  266. // Check that no scaling occured and we returned a proxy of the same size as the producer.
  267. SkASSERT(!result || (result->width() == this->width() && result->height() == this->height()));
  268. return result;
  269. }