Ensure that textures exported via SkImage::MakeBackendTextureFromSkImage
have consistent content in their mip map levels.

Bug= chromium:850617

Change-Id: I3ad918aa453bd8e4e625eb145de6ba2a5dab7b0c
Reviewed-on: https://skia-review.googlesource.com/136230
Commit-Queue: Brian Salomon <bsalomon@google.com>
Reviewed-by: Greg Daniel <egdaniel@google.com>
diff --git a/src/gpu/GrDrawingManager.cpp b/src/gpu/GrDrawingManager.cpp
index 7c6338f..2b8c262 100644
--- a/src/gpu/GrDrawingManager.cpp
+++ b/src/gpu/GrDrawingManager.cpp
@@ -19,14 +19,16 @@
 #include "GrResourceProvider.h"
 #include "GrSoftwarePathRenderer.h"
 #include "GrSurfaceProxyPriv.h"
+#include "GrTexture.h"
 #include "GrTextureContext.h"
 #include "GrTextureOpList.h"
+#include "GrTexturePriv.h"
 #include "GrTextureProxy.h"
 #include "GrTextureProxyPriv.h"
+#include "GrTracing.h"
 #include "SkDeferredDisplayList.h"
 #include "SkSurface_Gpu.h"
 #include "SkTTopoSort.h"
-#include "GrTracing.h"
 #include "ccpr/GrCoverageCountingPathRenderer.h"
 #include "text/GrTextContext.h"
 
@@ -377,8 +379,14 @@
     }
 
     GrSurface* surface = proxy->priv().peekSurface();
-    if (surface->asRenderTarget()) {
-        gpu->resolveRenderTarget(surface->asRenderTarget());
+    if (auto* rt = surface->asRenderTarget()) {
+        gpu->resolveRenderTarget(rt);
+    }
+    if (auto* tex = surface->asTexture()) {
+        if (tex->texturePriv().mipMapped() == GrMipMapped::kYes &&
+            tex->texturePriv().mipMapsAreDirty()) {
+            gpu->regenerateMipMapLevels(tex);
+        }
     }
     return result;
 }
diff --git a/src/gpu/GrGpu.cpp b/src/gpu/GrGpu.cpp
index 3ebfa36..4b7e786 100644
--- a/src/gpu/GrGpu.cpp
+++ b/src/gpu/GrGpu.cpp
@@ -291,6 +291,19 @@
     return false;
 }
 
