blob: f11d2a37fa87ae80041cee0f122df4708398c024 [file] [log] [blame]
Brian Osmane8e54582016-11-28 10:06:27 -05001/*
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
8#include "GrTextureProducer.h"
Brian Salomonc65aec92017-03-09 09:03:58 -05009#include "GrClip.h"
Robert Phillips1afd4cd2018-01-08 13:40:32 -050010#include "GrProxyProvider.h"
Brian Osmane8e54582016-11-28 10:06:27 -050011#include "GrRenderTargetContext.h"
Robert Phillipsd9d84852017-06-09 10:48:29 -040012#include "GrTextureProxy.h"
Greg Daniel09c94002018-06-08 22:11:51 +000013#include "SkGr.h"
Greg Daniel8e9b4c42018-07-20 10:30:48 -040014#include "SkMipMap.h"
Mike Reed274218e2018-01-08 15:05:02 -050015#include "SkRectPriv.h"
Brian Osmane8e54582016-11-28 10:06:27 -050016#include "effects/GrBicubicEffect.h"
17#include "effects/GrSimpleTextureEffect.h"
18#include "effects/GrTextureDomain.h"
19
Robert Phillipsb66b42f2017-03-14 08:53:02 -040020sk_sp<GrTextureProxy> GrTextureProducer::CopyOnGpu(GrContext* context,
21 sk_sp<GrTextureProxy> inputProxy,
Greg Daniele1da1d92017-10-06 15:59:27 -040022 const CopyParams& copyParams,
23 bool dstWillRequireMipMaps) {
Robert Phillipsb66b42f2017-03-14 08:53:02 -040024 SkASSERT(context);
25
Jim Van Verth1676cb92019-01-15 13:24:45 -050026 GrPixelConfig config = GrMakePixelConfigUncompressed(inputProxy->config());
27
Robert Phillipsb66b42f2017-03-14 08:53:02 -040028 const SkRect dstRect = SkRect::MakeIWH(copyParams.fWidth, copyParams.fHeight);
Greg Daniel45d63032017-10-30 13:41:26 -040029 GrMipMapped mipMapped = dstWillRequireMipMaps ? GrMipMapped::kYes : GrMipMapped::kNo;
Robert Phillipsb66b42f2017-03-14 08:53:02 -040030
Greg Daniel09c94002018-06-08 22:11:51 +000031 SkRect localRect = SkRect::MakeWH(inputProxy->width(), inputProxy->height());
32
33 bool needsDomain = false;
34 bool resizing = false;
35 if (copyParams.fFilter != GrSamplerState::Filter::kNearest) {
36 bool resizing = localRect.width() != dstRect.width() ||
37 localRect.height() != dstRect.height();
38 needsDomain = resizing && !GrProxyProvider::IsFunctionallyExact(inputProxy.get());
39 }
40
41 if (copyParams.fFilter == GrSamplerState::Filter::kNearest && !needsDomain && !resizing &&
42 dstWillRequireMipMaps) {
43 sk_sp<GrTextureProxy> proxy = GrCopyBaseMipMapToTextureProxy(context, inputProxy.get());
44 if (proxy) {
45 return proxy;
46 }
47 }
48
Greg Daniel4065d452018-11-16 15:43:41 -050049 GrBackendFormat format = inputProxy->backendFormat().makeTexture2D();
50 if (!format.isValid()) {
51 return nullptr;
52 }
53
Robert Phillips0c4b7b12018-03-06 08:20:37 -050054 sk_sp<GrRenderTargetContext> copyRTC =
Robert Phillips9da87e02019-02-04 13:26:26 -050055 context->priv().makeDeferredRenderTargetContextWithFallback(
Greg Daniel4065d452018-11-16 15:43:41 -050056 format, SkBackingFit::kExact, dstRect.width(), dstRect.height(),
Jim Van Verth1676cb92019-01-15 13:24:45 -050057 config, nullptr, 1, mipMapped, inputProxy->origin());
Robert Phillipsb66b42f2017-03-14 08:53:02 -040058 if (!copyRTC) {
59 return nullptr;
60 }
61
62 GrPaint paint;
Robert Phillipsb66b42f2017-03-14 08:53:02 -040063
64 if (needsDomain) {
65 const SkRect domain = localRect.makeInset(0.5f, 0.5f);
66 // This would cause us to read values from outside the subset. Surely, the caller knows
67 // better!
Brian Salomon2bbdcc42017-09-07 12:36:34 -040068 SkASSERT(copyParams.fFilter != GrSamplerState::Filter::kMipMap);
Robert Phillipsb66b42f2017-03-14 08:53:02 -040069 paint.addColorFragmentProcessor(
Brian Osman2240be92017-10-18 13:15:13 -040070 GrTextureDomainEffect::Make(std::move(inputProxy), SkMatrix::I(), domain,
71 GrTextureDomain::kClamp_Mode, copyParams.fFilter));
Robert Phillipsb66b42f2017-03-14 08:53:02 -040072 } else {
Brian Salomon2bbdcc42017-09-07 12:36:34 -040073 GrSamplerState samplerState(GrSamplerState::WrapMode::kClamp, copyParams.fFilter);
Brian Osman2240be92017-10-18 13:15:13 -040074 paint.addColorTextureProcessor(std::move(inputProxy), SkMatrix::I(), samplerState);
Robert Phillipsb66b42f2017-03-14 08:53:02 -040075 }
76 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
77
78 copyRTC->fillRectToRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), dstRect,
79 localRect);
80 return copyRTC->asTextureProxyRef();
81}
82
Brian Osmane8e54582016-11-28 10:06:27 -050083/** Determines whether a texture domain is necessary and if so what domain to use. There are two
84 * rectangles to consider:
Robert Phillips3798c862017-03-27 11:08:16 -040085 * - The first is the content area specified by the texture adjuster (i.e., textureContentArea).
86 * We can *never* allow filtering to cause bleed of pixels outside this rectangle.
87 * - The second rectangle is the constraint rectangle (i.e., constraintRect), which is known to
88 * be contained by the content area. The filterConstraint specifies whether we are allowed to
89 * bleed across this rect.
Brian Osmane8e54582016-11-28 10:06:27 -050090 *
91 * We want to avoid using a domain if possible. We consider the above rectangles, the filter type,
92 * and whether the coords generated by the draw would all fall within the constraint rect. If the
93 * latter is true we only need to consider whether the filter would extend beyond the rects.
94 */
95GrTextureProducer::DomainMode GrTextureProducer::DetermineDomainMode(
Brian Salomon2bbdcc42017-09-07 12:36:34 -040096 const SkRect& constraintRect,
97 FilterConstraint filterConstraint,
98 bool coordsLimitedToConstraintRect,
99 GrTextureProxy* proxy,
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400100 const GrSamplerState::Filter* filterModeOrNullForBicubic,
101 SkRect* domainRect) {
Robert Phillips51e7ca32017-03-27 10:14:08 -0400102 const SkIRect proxyBounds = SkIRect::MakeWH(proxy->width(), proxy->height());
103
104 SkASSERT(proxyBounds.contains(constraintRect));
Robert Phillips51e7ca32017-03-27 10:14:08 -0400105
Robert Phillips1afd4cd2018-01-08 13:40:32 -0500106 const bool proxyIsExact = GrProxyProvider::IsFunctionallyExact(proxy);
Brian Salomon4df00922017-09-07 16:34:11 +0000107
Robert Phillips51e7ca32017-03-27 10:14:08 -0400108 // If the constraint rectangle contains the whole proxy then no need for a domain.
109 if (constraintRect.contains(proxyBounds) && proxyIsExact) {
110 return kNoDomain_DomainMode;
111 }
112
Robert Phillips51e7ca32017-03-27 10:14:08 -0400113 bool restrictFilterToRect = (filterConstraint == GrTextureProducer::kYes_FilterConstraint);
114
115 // If we can filter outside the constraint rect, and there is no non-content area of the
116 // proxy, and we aren't going to generate sample coords outside the constraint rect then we
117 // don't need a domain.
Greg Danielc77085d2017-11-01 16:38:48 -0400118 if (!restrictFilterToRect && proxyIsExact && coordsLimitedToConstraintRect) {
Robert Phillips51e7ca32017-03-27 10:14:08 -0400119 return kNoDomain_DomainMode;
120 }
121
122 // Get the domain inset based on sampling mode (or bail if mipped)
Brian Salomon4df00922017-09-07 16:34:11 +0000123 SkScalar filterHalfWidth = 0.f;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400124 if (filterModeOrNullForBicubic) {
125 switch (*filterModeOrNullForBicubic) {
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400126 case GrSamplerState::Filter::kNearest:
Robert Phillips51e7ca32017-03-27 10:14:08 -0400127 if (coordsLimitedToConstraintRect) {
128 return kNoDomain_DomainMode;
Brian Salomon4df00922017-09-07 16:34:11 +0000129 } else {
130 filterHalfWidth = 0.f;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400131 }
132 break;
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400133 case GrSamplerState::Filter::kBilerp:
Brian Salomon4df00922017-09-07 16:34:11 +0000134 filterHalfWidth = .5f;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400135 break;
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400136 case GrSamplerState::Filter::kMipMap:
Greg Danielc77085d2017-11-01 16:38:48 -0400137 if (restrictFilterToRect || !proxyIsExact) {
Brian Salomon4df00922017-09-07 16:34:11 +0000138 // No domain can save us here.
139 return kTightCopy_DomainMode;
140 }
141 return kNoDomain_DomainMode;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400142 }
Brian Salomon4df00922017-09-07 16:34:11 +0000143 } else {
144 // bicubic does nearest filtering internally.
145 filterHalfWidth = 1.5f;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400146 }
147
148 // Both bilerp and bicubic use bilinear filtering and so need to be clamped to the center
149 // of the edge texel. Pinning to the texel center has no impact on nearest mode and MIP-maps
150
151 static const SkScalar kDomainInset = 0.5f;
152 // Figure out the limits of pixels we're allowed to sample from.
153 // Unless we know the amount of outset and the texture matrix we have to conservatively enforce
154 // the domain.
155 if (restrictFilterToRect) {
156 *domainRect = constraintRect.makeInset(kDomainInset, kDomainInset);
Greg Danielc77085d2017-11-01 16:38:48 -0400157 } else if (!proxyIsExact) {
158 // If we got here then: proxy is not exact, the coords are limited to the
Brian Salomon4df00922017-09-07 16:34:11 +0000159 // constraint rect, and we're allowed to filter across the constraint rect boundary. So
Greg Danielc77085d2017-11-01 16:38:48 -0400160 // we check whether the filter would reach across the edge of the proxy.
Brian Salomon4df00922017-09-07 16:34:11 +0000161 // We will only set the sides that are required.
162
Mike Reed274218e2018-01-08 15:05:02 -0500163 *domainRect = SkRectPriv::MakeLargest();
Brian Salomon4df00922017-09-07 16:34:11 +0000164 if (coordsLimitedToConstraintRect) {
165 // We may be able to use the fact that the texture coords are limited to the constraint
166 // rect in order to avoid having to add a domain.
167 bool needContentAreaConstraint = false;
Greg Danielc77085d2017-11-01 16:38:48 -0400168 if (proxyBounds.fRight - filterHalfWidth < constraintRect.fRight) {
169 domainRect->fRight = proxyBounds.fRight - kDomainInset;
Brian Salomon4df00922017-09-07 16:34:11 +0000170 needContentAreaConstraint = true;
171 }
Greg Danielc77085d2017-11-01 16:38:48 -0400172 if (proxyBounds.fBottom - filterHalfWidth < constraintRect.fBottom) {
173 domainRect->fBottom = proxyBounds.fBottom - kDomainInset;
Brian Salomon4df00922017-09-07 16:34:11 +0000174 needContentAreaConstraint = true;
175 }
176 if (!needContentAreaConstraint) {
177 return kNoDomain_DomainMode;
178 }
179 } else {
180 // Our sample coords for the texture are allowed to be outside the constraintRect so we
181 // don't consider it when computing the domain.
Greg Danielc77085d2017-11-01 16:38:48 -0400182 domainRect->fRight = proxyBounds.fRight - kDomainInset;
183 domainRect->fBottom = proxyBounds.fBottom - kDomainInset;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400184 }
Brian Salomon4df00922017-09-07 16:34:11 +0000185 } else {
186 return kNoDomain_DomainMode;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400187 }
Brian Salomon4df00922017-09-07 16:34:11 +0000188
189 if (domainRect->fLeft > domainRect->fRight) {
190 domainRect->fLeft = domainRect->fRight = SkScalarAve(domainRect->fLeft, domainRect->fRight);
191 }
192 if (domainRect->fTop > domainRect->fBottom) {
193 domainRect->fTop = domainRect->fBottom = SkScalarAve(domainRect->fTop, domainRect->fBottom);
194 }
195 return kDomain_DomainMode;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400196}
197
Brian Salomonaff329b2017-08-11 09:40:37 -0400198std::unique_ptr<GrFragmentProcessor> GrTextureProducer::CreateFragmentProcessorForDomainAndFilter(
199 sk_sp<GrTextureProxy> proxy,
Brian Salomonaff329b2017-08-11 09:40:37 -0400200 const SkMatrix& textureMatrix,
201 DomainMode domainMode,
202 const SkRect& domain,
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400203 const GrSamplerState::Filter* filterOrNullForBicubic) {
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400204 SkASSERT(kTightCopy_DomainMode != domainMode);
205 if (filterOrNullForBicubic) {
206 if (kDomain_DomainMode == domainMode) {
Brian Osman2240be92017-10-18 13:15:13 -0400207 return GrTextureDomainEffect::Make(std::move(proxy), textureMatrix, domain,
Brian Osman5e341672017-10-18 10:23:18 -0400208 GrTextureDomain::kClamp_Mode,
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400209 *filterOrNullForBicubic);
210 } else {
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400211 GrSamplerState samplerState(GrSamplerState::WrapMode::kClamp, *filterOrNullForBicubic);
Brian Osman2240be92017-10-18 13:15:13 -0400212 return GrSimpleTextureEffect::Make(std::move(proxy), textureMatrix, samplerState);
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400213 }
214 } else {
215 if (kDomain_DomainMode == domainMode) {
Brian Osman5e341672017-10-18 10:23:18 -0400216 return GrBicubicEffect::Make(std::move(proxy), textureMatrix, domain);
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400217 } else {
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400218 static const GrSamplerState::WrapMode kClampClamp[] = {
219 GrSamplerState::WrapMode::kClamp, GrSamplerState::WrapMode::kClamp};
Brian Osman5e341672017-10-18 10:23:18 -0400220 return GrBicubicEffect::Make(std::move(proxy), textureMatrix, kClampClamp);
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400221 }
222 }
223}
Brian Salomon2a943df2018-05-04 13:43:19 -0400224
225sk_sp<GrTextureProxy> GrTextureProducer::refTextureProxyForParams(
226 const GrSamplerState& sampler,
Brian Salomon2a943df2018-05-04 13:43:19 -0400227 SkScalar scaleAdjust[2]) {
228 // Check that the caller pre-initialized scaleAdjust
229 SkASSERT(!scaleAdjust || (scaleAdjust[0] == 1 && scaleAdjust[1] == 1));
230 // Check that if the caller passed nullptr for scaleAdjust that we're in the case where there
231 // can be no scaling.
232 SkDEBUGCODE(bool expectNoScale = (sampler.filter() != GrSamplerState::Filter::kMipMap &&
233 !sampler.isRepeated()));
234 SkASSERT(scaleAdjust || expectNoScale);
Greg Daniel8e9b4c42018-07-20 10:30:48 -0400235
236 int mipCount = SkMipMap::ComputeLevelCount(this->width(), this->height());
237 bool willBeMipped = GrSamplerState::Filter::kMipMap == sampler.filter() && mipCount &&
Robert Phillips9da87e02019-02-04 13:26:26 -0500238 fContext->priv().caps()->mipMapSupport();
Greg Daniel8e9b4c42018-07-20 10:30:48 -0400239
Brian Osman6064e1c2018-10-19 14:27:54 -0400240 auto result = this->onRefTextureProxyForParams(sampler, willBeMipped, scaleAdjust);
Brian Salomon2a943df2018-05-04 13:43:19 -0400241
Greg Daniel022b1e02018-07-20 14:54:00 -0400242 // Check to make sure that if we say the texture willBeMipped that the returned texture has mip
243 // maps, unless the config is not copyable.
244 SkASSERT(!result || !willBeMipped || result->mipMapped() == GrMipMapped::kYes ||
Robert Phillips9da87e02019-02-04 13:26:26 -0500245 !fContext->priv().caps()->isConfigCopyable(result->config()));
Greg Daniel022b1e02018-07-20 14:54:00 -0400246
Brian Salomon2a943df2018-05-04 13:43:19 -0400247 // 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}
Greg Daniel5f4b09d2018-06-12 16:39:59 -0400253
Brian Osman6064e1c2018-10-19 14:27:54 -0400254sk_sp<GrTextureProxy> GrTextureProducer::refTextureProxy(GrMipMapped willNeedMips) {
Greg Daniel5f4b09d2018-06-12 16:39:59 -0400255 GrSamplerState::Filter filter =
256 GrMipMapped::kNo == willNeedMips ? GrSamplerState::Filter::kNearest
257 : GrSamplerState::Filter::kMipMap;
258 GrSamplerState sampler(GrSamplerState::WrapMode::kClamp, filter);
Greg Daniel8e9b4c42018-07-20 10:30:48 -0400259
260 int mipCount = SkMipMap::ComputeLevelCount(this->width(), this->height());
261 bool willBeMipped = GrSamplerState::Filter::kMipMap == sampler.filter() && mipCount &&
Robert Phillips9da87e02019-02-04 13:26:26 -0500262 fContext->priv().caps()->mipMapSupport();
Greg Daniel8e9b4c42018-07-20 10:30:48 -0400263
Brian Osman6064e1c2018-10-19 14:27:54 -0400264 auto result = this->onRefTextureProxyForParams(sampler, willBeMipped, nullptr);
Greg Daniel5f4b09d2018-06-12 16:39:59 -0400265
Greg Daniel022b1e02018-07-20 14:54:00 -0400266 // Check to make sure that if we say the texture willBeMipped that the returned texture has mip
267 // maps, unless the config is not copyable.
268 SkASSERT(!result || !willBeMipped || result->mipMapped() == GrMipMapped::kYes ||
Robert Phillips9da87e02019-02-04 13:26:26 -0500269 !fContext->priv().caps()->isConfigCopyable(result->config()));
Greg Daniel022b1e02018-07-20 14:54:00 -0400270
Greg Daniel5f4b09d2018-06-12 16:39:59 -0400271 // Check that no scaling occured and we returned a proxy of the same size as the producer.
272 SkASSERT(!result || (result->width() == this->width() && result->height() == this->height()));
273 return result;
274}