diff options
author | Greg Daniel <egdaniel@google.com> | 2017-09-28 12:07:28 -0400 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2017-09-28 19:16:29 +0000 |
commit | d1935c16e889b6707a522f711e79c75353caa343 (patch) | |
tree | f8fe713b45c6cbe072667e06ddc24a7052a385cc | |
parent | 29011a2bda560a645e6ddbe162df0856fe259e7b (diff) |
Update GrBitmapTextureMaker for handling mipped requests
Specifically this updates the case when we are requesting to use mip
maps but there is already an unmipped version in the cache. Previously
we just grabbed the unmipped.
Now we will create a new mipped resource. Upload the cpu data to all
the levels besides the base, copy the base level on GPU from the
original resource to the mipped one. Then the mipped resource will
take over the originals unique key.
Bug: skia:
Change-Id: I38e9725c93280dc2460a0be8a7a229e7f20e1614
Reviewed-on: https://skia-review.googlesource.com/43840
Commit-Queue: Greg Daniel <egdaniel@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
-rw-r--r-- | src/gpu/GrBitmapTextureMaker.cpp | 32 | ||||
-rw-r--r-- | src/gpu/GrGpu.cpp | 5 | ||||
-rw-r--r-- | src/gpu/GrSurfaceProxy.cpp | 31 | ||||
-rw-r--r-- | src/gpu/SkGr.cpp | 46 | ||||
-rw-r--r-- | src/gpu/SkGr.h | 7 | ||||
-rw-r--r-- | src/gpu/gl/GrGLGpu.cpp | 48 | ||||
-rw-r--r-- | src/gpu/vk/GrVkGpu.cpp | 84 | ||||
-rw-r--r-- | src/image/SkImage_Gpu.cpp | 7 |
8 files changed, 184 insertions, 76 deletions
diff --git a/src/gpu/GrBitmapTextureMaker.cpp b/src/gpu/GrBitmapTextureMaker.cpp index 6654901e20..00dd48d0a9 100644 --- a/src/gpu/GrBitmapTextureMaker.cpp +++ b/src/gpu/GrBitmapTextureMaker.cpp @@ -8,10 +8,13 @@ #include "GrBitmapTextureMaker.h" #include "GrContext.h" +#include "GrContextPriv.h" #include "GrGpuResourcePriv.h" #include "GrResourceProvider.h" +#include "GrSurfaceContext.h" #include "SkBitmap.h" #include "SkGr.h" +#include "SkMipMap.h" #include "SkPixelRef.h" static bool bmp_is_alpha_only(const SkBitmap& bm) { return kAlpha_8_SkColorType == bm.colorType(); } @@ -34,17 +37,25 @@ sk_sp<GrTextureProxy> GrBitmapTextureMaker::refOriginalTextureProxy(bool willBeM return nullptr; } - sk_sp<GrTextureProxy> proxy; + sk_sp<GrTextureProxy> originalProxy; if (fOriginalKey.isValid()) { - proxy = this->context()->resourceProvider()->findOrCreateProxyByUniqueKey( - fOriginalKey, kTopLeft_GrSurfaceOrigin); - if (proxy) { - return proxy; + originalProxy = this->context()->resourceProvider()->findOrCreateProxyByUniqueKey( + fOriginalKey, kTopLeft_GrSurfaceOrigin); + if (originalProxy && (!willBeMipped || originalProxy->isMipMapped())) { + return originalProxy; } } + + sk_sp<GrTextureProxy> proxy; if (willBeMipped) { - proxy = GrGenerateMipMapsAndUploadToTextureProxy(this->context(), fBitmap, dstColorSpace); + if (!originalProxy) { + proxy = GrGenerateMipMapsAndUploadToTextureProxy(this->context(), fBitmap, + dstColorSpace); + } else { + proxy = GrCopyBaseMipMapToTextureProxy(this->context(), originalProxy.get(), + dstColorSpace); + } } if (!proxy) { proxy = GrUploadBitmapToTextureProxy(this->context()->resourceProvider(), fBitmap, @@ -52,6 +63,15 @@ sk_sp<GrTextureProxy> GrBitmapTextureMaker::refOriginalTextureProxy(bool willBeM } if (proxy && fOriginalKey.isValid()) { SkASSERT(proxy->origin() == kTopLeft_GrSurfaceOrigin); + if (originalProxy) { + // In this case we are stealing the key from the original proxy which should only happen + // when we have just generated mipmaps for an originally unmipped proxy/texture. This + // means that all future uses of the key will access the mipmapped version. The texture + // backing the unmipped version will remain in the resource cache until the last texture + // proxy referencing it is deleted at which time it too will be deleted or recycled. + this->context()->resourceProvider()->removeUniqueKeyFromProxy(fOriginalKey, + originalProxy.get()); + } this->context()->resourceProvider()->assignUniqueKeyToProxy(fOriginalKey, proxy.get()); GrInstallBitmapUniqueKeyInvalidator(fOriginalKey, fBitmap.pixelRef()); } diff --git a/src/gpu/GrGpu.cpp b/src/gpu/GrGpu.cpp index c83d9828e8..31939bc1fa 100644 --- a/src/gpu/GrGpu.cpp +++ b/src/gpu/GrGpu.cpp @@ -114,11 +114,6 @@ static bool check_texture_creation_params(const GrCaps& caps, const GrSurfaceDes } } - for (int i = 0; i < mipLevelCount; ++i) { - if (!texels[i].fPixels) { - return false; - } - } return true; } diff --git a/src/gpu/GrSurfaceProxy.cpp b/src/gpu/GrSurfaceProxy.cpp index d9711e2365..2ef384677b 100644 --- a/src/gpu/GrSurfaceProxy.cpp +++ b/src/gpu/GrSurfaceProxy.cpp @@ -303,21 +303,36 @@ sk_sp<GrTextureProxy> GrSurfaceProxy::MakeDeferredMipMap( return nullptr; } return GrSurfaceProxy::MakeDeferred(resourceProvider, desc, budgeted, nullptr, 0); - } else if (1 == mipLevelCount) { - if (!texels) { - return nullptr; - } + } + if (!texels) { + return nullptr; + } + + if (1 == mipLevelCount) { return resourceProvider->createTextureProxy(desc, budgeted, texels[0]); } - for (int i = 0; i < mipLevelCount; ++i) { - if (!texels[i].fPixels) { - return nullptr; +#ifdef SK_DEBUG + // There are only three states we want to be in when uploading data to a mipped surface. + // 1) We have data to upload to all layers + // 2) We are not uploading data to any layers + // 3) We are only uploading data to the base layer + // We check here to make sure we do not have any other state. + bool firstLevelHasData = SkToBool(texels[0].fPixels); + bool allOtherLevelsHaveData = true, allOtherLevelsLackData = true; + for (int i = 1; i < mipLevelCount; ++i) { + if (texels[i].fPixels) { + allOtherLevelsLackData = false; + } else { + allOtherLevelsHaveData = false; } } + SkASSERT((firstLevelHasData && allOtherLevelsHaveData) || allOtherLevelsLackData); +#endif sk_sp<GrTexture> tex(resourceProvider->createTexture(desc, budgeted, - texels, mipLevelCount, mipColorMode)); + texels, mipLevelCount, + mipColorMode)); if (!tex) { return nullptr; } diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp index 5c26877101..e26de49e36 100644 --- a/src/gpu/SkGr.cpp +++ b/src/gpu/SkGr.cpp @@ -10,6 +10,7 @@ #include "GrBitmapTextureMaker.h" #include "GrCaps.h" #include "GrContext.h" +#include "GrContextPriv.h" #include "GrGpuResourcePriv.h" #include "GrRenderTargetContext.h" #include "GrResourceProvider.h" @@ -217,6 +218,51 @@ sk_sp<GrTextureProxy> GrGenerateMipMapsAndUploadToTextureProxy(GrContext* ctx, colorMode); } +sk_sp<GrTextureProxy> GrCopyBaseMipMapToTextureProxy(GrContext* ctx, + GrTextureProxy* baseProxy, + SkColorSpace* dstColorSpace) { + SkASSERT(baseProxy); + + SkDestinationSurfaceColorMode colorMode = dstColorSpace + ? SkDestinationSurfaceColorMode::kGammaAndColorSpaceAware + : SkDestinationSurfaceColorMode::kLegacy; + + // SkMipMap doesn't include the base level in the level count so we have to add 1 + int mipLevelCount = SkMipMap::ComputeLevelCount(baseProxy->width(), baseProxy->height()) + 1; + + std::unique_ptr<GrMipLevel[]> texels(new GrMipLevel[mipLevelCount]); + + // We don't want to upload any texel data + for (int i = 0; i < mipLevelCount; i++) { + texels[i].fPixels = nullptr; + texels[i].fRowBytes = 0; + } + + GrSurfaceDesc desc; + desc.fFlags = kNone_GrSurfaceFlags; + desc.fOrigin = baseProxy->origin(); + desc.fWidth = baseProxy->width(); + desc.fHeight = baseProxy->height(); + desc.fConfig = baseProxy->config(); + desc.fSampleCnt = 0; + + sk_sp<GrTextureProxy> proxy = GrSurfaceProxy::MakeDeferredMipMap(ctx->resourceProvider(), + desc, + SkBudgeted::kYes, texels.get(), + mipLevelCount, colorMode); + if (!proxy) { + return nullptr; + } + + // Copy the base layer to our proxy + sk_sp<GrSurfaceContext> sContext = ctx->contextPriv().makeWrappedSurfaceContext(proxy, nullptr); + SkASSERT(sContext); + SkAssertResult(sContext->copy(baseProxy)); + + return proxy; +} + + sk_sp<GrTextureProxy> GrUploadMipMapToTextureProxy(GrContext* ctx, const SkImageInfo& info, const GrMipLevel texels[], int mipLevelCount, diff --git a/src/gpu/SkGr.h b/src/gpu/SkGr.h index 7ecec96477..20110a516f 100644 --- a/src/gpu/SkGr.h +++ b/src/gpu/SkGr.h @@ -225,6 +225,13 @@ sk_sp<GrTextureProxy> GrUploadPixmapToTextureProxy(GrResourceProvider*, const SkPixmap&, SkBudgeted, SkColorSpace*); /** + * Creates a new texture with mipmap levels and copies the baseProxy into the base layer. + */ +sk_sp<GrTextureProxy> GrCopyBaseMipMapToTextureProxy(GrContext*, + GrTextureProxy* baseProxy, + SkColorSpace* dstColorSpace); + +/** * Creates a new texture populated with the mipmap levels. */ sk_sp<GrTextureProxy> GrUploadMipMapToTextureProxy(GrContext*, const SkImageInfo&, diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp index ff19bb6fc3..930e2e447a 100644 --- a/src/gpu/gl/GrGLGpu.cpp +++ b/src/gpu/gl/GrGLGpu.cpp @@ -952,10 +952,13 @@ static bool allocate_and_populate_texture(GrPixelConfig config, } } else { for (int currentMipLevel = 0; currentMipLevel < mipLevelCount; currentMipLevel++) { + const void* currentMipData = texels[currentMipLevel].fPixels; + if (!currentMipData) { + continue; + } int twoToTheMipLevel = 1 << currentMipLevel; int currentWidth = SkTMax(1, baseWidth / twoToTheMipLevel); int currentHeight = SkTMax(1, baseHeight / twoToTheMipLevel); - const void* currentMipData = texels[currentMipLevel].fPixels; // Even if curremtMipData is nullptr, continue to call TexImage2D. // This will allocate texture memory which we can later populate. GL_ALLOC_CALL(&interface, @@ -1033,10 +1036,6 @@ bool GrGLGpu::uploadTexData(GrPixelConfig texConfig, int texWidth, int texHeight memcpy(texelsShallowCopy.get(), texels, mipLevelCount*sizeof(GrMipLevel)); } - for (int currentMipLevel = 0; currentMipLevel < mipLevelCount; ++currentMipLevel) { - SkASSERT(texelsShallowCopy[currentMipLevel].fPixels); - } - const GrGLInterface* interface = this->glInterface(); const GrGLCaps& caps = this->glCaps(); @@ -1081,19 +1080,27 @@ bool GrGLGpu::uploadTexData(GrPixelConfig texConfig, int texWidth, int texHeight // find the combined size of all the mip levels and the relative offset of // each into the collective buffer - size_t combined_buffer_size = 0; - SkTArray<size_t> individual_mip_offsets(mipLevelCount); + size_t combinedBufferSize = 0; + SkTArray<size_t> individualMipOffsets(mipLevelCount); for (int currentMipLevel = 0; currentMipLevel < mipLevelCount; currentMipLevel++) { - int twoToTheMipLevel = 1 << currentMipLevel; - int currentWidth = SkTMax(1, width / twoToTheMipLevel); - int currentHeight = SkTMax(1, height / twoToTheMipLevel); - const size_t trimmedSize = currentWidth * bpp * currentHeight; - individual_mip_offsets.push_back(combined_buffer_size); - combined_buffer_size += trimmedSize; + if (texelsShallowCopy[currentMipLevel].fPixels) { + int twoToTheMipLevel = 1 << currentMipLevel; + int currentWidth = SkTMax(1, width / twoToTheMipLevel); + int currentHeight = SkTMax(1, height / twoToTheMipLevel); + const size_t trimmedSize = currentWidth * bpp * currentHeight; + individualMipOffsets.push_back(combinedBufferSize); + combinedBufferSize += trimmedSize; + } else { + individualMipOffsets.push_back(0); + } + } - char* buffer = (char*)tempStorage.reset(combined_buffer_size); + char* buffer = (char*)tempStorage.reset(combinedBufferSize); for (int currentMipLevel = 0; currentMipLevel < mipLevelCount; currentMipLevel++) { + if (!texelsShallowCopy[currentMipLevel].fPixels) { + continue; + } int twoToTheMipLevel = 1 << currentMipLevel; int currentWidth = SkTMax(1, width / twoToTheMipLevel); int currentHeight = SkTMax(1, height / twoToTheMipLevel); @@ -1107,9 +1114,9 @@ bool GrGLGpu::uploadTexData(GrPixelConfig texConfig, int texWidth, int texHeight */ restoreGLRowLength = false; - const size_t rowBytes = texelsShallowCopy[currentMipLevel].fRowBytes ? - texelsShallowCopy[currentMipLevel].fRowBytes : - trimRowBytes; + const size_t rowBytes = texelsShallowCopy[currentMipLevel].fRowBytes + ? texelsShallowCopy[currentMipLevel].fRowBytes + : trimRowBytes; // TODO: This optimization should be enabled with or without mips. // For use with mips, we must set GR_GL_UNPACK_ROW_LENGTH once per @@ -1128,7 +1135,7 @@ bool GrGLGpu::uploadTexData(GrPixelConfig texConfig, int texWidth, int texHeight if (swFlipY && currentHeight >= 1) { src += (currentHeight - 1) * rowBytes; } - char* dst = buffer + individual_mip_offsets[currentMipLevel]; + char* dst = buffer + individualMipOffsets[currentMipLevel]; for (int y = 0; y < currentHeight; y++) { memcpy(dst, src, trimRowBytes); if (swFlipY) { @@ -1140,7 +1147,7 @@ bool GrGLGpu::uploadTexData(GrPixelConfig texConfig, int texWidth, int texHeight } // now point data to our copied version texelsShallowCopy[currentMipLevel].fPixels = buffer + - individual_mip_offsets[currentMipLevel]; + individualMipOffsets[currentMipLevel]; texelsShallowCopy[currentMipLevel].fRowBytes = trimRowBytes; } } @@ -1167,6 +1174,9 @@ bool GrGLGpu::uploadTexData(GrPixelConfig texConfig, int texWidth, int texHeight top = texHeight - (top + height); } for (int currentMipLevel = 0; currentMipLevel < mipLevelCount; currentMipLevel++) { + if (!texelsShallowCopy[currentMipLevel].fPixels) { + continue; + } int twoToTheMipLevel = 1 << currentMipLevel; int currentWidth = SkTMax(1, width / twoToTheMipLevel); int currentHeight = SkTMax(1, height / twoToTheMipLevel); diff --git a/src/gpu/vk/GrVkGpu.cpp b/src/gpu/vk/GrVkGpu.cpp index 6617318f26..e9adb4db67 100644 --- a/src/gpu/vk/GrVkGpu.cpp +++ b/src/gpu/vk/GrVkGpu.cpp @@ -661,10 +661,6 @@ bool GrVkGpu::uploadTexDataOptimal(GrVkTexture* tex, GrSurfaceOrigin texOrigin, memcpy(texelsShallowCopy.get(), texels, mipLevelCount*sizeof(GrMipLevel)); } - for (int currentMipLevel = 0; currentMipLevel < mipLevelCount; ++currentMipLevel) { - SkASSERT(texelsShallowCopy[currentMipLevel].fPixels); - } - // Determine whether we need to flip when we copy into the buffer bool flipY = (kBottomLeft_GrSurfaceOrigin == texOrigin && mipLevelCount); @@ -673,6 +669,10 @@ bool GrVkGpu::uploadTexDataOptimal(GrVkTexture* tex, GrSurfaceOrigin texOrigin, size_t combinedBufferSize = width * bpp * height; int currentWidth = width; int currentHeight = height; + if (mipLevelCount > 0 && !texelsShallowCopy[0].fPixels) { + combinedBufferSize = 0; + } + // The alignment must be at least 4 bytes and a multiple of the bytes per pixel of the image // config. This works with the assumption that the bytes in pixel config is always a power of 2. SkASSERT((bpp & (bpp - 1)) == 0); @@ -681,13 +681,21 @@ bool GrVkGpu::uploadTexDataOptimal(GrVkTexture* tex, GrSurfaceOrigin texOrigin, currentWidth = SkTMax(1, currentWidth/2); currentHeight = SkTMax(1, currentHeight/2); - const size_t trimmedSize = currentWidth * bpp * currentHeight; - const size_t alignmentDiff = combinedBufferSize & alignmentMask; - if (alignmentDiff != 0) { - combinedBufferSize += alignmentMask - alignmentDiff + 1; + if (texelsShallowCopy[currentMipLevel].fPixels) { + const size_t trimmedSize = currentWidth * bpp * currentHeight; + const size_t alignmentDiff = combinedBufferSize & alignmentMask; + if (alignmentDiff != 0) { + combinedBufferSize += alignmentMask - alignmentDiff + 1; + } + individualMipOffsets.push_back(combinedBufferSize); + combinedBufferSize += trimmedSize; + } else { + individualMipOffsets.push_back(0); } - individualMipOffsets.push_back(combinedBufferSize); - combinedBufferSize += trimmedSize; + } + if (0 == combinedBufferSize) { + // We don't actually have any data to upload so just return success + return true; } // allocate buffer to hold our mip data @@ -704,35 +712,36 @@ bool GrVkGpu::uploadTexDataOptimal(GrVkTexture* tex, GrSurfaceOrigin texOrigin, currentHeight = height; int layerHeight = tex->height(); for (int currentMipLevel = 0; currentMipLevel < mipLevelCount; currentMipLevel++) { - SkASSERT(1 == mipLevelCount || currentHeight == layerHeight); - const size_t trimRowBytes = currentWidth * bpp; - const size_t rowBytes = texelsShallowCopy[currentMipLevel].fRowBytes ? - texelsShallowCopy[currentMipLevel].fRowBytes : - trimRowBytes; - - // copy data into the buffer, skipping the trailing bytes - char* dst = buffer + individualMipOffsets[currentMipLevel]; - const char* src = (const char*)texelsShallowCopy[currentMipLevel].fPixels; - if (flipY) { - src += (currentHeight - 1) * rowBytes; - for (int y = 0; y < currentHeight; y++) { - memcpy(dst, src, trimRowBytes); - src -= rowBytes; - dst += trimRowBytes; + if (texelsShallowCopy[currentMipLevel].fPixels) { + SkASSERT(1 == mipLevelCount || currentHeight == layerHeight); + const size_t trimRowBytes = currentWidth * bpp; + const size_t rowBytes = texelsShallowCopy[currentMipLevel].fRowBytes + ? texelsShallowCopy[currentMipLevel].fRowBytes + : trimRowBytes; + + // copy data into the buffer, skipping the trailing bytes + char* dst = buffer + individualMipOffsets[currentMipLevel]; + const char* src = (const char*)texelsShallowCopy[currentMipLevel].fPixels; + if (flipY) { + src += (currentHeight - 1) * rowBytes; + for (int y = 0; y < currentHeight; y++) { + memcpy(dst, src, trimRowBytes); + src -= rowBytes; + dst += trimRowBytes; + } + } else { + SkRectMemcpy(dst, trimRowBytes, src, rowBytes, trimRowBytes, currentHeight); } - } else { - SkRectMemcpy(dst, trimRowBytes, src, rowBytes, trimRowBytes, currentHeight); - } - - VkBufferImageCopy& region = regions.push_back(); - memset(®ion, 0, sizeof(VkBufferImageCopy)); - region.bufferOffset = transferBuffer->offset() + individualMipOffsets[currentMipLevel]; - region.bufferRowLength = currentWidth; - region.bufferImageHeight = currentHeight; - region.imageSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, SkToU32(currentMipLevel), 0, 1 }; - region.imageOffset = { left, flipY ? layerHeight - top - currentHeight : top, 0 }; - region.imageExtent = { (uint32_t)currentWidth, (uint32_t)currentHeight, 1 }; + VkBufferImageCopy& region = regions.push_back(); + memset(®ion, 0, sizeof(VkBufferImageCopy)); + region.bufferOffset = transferBuffer->offset() + individualMipOffsets[currentMipLevel]; + region.bufferRowLength = currentWidth; + region.bufferImageHeight = currentHeight; + region.imageSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, SkToU32(currentMipLevel), 0, 1 }; + region.imageOffset = { left, flipY ? layerHeight - top - currentHeight : top, 0 }; + region.imageExtent = { (uint32_t)currentWidth, (uint32_t)currentHeight, 1 }; + } currentWidth = SkTMax(1, currentWidth/2); currentHeight = SkTMax(1, currentHeight/2); layerHeight = currentHeight; @@ -822,7 +831,6 @@ sk_sp<GrTexture> GrVkGpu::onCreateTexture(const GrSurfaceDesc& desc, SkBudgeted } if (mipLevelCount) { - SkASSERT(texels[0].fPixels); if (!this->uploadTexDataOptimal(tex.get(), desc.fOrigin, 0, 0, desc.fWidth, desc.fHeight, desc.fConfig, texels, mipLevelCount)) { tex->unref(); diff --git a/src/image/SkImage_Gpu.cpp b/src/image/SkImage_Gpu.cpp index d3a1865039..356b800e83 100644 --- a/src/image/SkImage_Gpu.cpp +++ b/src/image/SkImage_Gpu.cpp @@ -922,6 +922,13 @@ sk_sp<SkImage> SkImage::MakeTextureFromMipMap(GrContext* ctx, const SkImageInfo& if (!ctx) { return nullptr; } + // For images where the client is passing the mip data we require that all the mip levels have + // valid data. + for (int i = 0; i < mipLevelCount; ++i) { + if (!texels[i].fPixels) { + return nullptr; + } + } sk_sp<GrTextureProxy> proxy(GrUploadMipMapToTextureProxy(ctx, info, texels, mipLevelCount, colorMode)); if (!proxy) { |