+bool GrGpu::regenerateMipMapLevels(GrTexture* texture) {
+    SkASSERT(texture);
+    SkASSERT(this->caps()->mipMapSupport());
+    SkASSERT(texture->texturePriv().mipMapped() == GrMipMapped::kYes);
+    SkASSERT(texture->texturePriv().mipMapsAreDirty());
+    SkASSERT(!texture->asRenderTarget() || !texture->asRenderTarget()->needsResolve());
+    if (this->onRegenerateMipMapLevels(texture)) {
+        texture->texturePriv().markMipMapsClean();
+        return true;
+    }
+    return false;
+}
+
 void GrGpu::resolveRenderTarget(GrRenderTarget* target) {
     SkASSERT(target);
     this->handleDirtyContext();
diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h
index d97a22e..7ab7995 100644
--- a/src/gpu/GrGpu.h
+++ b/src/gpu/GrGpu.h
@@ -142,6 +142,11 @@
     void resolveRenderTarget(GrRenderTarget*);
 
     /**
+     * Uses the base of the texture to recompute the contents of the other levels.
+     */
+    bool regenerateMipMapLevels(GrTexture*);
+
+    /**
      * Reads a rectangle of pixels from a render target. No sRGB/linear conversions are performed.
      *
      * @param surface       The surface to read from
@@ -451,6 +456,9 @@
     // overridden by backend-specific derived class to perform the resolve
     virtual void onResolveRenderTarget(GrRenderTarget* target) = 0;
 
+    // overridden by backend specific derived class to perform mip map level regeneration.
+    virtual bool onRegenerateMipMapLevels(GrTexture*) = 0;
+
     // overridden by backend specific derived class to perform the copy surface
     virtual bool onCopySurface(GrSurface* dst, GrSurfaceOrigin dstOrigin,
                                GrSurface* src, GrSurfaceOrigin srcOrigin,
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index 08bd168..9e73259 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -2716,8 +2716,7 @@
     return 0;
 }
 
-void GrGLGpu::bindTexture(int unitIdx, const GrSamplerState& samplerState, GrGLTexture* texture,
-                          GrSurfaceOrigin textureOrigin) {
+void GrGLGpu::bindTexture(int unitIdx, const GrSamplerState& samplerState, GrGLTexture* texture) {
     SkASSERT(texture);
 
 #ifdef SK_DEBUG
@@ -2858,53 +2857,6 @@
     }
 }
 
-void GrGLGpu::generateMipmaps(const GrSamplerState& params, GrGLTexture* texture,
-                              GrSurfaceOrigin textureOrigin) {
-    SkASSERT(texture);
-
-    // First, figure out if we need mips for this texture at all:
-    GrSamplerState::Filter filterMode = params.filter();
-
-    if (GrSamplerState::Filter::kMipMap == filterMode) {
-        if (!this->caps()->mipMapSupport() ||
-            texture->texturePriv().mipMapped() == GrMipMapped::kNo) {
-            filterMode = GrSamplerState::Filter::kBilerp;
-        }
-    }
-
-    if (GrSamplerState::Filter::kMipMap != filterMode) {
-        return;
-    }
-
-    SkASSERT(texture->texturePriv().mipMapped() == GrMipMapped::kYes);
-
-    // If the mips aren't dirty, we're done:
-    if (!texture->texturePriv().mipMapsAreDirty()) {
-        return;
-    }
-
-    // If we created a rt/tex and rendered to it without using a texture and now we're texturing
-    // from the rt it will still be the last bound texture, but it needs resolving.
-    GrGLRenderTarget* texRT = static_cast<GrGLRenderTarget*>(texture->asRenderTarget());
-    if (texRT) {
-        this->onResolveRenderTarget(texRT);
-    }
-
-    GrGLenum target = texture->target();
-    this->setScratchTextureUnit();
-    GL_CALL(BindTexture(target, texture->textureID()));
-
-    // Either do manual mipmap generation or (if that fails), just rely on the driver:
-    if (!this->generateMipmap(texture, textureOrigin)) {
-        GL_CALL(GenerateMipmap(target));
-    }
-
-    texture->texturePriv().markMipMapsClean();
-
-    // We have potentially set lots of state on the texture. Easiest to dirty it all:
-    texture->textureParamsModified();
-}
-
 void GrGLGpu::setTextureSwizzle(int unitIdx, GrGLenum target, const GrGLenum swizzle[]) {
     this->setTextureUnit(unitIdx);
     if (this->glStandard() == kGLES_GrGLStandard) {
@@ -3664,7 +3616,7 @@
     int w = srcRect.width();
     int h = srcRect.height();
 
-    this->bindTexture(0, GrSamplerState::ClampNearest(), srcTex, srcOrigin);
+    this->bindTexture(0, GrSamplerState::ClampNearest(), srcTex);
 
     GrGLIRect dstVP;
     this->bindSurfaceFBOForPixelOps(dst, GR_GL_FRAMEBUFFER, &dstVP, kDst_TempFBOTarget);
@@ -3820,23 +3772,25 @@
     return true;
 }
 
-// Manual implementation of mipmap generation, to work around driver bugs w/sRGB.
-// Uses draw calls to do a series of downsample operations to successive mips.
-// If this returns false, then the calling code falls back to using glGenerateMipmap.
-bool GrGLGpu::generateMipmap(GrGLTexture* texture, GrSurfaceOrigin textureOrigin) {
-    // Our iterative downsample requires the ability to limit which level we're sampling:
-    if (!this->glCaps().doManualMipmapping()) {
-        return false;
-    }
-
+bool GrGLGpu::onRegenerateMipMapLevels(GrTexture* texture) {
+    auto glTex = static_cast<GrGLTexture*>(texture);
     // Mipmaps are only supported on 2D textures:
-    if (GR_GL_TEXTURE_2D != texture->target()) {
+    if (GR_GL_TEXTURE_2D != glTex->target()) {
         return false;
     }
 
-    // We need to be able to render to the texture for this to work:
-    if (!this->glCaps().canConfigBeFBOColorAttachment(texture->config())) {
-        return false;
+    // Manual implementation of mipmap generation, to work around driver bugs w/sRGB.
+    // Uses draw calls to do a series of downsample operations to successive mips.
+
+    // The manual approach requires the ability to limit which level we're sampling and that the
+    // destination can be bound to a FBO:
+    if (!this->glCaps().doManualMipmapping() ||
+        !this->glCaps().canConfigBeFBOColorAttachment(texture->config())) {
+        GrGLenum target = glTex->target();
+        this->setScratchTextureUnit();
+        GL_CALL(BindTexture(target, glTex->textureID()));
+        GL_CALL(GenerateMipmap(glTex->target()));
+        return true;
     }
 
     int width = texture->width();
@@ -3854,7 +3808,7 @@
     // Bind the texture, to get things configured for filtering.
     // We'll be changing our base level further below:
     this->setTextureUnit(0);
-    this->bindTexture(0, GrSamplerState::ClampBilerp(), texture, textureOrigin);
+    this->bindTexture(0, GrSamplerState::ClampBilerp(), glTex);
 
     // Vertex data:
     if (!fMipmapProgramArrayBuffer) {
@@ -3916,8 +3870,8 @@
         // Only sample from previous mip
         GL_CALL(TexParameteri(GR_GL_TEXTURE_2D, GR_GL_TEXTURE_BASE_LEVEL, level - 1));
 
-        GL_CALL(FramebufferTexture2D(GR_GL_FRAMEBUFFER, GR_GL_COLOR_ATTACHMENT0,
-                                     GR_GL_TEXTURE_2D, texture->textureID(), level));
+        GL_CALL(FramebufferTexture2D(GR_GL_FRAMEBUFFER, GR_GL_COLOR_ATTACHMENT0, GR_GL_TEXTURE_2D,
+                                     glTex->textureID(), level));
 
         width = SkTMax(1, width / 2);
         height = SkTMax(1, height / 2);
@@ -3932,6 +3886,8 @@
     GL_CALL(FramebufferTexture2D(GR_GL_FRAMEBUFFER, GR_GL_COLOR_ATTACHMENT0,
                                  GR_GL_TEXTURE_2D, 0, 0));
 
+    // We modified the base level param.
+    texture->textureParamsModified();
     return true;
 }
 
diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h
index 48cd21b..d089744 100644
--- a/src/gpu/gl/GrGLGpu.h
+++ b/src/gpu/gl/GrGLGpu.h
@@ -54,14 +54,10 @@
     }
 
     // Used by GrGLProgram to configure OpenGL state.
-    void bindTexture(int unitIdx, const GrSamplerState& samplerState, GrGLTexture* texture,
-                     GrSurfaceOrigin textureOrigin);
+    void bindTexture(int unitIdx, const GrSamplerState& samplerState, GrGLTexture* texture);
 
     void bindTexelBuffer(int unitIdx, GrPixelConfig, GrGLBuffer*);
 
-    void generateMipmaps(const GrSamplerState& params, GrGLTexture* texture,
-                         GrSurfaceOrigin textureOrigin);
-
     // These functions should be used to bind GL objects. They track the GL state and skip redundant
     // bindings. Making the equivalent glBind calls directly will confuse the state tracking.
     void bindVertexArray(GrGLuint id) {
@@ -244,6 +240,8 @@
 
     void onResolveRenderTarget(GrRenderTarget* target) override;
 
+    bool onRegenerateMipMapLevels(GrTexture*) override;
+
     bool onCopySurface(GrSurface* dst, GrSurfaceOrigin dstOrigin,
                        GrSurface* src, GrSurfaceOrigin srcOrigin,
                        const SkIRect& srcRect, const SkIPoint& dstPoint,
@@ -286,7 +284,6 @@
     bool copySurfaceAsBlitFramebuffer(GrSurface* dst, GrSurfaceOrigin dstOrigin,
                                       GrSurface* src, GrSurfaceOrigin srcOrigin,
                                       const SkIRect& srcRect, const SkIPoint& dstPoint);
-    bool generateMipmap(GrGLTexture* texture, GrSurfaceOrigin textureOrigin);
     void clearStencilClipAsDraw(const GrFixedClip&, bool insideStencilMask,
                                 GrRenderTarget*, GrSurfaceOrigin);
 
diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp
index 3d4bd38..2a876d0 100644
--- a/src/gpu/gl/GrGLProgram.cpp
+++ b/src/gpu/gl/GrGLProgram.cpp
@@ -6,15 +6,15 @@
  */
 
 #include "GrGLProgram.h"
-
 #include "GrAllocator.h"
-#include "GrProcessor.h"
 #include "GrCoordTransform.h"
-#include "GrGLGpu.h"
 #include "GrGLBuffer.h"
+#include "GrGLGpu.h"
 #include "GrGLPathRendering.h"
 #include "GrPathProcessor.h"
 #include "GrPipeline.h"
+#include "GrProcessor.h"
+#include "GrTexturePriv.h"
 #include "GrXferProcessor.h"
 #include "glsl/GrGLSLFragmentProcessor.h"
 #include "glsl/GrGLSLGeometryProcessor.h"
@@ -99,8 +99,7 @@
     fXferProcessor->setData(fProgramDataManager, xp, dstTexture, offset);
     if (dstTexture) {
         fGpu->bindTexture(nextTexSamplerIdx++, GrSamplerState::ClampNearest(),
-                          static_cast<GrGLTexture*>(dstTexture),
-                          pipeline.dstTextureProxy()->origin());
+                          static_cast<GrGLTexture*>(dstTexture));
     }
     SkASSERT(nextTexSamplerIdx == fNumTextureSamplers);
     SkASSERT(nextTexelBufferIdx == fNumTextureSamplers + fNumTexelBuffers);
