blob: 55fccc0367ed98062e983b47ec4dc7c87faa366d [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
Brian Salomon7fba2442020-02-20 11:29:18 -050024GrSurfaceProxyView GrTextureProducer::MakeMipMappedCopy(GrRecordingContext* context,
25 GrSurfaceProxyView inputView,
26 GrColorType colorType) {
Robert Phillipsb66b42f2017-03-14 08:53:02 -040027 SkASSERT(context);
Greg Danielcc104db2020-02-03 14:17:08 -050028 SkASSERT(inputView.asTextureProxy());
Robert Phillipsb66b42f2017-03-14 08:53:02 -040029
Greg Danielcc104db2020-02-03 14:17:08 -050030 GrSurfaceProxy* proxy = inputView.proxy();
Brian Salomon7fba2442020-02-20 11:29:18 -050031 SkRect proxyRect = proxy->getBoundsRect();
Greg Daniel09c94002018-06-08 22:11:51 +000032
Brian Salomon7fba2442020-02-20 11:29:18 -050033 GrSurfaceProxyView view =
34 GrCopyBaseMipMapToTextureProxy(context, proxy, inputView.origin(), colorType);
35 if (view) {
36 return view;
Greg Daniel09c94002018-06-08 22:11:51 +000037 }
38
Greg Daniele20fcad2020-01-08 11:52:34 -050039 auto copyRTC = GrRenderTargetContext::MakeWithFallback(
Brian Salomon7fba2442020-02-20 11:29:18 -050040 context, colorType, nullptr, SkBackingFit::kExact, inputView.dimensions(), 1,
41 GrMipMapped::kYes, proxy->isProtected(), inputView.origin());
Robert Phillipsb66b42f2017-03-14 08:53:02 -040042 if (!copyRTC) {
Greg Danielcc104db2020-02-03 14:17:08 -050043 return {};
Robert Phillipsb66b42f2017-03-14 08:53:02 -040044 }
45
46 GrPaint paint;
Brian Salomon7fba2442020-02-20 11:29:18 -050047 auto fp = GrTextureEffect::Make(std::move(inputView), kUnknown_SkAlphaType, SkMatrix::I(),
48 GrSamplerState::Filter::kNearest);
Brian Salomon7eabfe82019-12-02 14:20:20 -050049 paint.addColorFragmentProcessor(std::move(fp));
Robert Phillipsb66b42f2017-03-14 08:53:02 -040050 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
51
Brian Salomon7fba2442020-02-20 11:29:18 -050052 copyRTC->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), proxyRect);
Greg Danielcc104db2020-02-03 14:17:08 -050053 return copyRTC->readSurfaceView();
Robert Phillipsb66b42f2017-03-14 08:53:02 -040054}
55
Brian Osmane8e54582016-11-28 10:06:27 -050056/** Determines whether a texture domain is necessary and if so what domain to use. There are two
57 * rectangles to consider:
Robert Phillips3798c862017-03-27 11:08:16 -040058 * - The first is the content area specified by the texture adjuster (i.e., textureContentArea).
59 * We can *never* allow filtering to cause bleed of pixels outside this rectangle.
60 * - The second rectangle is the constraint rectangle (i.e., constraintRect), which is known to
61 * be contained by the content area. The filterConstraint specifies whether we are allowed to
62 * bleed across this rect.
Brian Osmane8e54582016-11-28 10:06:27 -050063 *
64 * We want to avoid using a domain if possible. We consider the above rectangles, the filter type,
65 * and whether the coords generated by the draw would all fall within the constraint rect. If the
66 * latter is true we only need to consider whether the filter would extend beyond the rects.
67 */
68GrTextureProducer::DomainMode GrTextureProducer::DetermineDomainMode(
Brian Salomon2bbdcc42017-09-07 12:36:34 -040069 const SkRect& constraintRect,
70 FilterConstraint filterConstraint,
71 bool coordsLimitedToConstraintRect,
Greg Danielcc104db2020-02-03 14:17:08 -050072 GrSurfaceProxy* proxy,
Brian Salomon2bbdcc42017-09-07 12:36:34 -040073 const GrSamplerState::Filter* filterModeOrNullForBicubic,
74 SkRect* domainRect) {
Brian Salomon9f2b86c2019-10-22 10:37:46 -040075 const SkIRect proxyBounds = SkIRect::MakeSize(proxy->dimensions());
Robert Phillips51e7ca32017-03-27 10:14:08 -040076
77 SkASSERT(proxyBounds.contains(constraintRect));
Robert Phillips51e7ca32017-03-27 10:14:08 -040078
Brian Salomon5c60b752019-12-13 15:03:43 -050079 const bool proxyIsExact = proxy->isFunctionallyExact();
Brian Salomon4df00922017-09-07 16:34:11 +000080
Robert Phillips51e7ca32017-03-27 10:14:08 -040081 // If the constraint rectangle contains the whole proxy then no need for a domain.
82 if (constraintRect.contains(proxyBounds) && proxyIsExact) {
83 return kNoDomain_DomainMode;
84 }
85
Robert Phillips51e7ca32017-03-27 10:14:08 -040086 bool restrictFilterToRect = (filterConstraint == GrTextureProducer::kYes_FilterConstraint);
87
88 // If we can filter outside the constraint rect, and there is no non-content area of the
89 // proxy, and we aren't going to generate sample coords outside the constraint rect then we
90 // don't need a domain.
Greg Danielc77085d2017-11-01 16:38:48 -040091 if (!restrictFilterToRect && proxyIsExact && coordsLimitedToConstraintRect) {
Robert Phillips51e7ca32017-03-27 10:14:08 -040092 return kNoDomain_DomainMode;
93 }
94
Brian Salomoned729f92020-02-05 12:15:18 -050095 // Get the domain inset based on sampling mode (or bail if mipped). This is used
96 // to evaluate whether we will read outside a non-exact proxy's dimensions.
97 // TODO: Let GrTextureEffect handle this.
Brian Salomon4df00922017-09-07 16:34:11 +000098 SkScalar filterHalfWidth = 0.f;
Robert Phillips51e7ca32017-03-27 10:14:08 -040099 if (filterModeOrNullForBicubic) {
100 switch (*filterModeOrNullForBicubic) {
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400101 case GrSamplerState::Filter::kNearest:
Robert Phillips51e7ca32017-03-27 10:14:08 -0400102 if (coordsLimitedToConstraintRect) {
103 return kNoDomain_DomainMode;
Brian Salomon4df00922017-09-07 16:34:11 +0000104 } else {
105 filterHalfWidth = 0.f;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400106 }
107 break;
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400108 case GrSamplerState::Filter::kBilerp:
Brian Salomon4df00922017-09-07 16:34:11 +0000109 filterHalfWidth = .5f;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400110 break;
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400111 case GrSamplerState::Filter::kMipMap:
Greg Danielc77085d2017-11-01 16:38:48 -0400112 if (restrictFilterToRect || !proxyIsExact) {
Brian Salomon4df00922017-09-07 16:34:11 +0000113 // No domain can save us here.
114 return kTightCopy_DomainMode;
115 }
116 return kNoDomain_DomainMode;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400117 }
Brian Salomon4df00922017-09-07 16:34:11 +0000118 } else {
119 // bicubic does nearest filtering internally.
120 filterHalfWidth = 1.5f;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400121 }
122
Robert Phillips51e7ca32017-03-27 10:14:08 -0400123 if (restrictFilterToRect) {
Brian Salomoned729f92020-02-05 12:15:18 -0500124 *domainRect = constraintRect;
Greg Danielc77085d2017-11-01 16:38:48 -0400125 } else if (!proxyIsExact) {
126 // If we got here then: proxy is not exact, the coords are limited to the
Brian Salomon4df00922017-09-07 16:34:11 +0000127 // constraint rect, and we're allowed to filter across the constraint rect boundary. So
Greg Danielc77085d2017-11-01 16:38:48 -0400128 // we check whether the filter would reach across the edge of the proxy.
Brian Salomon4df00922017-09-07 16:34:11 +0000129 // We will only set the sides that are required.
130
Mike Reed274218e2018-01-08 15:05:02 -0500131 *domainRect = SkRectPriv::MakeLargest();
Brian Salomon4df00922017-09-07 16:34:11 +0000132 if (coordsLimitedToConstraintRect) {
133 // We may be able to use the fact that the texture coords are limited to the constraint
134 // rect in order to avoid having to add a domain.
135 bool needContentAreaConstraint = false;
Greg Danielc77085d2017-11-01 16:38:48 -0400136 if (proxyBounds.fRight - filterHalfWidth < constraintRect.fRight) {
Brian Salomoned729f92020-02-05 12:15:18 -0500137 domainRect->fRight = proxyBounds.fRight;
Brian Salomon4df00922017-09-07 16:34:11 +0000138 needContentAreaConstraint = true;
139 }
Greg Danielc77085d2017-11-01 16:38:48 -0400140 if (proxyBounds.fBottom - filterHalfWidth < constraintRect.fBottom) {
Brian Salomoned729f92020-02-05 12:15:18 -0500141 domainRect->fBottom = proxyBounds.fBottom;
Brian Salomon4df00922017-09-07 16:34:11 +0000142 needContentAreaConstraint = true;
143 }
144 if (!needContentAreaConstraint) {
145 return kNoDomain_DomainMode;
146 }
Robert Phillips51e7ca32017-03-27 10:14:08 -0400147 }
Brian Salomon4df00922017-09-07 16:34:11 +0000148 } else {
149 return kNoDomain_DomainMode;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400150 }
Brian Salomon4df00922017-09-07 16:34:11 +0000151
Brian Salomoned729f92020-02-05 12:15:18 -0500152 if (!filterModeOrNullForBicubic) {
153 // Bicubic doesn't yet rely on GrTextureEffect to do this insetting.
154 domainRect->inset(0.5f, 0.5f);
155 if (domainRect->fLeft > domainRect->fRight) {
156 domainRect->fLeft = domainRect->fRight =
157 SkScalarAve(domainRect->fLeft, domainRect->fRight);
158 }
159 if (domainRect->fTop > domainRect->fBottom) {
160 domainRect->fTop = domainRect->fBottom =
161 SkScalarAve(domainRect->fTop, domainRect->fBottom);
162 }
Brian Salomon4df00922017-09-07 16:34:11 +0000163 }
Brian Salomoned729f92020-02-05 12:15:18 -0500164
Brian Salomon4df00922017-09-07 16:34:11 +0000165 return kDomain_DomainMode;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400166}
167
Michael Ludwigddeed372019-02-20 16:50:10 -0500168std::unique_ptr<GrFragmentProcessor> GrTextureProducer::createFragmentProcessorForDomainAndFilter(
Greg Danielcc104db2020-02-03 14:17:08 -0500169 GrSurfaceProxyView view,
Brian Salomonaff329b2017-08-11 09:40:37 -0400170 const SkMatrix& textureMatrix,
171 DomainMode domainMode,
172 const SkRect& domain,
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400173 const GrSamplerState::Filter* filterOrNullForBicubic) {
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400174 SkASSERT(kTightCopy_DomainMode != domainMode);
Greg Danielcc104db2020-02-03 14:17:08 -0500175 SkASSERT(view.asTextureProxy());
Brian Salomonca6b2f42020-01-24 11:31:21 -0500176 const auto& caps = *fContext->priv().caps();
Brian Salomonfc118442019-11-22 19:09:27 -0500177 SkAlphaType srcAlphaType = this->alphaType();
Brian Salomond0d033a2020-02-18 16:59:28 -0500178 auto wm = fDomainNeedsDecal ? GrSamplerState::WrapMode::kClampToBorder
179 : GrSamplerState::WrapMode::kClamp;
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400180 if (filterOrNullForBicubic) {
Brian Salomond0d033a2020-02-18 16:59:28 -0500181 GrSamplerState samplerState(wm, *filterOrNullForBicubic);
Brian Salomonca6b2f42020-01-24 11:31:21 -0500182 if (kNoDomain_DomainMode == domainMode) {
Greg Danield2ccbb52020-02-05 10:45:39 -0500183 return GrTextureEffect::Make(std::move(view), srcAlphaType, textureMatrix, samplerState,
184 caps);
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400185 }
Greg Danield2ccbb52020-02-05 10:45:39 -0500186 return GrTextureEffect::MakeSubset(std::move(view), srcAlphaType, textureMatrix,
Brian Salomonca6b2f42020-01-24 11:31:21 -0500187 samplerState, domain, caps);
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400188 } else {
Michael Ludwigddeed372019-02-20 16:50:10 -0500189
Brian Salomona86fc7a2019-05-28 20:42:58 -0400190 static constexpr auto kDir = GrBicubicEffect::Direction::kXY;
Brian Salomond0d033a2020-02-18 16:59:28 -0500191 const auto& caps = *fContext->priv().caps();
192 if (kDomain_DomainMode == domainMode) {
193 return GrBicubicEffect::MakeSubset(std::move(view), srcAlphaType, textureMatrix, wm, wm,
194 domain, kDir, caps);
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400195 } else {
Brian Salomond0d033a2020-02-18 16:59:28 -0500196 return GrBicubicEffect::Make(std::move(view), srcAlphaType, textureMatrix, wm, wm, kDir,
197 caps);
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400198 }
199 }
200}
Brian Salomon2a943df2018-05-04 13:43:19 -0400201
Greg Danielc61d7e32020-02-04 14:27:45 -0500202GrSurfaceProxyView GrTextureProducer::viewForParams(
Brian Salomon7fba2442020-02-20 11:29:18 -0500203 const GrSamplerState::Filter* filterOrNullForBicubic) {
Michael Ludwigddeed372019-02-20 16:50:10 -0500204 GrSamplerState sampler; // Default is nearest + clamp
205 if (filterOrNullForBicubic) {
206 sampler.setFilterMode(*filterOrNullForBicubic);
207 }
208 if (fDomainNeedsDecal) {
209 // Assuming hardware support, switch to clamp-to-border instead of clamp
210 if (fContext->priv().caps()->clampToBorderSupport()) {
211 sampler.setWrapModeX(GrSamplerState::WrapMode::kClampToBorder);
212 sampler.setWrapModeY(GrSamplerState::WrapMode::kClampToBorder);
213 }
214 }
Brian Salomon7fba2442020-02-20 11:29:18 -0500215 return this->viewForParams(sampler);
Michael Ludwigddeed372019-02-20 16:50:10 -0500216}
217
Brian Salomon7fba2442020-02-20 11:29:18 -0500218GrSurfaceProxyView GrTextureProducer::viewForParams(GrSamplerState sampler) {
Greg Daniel82c6b102020-01-21 10:33:22 -0500219 const GrCaps* caps = this->context()->priv().caps();
Greg Daniel8e9b4c42018-07-20 10:30:48 -0400220
221 int mipCount = SkMipMap::ComputeLevelCount(this->width(), this->height());
222 bool willBeMipped = GrSamplerState::Filter::kMipMap == sampler.filter() && mipCount &&
Greg Daniel82c6b102020-01-21 10:33:22 -0500223 caps->mipMapSupport();
Greg Daniel8e9b4c42018-07-20 10:30:48 -0400224
Brian Salomon7fba2442020-02-20 11:29:18 -0500225 auto result = this->onRefTextureProxyViewForParams(sampler, willBeMipped);
226 if (!result) {
227 return {};
228 }
229
230 SkASSERT(result.asTextureProxy());
Brian Salomon2a943df2018-05-04 13:43:19 -0400231
Greg Daniel022b1e02018-07-20 14:54:00 -0400232 // Check to make sure that if we say the texture willBeMipped that the returned texture has mip
233 // maps, unless the config is not copyable.
Brian Salomon7fba2442020-02-20 11:29:18 -0500234 SkASSERT(!willBeMipped || result.asTextureProxy()->mipMapped() == GrMipMapped::kYes ||
Greg Danielcc104db2020-02-03 14:17:08 -0500235 !caps->isFormatCopyable(result.proxy()->backendFormat()));
236
Brian Salomon7fba2442020-02-20 11:29:18 -0500237 SkASSERT(result.proxy()->dimensions() == this->dimensions());
Greg Daniel82c6b102020-01-21 10:33:22 -0500238
Brian Salomon2a943df2018-05-04 13:43:19 -0400239 return result;
240}
Greg Daniel5f4b09d2018-06-12 16:39:59 -0400241
Greg Danielc61d7e32020-02-04 14:27:45 -0500242std::pair<GrSurfaceProxyView, GrColorType> GrTextureProducer::view(GrMipMapped willNeedMips) {
Greg Daniel5f4b09d2018-06-12 16:39:59 -0400243 GrSamplerState::Filter filter =
244 GrMipMapped::kNo == willNeedMips ? GrSamplerState::Filter::kNearest
245 : GrSamplerState::Filter::kMipMap;
246 GrSamplerState sampler(GrSamplerState::WrapMode::kClamp, filter);
Greg Daniel8e9b4c42018-07-20 10:30:48 -0400247
Brian Salomon7fba2442020-02-20 11:29:18 -0500248 auto result = this->viewForParams(sampler);
Greg Daniel8e9b4c42018-07-20 10:30:48 -0400249
Greg Daniel82c6b102020-01-21 10:33:22 -0500250#ifdef SK_DEBUG
251 const GrCaps* caps = this->context()->priv().caps();
252 // Check that the resulting proxy format is compatible with the GrColorType of this producer
Greg Danielcc104db2020-02-03 14:17:08 -0500253 SkASSERT(!result.proxy() ||
254 result.proxy()->isFormatCompressed(caps) ||
255 caps->areColorTypeAndFormatCompatible(this->colorType(),
256 result.proxy()->backendFormat()));
Greg Daniel82c6b102020-01-21 10:33:22 -0500257#endif
Greg Daniel5f4b09d2018-06-12 16:39:59 -0400258
Greg Daniel82c6b102020-01-21 10:33:22 -0500259 return {result, this->colorType()};
Greg Daniel5f4b09d2018-06-12 16:39:59 -0400260}