aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-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) {