@@ -170,8 +169,7 @@
     for (int i = 0; i < processor.numTextureSamplers(); ++i) {
         const GrResourceIOProcessor::TextureSampler& sampler = processor.textureSampler(i);
         fGpu->bindTexture((*nextTexSamplerIdx)++, sampler.samplerState(),
-                          static_cast<GrGLTexture*>(sampler.peekTexture()),
-                          sampler.proxy()->origin());
+                          static_cast<GrGLTexture*>(sampler.peekTexture()));
     }
     for (int i = 0; i < processor.numBuffers(); ++i) {
         const GrResourceIOProcessor::BufferAccess& access = processor.bufferAccess(i);
@@ -183,8 +181,12 @@
 void GrGLProgram::generateMipmaps(const GrResourceIOProcessor& processor) {
     for (int i = 0; i < processor.numTextureSamplers(); ++i) {
         const GrResourceIOProcessor::TextureSampler& sampler = processor.textureSampler(i);
-        fGpu->generateMipmaps(sampler.samplerState(),
-                              static_cast<GrGLTexture*>(sampler.peekTexture()),
-                              sampler.proxy()->origin());
+        auto* tex = sampler.peekTexture();
+        if (sampler.samplerState().filter() == GrSamplerState::Filter::kMipMap &&
+            tex->texturePriv().mipMapped() == GrMipMapped::kYes &&
+            tex->texturePriv().mipMapsAreDirty()) {
+            SkASSERT(fGpu->caps()->mipMapSupport());
+            fGpu->regenerateMipMapLevels(static_cast<GrGLTexture*>(sampler.peekTexture()));
+        }
     }
 }
diff --git a/src/gpu/mock/GrMockGpu.h b/src/gpu/mock/GrMockGpu.h
index 3bb4a85..a8ec3aa 100644
--- a/src/gpu/mock/GrMockGpu.h
+++ b/src/gpu/mock/GrMockGpu.h
@@ -92,6 +92,8 @@
         return true;
     }
 
