blob: 87c06338d86f38981fb63d5554484e5ce7cbf3fd [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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/private/GrRecordingContext.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -05009#include "src/core/SkMipMap.h"
10#include "src/core/SkRectPriv.h"
11#include "src/gpu/GrClip.h"
12#include "src/gpu/GrContextPriv.h"
13#include "src/gpu/GrProxyProvider.h"
14#include "src/gpu/GrRecordingContextPriv.h"
15#include "src/gpu/GrRenderTargetContext.h"
16#include "src/gpu/GrTextureProducer.h"
Greg Danielf91aeb22019-06-18 09:58:02 -040017#include "src/gpu/GrTextureProxy.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050018#include "src/gpu/SkGr.h"
19#include "src/gpu/effects/GrBicubicEffect.h"
20#include "src/gpu/effects/GrTextureDomain.h"
21#include "src/gpu/effects/generated/GrSimpleTextureEffect.h"
Brian Osmane8e54582016-11-28 10:06:27 -050022
Robert Phillips9338c602019-02-19 12:52:29 -050023sk_sp<GrTextureProxy> GrTextureProducer::CopyOnGpu(GrRecordingContext* context,
Robert Phillipsb66b42f2017-03-14 08:53:02 -040024 sk_sp<GrTextureProxy> inputProxy,
Brian Salomond6287472019-06-24 15:50:07 -040025 GrColorType colorType,
Greg Daniele1da1d92017-10-06 15:59:27 -040026 const CopyParams& copyParams,
27 bool dstWillRequireMipMaps) {
Robert Phillipsb66b42f2017-03-14 08:53:02 -040028 SkASSERT(context);
29
Jim Van Verth1676cb92019-01-15 13:24:45 -050030 GrPixelConfig config = GrMakePixelConfigUncompressed(inputProxy->config());
31
Robert Phillipsb66b42f2017-03-14 08:53:02 -040032 const SkRect dstRect = SkRect::MakeIWH(copyParams.fWidth, copyParams.fHeight);
Greg Daniel45d63032017-10-30 13:41:26 -040033 GrMipMapped mipMapped = dstWillRequireMipMaps ? GrMipMapped::kYes : GrMipMapped::kNo;
Robert Phillipsb66b42f2017-03-14 08:53:02 -040034
Greg Daniel09c94002018-06-08 22:11:51 +000035 SkRect localRect = SkRect::MakeWH(inputProxy->width(), inputProxy->height());
36
37 bool needsDomain = false;
38 bool resizing = false;
39 if (copyParams.fFilter != GrSamplerState::Filter::kNearest) {
40 bool resizing = localRect.width() != dstRect.width() ||
41 localRect.height() != dstRect.height();
42 needsDomain = resizing && !GrProxyProvider::IsFunctionallyExact(inputProxy.get());
43 }
44
45 if (copyParams.fFilter == GrSamplerState::Filter::kNearest && !needsDomain && !resizing &&
46 dstWillRequireMipMaps) {
47 sk_sp<GrTextureProxy> proxy = GrCopyBaseMipMapToTextureProxy(context, inputProxy.get());
48 if (proxy) {
49 return proxy;
50 }
51 }
52
Greg Daniel4065d452018-11-16 15:43:41 -050053 GrBackendFormat format = inputProxy->backendFormat().makeTexture2D();
54 if (!format.isValid()) {
55 return nullptr;
56 }
57
Robert Phillips0c4b7b12018-03-06 08:20:37 -050058 sk_sp<GrRenderTargetContext> copyRTC =
Brian Salomond6287472019-06-24 15:50:07 -040059 context->priv().makeDeferredRenderTargetContextWithFallback(
60 format, SkBackingFit::kExact, dstRect.width(), dstRect.height(), config,
61 colorType, nullptr, 1, mipMapped, inputProxy->origin());
Robert Phillipsb66b42f2017-03-14 08:53:02 -040062 if (!copyRTC) {
63 return nullptr;
64 }
65
66 GrPaint paint;
Robert Phillipsb66b42f2017-03-14 08:53:02 -040067
68 if (needsDomain) {
69 const SkRect domain = localRect.makeInset(0.5f, 0.5f);
70 // This would cause us to read values from outside the subset. Surely, the caller knows
71 // better!
Brian Salomon2bbdcc42017-09-07 12:36:34 -040072 SkASSERT(copyParams.fFilter != GrSamplerState::Filter::kMipMap);
Robert Phillipsb66b42f2017-03-14 08:53:02 -040073 paint.addColorFragmentProcessor(
Brian Osman2240be92017-10-18 13:15:13 -040074 GrTextureDomainEffect::Make(std::move(inputProxy), SkMatrix::I(), domain,
75 GrTextureDomain::kClamp_Mode, copyParams.fFilter));
Robert Phillipsb66b42f2017-03-14 08:53:02 -040076 } else {
Brian Salomon2bbdcc42017-09-07 12:36:34 -040077 GrSamplerState samplerState(GrSamplerState::WrapMode::kClamp, copyParams.fFilter);
Brian Osman2240be92017-10-18 13:15:13 -040078 paint.addColorTextureProcessor(std::move(inputProxy), SkMatrix::I(), samplerState);
Robert Phillipsb66b42f2017-03-14 08:53:02 -040079 }
80 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
81
82 copyRTC->fillRectToRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), dstRect,
83 localRect);
84 return copyRTC->asTextureProxyRef();
85}
86
Brian Osmane8e54582016-11-28 10:06:27 -050087/** Determines whether a texture domain is necessary and if so what domain to use. There are two
88 * rectangles to consider:
Robert Phillips3798c862017-03-27 11:08:16 -040089 * - The first is the content area specified by the texture adjuster (i.e., textureContentArea).
90 * We can *never* allow filtering to cause bleed of pixels outside this rectangle.
91 * - The second rectangle is the constraint rectangle (i.e., constraintRect), which is known to
92 * be contained by the content area. The filterConstraint specifies whether we are allowed to
93 * bleed across this rect.
Brian Osmane8e54582016-11-28 10:06:27 -050094 *
95 * We want to avoid using a domain if possible. We consider the above rectangles, the filter type,
96 * and whether the coords generated by the draw would all fall within the constraint rect. If the
97 * latter is true we only need to consider whether the filter would extend beyond the rects.
98 */
99GrTextureProducer::DomainMode GrTextureProducer::DetermineDomainMode(
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400100 const SkRect& constraintRect,
101 FilterConstraint filterConstraint,
102 bool coordsLimitedToConstraintRect,
103 GrTextureProxy* proxy,
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400104 const GrSamplerState::Filter* filterModeOrNullForBicubic,
105 SkRect* domainRect) {
Robert Phillips51e7ca32017-03-27 10:14:08 -0400106 const SkIRect proxyBounds = SkIRect::MakeWH(proxy->width(), proxy->height());
107
108 SkASSERT(proxyBounds.contains(constraintRect));
Robert Phillips51e7ca32017-03-27 10:14:08 -0400109
Robert Phillips1afd4cd2018-01-08 13:40:32 -0500110 const bool proxyIsExact = GrProxyProvider::IsFunctionallyExact(proxy);
Brian Salomon4df00922017-09-07 16:34:11 +0000111
Robert Phillips51e7ca32017-03-27 10:14:08 -0400112 // If the constraint rectangle contains the whole proxy then no need for a domain.
113 if (constraintRect.contains(proxyBounds) && proxyIsExact) {
114 return kNoDomain_DomainMode;
115 }
116
Robert Phillips51e7ca32017-03-27 10:14:08 -0400117 bool restrictFilterToRect = (filterConstraint == GrTextureProducer::kYes_FilterConstraint);
118
119 // If we can filter outside the constraint rect, and there is no non-content area of the
120 // proxy, and we aren't going to generate sample coords outside the constraint rect then we
121 // don't need a domain.
Greg Danielc77085d2017-11-01 16:38:48 -0400122 if (!restrictFilterToRect && proxyIsExact && coordsLimitedToConstraintRect) {
Robert Phillips51e7ca32017-03-27 10:14:08 -0400123 return kNoDomain_DomainMode;
124 }
125
126 // Get the domain inset based on sampling mode (or bail if mipped)
Brian Salomon4df00922017-09-07 16:34:11 +0000127 SkScalar filterHalfWidth = 0.f;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400128 if (filterModeOrNullForBicubic) {
129 switch (*filterModeOrNullForBicubic) {
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400130 case GrSamplerState::Filter::kNearest:
Robert Phillips51e7ca32017-03-27 10:14:08 -0400131 if (coordsLimitedToConstraintRect) {
132 return kNoDomain_DomainMode;
Brian Salomon4df00922017-09-07 16:34:11 +0000133 } else {
134 filterHalfWidth = 0.f;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400135 }
136 break;
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400137 case GrSamplerState::Filter::kBilerp:
Brian Salomon4df00922017-09-07 16:34:11 +0000138 filterHalfWidth = .5f;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400139 break;
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400140 case GrSamplerState::Filter::kMipMap:
Greg Danielc77085d2017-11-01 16:38:48 -0400141 if (restrictFilterToRect || !proxyIsExact) {
Brian Salomon4df00922017-09-07 16:34:11 +0000142 // No domain can save us here.
143 return kTightCopy_DomainMode;
144 }
145 return kNoDomain_DomainMode;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400146 }
Brian Salomon4df00922017-09-07 16:34:11 +0000147 } else {
148 // bicubic does nearest filtering internally.
149 filterHalfWidth = 1.5f;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400150 }
151
152 // Both bilerp and bicubic use bilinear filtering and so need to be clamped to the center
153 // of the edge texel. Pinning to the texel center has no impact on nearest mode and MIP-maps
154
155 static const SkScalar kDomainInset = 0.5f;
156 // Figure out the limits of pixels we're allowed to sample from.
157 // Unless we know the amount of outset and the texture matrix we have to conservatively enforce
158 // the domain.
159 if (restrictFilterToRect) {
160 *domainRect = constraintRect.makeInset(kDomainInset, kDomainInset);
Greg Danielc77085d2017-11-01 16:38:48 -0400161 } else if (!proxyIsExact) {
162 // If we got here then: proxy is not exact, the coords are limited to the
Brian Salomon4df00922017-09-07 16:34:11 +0000163 // constraint rect, and we're allowed to filter across the constraint rect boundary. So
Greg Danielc77085d2017-11-01 16:38:48 -0400164 // we check whether the filter would reach across the edge of the proxy.
Brian Salomon4df00922017-09-07 16:34:11 +0000165 // We will only set the sides that are required.
166
Mike Reed274218e2018-01-08 15:05:02 -0500167 *domainRect = SkRectPriv::MakeLargest();
Brian Salomon4df00922017-09-07 16:34:11 +0000168 if (coordsLimitedToConstraintRect) {
169 // We may be able to use the fact that the texture coords are limited to the constraint
170 // rect in order to avoid having to add a domain.
171 bool needContentAreaConstraint = false;
Greg Danielc77085d2017-11-01 16:38:48 -0400172 if (proxyBounds.fRight - filterHalfWidth < constraintRect.fRight) {
173 domainRect->fRight = proxyBounds.fRight - kDomainInset;
Brian Salomon4df00922017-09-07 16:34:11 +0000174 needContentAreaConstraint = true;
175 }
Greg Danielc77085d2017-11-01 16:38:48 -0400176 if (proxyBounds.fBottom - filterHalfWidth < constraintRect.fBottom) {
177 domainRect->fBottom = proxyBounds.fBottom - kDomainInset;
Brian Salomon4df00922017-09-07 16:34:11 +0000178 needContentAreaConstraint = true;
179 }
180 if (!needContentAreaConstraint) {
181 return kNoDomain_DomainMode;
182 }
183 } else {
184 // Our sample coords for the texture are allowed to be outside the constraintRect so we
185 // don't consider it when computing the domain.
Greg Danielc77085d2017-11-01 16:38:48 -0400186 domainRect->fRight = proxyBounds.fRight - kDomainInset;
187 domainRect->fBottom = proxyBounds.fBottom - kDomainInset;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400188 }
Brian Salomon4df00922017-09-07 16:34:11 +0000189 } else {
190 return kNoDomain_DomainMode;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400191 }
Brian Salomon4df00922017-09-07 16:34:11 +0000192
193 if (domainRect->fLeft > domainRect->fRight) {
194 domainRect->fLeft = domainRect->fRight = SkScalarAve(domainRect->fLeft, domainRect->fRight);
195 }
196 if (domainRect->fTop > domainRect->fBottom) {
197 domainRect->fTop = domainRect->fBottom = SkScalarAve(domainRect->fTop, domainRect->fBottom);
198 }
199 return kDomain_DomainMode;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400200}
201
Michael Ludwigddeed372019-02-20 16:50:10 -0500202std::unique_ptr<GrFragmentProcessor> GrTextureProducer::createFragmentProcessorForDomainAndFilter(
Brian Salomonaff329b2017-08-11 09:40:37 -0400203 sk_sp<GrTextureProxy> proxy,
Brian Salomonaff329b2017-08-11 09:40:37 -0400204 const SkMatrix& textureMatrix,
205 DomainMode domainMode,
206 const SkRect& domain,
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400207 const GrSamplerState::Filter* filterOrNullForBicubic) {
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400208 SkASSERT(kTightCopy_DomainMode != domainMode);
Michael Ludwigddeed372019-02-20 16:50:10 -0500209 bool clampToBorderSupport = fContext->priv().caps()->clampToBorderSupport();
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400210 if (filterOrNullForBicubic) {
Michael Ludwigddeed372019-02-20 16:50:10 -0500211 if (kDomain_DomainMode == domainMode || (fDomainNeedsDecal && !clampToBorderSupport)) {
212 GrTextureDomain::Mode wrapMode = fDomainNeedsDecal ? GrTextureDomain::kDecal_Mode
213 : GrTextureDomain::kClamp_Mode;
Brian Osman2240be92017-10-18 13:15:13 -0400214 return GrTextureDomainEffect::Make(std::move(proxy), textureMatrix, domain,
Michael Ludwigddeed372019-02-20 16:50:10 -0500215 wrapMode, *filterOrNullForBicubic);
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400216 } else {
Michael Ludwigddeed372019-02-20 16:50:10 -0500217 GrSamplerState::WrapMode wrapMode =
218 fDomainNeedsDecal ? GrSamplerState::WrapMode::kClampToBorder
219 : GrSamplerState::WrapMode::kClamp;
220 GrSamplerState samplerState(wrapMode, *filterOrNullForBicubic);
Brian Osman2240be92017-10-18 13:15:13 -0400221 return GrSimpleTextureEffect::Make(std::move(proxy), textureMatrix, samplerState);
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400222 }
223 } else {
Michael Ludwigddeed372019-02-20 16:50:10 -0500224 static const GrSamplerState::WrapMode kClampClamp[] = {
225 GrSamplerState::WrapMode::kClamp, GrSamplerState::WrapMode::kClamp};
226 static const GrSamplerState::WrapMode kDecalDecal[] = {
227 GrSamplerState::WrapMode::kClampToBorder, GrSamplerState::WrapMode::kClampToBorder};
228
Brian Salomona86fc7a2019-05-28 20:42:58 -0400229 static constexpr auto kDir = GrBicubicEffect::Direction::kXY;
Michael Ludwigddeed372019-02-20 16:50:10 -0500230 if (kDomain_DomainMode == domainMode || (fDomainNeedsDecal && !clampToBorderSupport)) {
231 GrTextureDomain::Mode wrapMode = fDomainNeedsDecal ? GrTextureDomain::kDecal_Mode
232 : GrTextureDomain::kClamp_Mode;
Brian Salomona86fc7a2019-05-28 20:42:58 -0400233 return GrBicubicEffect::Make(std::move(proxy), textureMatrix, kClampClamp, wrapMode,
Brian Salomon1127c0b2019-06-13 20:22:10 +0000234 wrapMode, kDir, this->alphaType(),
Michael Ludwigddeed372019-02-20 16:50:10 -0500235 kDomain_DomainMode == domainMode ? &domain : nullptr);
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400236 } else {
Michael Ludwigddeed372019-02-20 16:50:10 -0500237 return GrBicubicEffect::Make(std::move(proxy), textureMatrix,
Brian Salomon1127c0b2019-06-13 20:22:10 +0000238 fDomainNeedsDecal ? kDecalDecal : kClampClamp, kDir,
239 this->alphaType());
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400240 }
241 }
242}
Brian Salomon2a943df2018-05-04 13:43:19 -0400243
244sk_sp<GrTextureProxy> GrTextureProducer::refTextureProxyForParams(
Michael Ludwigddeed372019-02-20 16:50:10 -0500245 const GrSamplerState::Filter* filterOrNullForBicubic,
246 SkScalar scaleAdjust[2]) {
247 GrSamplerState sampler; // Default is nearest + clamp
248 if (filterOrNullForBicubic) {
249 sampler.setFilterMode(*filterOrNullForBicubic);
250 }
251 if (fDomainNeedsDecal) {
252 // Assuming hardware support, switch to clamp-to-border instead of clamp
253 if (fContext->priv().caps()->clampToBorderSupport()) {
254 sampler.setWrapModeX(GrSamplerState::WrapMode::kClampToBorder);
255 sampler.setWrapModeY(GrSamplerState::WrapMode::kClampToBorder);
256 }
257 }
258 return this->refTextureProxyForParams(sampler, scaleAdjust);
259}
260
261sk_sp<GrTextureProxy> GrTextureProducer::refTextureProxyForParams(
Brian Salomon2a943df2018-05-04 13:43:19 -0400262 const GrSamplerState& sampler,
Brian Salomon2a943df2018-05-04 13:43:19 -0400263 SkScalar scaleAdjust[2]) {
264 // Check that the caller pre-initialized scaleAdjust
265 SkASSERT(!scaleAdjust || (scaleAdjust[0] == 1 && scaleAdjust[1] == 1));
266 // Check that if the caller passed nullptr for scaleAdjust that we're in the case where there
267 // can be no scaling.
268 SkDEBUGCODE(bool expectNoScale = (sampler.filter() != GrSamplerState::Filter::kMipMap &&
269 !sampler.isRepeated()));
270 SkASSERT(scaleAdjust || expectNoScale);
Greg Daniel8e9b4c42018-07-20 10:30:48 -0400271
272 int mipCount = SkMipMap::ComputeLevelCount(this->width(), this->height());
273 bool willBeMipped = GrSamplerState::Filter::kMipMap == sampler.filter() && mipCount &&
Robert Phillips9338c602019-02-19 12:52:29 -0500274 this->context()->priv().caps()->mipMapSupport();
Greg Daniel8e9b4c42018-07-20 10:30:48 -0400275
Brian Osman6064e1c2018-10-19 14:27:54 -0400276 auto result = this->onRefTextureProxyForParams(sampler, willBeMipped, scaleAdjust);
Brian Salomon2a943df2018-05-04 13:43:19 -0400277
Greg Daniel022b1e02018-07-20 14:54:00 -0400278 // Check to make sure that if we say the texture willBeMipped that the returned texture has mip
279 // maps, unless the config is not copyable.
280 SkASSERT(!result || !willBeMipped || result->mipMapped() == GrMipMapped::kYes ||
Robert Phillips9338c602019-02-19 12:52:29 -0500281 !this->context()->priv().caps()->isConfigCopyable(result->config()));
Greg Daniel022b1e02018-07-20 14:54:00 -0400282
Brian Salomon2a943df2018-05-04 13:43:19 -0400283 // Check that the "no scaling expected" case always returns a proxy of the same size as the
284 // producer.
285 SkASSERT(!result || !expectNoScale ||
286 (result->width() == this->width() && result->height() == this->height()));
287 return result;
288}
Greg Daniel5f4b09d2018-06-12 16:39:59 -0400289
Brian Osman6064e1c2018-10-19 14:27:54 -0400290sk_sp<GrTextureProxy> GrTextureProducer::refTextureProxy(GrMipMapped willNeedMips) {
Greg Daniel5f4b09d2018-06-12 16:39:59 -0400291 GrSamplerState::Filter filter =
292 GrMipMapped::kNo == willNeedMips ? GrSamplerState::Filter::kNearest
293 : GrSamplerState::Filter::kMipMap;
294 GrSamplerState sampler(GrSamplerState::WrapMode::kClamp, filter);
Greg Daniel8e9b4c42018-07-20 10:30:48 -0400295
296 int mipCount = SkMipMap::ComputeLevelCount(this->width(), this->height());
297 bool willBeMipped = GrSamplerState::Filter::kMipMap == sampler.filter() && mipCount &&
Robert Phillips9338c602019-02-19 12:52:29 -0500298 this->context()->priv().caps()->mipMapSupport();
Greg Daniel8e9b4c42018-07-20 10:30:48 -0400299
Brian Osman6064e1c2018-10-19 14:27:54 -0400300 auto result = this->onRefTextureProxyForParams(sampler, willBeMipped, nullptr);
Greg Daniel5f4b09d2018-06-12 16:39:59 -0400301
Greg Daniel022b1e02018-07-20 14:54:00 -0400302 // Check to make sure that if we say the texture willBeMipped that the returned texture has mip
303 // maps, unless the config is not copyable.
304 SkASSERT(!result || !willBeMipped || result->mipMapped() == GrMipMapped::kYes ||
Robert Phillips9338c602019-02-19 12:52:29 -0500305 !this->context()->priv().caps()->isConfigCopyable(result->config()));
Greg Daniel022b1e02018-07-20 14:54:00 -0400306
Greg Daniel5f4b09d2018-06-12 16:39:59 -0400307 // Check that no scaling occured and we returned a proxy of the same size as the producer.
308 SkASSERT(!result || (result->width() == this->width() && result->height() == this->height()));
309 return result;
310}