Separate MIP filtering from min/mag filtering.
Does not add kNearest as a mip map mode yet.
Bug: skia:10344
Change-Id: Ida80cbf19e2b1eed5209d0680837fb45e54b4261
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/303481
Commit-Queue: Brian Salomon <bsalomon@google.com>
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
Reviewed-by: Greg Daniel <egdaniel@google.com>
diff --git a/src/gpu/ops/GrTextureOp.cpp b/src/gpu/ops/GrTextureOp.cpp
index b3b32b7..b672d1a 100644
--- a/src/gpu/ops/GrTextureOp.cpp
+++ b/src/gpu/ops/GrTextureOp.cpp
@@ -58,11 +58,13 @@
return {dw, dh};
}
-static bool filter_has_effect(const GrQuad& srcQuad, const GrQuad& dstQuad) {
+static std::tuple<bool /* filter */,
+ bool /* mipmap */>
+filter_and_mm_have_effect(const GrQuad& srcQuad, const GrQuad& dstQuad) {
// If not axis-aligned in src or dst, then always say it has an effect
if (srcQuad.quadType() != GrQuad::Type::kAxisAligned ||
dstQuad.quadType() != GrQuad::Type::kAxisAligned) {
- return true;
+ return {true, true};
}
SkRect srcRect;
@@ -72,24 +74,26 @@
// top-left corners have the same fraction (so src and dst snap to the pixel grid
// identically).
SkASSERT(srcRect.isSorted());
- return srcRect.width() != dstRect.width() || srcRect.height() != dstRect.height() ||
- SkScalarFraction(srcRect.fLeft) != SkScalarFraction(dstRect.fLeft) ||
- SkScalarFraction(srcRect.fTop) != SkScalarFraction(dstRect.fTop);
- } else {
- // Although the quads are axis-aligned, the local coordinate system is transformed such
- // that fractionally-aligned sample centers will not align with the device coordinate system
- // So disable filtering when edges are the same length and both srcQuad and dstQuad
- // 0th vertex is integer aligned.
- if (SkScalarIsInt(srcQuad.x(0)) && SkScalarIsInt(srcQuad.y(0)) &&
- SkScalarIsInt(dstQuad.x(0)) && SkScalarIsInt(dstQuad.y(0))) {
- // Extract edge lengths
- SkSize srcSize = axis_aligned_quad_size(srcQuad);
- SkSize dstSize = axis_aligned_quad_size(dstQuad);
- return srcSize.fWidth != dstSize.fWidth || srcSize.fHeight != dstSize.fHeight;
- } else {
- return true;
- }
+ bool filter = srcRect.width() != dstRect.width() || srcRect.height() != dstRect.height() ||
+ SkScalarFraction(srcRect.fLeft) != SkScalarFraction(dstRect.fLeft) ||
+ SkScalarFraction(srcRect.fTop) != SkScalarFraction(dstRect.fTop);
+ bool mm = srcRect.width() > dstRect.width() || srcRect.height() > dstRect.height();
+ return {filter, mm};
}
+ // Extract edge lengths
+ SkSize srcSize = axis_aligned_quad_size(srcQuad);
+ SkSize dstSize = axis_aligned_quad_size(dstQuad);
+ // Although the quads are axis-aligned, the local coordinate system is transformed such
+ // that fractionally-aligned sample centers will not align with the device coordinate system
+ // So disable filtering when edges are the same length and both srcQuad and dstQuad
+ // 0th vertex is integer aligned.
+ bool filter = srcSize != dstSize ||
+ !SkScalarIsInt(srcQuad.x(0)) ||
+ !SkScalarIsInt(srcQuad.y(0)) ||
+ !SkScalarIsInt(dstQuad.x(0)) ||
+ !SkScalarIsInt(dstQuad.y(0));
+ bool mm = srcSize.fWidth > dstSize.fWidth || srcSize.fHeight > dstSize.fHeight;
+ return {filter, mm};
}
// Describes function for normalizing src coords: [x * iw, y * ih + yOffset] can represent
@@ -222,13 +226,14 @@
GrSurfaceProxyView proxyView,
sk_sp<GrColorSpaceXform> textureXform,
GrSamplerState::Filter filter,
+ GrSamplerState::MipmapMode mm,
const SkPMColor4f& color,
GrTextureOp::Saturate saturate,
GrAAType aaType,
DrawQuad* quad,
const SkRect* subset) {
GrOpMemoryPool* pool = context->priv().opMemoryPool();
- return pool->allocate<TextureOp>(std::move(proxyView), std::move(textureXform), filter,
+ return pool->allocate<TextureOp>(std::move(proxyView), std::move(textureXform), filter, mm,
color, saturate, aaType, quad, subset);
}
@@ -237,6 +242,7 @@
int cnt,
int proxyRunCnt,
GrSamplerState::Filter filter,
+ GrSamplerState::MipmapMode mm,
GrTextureOp::Saturate saturate,
GrAAType aaType,
SkCanvas::SrcRectConstraint constraint,
@@ -249,7 +255,7 @@
GrOpMemoryPool* pool = context->priv().opMemoryPool();
void* mem = pool->allocate(size);
return std::unique_ptr<GrDrawOp>(
- new (mem) TextureOp(set, cnt, proxyRunCnt, filter, saturate, aaType, constraint,
+ new (mem) TextureOp(set, cnt, proxyRunCnt, filter, mm, saturate, aaType, constraint,
viewMatrix, std::move(textureColorSpaceXform)));
}
@@ -262,7 +268,7 @@
const char* name() const override { return "TextureOp"; }
void visitProxies(const VisitProxyFunc& func) const override {
- bool mipped = (GrSamplerState::Filter::kMipMap == fMetadata.filter());
+ bool mipped = (fMetadata.mipmapMode() != GrSamplerState::MipmapMode::kNone);
for (unsigned p = 0; p < fMetadata.fProxyCount; ++p) {
func(fViewCountPairs[p].fProxy.get(), GrMipmapped(mipped));
}
@@ -277,9 +283,10 @@
str.appendf("# draws: %d\n", fQuads.count());
auto iter = fQuads.iterator();
for (unsigned p = 0; p < fMetadata.fProxyCount; ++p) {
- str.appendf("Proxy ID: %d, Filter: %d\n",
+ str.appendf("Proxy ID: %d, Filter: %d, MM: %d\n",
fViewCountPairs[p].fProxy->uniqueID().asUInt(),
- static_cast<int>(fMetadata.fFilter));
+ static_cast<int>(fMetadata.fFilter),
+ static_cast<int>(fMetadata.fMipmapMode));
int i = 0;
while(i < fViewCountPairs[p].fQuadCnt && iter.next()) {
const GrQuad* quad = iter.deviceQuad();
@@ -368,12 +375,16 @@
// performance (since texture ops are one of the most commonly used in an app).
struct Metadata {
// AAType must be filled after initialization; ColorType is determined in finalize()
- Metadata(const GrSwizzle& swizzle, GrSamplerState::Filter filter,
- GrQuadPerEdgeAA::Subset subset, GrTextureOp::Saturate saturate)
+ Metadata(const GrSwizzle& swizzle,
+ GrSamplerState::Filter filter,
+ GrSamplerState::MipmapMode mm,
+ GrQuadPerEdgeAA::Subset subset,
+ GrTextureOp::Saturate saturate)
: fSwizzle(swizzle)
, fProxyCount(1)
, fTotalQuadCount(1)
, fFilter(static_cast<uint16_t>(filter))
+ , fMipmapMode(static_cast<uint16_t>(mm))
, fAAType(static_cast<uint16_t>(GrAAType::kNone))
, fColorType(static_cast<uint16_t>(ColorType::kNone))
, fSubset(static_cast<uint16_t>(subset))
@@ -386,15 +397,19 @@
// These must be based on uint16_t to help MSVC's pack bitfields optimally
uint16_t fFilter : 2; // GrSamplerState::Filter
+ uint16_t fMipmapMode : 2; // GrSamplerState::MipmapMode
uint16_t fAAType : 2; // GrAAType
uint16_t fColorType : 2; // GrQuadPerEdgeAA::ColorType
uint16_t fSubset : 1; // bool
uint16_t fSaturate : 1; // bool
- uint16_t fUnused : 8; // # of bits left before Metadata exceeds 8 bytes
+ uint16_t fUnused : 6; // # of bits left before Metadata exceeds 8 bytes
GrSamplerState::Filter filter() const {
return static_cast<GrSamplerState::Filter>(fFilter);
}
+ GrSamplerState::MipmapMode mipmapMode() const {
+ return static_cast<GrSamplerState::MipmapMode>(fMipmapMode);
+ }
GrAAType aaType() const { return static_cast<GrAAType>(fAAType); }
ColorType colorType() const { return static_cast<ColorType>(fColorType); }
Subset subset() const { return static_cast<Subset>(fSubset); }
@@ -449,6 +464,7 @@
TextureOp(GrSurfaceProxyView proxyView,
sk_sp<GrColorSpaceXform> textureColorSpaceXform,
GrSamplerState::Filter filter,
+ GrSamplerState::MipmapMode mm,
const SkPMColor4f& color,
GrTextureOp::Saturate saturate,
GrAAType aaType,
@@ -458,8 +474,7 @@
, fQuads(1, true /* includes locals */)
, fTextureColorSpaceXform(std::move(textureColorSpaceXform))
, fDesc(nullptr)
- , fMetadata(proxyView.swizzle(), filter, Subset(!!subsetRect), saturate) {
-
+ , fMetadata(proxyView.swizzle(), filter, mm, Subset(!!subsetRect), saturate) {
// Clean up disparities between the overall aa type and edge configuration and apply
// optimizations based on the rect and matrix when appropriate
GrQuadUtils::ResolveAAType(aaType, quad->fEdgeFlags, quad->fDevice,
@@ -498,6 +513,7 @@
int cnt,
int proxyRunCnt,
GrSamplerState::Filter filter,
+ GrSamplerState::MipmapMode mm,
GrTextureOp::Saturate saturate,
GrAAType aaType,
SkCanvas::SrcRectConstraint constraint,
@@ -507,8 +523,11 @@
, fQuads(cnt, true /* includes locals */)
, fTextureColorSpaceXform(std::move(textureColorSpaceXform))
, fDesc(nullptr)
- , fMetadata(set[0].fProxyView.swizzle(), GrSamplerState::Filter::kNearest,
- Subset::kNo, saturate) {
+ , fMetadata(set[0].fProxyView.swizzle(),
+ GrSamplerState::Filter::kNearest,
+ GrSamplerState::MipmapMode::kNone,
+ Subset::kNo,
+ saturate) {
// Update counts to reflect the batch op
fMetadata.fProxyCount = SkToUInt(proxyRunCnt);
fMetadata.fTotalQuadCount = SkToUInt(cnt);
@@ -518,6 +537,7 @@
GrAAType netAAType = GrAAType::kNone; // aa type maximally compatible with all dst rects
Subset netSubset = Subset::kNo;
GrSamplerState::Filter netFilter = GrSamplerState::Filter::kNearest;
+ GrSamplerState::MipmapMode netMM = GrSamplerState::MipmapMode::kNone;
const GrSurfaceProxy* curProxy = nullptr;
@@ -525,6 +545,9 @@
// increases when set[q]'s proxy changes.
int p = 0;
for (int q = 0; q < cnt; ++q) {
+ SkASSERT(mm == GrSamplerState::MipmapMode::kNone ||
+ (set[0].fProxyView.proxy()->asTextureProxy()->mipmapped() ==
+ GrMipmapped::kYes));
if (q == 0) {
// We do not placement new the first ViewCountPair since that one is allocated and
// initialized as part of the GrTextureOp creation.
@@ -561,13 +584,19 @@
quad.fLocal = GrQuad(set[q].fSrcRect);
}
- if (netFilter != filter && filter_has_effect(quad.fLocal, quad.fDevice)) {
- // The only way netFilter != filter is if linear filtering is requested and we
- // haven't yet found a quad that requires linear filtering (so net is still
- // nearest).
- SkASSERT(netFilter == GrSamplerState::Filter::kNearest &&
- filter == GrSamplerState::Filter::kLinear);
- netFilter = GrSamplerState::Filter::kLinear;
+ if (netFilter != filter || netMM != mm) {
+ // The only way netFilter != filter is if linear is requested and we haven't yet
+ // found a quad that requires linear (so net is still nearest). Similar for mip
+ // mapping.
+ SkASSERT(netFilter <= filter);
+ SkASSERT(netMM <= mm);
+ auto [mustFilter, mustMM] = filter_and_mm_have_effect(quad.fLocal, quad.fDevice);
+ if (mustFilter && filter != GrSamplerState::Filter::kNearest) {
+ netFilter = GrSamplerState::Filter::kLinear;
+ }
+ if (mustMM && mm != GrSamplerState::MipmapMode::kNone) {
+ netMM = GrSamplerState::MipmapMode::kLinear;
+ }
}
// Update overall bounds of the op as the union of all quads
@@ -970,6 +999,9 @@
if (fMetadata.filter() != that->fMetadata.filter()) {
return CombineResult::kCannotCombine;
}
+ if (fMetadata.mipmapMode() != that->fMetadata.mipmapMode()) {
+ return CombineResult::kCannotCombine;
+ }
if (fMetadata.fSwizzle != that->fMetadata.fSwizzle) {
return CombineResult::kCannotCombine;
}
@@ -1028,6 +1060,7 @@
SkAlphaType alphaType,
sk_sp<GrColorSpaceXform> textureXform,
GrSamplerState::Filter filter,
+ GrSamplerState::MipmapMode mm,
const SkPMColor4f& color,
Saturate saturate,
SkBlendMode blendMode,
@@ -1040,13 +1073,18 @@
subset = nullptr;
}
- if (filter != GrSamplerState::Filter::kNearest &&
- !filter_has_effect(quad->fLocal, quad->fDevice)) {
- filter = GrSamplerState::Filter::kNearest;
+ if (filter != GrSamplerState::Filter::kNearest || mm != GrSamplerState::MipmapMode::kNone) {
+ auto [mustFilter, mustMM] = filter_and_mm_have_effect(quad->fLocal, quad->fDevice);
+ if (!mustFilter) {
+ filter = GrSamplerState::Filter::kNearest;
+ }
+ if (!mustMM) {
+ mm = GrSamplerState::MipmapMode::kNone;
+ }
}
if (blendMode == SkBlendMode::kSrcOver) {
- return TextureOp::Make(context, std::move(proxyView), std::move(textureXform), filter,
+ return TextureOp::Make(context, std::move(proxyView), std::move(textureXform), filter, mm,
color, saturate, aaType, std::move(quad), subset);
} else {
// Emulate complex blending using GrFillRectOp
@@ -1086,6 +1124,7 @@
GrRecordingContext* context,
int numEntries,
GrSamplerState::Filter filter,
+ GrSamplerState::MipmapMode mm,
GrTextureOp::Saturate saturate,
SkCanvas::SrcRectConstraint constraint,
const SkMatrix& viewMatrix,
@@ -1094,20 +1133,27 @@
, fClip(clip)
, fContext(context)
, fFilter(filter)
+ , fMipmapMode(mm)
, fSaturate(saturate)
, fConstraint(constraint)
, fViewMatrix(viewMatrix)
, fTextureColorSpaceXform(textureColorSpaceXform)
- , fNumLeft(numEntries) {
- }
+ , fNumLeft(numEntries) {}
void createOp(GrRenderTargetContext::TextureSetEntry set[],
int clumpSize,
GrAAType aaType) {
int clumpProxyCount = proxy_run_count(&set[fNumClumped], clumpSize);
- std::unique_ptr<GrDrawOp> op = TextureOp::Make(fContext, &set[fNumClumped], clumpSize,
- clumpProxyCount, fFilter, fSaturate, aaType,
- fConstraint, fViewMatrix,
+ std::unique_ptr<GrDrawOp> op = TextureOp::Make(fContext,
+ &set[fNumClumped],
+ clumpSize,
+ clumpProxyCount,
+ fFilter,
+ fMipmapMode,
+ fSaturate,
+ aaType,
+ fConstraint,
+ fViewMatrix,
fTextureColorSpaceXform);
fRTC->addDrawOp(fClip, std::move(op));
@@ -1123,6 +1169,7 @@
const GrClip* fClip;
GrRecordingContext* fContext;
GrSamplerState::Filter fFilter;
+ GrSamplerState::MipmapMode fMipmapMode;
GrTextureOp::Saturate fSaturate;
SkCanvas::SrcRectConstraint fConstraint;
const SkMatrix& fViewMatrix;
@@ -1140,6 +1187,7 @@
int cnt,
int proxyRunCnt,
GrSamplerState::Filter filter,
+ GrSamplerState::MipmapMode mm,
Saturate saturate,
SkBlendMode blendMode,
GrAAType aaType,
@@ -1181,8 +1229,7 @@
? &set[i].fSrcRect : nullptr;
auto op = Make(context, set[i].fProxyView, set[i].fSrcAlphaType, textureColorSpaceXform,
- filter, set[i].fColor, saturate, blendMode, aaType,
- &quad, subset);
+ filter, mm, set[i].fColor, saturate, blendMode, aaType, &quad, subset);
rtc->addDrawOp(clip, std::move(op));
}
return;
@@ -1192,13 +1239,13 @@
// needed to clump things together.
if (cnt <= std::min(GrResourceProvider::MaxNumNonAAQuads(),
GrResourceProvider::MaxNumAAQuads())) {
- auto op = TextureOp::Make(context, set, cnt, proxyRunCnt, filter, saturate, aaType,
+ auto op = TextureOp::Make(context, set, cnt, proxyRunCnt, filter, mm, saturate, aaType,
constraint, viewMatrix, std::move(textureColorSpaceXform));
rtc->addDrawOp(clip, std::move(op));
return;
}
- BatchSizeLimiter state(rtc, clip, context, cnt, filter, saturate, constraint, viewMatrix,
+ BatchSizeLimiter state(rtc, clip, context, cnt, filter, mm, saturate, constraint, viewMatrix,
std::move(textureColorSpaceXform));
// kNone and kMSAA never get altered
@@ -1292,11 +1339,13 @@
SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random);
SkPMColor4f color = SkPMColor4f::FromBytes_RGBA(SkColorToPremulGrColor(random->nextU()));
GrSamplerState::Filter filter = (GrSamplerState::Filter)random->nextULessThan(
- static_cast<uint32_t>(GrSamplerState::Filter::kMipMap) + 1);
- while (mipMapped == GrMipmapped::kNo && filter == GrSamplerState::Filter::kMipMap) {
- filter = (GrSamplerState::Filter)random->nextULessThan(
- static_cast<uint32_t>(GrSamplerState::Filter::kMipMap) + 1);
+ static_cast<uint32_t>(GrSamplerState::Filter::kLast) + 1);
+ GrSamplerState::MipmapMode mm = GrSamplerState::MipmapMode::kNone;
+ if (mipMapped == GrMipmapped::kYes) {
+ mm = (GrSamplerState::MipmapMode)random->nextULessThan(
+ static_cast<uint32_t>(GrSamplerState::MipmapMode::kLast) + 1);
}
+
auto texXform = GrTest::TestColorXform(random);
GrAAType aaType = GrAAType::kNone;
if (random->nextBool()) {
@@ -1317,8 +1366,8 @@
DrawQuad quad = {GrQuad::MakeFromRect(rect, viewMatrix), GrQuad(srcRect), aaFlags};
return GrTextureOp::Make(context, std::move(proxyView), alphaType, std::move(texXform), filter,
- color, saturate, SkBlendMode::kSrcOver, aaType,
- &quad, useSubset ? &srcRect : nullptr);
+ mm, color, saturate, SkBlendMode::kSrcOver, aaType, &quad,
+ useSubset ? &srcRect : nullptr);
}
#endif