blob: e61f1e8e0b9797c8dc2947458f864c0a92c4a890 [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
Greg Danielcc104db2020-02-03 14:17:08 -050024GrSurfaceProxyView GrTextureProducer::CopyOnGpu(GrRecordingContext* context,
25 GrSurfaceProxyView inputView,
26 GrColorType colorType,
27 const CopyParams& copyParams,
28 bool dstWillRequireMipMaps) {
Robert Phillipsb66b42f2017-03-14 08:53:02 -040029 SkASSERT(context);
Greg Danielcc104db2020-02-03 14:17:08 -050030 SkASSERT(inputView.asTextureProxy());
Robert Phillipsb66b42f2017-03-14 08:53:02 -040031
Brian Salomon1a217eb2019-10-24 10:50:36 -040032 const SkRect dstRect = SkRect::Make(copyParams.fDimensions);
Greg Daniel45d63032017-10-30 13:41:26 -040033 GrMipMapped mipMapped = dstWillRequireMipMaps ? GrMipMapped::kYes : GrMipMapped::kNo;
Robert Phillipsb66b42f2017-03-14 08:53:02 -040034
Greg Danielcc104db2020-02-03 14:17:08 -050035 GrSurfaceProxy* proxy = inputView.proxy();
36 SkRect localRect = proxy->getBoundsRect();
Greg Daniel09c94002018-06-08 22:11:51 +000037
Greg Daniel09c94002018-06-08 22:11:51 +000038 bool resizing = false;
39 if (copyParams.fFilter != GrSamplerState::Filter::kNearest) {
Brian Salomonca6b2f42020-01-24 11:31:21 -050040 resizing = localRect.width() != dstRect.width() || localRect.height() != dstRect.height();
Greg Daniel09c94002018-06-08 22:11:51 +000041 }
42
Brian Salomonca6b2f42020-01-24 11:31:21 -050043 if (copyParams.fFilter == GrSamplerState::Filter::kNearest && !resizing &&
Greg Daniel09c94002018-06-08 22:11:51 +000044 dstWillRequireMipMaps) {
Greg Danielcc104db2020-02-03 14:17:08 -050045 GrSurfaceProxyView view = GrCopyBaseMipMapToTextureProxy(context, proxy, inputView.origin(),
46 colorType);
47 if (view.proxy()) {
48 return view;
Greg Daniel09c94002018-06-08 22:11:51 +000049 }
50 }
51
Greg Daniele20fcad2020-01-08 11:52:34 -050052 auto copyRTC = GrRenderTargetContext::MakeWithFallback(
53 context, colorType, nullptr, SkBackingFit::kExact, copyParams.fDimensions, 1,
Greg Danielcc104db2020-02-03 14:17:08 -050054 mipMapped, proxy->isProtected(), inputView.origin());
Robert Phillipsb66b42f2017-03-14 08:53:02 -040055 if (!copyRTC) {
Greg Danielcc104db2020-02-03 14:17:08 -050056 return {};
Robert Phillipsb66b42f2017-03-14 08:53:02 -040057 }
58
Brian Salomonca6b2f42020-01-24 11:31:21 -050059 const auto& caps = *context->priv().caps();
Robert Phillipsb66b42f2017-03-14 08:53:02 -040060 GrPaint paint;
Robert Phillipsb66b42f2017-03-14 08:53:02 -040061
Brian Salomonca6b2f42020-01-24 11:31:21 -050062 GrSamplerState sampler(GrSamplerState::WrapMode::kClamp, copyParams.fFilter);
Greg Danielcc104db2020-02-03 14:17:08 -050063 auto boundsRect = SkIRect::MakeSize(proxy->dimensions());
64 auto fp = GrTextureEffect::MakeTexelSubset(inputView.detachProxy(), kUnknown_SkAlphaType,
Brian Salomonca6b2f42020-01-24 11:31:21 -050065 SkMatrix::I(), sampler, boundsRect, localRect, caps);
Brian Salomon7eabfe82019-12-02 14:20:20 -050066 paint.addColorFragmentProcessor(std::move(fp));
Robert Phillipsb66b42f2017-03-14 08:53:02 -040067 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
68
69 copyRTC->fillRectToRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), dstRect,
70 localRect);
Greg Danielcc104db2020-02-03 14:17:08 -050071 return copyRTC->readSurfaceView();
Robert Phillipsb66b42f2017-03-14 08:53:02 -040072}
73
Brian Osmane8e54582016-11-28 10:06:27 -050074/** Determines whether a texture domain is necessary and if so what domain to use. There are two
75 * rectangles to consider:
Robert Phillips3798c862017-03-27 11:08:16 -040076 * - The first is the content area specified by the texture adjuster (i.e., textureContentArea).
77 * We can *never* allow filtering to cause bleed of pixels outside this rectangle.
78 * - The second rectangle is the constraint rectangle (i.e., constraintRect), which is known to
79 * be contained by the content area. The filterConstraint specifies whether we are allowed to
80 * bleed across this rect.
Brian Osmane8e54582016-11-28 10:06:27 -050081 *
82 * We want to avoid using a domain if possible. We consider the above rectangles, the filter type,
83 * and whether the coords generated by the draw would all fall within the constraint rect. If the
84 * latter is true we only need to consider whether the filter would extend beyond the rects.
85 */
86GrTextureProducer::DomainMode GrTextureProducer::DetermineDomainMode(
Brian Salomon2bbdcc42017-09-07 12:36:34 -040087 const SkRect& constraintRect,
88 FilterConstraint filterConstraint,
89 bool coordsLimitedToConstraintRect,
Greg Danielcc104db2020-02-03 14:17:08 -050090 GrSurfaceProxy* proxy,
Brian Salomon2bbdcc42017-09-07 12:36:34 -040091 const GrSamplerState::Filter* filterModeOrNullForBicubic,
92 SkRect* domainRect) {
Brian Salomon9f2b86c2019-10-22 10:37:46 -040093 const SkIRect proxyBounds = SkIRect::MakeSize(proxy->dimensions());
Robert Phillips51e7ca32017-03-27 10:14:08 -040094
95 SkASSERT(proxyBounds.contains(constraintRect));
Robert Phillips51e7ca32017-03-27 10:14:08 -040096
Brian Salomon5c60b752019-12-13 15:03:43 -050097 const bool proxyIsExact = proxy->isFunctionallyExact();
Brian Salomon4df00922017-09-07 16:34:11 +000098
Robert Phillips51e7ca32017-03-27 10:14:08 -040099 // If the constraint rectangle contains the whole proxy then no need for a domain.
100 if (constraintRect.contains(proxyBounds) && proxyIsExact) {
101 return kNoDomain_DomainMode;
102 }
103
Robert Phillips51e7ca32017-03-27 10:14:08 -0400104 bool restrictFilterToRect = (filterConstraint == GrTextureProducer::kYes_FilterConstraint);
105
106 // If we can filter outside the constraint rect, and there is no non-content area of the
107 // proxy, and we aren't going to generate sample coords outside the constraint rect then we
108 // don't need a domain.
Greg Danielc77085d2017-11-01 16:38:48 -0400109 if (!restrictFilterToRect && proxyIsExact && coordsLimitedToConstraintRect) {
Robert Phillips51e7ca32017-03-27 10:14:08 -0400110 return kNoDomain_DomainMode;
111 }
112
113 // Get the domain inset based on sampling mode (or bail if mipped)
Brian Salomon4df00922017-09-07 16:34:11 +0000114 SkScalar filterHalfWidth = 0.f;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400115 if (filterModeOrNullForBicubic) {
116 switch (*filterModeOrNullForBicubic) {
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400117 case GrSamplerState::Filter::kNearest:
Robert Phillips51e7ca32017-03-27 10:14:08 -0400118 if (coordsLimitedToConstraintRect) {
119 return kNoDomain_DomainMode;
Brian Salomon4df00922017-09-07 16:34:11 +0000120 } else {
121 filterHalfWidth = 0.f;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400122 }
123 break;
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400124 case GrSamplerState::Filter::kBilerp:
Brian Salomon4df00922017-09-07 16:34:11 +0000125 filterHalfWidth = .5f;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400126 break;
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400127 case GrSamplerState::Filter::kMipMap:
Greg Danielc77085d2017-11-01 16:38:48 -0400128 if (restrictFilterToRect || !proxyIsExact) {
Brian Salomon4df00922017-09-07 16:34:11 +0000129 // No domain can save us here.
130 return kTightCopy_DomainMode;
131 }
132 return kNoDomain_DomainMode;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400133 }
Brian Salomon4df00922017-09-07 16:34:11 +0000134 } else {
135 // bicubic does nearest filtering internally.
136 filterHalfWidth = 1.5f;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400137 }
138
139 // Both bilerp and bicubic use bilinear filtering and so need to be clamped to the center
140 // of the edge texel. Pinning to the texel center has no impact on nearest mode and MIP-maps
141
142 static const SkScalar kDomainInset = 0.5f;
143 // Figure out the limits of pixels we're allowed to sample from.
144 // Unless we know the amount of outset and the texture matrix we have to conservatively enforce
145 // the domain.
146 if (restrictFilterToRect) {
147 *domainRect = constraintRect.makeInset(kDomainInset, kDomainInset);
Greg Danielc77085d2017-11-01 16:38:48 -0400148 } else if (!proxyIsExact) {
149 // If we got here then: proxy is not exact, the coords are limited to the
Brian Salomon4df00922017-09-07 16:34:11 +0000150 // constraint rect, and we're allowed to filter across the constraint rect boundary. So
Greg Danielc77085d2017-11-01 16:38:48 -0400151 // we check whether the filter would reach across the edge of the proxy.
Brian Salomon4df00922017-09-07 16:34:11 +0000152 // We will only set the sides that are required.
153
Mike Reed274218e2018-01-08 15:05:02 -0500154 *domainRect = SkRectPriv::MakeLargest();
Brian Salomon4df00922017-09-07 16:34:11 +0000155 if (coordsLimitedToConstraintRect) {
156 // We may be able to use the fact that the texture coords are limited to the constraint
157 // rect in order to avoid having to add a domain.
158 bool needContentAreaConstraint = false;
Greg Danielc77085d2017-11-01 16:38:48 -0400159 if (proxyBounds.fRight - filterHalfWidth < constraintRect.fRight) {
160 domainRect->fRight = proxyBounds.fRight - kDomainInset;
Brian Salomon4df00922017-09-07 16:34:11 +0000161 needContentAreaConstraint = true;
162 }
Greg Danielc77085d2017-11-01 16:38:48 -0400163 if (proxyBounds.fBottom - filterHalfWidth < constraintRect.fBottom) {
164 domainRect->fBottom = proxyBounds.fBottom - kDomainInset;
Brian Salomon4df00922017-09-07 16:34:11 +0000165 needContentAreaConstraint = true;
166 }
167 if (!needContentAreaConstraint) {
168 return kNoDomain_DomainMode;
169 }
170 } else {
171 // Our sample coords for the texture are allowed to be outside the constraintRect so we
172 // don't consider it when computing the domain.
Greg Danielc77085d2017-11-01 16:38:48 -0400173 domainRect->fRight = proxyBounds.fRight - kDomainInset;
174 domainRect->fBottom = proxyBounds.fBottom - kDomainInset;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400175 }
Brian Salomon4df00922017-09-07 16:34:11 +0000176 } else {
177 return kNoDomain_DomainMode;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400178 }
Brian Salomon4df00922017-09-07 16:34:11 +0000179
180 if (domainRect->fLeft > domainRect->fRight) {
181 domainRect->fLeft = domainRect->fRight = SkScalarAve(domainRect->fLeft, domainRect->fRight);
182 }
183 if (domainRect->fTop > domainRect->fBottom) {
184 domainRect->fTop = domainRect->fBottom = SkScalarAve(domainRect->fTop, domainRect->fBottom);
185 }
186 return kDomain_DomainMode;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400187}
188
Michael Ludwigddeed372019-02-20 16:50:10 -0500189std::unique_ptr<GrFragmentProcessor> GrTextureProducer::createFragmentProcessorForDomainAndFilter(
Greg Danielcc104db2020-02-03 14:17:08 -0500190 GrSurfaceProxyView view,
Brian Salomonaff329b2017-08-11 09:40:37 -0400191 const SkMatrix& textureMatrix,
192 DomainMode domainMode,
193 const SkRect& domain,
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400194 const GrSamplerState::Filter* filterOrNullForBicubic) {
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400195 SkASSERT(kTightCopy_DomainMode != domainMode);
Greg Danielcc104db2020-02-03 14:17:08 -0500196 SkASSERT(view.asTextureProxy());
Brian Salomonca6b2f42020-01-24 11:31:21 -0500197 const auto& caps = *fContext->priv().caps();
Brian Salomonfc118442019-11-22 19:09:27 -0500198 SkAlphaType srcAlphaType = this->alphaType();
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400199 if (filterOrNullForBicubic) {
Brian Salomonca6b2f42020-01-24 11:31:21 -0500200 GrSamplerState::WrapMode wrapMode = fDomainNeedsDecal
Brian Salomon7eabfe82019-12-02 14:20:20 -0500201 ? GrSamplerState::WrapMode::kClampToBorder
202 : GrSamplerState::WrapMode::kClamp;
203 GrSamplerState samplerState(wrapMode, *filterOrNullForBicubic);
Brian Salomonca6b2f42020-01-24 11:31:21 -0500204 if (kNoDomain_DomainMode == domainMode) {
Greg Danielcc104db2020-02-03 14:17:08 -0500205 return GrTextureEffect::Make(view.detachProxy(), srcAlphaType, textureMatrix,
Brian Salomonca6b2f42020-01-24 11:31:21 -0500206 samplerState, caps);
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400207 }
Greg Danielcc104db2020-02-03 14:17:08 -0500208 return GrTextureEffect::MakeSubset(view.detachProxy(), srcAlphaType, textureMatrix,
Brian Salomonca6b2f42020-01-24 11:31:21 -0500209 samplerState, domain, caps);
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400210 } else {
Michael Ludwigddeed372019-02-20 16:50:10 -0500211 static const GrSamplerState::WrapMode kClampClamp[] = {
212 GrSamplerState::WrapMode::kClamp, GrSamplerState::WrapMode::kClamp};
213 static const GrSamplerState::WrapMode kDecalDecal[] = {
214 GrSamplerState::WrapMode::kClampToBorder, GrSamplerState::WrapMode::kClampToBorder};
215
Brian Salomona86fc7a2019-05-28 20:42:58 -0400216 static constexpr auto kDir = GrBicubicEffect::Direction::kXY;
Brian Salomonca6b2f42020-01-24 11:31:21 -0500217 bool clampToBorderSupport = caps.clampToBorderSupport();
Michael Ludwigddeed372019-02-20 16:50:10 -0500218 if (kDomain_DomainMode == domainMode || (fDomainNeedsDecal && !clampToBorderSupport)) {
219 GrTextureDomain::Mode wrapMode = fDomainNeedsDecal ? GrTextureDomain::kDecal_Mode
220 : GrTextureDomain::kClamp_Mode;
Greg Danielcc104db2020-02-03 14:17:08 -0500221 return GrBicubicEffect::Make(view.detachProxy(), textureMatrix, kClampClamp, wrapMode,
Brian Salomonfc118442019-11-22 19:09:27 -0500222 wrapMode, kDir, srcAlphaType,
Michael Ludwigddeed372019-02-20 16:50:10 -0500223 kDomain_DomainMode == domainMode ? &domain : nullptr);
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400224 } else {
Greg Danielcc104db2020-02-03 14:17:08 -0500225 return GrBicubicEffect::Make(view.detachProxy(), textureMatrix,
Brian Salomon1127c0b2019-06-13 20:22:10 +0000226 fDomainNeedsDecal ? kDecalDecal : kClampClamp, kDir,
Brian Salomonfc118442019-11-22 19:09:27 -0500227 srcAlphaType);
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400228 }
229 }
230}
Brian Salomon2a943df2018-05-04 13:43:19 -0400231
Greg Danielcc104db2020-02-03 14:17:08 -0500232GrSurfaceProxyView GrTextureProducer::refTextureProxyViewForParams(
Michael Ludwigddeed372019-02-20 16:50:10 -0500233 const GrSamplerState::Filter* filterOrNullForBicubic,
234 SkScalar scaleAdjust[2]) {
235 GrSamplerState sampler; // Default is nearest + clamp
236 if (filterOrNullForBicubic) {
237 sampler.setFilterMode(*filterOrNullForBicubic);
238 }
239 if (fDomainNeedsDecal) {
240 // Assuming hardware support, switch to clamp-to-border instead of clamp
241 if (fContext->priv().caps()->clampToBorderSupport()) {
242 sampler.setWrapModeX(GrSamplerState::WrapMode::kClampToBorder);
243 sampler.setWrapModeY(GrSamplerState::WrapMode::kClampToBorder);
244 }
245 }
Greg Danielcc104db2020-02-03 14:17:08 -0500246 return this->refTextureProxyViewForParams(sampler, scaleAdjust);
Michael Ludwigddeed372019-02-20 16:50:10 -0500247}
248
Greg Danielcc104db2020-02-03 14:17:08 -0500249GrSurfaceProxyView GrTextureProducer::refTextureProxyViewForParams(GrSamplerState sampler,
250 SkScalar scaleAdjust[2]) {
Brian Salomon2a943df2018-05-04 13:43:19 -0400251 // Check that the caller pre-initialized scaleAdjust
252 SkASSERT(!scaleAdjust || (scaleAdjust[0] == 1 && scaleAdjust[1] == 1));
Greg Daniel82c6b102020-01-21 10:33:22 -0500253
254 const GrCaps* caps = this->context()->priv().caps();
Greg Daniel8e9b4c42018-07-20 10:30:48 -0400255
256 int mipCount = SkMipMap::ComputeLevelCount(this->width(), this->height());
257 bool willBeMipped = GrSamplerState::Filter::kMipMap == sampler.filter() && mipCount &&
Greg Daniel82c6b102020-01-21 10:33:22 -0500258 caps->mipMapSupport();
Greg Daniel8e9b4c42018-07-20 10:30:48 -0400259
Greg Danielcc104db2020-02-03 14:17:08 -0500260 auto result = this->onRefTextureProxyViewForParams(sampler, willBeMipped, scaleAdjust);
Brian Salomon2a943df2018-05-04 13:43:19 -0400261
Greg Daniel022b1e02018-07-20 14:54:00 -0400262 // 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.
Greg Danielcc104db2020-02-03 14:17:08 -0500264 SkASSERT(!result.proxy() || !willBeMipped ||
265 result.asTextureProxy()->mipMapped() == GrMipMapped::kYes ||
266 !caps->isFormatCopyable(result.proxy()->backendFormat()));
267
268 SkASSERT(!result.proxy() || result.asTextureProxy());
Greg Daniel022b1e02018-07-20 14:54:00 -0400269
Greg Daniel82c6b102020-01-21 10:33:22 -0500270 SkDEBUGCODE(bool expectNoScale = (sampler.filter() != GrSamplerState::Filter::kMipMap &&
271 !sampler.isRepeated()));
Brian Salomon2a943df2018-05-04 13:43:19 -0400272 // Check that the "no scaling expected" case always returns a proxy of the same size as the
273 // producer.
Greg Danielcc104db2020-02-03 14:17:08 -0500274 SkASSERT(!result.proxy() || !expectNoScale ||
275 result.proxy()->dimensions() == this->dimensions());
Greg Daniel82c6b102020-01-21 10:33:22 -0500276
Brian Salomon2a943df2018-05-04 13:43:19 -0400277 return result;
278}
Greg Daniel5f4b09d2018-06-12 16:39:59 -0400279
Greg Danielcc104db2020-02-03 14:17:08 -0500280std::pair<GrSurfaceProxyView, GrColorType> GrTextureProducer::refTextureProxyView(
Greg Daniel82c6b102020-01-21 10:33:22 -0500281 GrMipMapped willNeedMips) {
Greg Daniel5f4b09d2018-06-12 16:39:59 -0400282 GrSamplerState::Filter filter =
283 GrMipMapped::kNo == willNeedMips ? GrSamplerState::Filter::kNearest
284 : GrSamplerState::Filter::kMipMap;
285 GrSamplerState sampler(GrSamplerState::WrapMode::kClamp, filter);
Greg Daniel8e9b4c42018-07-20 10:30:48 -0400286
Greg Danielcc104db2020-02-03 14:17:08 -0500287 auto result = this->refTextureProxyViewForParams(sampler, nullptr);
Greg Daniel8e9b4c42018-07-20 10:30:48 -0400288
Greg Daniel82c6b102020-01-21 10:33:22 -0500289#ifdef SK_DEBUG
290 const GrCaps* caps = this->context()->priv().caps();
291 // Check that the resulting proxy format is compatible with the GrColorType of this producer
Greg Danielcc104db2020-02-03 14:17:08 -0500292 SkASSERT(!result.proxy() ||
293 result.proxy()->isFormatCompressed(caps) ||
294 caps->areColorTypeAndFormatCompatible(this->colorType(),
295 result.proxy()->backendFormat()));
Greg Daniel82c6b102020-01-21 10:33:22 -0500296#endif
Greg Daniel5f4b09d2018-06-12 16:39:59 -0400297
Greg Daniel82c6b102020-01-21 10:33:22 -0500298 return {result, this->colorType()};
Greg Daniel5f4b09d2018-06-12 16:39:59 -0400299}