blob: 97417f96b8378f70926c4b27bc8229470cebd646 [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
Robert Phillipsb66b42f2017-03-14 08:53:02 -040026 const SkRect dstRect = SkRect::MakeIWH(copyParams.fWidth, copyParams.fHeight);
Greg Daniel45d63032017-10-30 13:41:26 -040027 GrMipMapped mipMapped = dstWillRequireMipMaps ? GrMipMapped::kYes : GrMipMapped::kNo;
Robert Phillipsb66b42f2017-03-14 08:53:02 -040028
Greg Daniel09c94002018-06-08 22:11:51 +000029 SkRect localRect = SkRect::MakeWH(inputProxy->width(), inputProxy->height());
30
31 bool needsDomain = false;
32 bool resizing = false;
33 if (copyParams.fFilter != GrSamplerState::Filter::kNearest) {
34 bool resizing = localRect.width() != dstRect.width() ||
35 localRect.height() != dstRect.height();
36 needsDomain = resizing && !GrProxyProvider::IsFunctionallyExact(inputProxy.get());
37 }
38
39 if (copyParams.fFilter == GrSamplerState::Filter::kNearest && !needsDomain && !resizing &&
40 dstWillRequireMipMaps) {
41 sk_sp<GrTextureProxy> proxy = GrCopyBaseMipMapToTextureProxy(context, inputProxy.get());
42 if (proxy) {
43 return proxy;
44 }
45 }
46
Greg Daniel4065d452018-11-16 15:43:41 -050047 GrBackendFormat format = inputProxy->backendFormat().makeTexture2D();
48 if (!format.isValid()) {
49 return nullptr;
50 }
51
Robert Phillips0c4b7b12018-03-06 08:20:37 -050052 sk_sp<GrRenderTargetContext> copyRTC =
53 context->contextPriv().makeDeferredRenderTargetContextWithFallback(
Greg Daniel4065d452018-11-16 15:43:41 -050054 format, SkBackingFit::kExact, dstRect.width(), dstRect.height(),
55 inputProxy->config(), nullptr, 1, mipMapped, inputProxy->origin());
Robert Phillipsb66b42f2017-03-14 08:53:02 -040056 if (!copyRTC) {
57 return nullptr;
58 }
59
60 GrPaint paint;
Robert Phillipsb66b42f2017-03-14 08:53:02 -040061
62 if (needsDomain) {
63 const SkRect domain = localRect.makeInset(0.5f, 0.5f);
64 // This would cause us to read values from outside the subset. Surely, the caller knows
65 // better!
Brian Salomon2bbdcc42017-09-07 12:36:34 -040066 SkASSERT(copyParams.fFilter != GrSamplerState::Filter::kMipMap);
Robert Phillipsb66b42f2017-03-14 08:53:02 -040067 paint.addColorFragmentProcessor(
Brian Osman2240be92017-10-18 13:15:13 -040068 GrTextureDomainEffect::Make(std::move(inputProxy), SkMatrix::I(), domain,
69 GrTextureDomain::kClamp_Mode, copyParams.fFilter));
Robert Phillipsb66b42f2017-03-14 08:53:02 -040070 } else {
Brian Salomon2bbdcc42017-09-07 12:36:34 -040071 GrSamplerState samplerState(GrSamplerState::WrapMode::kClamp, copyParams.fFilter);
Brian Osman2240be92017-10-18 13:15:13 -040072 paint.addColorTextureProcessor(std::move(inputProxy), SkMatrix::I(), samplerState);
Robert Phillipsb66b42f2017-03-14 08:53:02 -040073 }
74 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
75
76 copyRTC->fillRectToRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), dstRect,
77 localRect);
78 return copyRTC->asTextureProxyRef();
79}
80
Brian Osmane8e54582016-11-28 10:06:27 -050081/** Determines whether a texture domain is necessary and if so what domain to use. There are two
82 * rectangles to consider:
Robert Phillips3798c862017-03-27 11:08:16 -040083 * - The first is the content area specified by the texture adjuster (i.e., textureContentArea).
84 * We can *never* allow filtering to cause bleed of pixels outside this rectangle.
85 * - The second rectangle is the constraint rectangle (i.e., constraintRect), which is known to
86 * be contained by the content area. The filterConstraint specifies whether we are allowed to
87 * bleed across this rect.
Brian Osmane8e54582016-11-28 10:06:27 -050088 *
89 * We want to avoid using a domain if possible. We consider the above rectangles, the filter type,
90 * and whether the coords generated by the draw would all fall within the constraint rect. If the
91 * latter is true we only need to consider whether the filter would extend beyond the rects.
92 */
93GrTextureProducer::DomainMode GrTextureProducer::DetermineDomainMode(
Brian Salomon2bbdcc42017-09-07 12:36:34 -040094 const SkRect& constraintRect,
95 FilterConstraint filterConstraint,
96 bool coordsLimitedToConstraintRect,
97 GrTextureProxy* proxy,
Brian Salomon2bbdcc42017-09-07 12:36:34 -040098 const GrSamplerState::Filter* filterModeOrNullForBicubic,
99 SkRect* domainRect) {
Robert Phillips51e7ca32017-03-27 10:14:08 -0400100 const SkIRect proxyBounds = SkIRect::MakeWH(proxy->width(), proxy->height());
101
102 SkASSERT(proxyBounds.contains(constraintRect));
Robert Phillips51e7ca32017-03-27 10:14:08 -0400103
Robert Phillips1afd4cd2018-01-08 13:40:32 -0500104 const bool proxyIsExact = GrProxyProvider::IsFunctionallyExact(proxy);
Brian Salomon4df00922017-09-07 16:34:11 +0000105
Robert Phillips51e7ca32017-03-27 10:14:08 -0400106 // If the constraint rectangle contains the whole proxy then no need for a domain.
107 if (constraintRect.contains(proxyBounds) && proxyIsExact) {
108 return kNoDomain_DomainMode;
109 }
110
Robert Phillips51e7ca32017-03-27 10:14:08 -0400111 bool restrictFilterToRect = (filterConstraint == GrTextureProducer::kYes_FilterConstraint);
112
113 // If we can filter outside the constraint rect, and there is no non-content area of the
114 // proxy, and we aren't going to generate sample coords outside the constraint rect then we
115 // don't need a domain.
Greg Danielc77085d2017-11-01 16:38:48 -0400116 if (!restrictFilterToRect && proxyIsExact && coordsLimitedToConstraintRect) {
Robert Phillips51e7ca32017-03-27 10:14:08 -0400117 return kNoDomain_DomainMode;
118 }
119
120 // Get the domain inset based on sampling mode (or bail if mipped)
Brian Salomon4df00922017-09-07 16:34:11 +0000121 SkScalar filterHalfWidth = 0.f;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400122 if (filterModeOrNullForBicubic) {
123 switch (*filterModeOrNullForBicubic) {
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400124 case GrSamplerState::Filter::kNearest:
Robert Phillips51e7ca32017-03-27 10:14:08 -0400125 if (coordsLimitedToConstraintRect) {
126 return kNoDomain_DomainMode;
Brian Salomon4df00922017-09-07 16:34:11 +0000127 } else {
128 filterHalfWidth = 0.f;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400129 }
130 break;
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400131 case GrSamplerState::Filter::kBilerp:
Brian Salomon4df00922017-09-07 16:34:11 +0000132 filterHalfWidth = .5f;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400133 break;
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400134 case GrSamplerState::Filter::kMipMap:
Greg Danielc77085d2017-11-01 16:38:48 -0400135 if (restrictFilterToRect || !proxyIsExact) {
Brian Salomon4df00922017-09-07 16:34:11 +0000136 // No domain can save us here.
137 return kTightCopy_DomainMode;
138 }
139 return kNoDomain_DomainMode;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400140 }
Brian Salomon4df00922017-09-07 16:34:11 +0000141 } else {
142 // bicubic does nearest filtering internally.
143 filterHalfWidth = 1.5f;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400144 }
145
146 // Both bilerp and bicubic use bilinear filtering and so need to be clamped to the center
147 // of the edge texel. Pinning to the texel center has no impact on nearest mode and MIP-maps
148
149 static const SkScalar kDomainInset = 0.5f;
150 // Figure out the limits of pixels we're allowed to sample from.
151 // Unless we know the amount of outset and the texture matrix we have to conservatively enforce
152 // the domain.
153 if (restrictFilterToRect) {
154 *domainRect = constraintRect.makeInset(kDomainInset, kDomainInset);
Greg Danielc77085d2017-11-01 16:38:48 -0400155 } else if (!proxyIsExact) {
156 // If we got here then: proxy is not exact, the coords are limited to the
Brian Salomon4df00922017-09-07 16:34:11 +0000157 // constraint rect, and we're allowed to filter across the constraint rect boundary. So
Greg Danielc77085d2017-11-01 16:38:48 -0400158 // we check whether the filter would reach across the edge of the proxy.
Brian Salomon4df00922017-09-07 16:34:11 +0000159 // We will only set the sides that are required.
160
Mike Reed274218e2018-01-08 15:05:02 -0500161 *domainRect = SkRectPriv::MakeLargest();
Brian Salomon4df00922017-09-07 16:34:11 +0000162 if (coordsLimitedToConstraintRect) {
163 // We may be able to use the fact that the texture coords are limited to the constraint
164 // rect in order to avoid having to add a domain.
165 bool needContentAreaConstraint = false;
Greg Danielc77085d2017-11-01 16:38:48 -0400166 if (proxyBounds.fRight - filterHalfWidth < constraintRect.fRight) {
167 domainRect->fRight = proxyBounds.fRight - kDomainInset;
Brian Salomon4df00922017-09-07 16:34:11 +0000168 needContentAreaConstraint = true;
169 }
Greg Danielc77085d2017-11-01 16:38:48 -0400170 if (proxyBounds.fBottom - filterHalfWidth < constraintRect.fBottom) {
171 domainRect->fBottom = proxyBounds.fBottom - kDomainInset;
Brian Salomon4df00922017-09-07 16:34:11 +0000172 needContentAreaConstraint = true;
173 }
174 if (!needContentAreaConstraint) {
175 return kNoDomain_DomainMode;
176 }
177 } else {
178 // Our sample coords for the texture are allowed to be outside the constraintRect so we
179 // don't consider it when computing the domain.
Greg Danielc77085d2017-11-01 16:38:48 -0400180 domainRect->fRight = proxyBounds.fRight - kDomainInset;
181 domainRect->fBottom = proxyBounds.fBottom - kDomainInset;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400182 }
Brian Salomon4df00922017-09-07 16:34:11 +0000183 } else {
184 return kNoDomain_DomainMode;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400185 }
Brian Salomon4df00922017-09-07 16:34:11 +0000186
187 if (domainRect->fLeft > domainRect->fRight) {
188 domainRect->fLeft = domainRect->fRight = SkScalarAve(domainRect->fLeft, domainRect->fRight);
189 }
190 if (domainRect->fTop > domainRect->fBottom) {
191 domainRect->fTop = domainRect->fBottom = SkScalarAve(domainRect->fTop, domainRect->fBottom);
192 }
193 return kDomain_DomainMode;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400194}
195
Brian Salomonaff329b2017-08-11 09:40:37 -0400196std::unique_ptr<GrFragmentProcessor> GrTextureProducer::CreateFragmentProcessorForDomainAndFilter(
197 sk_sp<GrTextureProxy> proxy,
Brian Salomonaff329b2017-08-11 09:40:37 -0400198 const SkMatrix& textureMatrix,
199 DomainMode domainMode,
200 const SkRect& domain,
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400201 const GrSamplerState::Filter* filterOrNullForBicubic) {
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400202 SkASSERT(kTightCopy_DomainMode != domainMode);
203 if (filterOrNullForBicubic) {
204 if (kDomain_DomainMode == domainMode) {
Brian Osman2240be92017-10-18 13:15:13 -0400205 return GrTextureDomainEffect::Make(std::move(proxy), textureMatrix, domain,
Brian Osman5e341672017-10-18 10:23:18 -0400206 GrTextureDomain::kClamp_Mode,
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400207 *filterOrNullForBicubic);
208 } else {
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400209 GrSamplerState samplerState(GrSamplerState::WrapMode::kClamp, *filterOrNullForBicubic);
Brian Osman2240be92017-10-18 13:15:13 -0400210 return GrSimpleTextureEffect::Make(std::move(proxy), textureMatrix, samplerState);
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400211 }
212 } else {
213 if (kDomain_DomainMode == domainMode) {
Brian Osman5e341672017-10-18 10:23:18 -0400214 return GrBicubicEffect::Make(std::move(proxy), textureMatrix, domain);
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400215 } else {
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400216 static const GrSamplerState::WrapMode kClampClamp[] = {
217 GrSamplerState::WrapMode::kClamp, GrSamplerState::WrapMode::kClamp};
Brian Osman5e341672017-10-18 10:23:18 -0400218 return GrBicubicEffect::Make(std::move(proxy), textureMatrix, kClampClamp);
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400219 }
220 }
221}
Brian Salomon2a943df2018-05-04 13:43:19 -0400222
223sk_sp<GrTextureProxy> GrTextureProducer::refTextureProxyForParams(
224 const GrSamplerState& sampler,
Brian Salomon2a943df2018-05-04 13:43:19 -0400225 SkScalar scaleAdjust[2]) {
226 // Check that the caller pre-initialized scaleAdjust
227 SkASSERT(!scaleAdjust || (scaleAdjust[0] == 1 && scaleAdjust[1] == 1));
228 // Check that if the caller passed nullptr for scaleAdjust that we're in the case where there
229 // can be no scaling.
230 SkDEBUGCODE(bool expectNoScale = (sampler.filter() != GrSamplerState::Filter::kMipMap &&
231 !sampler.isRepeated()));
232 SkASSERT(scaleAdjust || expectNoScale);
Greg Daniel8e9b4c42018-07-20 10:30:48 -0400233
234 int mipCount = SkMipMap::ComputeLevelCount(this->width(), this->height());
235 bool willBeMipped = GrSamplerState::Filter::kMipMap == sampler.filter() && mipCount &&
236 fContext->contextPriv().caps()->mipMapSupport();
237
Brian Osman6064e1c2018-10-19 14:27:54 -0400238 auto result = this->onRefTextureProxyForParams(sampler, willBeMipped, scaleAdjust);
Brian Salomon2a943df2018-05-04 13:43:19 -0400239
Greg Daniel022b1e02018-07-20 14:54:00 -0400240 // Check to make sure that if we say the texture willBeMipped that the returned texture has mip
241 // maps, unless the config is not copyable.
242 SkASSERT(!result || !willBeMipped || result->mipMapped() == GrMipMapped::kYes ||
243 !fContext->contextPriv().caps()->isConfigCopyable(result->config()));
244
Brian Salomon2a943df2018-05-04 13:43:19 -0400245 // Check that the "no scaling expected" case always returns a proxy of the same size as the
246 // producer.
247 SkASSERT(!result || !expectNoScale ||
248 (result->width() == this->width() && result->height() == this->height()));
249 return result;
250}
Greg Daniel5f4b09d2018-06-12 16:39:59 -0400251
Brian Osman6064e1c2018-10-19 14:27:54 -0400252sk_sp<GrTextureProxy> GrTextureProducer::refTextureProxy(GrMipMapped willNeedMips) {
Greg Daniel5f4b09d2018-06-12 16:39:59 -0400253 GrSamplerState::Filter filter =
254 GrMipMapped::kNo == willNeedMips ? GrSamplerState::Filter::kNearest
255 : GrSamplerState::Filter::kMipMap;
256 GrSamplerState sampler(GrSamplerState::WrapMode::kClamp, filter);
Greg Daniel8e9b4c42018-07-20 10:30:48 -0400257
258 int mipCount = SkMipMap::ComputeLevelCount(this->width(), this->height());
259 bool willBeMipped = GrSamplerState::Filter::kMipMap == sampler.filter() && mipCount &&
260 fContext->contextPriv().caps()->mipMapSupport();
261
Brian Osman6064e1c2018-10-19 14:27:54 -0400262 auto result = this->onRefTextureProxyForParams(sampler, willBeMipped, nullptr);
Greg Daniel5f4b09d2018-06-12 16:39:59 -0400263
Greg Daniel022b1e02018-07-20 14:54:00 -0400264 // Check to make sure that if we say the texture willBeMipped that the returned texture has mip
265 // maps, unless the config is not copyable.
266 SkASSERT(!result || !willBeMipped || result->mipMapped() == GrMipMapped::kYes ||
267 !fContext->contextPriv().caps()->isConfigCopyable(result->config()));
268
Greg Daniel5f4b09d2018-06-12 16:39:59 -0400269 // Check that no scaling occured and we returned a proxy of the same size as the producer.
270 SkASSERT(!result || (result->width() == this->width() && result->height() == this->height()));
271 return result;
272}