diff options
-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) { |