+    bool onRegenerateMipMapLevels(GrTexture*) override { return true; }
+
     void onResolveRenderTarget(GrRenderTarget* target) override { return; }
 
     void onFinishFlush(bool insertedSemaphores) override {}
diff --git a/src/gpu/mtl/GrMtlGpu.h b/src/gpu/mtl/GrMtlGpu.h
index e325ad6..b230ef7 100644
--- a/src/gpu/mtl/GrMtlGpu.h
+++ b/src/gpu/mtl/GrMtlGpu.h
@@ -113,6 +113,8 @@
         return false;
     }
 
+    bool onRegenerateMipMapLevels(GrTexture*) override { return false; }
+
     void onResolveRenderTarget(GrRenderTarget* target) override { return; }
 
     void onFinishFlush(bool insertedSemaphores) override {}
diff --git a/src/gpu/vk/GrVkGpu.cpp b/src/gpu/vk/GrVkGpu.cpp
index 1149855..1d89566 100644
--- a/src/gpu/vk/GrVkGpu.cpp
+++ b/src/gpu/vk/GrVkGpu.cpp
@@ -894,11 +894,12 @@
     return GrVkRenderTarget::MakeWrappedRenderTarget(this, desc, imageInfo, std::move(layout));
 }
 
-void GrVkGpu::generateMipmap(GrVkTexture* tex, GrSurfaceOrigin texOrigin) {
+bool GrVkGpu::onRegenerateMipMapLevels(GrTexture* tex) {
+    auto* vkTex = static_cast<GrVkTexture*>(tex);
     // don't do anything for linearly tiled textures (can't have mipmaps)
-    if (tex->isLinearTiled()) {
+    if (vkTex->isLinearTiled()) {
         SkDebugf("Trying to create mipmap for linear tiled texture");
-        return;
+        return false;
     }
 
     // determine if we can blit to and from this format
@@ -906,19 +907,13 @@
     if (!caps.configCanBeDstofBlit(tex->config(), false) ||
         !caps.configCanBeSrcofBlit(tex->config(), false) ||
         !caps.mipMapSupport()) {
-        return;
+        return false;
     }
 
     if (this->vkCaps().mustSubmitCommandsBeforeCopyOp()) {
         this->submitCommandBuffer(kSkip_SyncQueue);
     }
 
-    // We may need to resolve the texture first if it is also a render target
-    GrVkRenderTarget* texRT = static_cast<GrVkRenderTarget*>(tex->asRenderTarget());
-    if (texRT) {
-        this->internalResolveRenderTarget(texRT, false);
-    }
-
     int width = tex->width();
     int height = tex->height();
     VkImageBlit blitRegion;
@@ -926,26 +921,26 @@
 
     // SkMipMap doesn't include the base level in the level count so we have to add 1
     uint32_t levelCount = SkMipMap::ComputeLevelCount(tex->width(), tex->height()) + 1;
-    SkASSERT(levelCount == tex->mipLevels());
+    SkASSERT(levelCount == vkTex->mipLevels());
 
     // change layout of the layers so we can write to them.
-    tex->setImageLayout(this, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
-                        VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, false);
+    vkTex->setImageLayout(this, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT,
+                          VK_PIPELINE_STAGE_TRANSFER_BIT, false);
 
     // setup memory barrier
-    SkASSERT(GrVkFormatIsSupported(tex->imageFormat()));
+    SkASSERT(GrVkFormatIsSupported(vkTex->imageFormat()));
     VkImageAspectFlags aspectFlags = VK_IMAGE_ASPECT_COLOR_BIT;
     VkImageMemoryBarrier imageMemoryBarrier = {
-        VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,          // sType
-        nullptr,                                         // pNext
-        VK_ACCESS_TRANSFER_WRITE_BIT,                    // srcAccessMask
-        VK_ACCESS_TRANSFER_READ_BIT,                     // dstAccessMask
-        VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,            // oldLayout
-        VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,            // newLayout
-        VK_QUEUE_FAMILY_IGNORED,                         // srcQueueFamilyIndex
-        VK_QUEUE_FAMILY_IGNORED,                         // dstQueueFamilyIndex
-        tex->image(),                                    // image
-        { aspectFlags, 0, 1, 0, 1 }                      // subresourceRange
+            VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,  // sType
+            nullptr,                                 // pNext
+            VK_ACCESS_TRANSFER_WRITE_BIT,            // srcAccessMask
+            VK_ACCESS_TRANSFER_READ_BIT,             // dstAccessMask
+            VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,    // oldLayout
+            VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,    // newLayout
+            VK_QUEUE_FAMILY_IGNORED,                 // srcQueueFamilyIndex
+            VK_QUEUE_FAMILY_IGNORED,                 // dstQueueFamilyIndex
+            vkTex->image(),                          // image
+            {aspectFlags, 0, 1, 0, 1}                // subresourceRange
     };
 
     // Blit the miplevels
@@ -967,11 +962,11 @@
         blitRegion.dstOffsets[0] = { 0, 0, 0 };
         blitRegion.dstOffsets[1] = { width, height, 1 };
         fCurrentCmdBuffer->blitImage(this,
-                                     tex->resource(),
-                                     tex->image(),
+                                     vkTex->resource(),
+                                     vkTex->image(),
                                      VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
-                                     tex->resource(),
-                                     tex->image(),
+                                     vkTex->resource(),
+                                     vkTex->image(),
                                      VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
                                      1,
                                      &blitRegion,
@@ -984,7 +979,8 @@
     imageMemoryBarrier.subresourceRange.baseMipLevel = mipLevel - 1;
     this->addImageMemoryBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
                                 false, &imageMemoryBarrier);
-    tex->updateImageLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
+    vkTex->updateImageLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
+    return true;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/vk/GrVkGpu.h b/src/gpu/vk/GrVkGpu.h
index 5df1675..0524760 100644
--- a/src/gpu/vk/GrVkGpu.h
+++ b/src/gpu/vk/GrVkGpu.h
@@ -114,6 +114,8 @@
         return fCompiler;
     }
 
+    bool onRegenerateMipMapLevels(GrTexture* tex) override;
+
     void onResolveRenderTarget(GrRenderTarget* target) override {
         this->internalResolveRenderTarget(target, true);
     }
@@ -137,8 +139,6 @@
 
     sk_sp<GrSemaphore> prepareTextureForCrossContextUsage(GrTexture*) override;
 
-    void generateMipmap(GrVkTexture* tex, GrSurfaceOrigin texOrigin);
-
     void copyBuffer(GrVkBuffer* srcBuffer, GrVkBuffer* dstBuffer, VkDeviceSize srcOffset,
                     VkDeviceSize dstOffset, VkDeviceSize size);
     bool updateBuffer(GrVkBuffer* buffer, const void* src, VkDeviceSize offset, VkDeviceSize size);
diff --git a/src/gpu/vk/GrVkGpuCommandBuffer.cpp b/src/gpu/vk/GrVkGpuCommandBuffer.cpp
index e28a532..0058a94 100644
--- a/src/gpu/vk/GrVkGpuCommandBuffer.cpp
+++ b/src/gpu/vk/GrVkGpuCommandBuffer.cpp
@@ -622,8 +622,7 @@
         if (GrSamplerState::Filter::kMipMap == sampler.samplerState().filter()) {
             SkASSERT(vkTexture->texturePriv().mipMapped() == GrMipMapped::kYes);
             if (vkTexture->texturePriv().mipMapsAreDirty()) {
-                gpu->generateMipmap(vkTexture, sampler.proxy()->origin());
-                vkTexture->texturePriv().markMipMapsClean();
+                gpu->regenerateMipMapLevels(vkTexture);
             }
         }
         sampledImages->push_back(vkTexture);