aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar Eric Karl <ericrk@chromium.org>2017-12-15 23:37:45 +0000
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2017-12-15 23:57:41 +0000
commitaae533e418f37d788cdb1a1177e882d0f8868b62 (patch)
tree6e2b29d025b4058f86fb834c83b037efec0d5232 /src
parentb2e3a3ada25301a2141b800c9b9278d6d07975f7 (diff)
Revert "Remove SkImage deferred texture image data APIs."
This reverts commit 4f5e1d4ff3fa9f240398c9a08be94beb1c16dad0. Reason for revert: Unfortunately, we need this in Chrome for a bit longer. Working on understanding why the new path led to regressions. Will re-land this once the new path sticks. Original change's description: > Remove SkImage deferred texture image data APIs. > > These APIs existed for Chrome. Chrome is no longer using them. > > Change-Id: I15a5e2f88c7e8d1356188748fc68d4658f6f1849 > Reviewed-on: https://skia-review.googlesource.com/81021 > Reviewed-by: Brian Osman <brianosman@google.com> > Reviewed-by: Cary Clark <caryclark@google.com> > Commit-Queue: Brian Salomon <bsalomon@google.com> TBR=bsalomon@google.com,brianosman@google.com,caryclark@google.com,caryclark@skia.org # Not skipping CQ checks because original CL landed > 1 day ago. Change-Id: Ic9f683f262f2e1d0469156360f5ffaee977ca44a Reviewed-on: https://skia-review.googlesource.com/86280 Reviewed-by: Eric Karl <ericrk@chromium.org> Commit-Queue: Eric Karl <ericrk@chromium.org>
Diffstat (limited to 'src')
-rw-r--r--src/image/SkImage.cpp13
-rw-r--r--src/image/SkImage_Gpu.cpp379
2 files changed, 392 insertions, 0 deletions
diff --git a/src/image/SkImage.cpp b/src/image/SkImage.cpp
index 31104d2548..af791b4380 100644
--- a/src/image/SkImage.cpp
+++ b/src/image/SkImage.cpp
@@ -358,6 +358,19 @@ sk_sp<SkImage> SkImage::MakeFromTexture(GrContext* ctx,
return nullptr;
}
+size_t SkImage::getDeferredTextureImageData(const GrContextThreadSafeProxy&,
+ const DeferredTextureImageUsageParams[],
+ int paramCnt, void* buffer,
+ SkColorSpace* dstColorSpace,
+ SkColorType dstColorType) const {
+ return 0;
+}
+
+sk_sp<SkImage> SkImage::MakeFromDeferredTextureImageData(GrContext* context, const void*,
+ SkBudgeted) {
+ return nullptr;
+}
+
bool SkImage::MakeBackendTextureFromSkImage(GrContext*,
sk_sp<SkImage>,
GrBackendTexture*,
diff --git a/src/image/SkImage_Gpu.cpp b/src/image/SkImage_Gpu.cpp
index 05c2670ca4..8766dadeab 100644
--- a/src/image/SkImage_Gpu.cpp
+++ b/src/image/SkImage_Gpu.cpp
@@ -617,6 +617,385 @@ sk_sp<SkImage> SkImage::makeNonTextureImage() const {
///////////////////////////////////////////////////////////////////////////////////////////////////
+namespace {
+struct MipMapLevelData {
+ void* fPixelData;
+ size_t fRowBytes;
+};
+
+struct DeferredTextureImage {
+ uint32_t fContextUniqueID;
+ // Right now, the destination color mode is only considered when generating mipmaps
+ SkDestinationSurfaceColorMode fColorMode;
+ // 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 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];
+};
+} // anonymous namespace
+
+static bool should_use_mip_maps(const SkImage::DeferredTextureImageUsageParams & param) {
+ // There is a bug in the mipmap pre-generation logic in use in getDeferredTextureImageData.
+ // This can cause runaway memory leaks, so we are disabling this path until we can
+ // investigate further. crbug.com/669775
+ return false;
+}
+
+namespace {
+
+class DTIBufferFiller
+{
+public:
+ explicit DTIBufferFiller(char* bufferAsCharPtr)
+ : bufferAsCharPtr_(bufferAsCharPtr) {}
+
+ void fillMember(const void* source, size_t memberOffset, size_t size) {
+ memcpy(bufferAsCharPtr_ + memberOffset, source, size);
+ }
+
+private:
+
+ char* bufferAsCharPtr_;
+};
+}
+
+#define FILL_MEMBER(bufferFiller, member, source) \
+ bufferFiller.fillMember(source, \
+ offsetof(DeferredTextureImage, member), \
+ sizeof(DeferredTextureImage::member));
+
+static bool SupportsColorSpace(SkColorType colorType) {
+ switch (colorType) {
+ case kRGBA_8888_SkColorType:
+ case kBGRA_8888_SkColorType:
+ case kRGBA_F16_SkColorType:
+ return true;
+ default:
+ return false;
+ }
+}
+
+size_t SkImage::getDeferredTextureImageData(const GrContextThreadSafeProxy& proxy,
+ const DeferredTextureImageUsageParams params[],
+ int paramCnt, void* buffer,
+ SkColorSpace* dstColorSpace,
+ SkColorType dstColorType) const {
+ // Some quick-rejects where is makes no sense to return CPU data
+ // e.g.
+ // - texture backed
+ // - picture backed
+ //
+ if (this->isTextureBacked()) {
+ return 0;
+ }
+ if (as_IB(this)->onCanLazyGenerateOnGPU()) {
+ return 0;
+ }
+
+ bool supportsColorSpace = SupportsColorSpace(dstColorType);
+ // Quick reject if the caller requests a color space with an unsupported color type.
+ if (SkToBool(dstColorSpace) && !supportsColorSpace) {
+ return 0;
+ }
+
+ // 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);
+ if (fillMode && !SkIsAlign8(reinterpret_cast<intptr_t>(buffer))) {
+ return 0;
+ }
+
+ // Calculate scaling parameters.
+ bool isScaled = lowestPreScaleMipLevel != 0;
+
+ SkISize scaledSize;
+ if (isScaled) {
+ // SkMipMap::ComputeLevelSize takes an index into an SkMipMap. SkMipMaps don't contain the
+ // base level, so to get an SkMipMap index we must subtract one from the GL MipMap level.
+ scaledSize = SkMipMap::ComputeLevelSize(this->width(), this->height(),
+ lowestPreScaleMipLevel - 1);
+ } else {
+ scaledSize = SkISize::Make(this->width(), this->height());
+ }
+
+ // We never want to scale at higher than SW medium quality, as SW medium matches GPU high.
+ SkFilterQuality scaleFilterQuality = highestFilterQuality;
+ if (scaleFilterQuality > kMedium_SkFilterQuality) {
+ scaleFilterQuality = kMedium_SkFilterQuality;
+ }
+
+ const int maxTextureSize = proxy.fCaps->maxTextureSize();
+ if (scaledSize.width() > maxTextureSize || scaledSize.height() > maxTextureSize) {
+ return 0;
+ }
+
+ SkAutoPixmapStorage pixmap;
+ SkImageInfo info;
+ size_t pixelSize = 0;
+ if (!isScaled && this->peekPixels(&pixmap) && pixmap.info().colorType() == dstColorType) {
+ info = pixmap.info();
+ pixelSize = SkAlign8(pixmap.computeByteSize());
+ if (!dstColorSpace) {
+ pixmap.setColorSpace(nullptr);
+ info = info.makeColorSpace(nullptr);
+ }
+ } else {
+ if (!this->isLazyGenerated() && !this->peekPixels(nullptr)) {
+ return 0;
+ }
+ if (SkImageCacherator* cacher = as_IB(this)->peekCacherator()) {
+ // Generator backed image. Tweak info to trigger correct kind of decode.
+ SkImageCacherator::CachedFormat cacheFormat = cacher->chooseCacheFormat(
+ dstColorSpace, proxy.fCaps.get());
+ info = cacher->buildCacheInfo(cacheFormat).makeWH(scaledSize.width(),
+ scaledSize.height());
+ } else {
+ info = as_IB(this)->onImageInfo().makeWH(scaledSize.width(), scaledSize.height());
+ if (!dstColorSpace) {
+ info = info.makeColorSpace(nullptr);
+ }
+ }
+ // Force color type to be the requested type.
+ info = info.makeColorType(dstColorType);
+ pixelSize = SkAlign8(SkAutoPixmapStorage::AllocSize(info, nullptr));
+ if (fillMode) {
+ // Always decode to N32 and convert to the requested type if necessary.
+ SkImageInfo decodeInfo = info.makeColorType(kN32_SkColorType);
+ SkAutoPixmapStorage decodePixmap;
+ decodePixmap.alloc(decodeInfo);
+
+ if (isScaled) {
+ if (!this->scalePixels(decodePixmap, scaleFilterQuality,
+ SkImage::kDisallow_CachingHint)) {
+ return 0;
+ }
+ } else {
+ if (!this->readPixels(decodePixmap, 0, 0, SkImage::kDisallow_CachingHint)) {
+ return 0;
+ }
+ }
+
+ if (decodeInfo.colorType() != info.colorType()) {
+ pixmap.alloc(info);
+ // Convert and copy the decoded pixmap to the target pixmap.
+ decodePixmap.readPixels(pixmap.info(), pixmap.writable_addr(), pixmap.rowBytes(), 0,
+ 0);
+ } else {
+ pixmap = std::move(decodePixmap);
+ }
+ }
+ }
+ 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 - 2; currentMipMapLevelIndex >= 0;
+ currentMipMapLevelIndex--) {
+ SkISize mipSize = SkMipMap::ComputeLevelSize(scaledSize.width(), scaledSize.height(),
+ currentMipMapLevelIndex);
+ SkImageInfo mipInfo = info.makeWH(mipSize.fWidth, mipSize.fHeight);
+ pixelSize += SkAlign8(SkAutoPixmapStorage::AllocSize(mipInfo, nullptr));
+ }
+ }
+ size_t size = 0;
+ size_t dtiSize = SkAlign8(sizeof(DeferredTextureImage));
+ size += dtiSize;
+ size += (mipMapLevelCount - 1) * sizeof(MipMapLevelData);
+ // We subtract 1 because DeferredTextureImage already includes the base
+ // level in its size
+ size_t pixelOffset = size;
+ size += pixelSize;
+ size_t colorSpaceOffset = 0;
+ size_t colorSpaceSize = 0;
+ SkColorSpaceTransferFn fn;
+ if (info.colorSpace()) {
+ SkASSERT(dstColorSpace);
+ SkASSERT(supportsColorSpace);
+ colorSpaceOffset = size;
+ colorSpaceSize = info.colorSpace()->writeToMemory(nullptr);
+ size += colorSpaceSize;
+ } else if (supportsColorSpace && this->colorSpace() && this->colorSpace()->isNumericalTransferFn(&fn)) {
+ // In legacy mode, preserve the color space tag on the SkImage. This is only
+ // supported if the color space has a parametric transfer function.
+ SkASSERT(!dstColorSpace);
+ colorSpaceOffset = size;
+ colorSpaceSize = this->colorSpace()->writeToMemory(nullptr);
+ size += colorSpaceSize;
+ }
+ if (!fillMode) {
+ return size;
+ }
+ char* bufferAsCharPtr = reinterpret_cast<char*>(buffer);
+ char* pixelsAsCharPtr = bufferAsCharPtr + pixelOffset;
+ void* pixels = pixelsAsCharPtr;
+
+ memcpy(reinterpret_cast<void*>(SkAlign8(reinterpret_cast<uintptr_t>(pixelsAsCharPtr))),
+ pixmap.addr(), pixmap.computeByteSize());
+
+ // If the context has sRGB support, and we're intending to render to a surface with an attached
+ // color space, and the image has an sRGB-like color space attached, then use our gamma (sRGB)
+ // aware mip-mapping.
+ SkDestinationSurfaceColorMode colorMode = SkDestinationSurfaceColorMode::kLegacy;
+ if (proxy.fCaps->srgbSupport() && SkToBool(dstColorSpace) &&
+ info.colorSpace() && info.colorSpace()->gammaCloseToSRGB()) {
+ SkASSERT(supportsColorSpace);
+ colorMode = SkDestinationSurfaceColorMode::kGammaAndColorSpaceAware;
+ }
+
+ SkASSERT(info == pixmap.info());
+ size_t rowBytes = pixmap.rowBytes();
+ static_assert(std::is_standard_layout<DeferredTextureImage>::value,
+ "offsetof, which we use below, requires the type have standard layout");
+ auto dtiBufferFiller = DTIBufferFiller{bufferAsCharPtr};
+ FILL_MEMBER(dtiBufferFiller, fColorMode, &colorMode);
+ FILL_MEMBER(dtiBufferFiller, fContextUniqueID, &proxy.fContextUniqueID);
+ int width = info.width();
+ FILL_MEMBER(dtiBufferFiller, fWidth, &width);
+ int height = info.height();
+ FILL_MEMBER(dtiBufferFiller, fHeight, &height);
+ SkColorType colorType = info.colorType();
+ FILL_MEMBER(dtiBufferFiller, fColorType, &colorType);
+ SkAlphaType alphaType = info.alphaType();
+ FILL_MEMBER(dtiBufferFiller, fAlphaType, &alphaType);
+ FILL_MEMBER(dtiBufferFiller, fMipMapLevelCount, &mipMapLevelCount);
+ memcpy(bufferAsCharPtr + offsetof(DeferredTextureImage, fMipMapLevelData[0].fPixelData),
+ &pixels, sizeof(pixels));
+ memcpy(bufferAsCharPtr + offsetof(DeferredTextureImage, fMipMapLevelData[0].fRowBytes),
+ &rowBytes, sizeof(rowBytes));
+ if (colorSpaceSize) {
+ void* colorSpace = bufferAsCharPtr + colorSpaceOffset;
+ FILL_MEMBER(dtiBufferFiller, fColorSpace, &colorSpace);
+ FILL_MEMBER(dtiBufferFiller, fColorSpaceSize, &colorSpaceSize);
+ if (info.colorSpace()) {
+ info.colorSpace()->writeToMemory(bufferAsCharPtr + colorSpaceOffset);
+ } else {
+ SkASSERT(this->colorSpace() && this->colorSpace()->isNumericalTransferFn(&fn));
+ SkASSERT(!dstColorSpace);
+ this->colorSpace()->writeToMemory(bufferAsCharPtr + colorSpaceOffset);
+ }
+ } else {
+ memset(bufferAsCharPtr + offsetof(DeferredTextureImage, fColorSpace),
+ 0, sizeof(DeferredTextureImage::fColorSpace));
+ memset(bufferAsCharPtr + offsetof(DeferredTextureImage, fColorSpaceSize),
+ 0, sizeof(DeferredTextureImage::fColorSpaceSize));
+ }
+
+ // Fill in the mipmap levels if they exist
+ char* mipLevelPtr = pixelsAsCharPtr + SkAlign8(pixmap.computeByteSize());
+
+ if (useMipMaps) {
+ static_assert(std::is_standard_layout<MipMapLevelData>::value,
+ "offsetof, which we use below, requires the type have a standard layout");
+
+ std::unique_ptr<SkMipMap> mipmaps(SkMipMap::Build(pixmap, colorMode, 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++) {
+ SkMipMap::Level mipLevel;
+ mipmaps->getLevel(generatedMipLevelIndex, &mipLevel);
+
+ // Make sure the mipmap data is after the start of the buffer
+ SkASSERT(mipLevelPtr > bufferAsCharPtr);
+ // Make sure the mipmap data starts before the end of the buffer
+ SkASSERT(mipLevelPtr < bufferAsCharPtr + pixelOffset + pixelSize);
+ // Make sure the mipmap data ends before the end of the buffer
+ SkASSERT(mipLevelPtr + mipLevel.fPixmap.computeByteSize() <=
+ bufferAsCharPtr + pixelOffset + pixelSize);
+
+ memcpy(mipLevelPtr, mipLevel.fPixmap.addr(), mipLevel.fPixmap.computeByteSize());
+
+ memcpy(bufferAsCharPtr + offsetof(DeferredTextureImage, fMipMapLevelData) +
+ sizeof(MipMapLevelData) * (generatedMipLevelIndex + 1) +
+ offsetof(MipMapLevelData, fPixelData), &mipLevelPtr, sizeof(void*));
+ size_t rowBytes = mipLevel.fPixmap.rowBytes();
+ memcpy(bufferAsCharPtr + offsetof(DeferredTextureImage, fMipMapLevelData) +
+ sizeof(MipMapLevelData) * (generatedMipLevelIndex + 1) +
+ offsetof(MipMapLevelData, fRowBytes), &rowBytes, sizeof(rowBytes));
+
+ mipLevelPtr += SkAlign8(mipLevel.fPixmap.computeByteSize());
+ }
+ }
+ return size;
+}
+
+sk_sp<SkImage> SkImage::MakeFromDeferredTextureImageData(GrContext* context, const void* data,
+ SkBudgeted budgeted) {
+ if (!data) {
+ return nullptr;
+ }
+ const DeferredTextureImage* dti = reinterpret_cast<const DeferredTextureImage*>(data);
+
+ if (!context || context->uniqueID() != dti->fContextUniqueID || context->abandoned()) {
+ return nullptr;
+ }
+ 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);
+ if (mipLevelCount == 1) {
+ SkPixmap pixmap;
+ pixmap.reset(info, dti->fMipMapLevelData[0].fPixelData, dti->fMipMapLevelData[0].fRowBytes);
+
+ // Pass nullptr for the |dstColorSpace|. This opts in to more lenient color space
+ // verification. This is ok because we've already verified the color space in
+ // getDeferredTextureImageData().
+ sk_sp<GrTextureProxy> proxy(GrUploadPixmapToTextureProxy(
+ context->resourceProvider(), pixmap, budgeted, nullptr));
+ if (!proxy) {
+ return nullptr;
+ }
+ return sk_make_sp<SkImage_Gpu>(context, kNeedNewImageUniqueID, pixmap.alphaType(),
+ std::move(proxy), std::move(colorSpace), budgeted);
+ } else {
+ std::unique_ptr<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->fColorMode);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
bool SkImage::MakeBackendTextureFromSkImage(GrContext* ctx,
sk_sp<SkImage> image,
GrBackendTexture* backendTexture,