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 "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