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