aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Greg Daniel <egdaniel@google.com>2017-09-28 12:07:28 -0400
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2017-09-28 19:16:29 +0000
commitd1935c16e889b6707a522f711e79c75353caa343 (patch)
treef8fe713b45c6cbe072667e06ddc24a7052a385cc
parent29011a2bda560a645e6ddbe162df0856fe259e7b (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.cpp32
-rw-r--r--src/gpu/GrGpu.cpp5
-rw-r--r--src/gpu/GrSurfaceProxy.cpp31
-rw-r--r--src/gpu/SkGr.cpp46
-rw-r--r--src/gpu/SkGr.h7
-rw-r--r--src/gpu/gl/GrGLGpu.cpp48
-rw-r--r--src/gpu/vk/GrVkGpu.cpp84
-rw-r--r--src/image/SkImage_Gpu.cpp7
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(&region, 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(&region, 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) {