diff options
author | cblume <cblume@chromium.org> | 2016-08-09 13:45:56 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-08-09 13:45:56 -0700 |
commit | d6113140f7ae8996f679ac6698a60fb8c1386da3 (patch) | |
tree | f641a12df7ca225733bd164297485f189f254db9 /src | |
parent | 8eccc308c8adcdf26ffc7c4dd538b71f33c6f22b (diff) |
Store mipmap levels in deferred texture image
This is a follow-up to https://codereview.chromium.org/2034933003/ which
was reverted due to a memory leak.
When creating the deferred texture image, detect if using medium / high
quality. If so, generate and store mipmaps in the deferred texture
image.
When creating a texture from that be sure to read it back out.
BUG=578304
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2115023002
Review-Url: https://codereview.chromium.org/2115023002
Diffstat (limited to 'src')
-rw-r--r-- | src/image/SkImage.cpp | 8 | ||||
-rw-r--r-- | src/image/SkImage_Gpu.cpp | 126 |
2 files changed, 115 insertions, 19 deletions
diff --git a/src/image/SkImage.cpp b/src/image/SkImage.cpp index 2d434aee19..a6011875e8 100644 --- a/src/image/SkImage.cpp +++ b/src/image/SkImage.cpp @@ -395,6 +395,11 @@ sk_sp<SkImage> SkImage::MakeTextureFromPixmap(GrContext*, const SkPixmap&, SkBud return nullptr; } +sk_sp<SkImage> MakeTextureFromMipMap(GrContext*, const SkImageInfo&, const GrMipLevel* texels, + int mipLevelCount, SkBudgeted, SkSourceGammaTreatment) { + return nullptr; +} + sk_sp<SkImage> SkImage::MakeFromTexture(GrContext*, const GrBackendTextureDesc&, SkAlphaType, sk_sp<SkColorSpace>, TextureReleaseProc, ReleaseContext) { return nullptr; @@ -402,7 +407,8 @@ sk_sp<SkImage> SkImage::MakeFromTexture(GrContext*, const GrBackendTextureDesc&, size_t SkImage::getDeferredTextureImageData(const GrContextThreadSafeProxy&, const DeferredTextureImageUsageParams[], - int paramCnt, void* buffer) const { + int paramCnt, void* buffer, + SkSourceGammaTreatment treatment) const { return 0; } diff --git a/src/image/SkImage_Gpu.cpp b/src/image/SkImage_Gpu.cpp index 44bb71c088..94fd8a3f3f 100644 --- a/src/image/SkImage_Gpu.cpp +++ b/src/image/SkImage_Gpu.cpp @@ -10,6 +10,7 @@ #include "GrContext.h" #include "GrDrawContext.h" #include "GrImageIDTextureAdjuster.h" +#include "GrTexturePriv.h" #include "effects/GrYUVEffect.h" #include "SkCanvas.h" #include "SkBitmapCache.h" @@ -364,35 +365,60 @@ struct MipMapLevelData { }; struct DeferredTextureImage { - uint32_t fContextUniqueID; + uint32_t fContextUniqueID; + // Right now, the gamma treatment is only considered when generating mipmaps + SkSourceGammaTreatment fGammaTreatment; // We don't store a SkImageInfo because it contains a ref-counted SkColorSpace. - int fWidth; - int fHeight; - SkColorType fColorType; - SkAlphaType fAlphaType; - void* fColorSpace; - size_t fColorSpaceSize; - int fColorTableCnt; - uint32_t* fColorTableData; - int fMipMapLevelCount; + int fWidth; + int fHeight; + SkColorType fColorType; + SkAlphaType fAlphaType; + void* fColorSpace; + size_t fColorSpaceSize; + int fColorTableCnt; + uint32_t* fColorTableData; + int fMipMapLevelCount; // The fMipMapLevelData array may contain more than 1 element. // It contains fMipMapLevelCount elements. // That means this struct's size is not known at compile-time. - MipMapLevelData fMipMapLevelData[1]; + MipMapLevelData fMipMapLevelData[1]; }; } // anonymous namespace +static bool should_use_mip_maps(const SkImage::DeferredTextureImageUsageParams & param) { + bool shouldUseMipMaps = false; + + // Use mipmaps if either + // 1.) it is a perspective matrix, or + // 2.) the quality is med/high and the scale is < 1 + if (param.fMatrix.hasPerspective()) { + shouldUseMipMaps = true; + } + if (param.fQuality == kMedium_SkFilterQuality || + param.fQuality == kHigh_SkFilterQuality) { + SkScalar minAxisScale = param.fMatrix.getMinScale(); + if (minAxisScale != -1.f && minAxisScale < 1.f) { + shouldUseMipMaps = true; + } + } + + return shouldUseMipMaps; +} + size_t SkImage::getDeferredTextureImageData(const GrContextThreadSafeProxy& proxy, const DeferredTextureImageUsageParams params[], - int paramCnt, void* buffer) const { + int paramCnt, void* buffer, + SkSourceGammaTreatment gammaTreatment) const { // Extract relevant min/max values from the params array. int lowestPreScaleMipLevel = params[0].fPreScaleMipLevel; SkFilterQuality highestFilterQuality = params[0].fQuality; + bool useMipMaps = should_use_mip_maps(params[0]); for (int i = 1; i < paramCnt; ++i) { if (lowestPreScaleMipLevel > params[i].fPreScaleMipLevel) lowestPreScaleMipLevel = params[i].fPreScaleMipLevel; if (highestFilterQuality < params[i].fQuality) highestFilterQuality = params[i].fQuality; + useMipMaps |= should_use_mip_maps(params[i]); } const bool fillMode = SkToBool(buffer); @@ -462,7 +488,29 @@ size_t SkImage::getDeferredTextureImageData(const GrContextThreadSafeProxy& prox SkASSERT(!pixmap.ctable()); } } + SkAlphaType at = this->isOpaque() ? kOpaque_SkAlphaType : kPremul_SkAlphaType; int mipMapLevelCount = 1; + if (useMipMaps) { + // SkMipMap only deals with the mipmap levels it generates, which does + // not include the base level. + // That means it generates and holds levels 1-x instead of 0-x. + // So the total mipmap level count is 1 more than what + // SkMipMap::ComputeLevelCount returns. + mipMapLevelCount = SkMipMap::ComputeLevelCount(scaledSize.width(), scaledSize.height()) + 1; + + // We already initialized pixelSize to the size of the base level. + // SkMipMap will generate the extra mipmap levels. Their sizes need to + // be added to the total. + // Index 0 here does not refer to the base mipmap level -- it is + // SkMipMap's first generated mipmap level (level 1). + for (int currentMipMapLevelIndex = mipMapLevelCount - 1; currentMipMapLevelIndex >= 0; + currentMipMapLevelIndex--) { + SkISize mipSize = SkMipMap::ComputeLevelSize(scaledSize.width(), scaledSize.height(), + currentMipMapLevelIndex); + SkImageInfo mipInfo = SkImageInfo::MakeN32(mipSize.fWidth, mipSize.fHeight, at); + pixelSize += SkAlign8(SkAutoPixmapStorage::AllocSize(mipInfo, nullptr)); + } + } size_t size = 0; size_t dtiSize = SkAlign8(sizeof(DeferredTextureImage)); size += dtiSize; @@ -496,6 +544,7 @@ size_t SkImage::getDeferredTextureImageData(const GrContextThreadSafeProxy& prox SkASSERT(info == pixmap.info()); size_t rowBytes = pixmap.rowBytes(); DeferredTextureImage* dti = new (buffer) DeferredTextureImage(); + dti->fGammaTreatment = gammaTreatment; dti->fContextUniqueID = proxy.fContextUniqueID; dti->fWidth = info.width(); dti->fHeight = info.height(); @@ -514,6 +563,32 @@ size_t SkImage::getDeferredTextureImageData(const GrContextThreadSafeProxy& prox dti->fColorSpace = nullptr; dti->fColorSpaceSize = 0; } + + // Fill in the mipmap levels if they exist + intptr_t mipLevelPtr = bufferAsInt + pixelOffset + SkAlign8(SkAutoPixmapStorage::AllocSize( + info, nullptr)); + if (useMipMaps) { + SkAutoTDelete<SkMipMap> mipmaps(SkMipMap::Build(pixmap, gammaTreatment, nullptr)); + // SkMipMap holds only the mipmap levels it generates. + // A programmer can use the data they provided to SkMipMap::Build as level 0. + // So the SkMipMap provides levels 1-x but it stores them in its own + // range 0-(x-1). + for (int generatedMipLevelIndex = 0; generatedMipLevelIndex < mipMapLevelCount - 1; + generatedMipLevelIndex++) { + SkISize mipSize = SkMipMap::ComputeLevelSize(scaledSize.width(), scaledSize.height(), + generatedMipLevelIndex); + SkImageInfo mipInfo = SkImageInfo::MakeN32(mipSize.fWidth, mipSize.fHeight, at); + SkMipMap::Level mipLevel; + mipmaps->getLevel(generatedMipLevelIndex, &mipLevel); + memcpy(reinterpret_cast<void*>(mipLevelPtr), mipLevel.fPixmap.addr(), + mipLevel.fPixmap.getSafeSize()); + dti->fMipMapLevelData[generatedMipLevelIndex + 1].fPixelData = + reinterpret_cast<void*>(mipLevelPtr); + dti->fMipMapLevelData[generatedMipLevelIndex + 1].fRowBytes = + mipLevel.fPixmap.rowBytes(); + mipLevelPtr += SkAlign8(mipLevel.fPixmap.getSafeSize()); + } + } return size; } @@ -532,17 +607,30 @@ sk_sp<SkImage> SkImage::MakeFromDeferredTextureImageData(GrContext* context, con SkASSERT(dti->fColorTableData); colorTable.reset(new SkColorTable(dti->fColorTableData, dti->fColorTableCnt)); } - SkASSERT(dti->fMipMapLevelCount == 1); + int mipLevelCount = dti->fMipMapLevelCount; + SkASSERT(mipLevelCount >= 1); sk_sp<SkColorSpace> colorSpace; if (dti->fColorSpaceSize) { colorSpace = SkColorSpace::Deserialize(dti->fColorSpace, dti->fColorSpaceSize); } SkImageInfo info = SkImageInfo::Make(dti->fWidth, dti->fHeight, dti->fColorType, dti->fAlphaType, colorSpace); - SkPixmap pixmap; - pixmap.reset(info, dti->fMipMapLevelData[0].fPixelData, - dti->fMipMapLevelData[0].fRowBytes, colorTable.get()); - return SkImage::MakeTextureFromPixmap(context, pixmap, budgeted); + if (mipLevelCount == 1) { + SkPixmap pixmap; + pixmap.reset(info, dti->fMipMapLevelData[0].fPixelData, + dti->fMipMapLevelData[0].fRowBytes, colorTable.get()); + return SkImage::MakeTextureFromPixmap(context, pixmap, budgeted); + } else { + SkAutoTDeleteArray<GrMipLevel> texels(new GrMipLevel[mipLevelCount]); + for (int i = 0; i < mipLevelCount; i++) { + texels[i].fPixels = dti->fMipMapLevelData[i].fPixelData; + texels[i].fRowBytes = dti->fMipMapLevelData[i].fRowBytes; + } + + return SkImage::MakeTextureFromMipMap(context, info, texels.get(), + mipLevelCount, SkBudgeted::kYes, + dti->fGammaTreatment); + } } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -565,7 +653,8 @@ GrTexture* GrDeepCopyTexture(GrTexture* src, SkBudgeted budgeted) { sk_sp<SkImage> SkImage::MakeTextureFromMipMap(GrContext* ctx, const SkImageInfo& info, const GrMipLevel* texels, int mipLevelCount, - SkBudgeted budgeted) { + SkBudgeted budgeted, + SkSourceGammaTreatment gammaTreatment) { if (!ctx) { return nullptr; } @@ -573,6 +662,7 @@ sk_sp<SkImage> SkImage::MakeTextureFromMipMap(GrContext* ctx, const SkImageInfo& if (!texture) { return nullptr; } + texture->texturePriv().setGammaTreatment(gammaTreatment); return sk_make_sp<SkImage_Gpu>(texture->width(), texture->height(), kNeedNewImageUniqueID, info.alphaType(), texture, sk_ref_sp(info.colorSpace()), budgeted); |