blob: 46c61346a771574351353954ebd87c482ffdea63 [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
Brian Salomonb8f098d2020-01-07 11:15:44 -05008#include "src/gpu/GrTextureProducer.h"
9
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "include/private/GrRecordingContext.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050011#include "src/core/SkMipMap.h"
12#include "src/core/SkRectPriv.h"
13#include "src/gpu/GrClip.h"
14#include "src/gpu/GrContextPriv.h"
15#include "src/gpu/GrProxyProvider.h"
16#include "src/gpu/GrRecordingContextPriv.h"
17#include "src/gpu/GrRenderTargetContext.h"
Greg Danielf91aeb22019-06-18 09:58:02 -040018#include "src/gpu/GrTextureProxy.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050019#include "src/gpu/SkGr.h"
20#include "src/gpu/effects/GrBicubicEffect.h"
21#include "src/gpu/effects/GrTextureDomain.h"
Brian Salomonb8f098d2020-01-07 11:15:44 -050022#include "src/gpu/effects/GrTextureEffect.h"
Brian Osmane8e54582016-11-28 10:06:27 -050023
Robert Phillips9338c602019-02-19 12:52:29 -050024sk_sp<GrTextureProxy> GrTextureProducer::CopyOnGpu(GrRecordingContext* context,
Robert Phillipsb66b42f2017-03-14 08:53:02 -040025 sk_sp<GrTextureProxy> inputProxy,
Brian Salomond6287472019-06-24 15:50:07 -040026 GrColorType colorType,
Greg Daniele1da1d92017-10-06 15:59:27 -040027 const CopyParams& copyParams,
28 bool dstWillRequireMipMaps) {
Robert Phillipsb66b42f2017-03-14 08:53:02 -040029 SkASSERT(context);
30
Brian Salomon1a217eb2019-10-24 10:50:36 -040031 const SkRect dstRect = SkRect::Make(copyParams.fDimensions);
Greg Daniel45d63032017-10-30 13:41:26 -040032 GrMipMapped mipMapped = dstWillRequireMipMaps ? GrMipMapped::kYes : GrMipMapped::kNo;
Robert Phillipsb66b42f2017-03-14 08:53:02 -040033
Brian Salomon9f2b86c2019-10-22 10:37:46 -040034 SkRect localRect = inputProxy->getBoundsRect();
Greg Daniel09c94002018-06-08 22:11:51 +000035
Brian Salomon1c3e6cf2020-01-24 14:49:46 +000036 bool needsDomain = false;
Greg Daniel09c94002018-06-08 22:11:51 +000037 bool resizing = false;
38 if (copyParams.fFilter != GrSamplerState::Filter::kNearest) {
Brian Salomon1c3e6cf2020-01-24 14:49:46 +000039 bool resizing = localRect.width() != dstRect.width() ||
40 localRect.height() != dstRect.height();
41 needsDomain = resizing && inputProxy->isFunctionallyExact();
Greg Daniel09c94002018-06-08 22:11:51 +000042 }
43
Brian Salomon1c3e6cf2020-01-24 14:49:46 +000044 if (copyParams.fFilter == GrSamplerState::Filter::kNearest && !needsDomain && !resizing &&
Greg Daniel09c94002018-06-08 22:11:51 +000045 dstWillRequireMipMaps) {
Greg Danielc594e622019-10-15 14:01:49 -040046 sk_sp<GrTextureProxy> proxy = GrCopyBaseMipMapToTextureProxy(context, inputProxy.get(),
47 colorType);
Greg Daniel09c94002018-06-08 22:11:51 +000048 if (proxy) {
49 return proxy;
50 }
51 }
52
Greg Daniele20fcad2020-01-08 11:52:34 -050053 auto copyRTC = GrRenderTargetContext::MakeWithFallback(
54 context, colorType, nullptr, SkBackingFit::kExact, copyParams.fDimensions, 1,
55 mipMapped, inputProxy->isProtected(), 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
Brian Salomon1c3e6cf2020-01-24 14:49:46 +000062 auto fp = GrTextureEffect::Make(std::move(inputProxy), kUnknown_SkAlphaType, SkMatrix::I(),
63 copyParams.fFilter);
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!
68 SkASSERT(copyParams.fFilter != GrSamplerState::Filter::kMipMap);
69 fp = GrDomainEffect::Make(std::move(fp), domain, GrTextureDomain::kClamp_Mode,
70 copyParams.fFilter);
71 }
Brian Salomon7eabfe82019-12-02 14:20:20 -050072 paint.addColorFragmentProcessor(std::move(fp));
Robert Phillipsb66b42f2017-03-14 08:53:02 -040073 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
74
75 copyRTC->fillRectToRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), dstRect,
76 localRect);
77 return copyRTC->asTextureProxyRef();
78}
79
Brian Osmane8e54582016-11-28 10:06:27 -050080/** Determines whether a texture domain is necessary and if so what domain to use. There are two
81 * rectangles to consider:
Robert Phillips3798c862017-03-27 11:08:16 -040082 * - The first is the content area specified by the texture adjuster (i.e., textureContentArea).
83 * We can *never* allow filtering to cause bleed of pixels outside this rectangle.
84 * - The second rectangle is the constraint rectangle (i.e., constraintRect), which is known to
85 * be contained by the content area. The filterConstraint specifies whether we are allowed to
86 * bleed across this rect.
Brian Osmane8e54582016-11-28 10:06:27 -050087 *
88 * We want to avoid using a domain if possible. We consider the above rectangles, the filter type,
89 * and whether the coords generated by the draw would all fall within the constraint rect. If the
90 * latter is true we only need to consider whether the filter would extend beyond the rects.
91 */
92GrTextureProducer::DomainMode GrTextureProducer::DetermineDomainMode(
Brian Salomon2bbdcc42017-09-07 12:36:34 -040093 const SkRect& constraintRect,
94 FilterConstraint filterConstraint,
95 bool coordsLimitedToConstraintRect,
96 GrTextureProxy* proxy,
Brian Salomon2bbdcc42017-09-07 12:36:34 -040097 const GrSamplerState::Filter* filterModeOrNullForBicubic,
98 SkRect* domainRect) {
Brian Salomon9f2b86c2019-10-22 10:37:46 -040099 const SkIRect proxyBounds = SkIRect::MakeSize(proxy->dimensions());
Robert Phillips51e7ca32017-03-27 10:14:08 -0400100
101 SkASSERT(proxyBounds.contains(constraintRect));
Robert Phillips51e7ca32017-03-27 10:14:08 -0400102
Brian Salomon5c60b752019-12-13 15:03:43 -0500103 const bool proxyIsExact = proxy->isFunctionallyExact();
Brian Salomon4df00922017-09-07 16:34:11 +0000104
Robert Phillips51e7ca32017-03-27 10:14:08 -0400105 // If the constraint rectangle contains the whole proxy then no need for a domain.
106 if (constraintRect.contains(proxyBounds) && proxyIsExact) {
107 return kNoDomain_DomainMode;
108 }
109
Robert Phillips51e7ca32017-03-27 10:14:08 -0400110 bool restrictFilterToRect = (filterConstraint == GrTextureProducer::kYes_FilterConstraint);
111
112 // If we can filter outside the constraint rect, and there is no non-content area of the
113 // proxy, and we aren't going to generate sample coords outside the constraint rect then we
114 // don't need a domain.
Greg Danielc77085d2017-11-01 16:38:48 -0400115 if (!restrictFilterToRect && proxyIsExact && coordsLimitedToConstraintRect) {
Robert Phillips51e7ca32017-03-27 10:14:08 -0400116 return kNoDomain_DomainMode;
117 }
118
119 // Get the domain inset based on sampling mode (or bail if mipped)
Brian Salomon4df00922017-09-07 16:34:11 +0000120 SkScalar filterHalfWidth = 0.f;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400121 if (filterModeOrNullForBicubic) {
122 switch (*filterModeOrNullForBicubic) {
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400123 case GrSamplerState::Filter::kNearest:
Robert Phillips51e7ca32017-03-27 10:14:08 -0400124 if (coordsLimitedToConstraintRect) {
125 return kNoDomain_DomainMode;
Brian Salomon4df00922017-09-07 16:34:11 +0000126 } else {
127 filterHalfWidth = 0.f;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400128 }
129 break;
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400130 case GrSamplerState::Filter::kBilerp:
Brian Salomon4df00922017-09-07 16:34:11 +0000131 filterHalfWidth = .5f;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400132 break;
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400133 case GrSamplerState::Filter::kMipMap:
Greg Danielc77085d2017-11-01 16:38:48 -0400134 if (restrictFilterToRect || !proxyIsExact) {
Brian Salomon4df00922017-09-07 16:34:11 +0000135 // No domain can save us here.
136 return kTightCopy_DomainMode;
137 }
138 return kNoDomain_DomainMode;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400139 }
Brian Salomon4df00922017-09-07 16:34:11 +0000140 } else {
141 // bicubic does nearest filtering internally.
142 filterHalfWidth = 1.5f;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400143 }
144
145 // Both bilerp and bicubic use bilinear filtering and so need to be clamped to the center
146 // of the edge texel. Pinning to the texel center has no impact on nearest mode and MIP-maps
147
148 static const SkScalar kDomainInset = 0.5f;
149 // Figure out the limits of pixels we're allowed to sample from.
150 // Unless we know the amount of outset and the texture matrix we have to conservatively enforce
151 // the domain.
152 if (restrictFilterToRect) {
153 *domainRect = constraintRect.makeInset(kDomainInset, kDomainInset);
Greg Danielc77085d2017-11-01 16:38:48 -0400154 } else if (!proxyIsExact) {
155 // If we got here then: proxy is not exact, the coords are limited to the
Brian Salomon4df00922017-09-07 16:34:11 +0000156 // constraint rect, and we're allowed to filter across the constraint rect boundary. So
Greg Danielc77085d2017-11-01 16:38:48 -0400157 // we check whether the filter would reach across the edge of the proxy.
Brian Salomon4df00922017-09-07 16:34:11 +0000158 // We will only set the sides that are required.
159
Mike Reed274218e2018-01-08 15:05:02 -0500160 *domainRect = SkRectPriv::MakeLargest();
Brian Salomon4df00922017-09-07 16:34:11 +0000161 if (coordsLimitedToConstraintRect) {
162 // We may be able to use the fact that the texture coords are limited to the constraint
163 // rect in order to avoid having to add a domain.
164 bool needContentAreaConstraint = false;
Greg Danielc77085d2017-11-01 16:38:48 -0400165 if (proxyBounds.fRight - filterHalfWidth < constraintRect.fRight) {
166 domainRect->fRight = proxyBounds.fRight - kDomainInset;
Brian Salomon4df00922017-09-07 16:34:11 +0000167 needContentAreaConstraint = true;
168 }
Greg Danielc77085d2017-11-01 16:38:48 -0400169 if (proxyBounds.fBottom - filterHalfWidth < constraintRect.fBottom) {
170 domainRect->fBottom = proxyBounds.fBottom - kDomainInset;
Brian Salomon4df00922017-09-07 16:34:11 +0000171 needContentAreaConstraint = true;
172 }
173 if (!needContentAreaConstraint) {
174 return kNoDomain_DomainMode;
175 }
176 } else {
177 // Our sample coords for the texture are allowed to be outside the constraintRect so we
178 // don't consider it when computing the domain.
Greg Danielc77085d2017-11-01 16:38:48 -0400179 domainRect->fRight = proxyBounds.fRight - kDomainInset;
180 domainRect->fBottom = proxyBounds.fBottom - kDomainInset;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400181 }
Brian Salomon4df00922017-09-07 16:34:11 +0000182 } else {
183 return kNoDomain_DomainMode;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400184 }
Brian Salomon4df00922017-09-07 16:34:11 +0000185
186 if (domainRect->fLeft > domainRect->fRight) {
187 domainRect->fLeft = domainRect->fRight = SkScalarAve(domainRect->fLeft, domainRect->fRight);
188 }
189 if (domainRect->fTop > domainRect->fBottom) {
190 domainRect->fTop = domainRect->fBottom = SkScalarAve(domainRect->fTop, domainRect->fBottom);
191 }
192 return kDomain_DomainMode;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400193}
194
Michael Ludwigddeed372019-02-20 16:50:10 -0500195std::unique_ptr<GrFragmentProcessor> GrTextureProducer::createFragmentProcessorForDomainAndFilter(
Brian Salomonaff329b2017-08-11 09:40:37 -0400196 sk_sp<GrTextureProxy> proxy,
Brian Salomonaff329b2017-08-11 09:40:37 -0400197 const SkMatrix& textureMatrix,
198 DomainMode domainMode,
199 const SkRect& domain,
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400200 const GrSamplerState::Filter* filterOrNullForBicubic) {
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400201 SkASSERT(kTightCopy_DomainMode != domainMode);
Brian Salomon1c3e6cf2020-01-24 14:49:46 +0000202 bool clampToBorderSupport = fContext->priv().caps()->clampToBorderSupport();
Brian Salomonfc118442019-11-22 19:09:27 -0500203 SkAlphaType srcAlphaType = this->alphaType();
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400204 if (filterOrNullForBicubic) {
Brian Salomon1c3e6cf2020-01-24 14:49:46 +0000205 GrSamplerState::WrapMode wrapMode = fDomainNeedsDecal && clampToBorderSupport
Brian Salomon7eabfe82019-12-02 14:20:20 -0500206 ? GrSamplerState::WrapMode::kClampToBorder
207 : GrSamplerState::WrapMode::kClamp;
208 GrSamplerState samplerState(wrapMode, *filterOrNullForBicubic);
Brian Salomon1c3e6cf2020-01-24 14:49:46 +0000209 auto fp =
210 GrTextureEffect::Make(std::move(proxy), srcAlphaType, textureMatrix, samplerState);
211 if (kDomain_DomainMode == domainMode || (fDomainNeedsDecal && !clampToBorderSupport)) {
212 GrTextureDomain::Mode wrapMode = fDomainNeedsDecal ? GrTextureDomain::kDecal_Mode
213 : GrTextureDomain::kClamp_Mode;
214 return GrDomainEffect::Make(std::move(fp), domain, wrapMode, *filterOrNullForBicubic);
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400215 }
Brian Salomon1c3e6cf2020-01-24 14:49:46 +0000216 return fp;
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400217 } else {
Michael Ludwigddeed372019-02-20 16:50:10 -0500218 static const GrSamplerState::WrapMode kClampClamp[] = {
219 GrSamplerState::WrapMode::kClamp, GrSamplerState::WrapMode::kClamp};
220 static const GrSamplerState::WrapMode kDecalDecal[] = {
221 GrSamplerState::WrapMode::kClampToBorder, GrSamplerState::WrapMode::kClampToBorder};
222
Brian Salomona86fc7a2019-05-28 20:42:58 -0400223 static constexpr auto kDir = GrBicubicEffect::Direction::kXY;
Michael Ludwigddeed372019-02-20 16:50:10 -0500224 if (kDomain_DomainMode == domainMode || (fDomainNeedsDecal && !clampToBorderSupport)) {
225 GrTextureDomain::Mode wrapMode = fDomainNeedsDecal ? GrTextureDomain::kDecal_Mode
226 : GrTextureDomain::kClamp_Mode;
Brian Salomonfc118442019-11-22 19:09:27 -0500227 return GrBicubicEffect::Make(std::move(proxy), textureMatrix, kClampClamp, wrapMode,
228 wrapMode, kDir, srcAlphaType,
Michael Ludwigddeed372019-02-20 16:50:10 -0500229 kDomain_DomainMode == domainMode ? &domain : nullptr);
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400230 } else {
Brian Salomonfc118442019-11-22 19:09:27 -0500231 return GrBicubicEffect::Make(std::move(proxy), textureMatrix,
Brian Salomon1127c0b2019-06-13 20:22:10 +0000232 fDomainNeedsDecal ? kDecalDecal : kClampClamp, kDir,
Brian Salomonfc118442019-11-22 19:09:27 -0500233 srcAlphaType);
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400234 }
235 }
236}
Brian Salomon2a943df2018-05-04 13:43:19 -0400237
238sk_sp<GrTextureProxy> GrTextureProducer::refTextureProxyForParams(
Michael Ludwigddeed372019-02-20 16:50:10 -0500239 const GrSamplerState::Filter* filterOrNullForBicubic,
240 SkScalar scaleAdjust[2]) {
241 GrSamplerState sampler; // Default is nearest + clamp
242 if (filterOrNullForBicubic) {
243 sampler.setFilterMode(*filterOrNullForBicubic);
244 }
245 if (fDomainNeedsDecal) {
246 // Assuming hardware support, switch to clamp-to-border instead of clamp
247 if (fContext->priv().caps()->clampToBorderSupport()) {
248 sampler.setWrapModeX(GrSamplerState::WrapMode::kClampToBorder);
249 sampler.setWrapModeY(GrSamplerState::WrapMode::kClampToBorder);
250 }
251 }
252 return this->refTextureProxyForParams(sampler, scaleAdjust);
253}
254
Brian Salomonccb61422020-01-09 10:46:36 -0500255sk_sp<GrTextureProxy> GrTextureProducer::refTextureProxyForParams(GrSamplerState sampler,
256 SkScalar scaleAdjust[2]) {
Brian Salomon2a943df2018-05-04 13:43:19 -0400257 // Check that the caller pre-initialized scaleAdjust
258 SkASSERT(!scaleAdjust || (scaleAdjust[0] == 1 && scaleAdjust[1] == 1));
Greg Daniel82c6b102020-01-21 10:33:22 -0500259
260 const GrCaps* caps = this->context()->priv().caps();
Greg Daniel8e9b4c42018-07-20 10:30:48 -0400261
262 int mipCount = SkMipMap::ComputeLevelCount(this->width(), this->height());
263 bool willBeMipped = GrSamplerState::Filter::kMipMap == sampler.filter() && mipCount &&
Greg Daniel82c6b102020-01-21 10:33:22 -0500264 caps->mipMapSupport();
Greg Daniel8e9b4c42018-07-20 10:30:48 -0400265
Brian Osman6064e1c2018-10-19 14:27:54 -0400266 auto result = this->onRefTextureProxyForParams(sampler, willBeMipped, scaleAdjust);
Brian Salomon2a943df2018-05-04 13:43:19 -0400267
Greg Daniel022b1e02018-07-20 14:54:00 -0400268 // Check to make sure that if we say the texture willBeMipped that the returned texture has mip
269 // maps, unless the config is not copyable.
270 SkASSERT(!result || !willBeMipped || result->mipMapped() == GrMipMapped::kYes ||
Greg Daniel82c6b102020-01-21 10:33:22 -0500271 !caps->isFormatCopyable(result->backendFormat()));
Greg Daniel022b1e02018-07-20 14:54:00 -0400272
Greg Daniel82c6b102020-01-21 10:33:22 -0500273 SkDEBUGCODE(bool expectNoScale = (sampler.filter() != GrSamplerState::Filter::kMipMap &&
274 !sampler.isRepeated()));
Brian Salomon2a943df2018-05-04 13:43:19 -0400275 // Check that the "no scaling expected" case always returns a proxy of the same size as the
276 // producer.
Brian Salomon1a217eb2019-10-24 10:50:36 -0400277 SkASSERT(!result || !expectNoScale || result->dimensions() == this->dimensions());
Greg Daniel82c6b102020-01-21 10:33:22 -0500278
Brian Salomon2a943df2018-05-04 13:43:19 -0400279 return result;
280}
Greg Daniel5f4b09d2018-06-12 16:39:59 -0400281
Greg Daniel82c6b102020-01-21 10:33:22 -0500282std::pair<sk_sp<GrTextureProxy>, GrColorType> GrTextureProducer::refTextureProxy(
283 GrMipMapped willNeedMips) {
Greg Daniel5f4b09d2018-06-12 16:39:59 -0400284 GrSamplerState::Filter filter =
285 GrMipMapped::kNo == willNeedMips ? GrSamplerState::Filter::kNearest
286 : GrSamplerState::Filter::kMipMap;
287 GrSamplerState sampler(GrSamplerState::WrapMode::kClamp, filter);
Greg Daniel8e9b4c42018-07-20 10:30:48 -0400288
Greg Daniel82c6b102020-01-21 10:33:22 -0500289 auto result = this->refTextureProxyForParams(sampler, nullptr);
Greg Daniel8e9b4c42018-07-20 10:30:48 -0400290
Greg Daniel82c6b102020-01-21 10:33:22 -0500291#ifdef SK_DEBUG
292 const GrCaps* caps = this->context()->priv().caps();
293 // Check that the resulting proxy format is compatible with the GrColorType of this producer
294 SkASSERT(!result ||
295 result->isFormatCompressed(caps) ||
296 caps->areColorTypeAndFormatCompatible(this->colorType(), result->backendFormat()));
297#endif
Greg Daniel5f4b09d2018-06-12 16:39:59 -0400298
Greg Daniel82c6b102020-01-21 10:33:22 -0500299 return {result, this->colorType()};
Greg Daniel5f4b09d2018-06-12 16:39:59 -0400300}