1 /* 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" 9 #include "GrClip.h" 10 #include "GrContextPriv.h" 11 #include "GrProxyProvider.h" 12 #include "GrRecordingContext.h" 13 #include "GrRecordingContextPriv.h" 14 #include "GrRenderTargetContext.h" 15 #include "GrTextureProxy.h" 16 #include "SkGr.h" 17 #include "SkMipMap.h" 18 #include "SkRectPriv.h" 19 #include "effects/GrBicubicEffect.h" 20 #include "effects/GrSimpleTextureEffect.h" 21 #include "effects/GrTextureDomain.h" 22 23 sk_sp<GrTextureProxy> GrTextureProducer::CopyOnGpu(GrRecordingContext* context, 24 sk_sp<GrTextureProxy> inputProxy, 25 const CopyParams& copyParams, 26 bool dstWillRequireMipMaps) { 27 SkASSERT(context); 28 29 GrPixelConfig config = GrMakePixelConfigUncompressed(inputProxy->config()); 30 31 const SkRect dstRect = SkRect::MakeIWH(copyParams.fWidth, copyParams.fHeight); 32 GrMipMapped mipMapped = dstWillRequireMipMaps ? GrMipMapped::kYes : GrMipMapped::kNo; 33 34 SkRect localRect = SkRect::MakeWH(inputProxy->width(), inputProxy->height()); 35 36 bool needsDomain = false; 37 bool resizing = false; 38 if (copyParams.fFilter != GrSamplerState::Filter::kNearest) { 39 bool resizing = localRect.width() != dstRect.width() || 40 localRect.height() != dstRect.height(); 41 needsDomain = resizing && !GrProxyProvider::IsFunctionallyExact(inputProxy.get()); 42 } 43 44 if (copyParams.fFilter == GrSamplerState::Filter::kNearest && !needsDomain && !resizing && 45 dstWillRequireMipMaps) { 46 sk_sp<GrTextureProxy> proxy = GrCopyBaseMipMapToTextureProxy(context, inputProxy.get()); 47 if (proxy) { 48 return proxy; 49 } 50 } 51 52 GrBackendFormat format = inputProxy->backendFormat().makeTexture2D(); 53 if (!format.isValid()) { 54 return nullptr; 55 } 56 57 sk_sp<GrRenderTargetContext> copyRTC = 58 context->priv().makeDeferredRenderTargetContextWithFallback( 59 format, SkBackingFit::kExact, dstRect.width(), dstRect.height(), 60 config, nullptr, 1, mipMapped, inputProxy->origin()); 61 if (!copyRTC) { 62 return nullptr; 63 } 64 65 GrPaint paint; 66 67 if (needsDomain) { 68 const SkRect domain = localRect.makeInset(0.5f, 0.5f); 69 // This would cause us to read values from outside the subset. Surely, the caller knows 70 // better! 71 SkASSERT(copyParams.fFilter != GrSamplerState::Filter::kMipMap); 72 paint.addColorFragmentProcessor( 73 GrTextureDomainEffect::Make(std::move(inputProxy), SkMatrix::I(), domain, 74 GrTextureDomain::kClamp_Mode, copyParams.fFilter)); 75 } else { 76 GrSamplerState samplerState(GrSamplerState::WrapMode::kClamp, copyParams.fFilter); 77 paint.addColorTextureProcessor(std::move(inputProxy), SkMatrix::I(), samplerState); 78 } 79 paint.setPorterDuffXPFactory(SkBlendMode::kSrc); 80 81 copyRTC->fillRectToRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), dstRect, 82 localRect); 83 return copyRTC->asTextureProxyRef(); 84 } 85 86 /** Determines whether a texture domain is necessary and if so what domain to use. There are two 87 * rectangles to consider: 88 * - The first is the content area specified by the texture adjuster (i.e., textureContentArea). 89 * We can *never* allow filtering to cause bleed of pixels outside this rectangle. 90 * - The second rectangle is the constraint rectangle (i.e., constraintRect), which is known to 91 * be contained by the content area. The filterConstraint specifies whether we are allowed to 92 * bleed across this rect. 93 * 94 * We want to avoid using a domain if possible. We consider the above rectangles, the filter type, 95 * and whether the coords generated by the draw would all fall within the constraint rect. If the 96 * latter is true we only need to consider whether the filter would extend beyond the rects. 97 */ 98 GrTextureProducer::DomainMode GrTextureProducer::DetermineDomainMode( 99 const SkRect& constraintRect, 100 FilterConstraint filterConstraint, 101 bool coordsLimitedToConstraintRect, 102 GrTextureProxy* proxy, 103 const GrSamplerState::Filter* filterModeOrNullForBicubic, 104 SkRect* domainRect) { 105 const SkIRect proxyBounds = SkIRect::MakeWH(proxy->width(), proxy->height()); 106 107 SkASSERT(proxyBounds.contains(constraintRect)); 108 109 const bool proxyIsExact = GrProxyProvider::IsFunctionallyExact(proxy); 110 111 // If the constraint rectangle contains the whole proxy then no need for a domain. 112 if (constraintRect.contains(proxyBounds) && proxyIsExact) { 113 return kNoDomain_DomainMode; 114 } 115 116 bool restrictFilterToRect = (filterConstraint == GrTextureProducer::kYes_FilterConstraint); 117 118 // If we can filter outside the constraint rect, and there is no non-content area of the 119 // proxy, and we aren't going to generate sample coords outside the constraint rect then we 120 // don't need a domain. 121 if (!restrictFilterToRect && proxyIsExact && coordsLimitedToConstraintRect) { 122 return kNoDomain_DomainMode; 123 } 124 125 // Get the domain inset based on sampling mode (or bail if mipped) 126 SkScalar filterHalfWidth = 0.f; 127 if (filterModeOrNullForBicubic) { 128 switch (*filterModeOrNullForBicubic) { 129 case GrSamplerState::Filter::kNearest: 130 if (coordsLimitedToConstraintRect) { 131 return kNoDomain_DomainMode; 132 } else { 133 filterHalfWidth = 0.f; 134 } 135 break; 136 case GrSamplerState::Filter::kBilerp: 137 filterHalfWidth = .5f; 138 break; 139 case GrSamplerState::Filter::kMipMap: 140 if (restrictFilterToRect || !proxyIsExact) { 141 // No domain can save us here. 142 return kTightCopy_DomainMode; 143 } 144 return kNoDomain_DomainMode; 145 } 146 } else { 147 // bicubic does nearest filtering internally. 148 filterHalfWidth = 1.5f; 149 } 150 151 // Both bilerp and bicubic use bilinear filtering and so need to be clamped to the center 152 // of the edge texel. Pinning to the texel center has no impact on nearest mode and MIP-maps 153 154 static const SkScalar kDomainInset = 0.5f; 155 // Figure out the limits of pixels we're allowed to sample from. 156 // Unless we know the amount of outset and the texture matrix we have to conservatively enforce 157 // the domain. 158 if (restrictFilterToRect) { 159 *domainRect = constraintRect.makeInset(kDomainInset, kDomainInset); 160 } else if (!proxyIsExact) { 161 // If we got here then: proxy is not exact, the coords are limited to the 162 // constraint rect, and we're allowed to filter across the constraint rect boundary. So 163 // we check whether the filter would reach across the edge of the proxy. 164 // We will only set the sides that are required. 165 166 *domainRect = SkRectPriv::MakeLargest(); 167 if (coordsLimitedToConstraintRect) { 168 // We may be able to use the fact that the texture coords are limited to the constraint 169 // rect in order to avoid having to add a domain. 170 bool needContentAreaConstraint = false; 171 if (proxyBounds.fRight - filterHalfWidth < constraintRect.fRight) { 172 domainRect->fRight = proxyBounds.fRight - kDomainInset; 173 needContentAreaConstraint = true; 174 } 175 if (proxyBounds.fBottom - filterHalfWidth < constraintRect.fBottom) { 176 domainRect->fBottom = proxyBounds.fBottom - kDomainInset; 177 needContentAreaConstraint = true; 178 } 179 if (!needContentAreaConstraint) { 180 return kNoDomain_DomainMode; 181 } 182 } else { 183 // Our sample coords for the texture are allowed to be outside the constraintRect so we 184 // don't consider it when computing the domain. 185 domainRect->fRight = proxyBounds.fRight - kDomainInset; 186 domainRect->fBottom = proxyBounds.fBottom - kDomainInset; 187 } 188 } else { 189 return kNoDomain_DomainMode; 190 } 191 192 if (domainRect->fLeft > domainRect->fRight) { 193 domainRect->fLeft = domainRect->fRight = SkScalarAve(domainRect->fLeft, domainRect->fRight); 194 } 195 if (domainRect->fTop > domainRect->fBottom) { 196 domainRect->fTop = domainRect->fBottom = SkScalarAve(domainRect->fTop, domainRect->fBottom); 197 } 198 return kDomain_DomainMode; 199 } 200 201 std::unique_ptr<GrFragmentProcessor> GrTextureProducer::createFragmentProcessorForDomainAndFilter( 202 sk_sp<GrTextureProxy> proxy, 203 const SkMatrix& textureMatrix, 204 DomainMode domainMode, 205 const SkRect& domain, 206 const GrSamplerState::Filter* filterOrNullForBicubic) { 207 SkASSERT(kTightCopy_DomainMode != domainMode); 208 bool clampToBorderSupport = fContext->priv().caps()->clampToBorderSupport(); 209 if (filterOrNullForBicubic) { 210 if (kDomain_DomainMode == domainMode || (fDomainNeedsDecal && !clampToBorderSupport)) { 211 GrTextureDomain::Mode wrapMode = fDomainNeedsDecal ? GrTextureDomain::kDecal_Mode 212 : GrTextureDomain::kClamp_Mode; 213 return GrTextureDomainEffect::Make(std::move(proxy), textureMatrix, domain, 214 wrapMode, *filterOrNullForBicubic); 215 } else { 216 GrSamplerState::WrapMode wrapMode = 217 fDomainNeedsDecal ? GrSamplerState::WrapMode::kClampToBorder 218 : GrSamplerState::WrapMode::kClamp; 219 GrSamplerState samplerState(wrapMode, *filterOrNullForBicubic); 220 return GrSimpleTextureEffect::Make(std::move(proxy), textureMatrix, samplerState); 221 } 222 } else { 223 static const GrSamplerState::WrapMode kClampClamp[] = { 224 GrSamplerState::WrapMode::kClamp, GrSamplerState::WrapMode::kClamp}; 225 static const GrSamplerState::WrapMode kDecalDecal[] = { 226 GrSamplerState::WrapMode::kClampToBorder, GrSamplerState::WrapMode::kClampToBorder}; 227 228 if (kDomain_DomainMode == domainMode || (fDomainNeedsDecal && !clampToBorderSupport)) { 229 GrTextureDomain::Mode wrapMode = fDomainNeedsDecal ? GrTextureDomain::kDecal_Mode 230 : GrTextureDomain::kClamp_Mode; 231 return GrBicubicEffect::Make(std::move(proxy), textureMatrix, kClampClamp, 232 wrapMode, wrapMode, 233 kDomain_DomainMode == domainMode ? &domain : nullptr); 234 } else { 235 return GrBicubicEffect::Make(std::move(proxy), textureMatrix, 236 fDomainNeedsDecal ? kDecalDecal : kClampClamp); 237 } 238 } 239 } 240 241 sk_sp<GrTextureProxy> GrTextureProducer::refTextureProxyForParams( 242 const GrSamplerState::Filter* filterOrNullForBicubic, 243 SkScalar scaleAdjust[2]) { 244 GrSamplerState sampler; // Default is nearest + clamp 245 if (filterOrNullForBicubic) { 246 sampler.setFilterMode(*filterOrNullForBicubic); 247 } 248 if (fDomainNeedsDecal) { 249 // Assuming hardware support, switch to clamp-to-border instead of clamp 250 if (fContext->priv().caps()->clampToBorderSupport()) { 251 sampler.setWrapModeX(GrSamplerState::WrapMode::kClampToBorder); 252 sampler.setWrapModeY(GrSamplerState::WrapMode::kClampToBorder); 253 } 254 } 255 return this->refTextureProxyForParams(sampler, scaleAdjust); 256 } 257 258 sk_sp<GrTextureProxy> GrTextureProducer::refTextureProxyForParams( 259 const GrSamplerState& sampler, 260 SkScalar scaleAdjust[2]) { 261 // Check that the caller pre-initialized scaleAdjust 262 SkASSERT(!scaleAdjust || (scaleAdjust[0] == 1 && scaleAdjust[1] == 1)); 263 // Check that if the caller passed nullptr for scaleAdjust that we're in the case where there 264 // can be no scaling. 265 SkDEBUGCODE(bool expectNoScale = (sampler.filter() != GrSamplerState::Filter::kMipMap && 266 !sampler.isRepeated())); 267 SkASSERT(scaleAdjust || expectNoScale); 268 269 int mipCount = SkMipMap::ComputeLevelCount(this->width(), this->height()); 270 bool willBeMipped = GrSamplerState::Filter::kMipMap == sampler.filter() && mipCount && 271 this->context()->priv().caps()->mipMapSupport(); 272 273 auto result = this->onRefTextureProxyForParams(sampler, willBeMipped, scaleAdjust); 274 275 // Check to make sure that if we say the texture willBeMipped that the returned texture has mip 276 // maps, unless the config is not copyable. 277 SkASSERT(!result || !willBeMipped || result->mipMapped() == GrMipMapped::kYes || 278 !this->context()->priv().caps()->isConfigCopyable(result->config())); 279 280 // Check that the "no scaling expected" case always returns a proxy of the same size as the 281 // producer. 282 SkASSERT(!result || !expectNoScale || 283 (result->width() == this->width() && result->height() == this->height())); 284 return result; 285 } 286 287 sk_sp<GrTextureProxy> GrTextureProducer::refTextureProxy(GrMipMapped willNeedMips) { 288 GrSamplerState::Filter filter = 289 GrMipMapped::kNo == willNeedMips ? GrSamplerState::Filter::kNearest 290 : GrSamplerState::Filter::kMipMap; 291 GrSamplerState sampler(GrSamplerState::WrapMode::kClamp, filter); 292 293 int mipCount = SkMipMap::ComputeLevelCount(this->width(), this->height()); 294 bool willBeMipped = GrSamplerState::Filter::kMipMap == sampler.filter() && mipCount && 295 this->context()->priv().caps()->mipMapSupport(); 296 297 auto result = this->onRefTextureProxyForParams(sampler, willBeMipped, nullptr); 298 299 // Check to make sure that if we say the texture willBeMipped that the returned texture has mip 300 // maps, unless the config is not copyable. 301 SkASSERT(!result || !willBeMipped || result->mipMapped() == GrMipMapped::kYes || 302 !this->context()->priv().caps()->isConfigCopyable(result->config())); 303 304 // Check that no scaling occured and we returned a proxy of the same size as the producer. 305 SkASSERT(!result || (result->width() == this->width() && result->height() == this->height())); 306 return result; 307 } 308