Add API to invalidate GL texture parameters from GrBackendTexture.

This is modeled on how we coordinate VkImage layout changes between
GrContext and clients.

A type GrGLTextureParameters is used to track the current parameter
state.

When a client creates a GrBackendTexture in order to wrap a resource
they created a new GrGLTextureParameters is created and the wrapped
GrGLTexture will share ownership.

When GrContext creates a non-wrapped GrGLTexture, the GrGLTexture
creates a new GrGLTextureParameters and any GrBackendTextures created
from that GrGLTexture will share ownership.

Clients indicate parameter changes by calling
GrBackendTexture::glTextureParametersModified().

We still assume all texture parameters may have changed after a call
to GrContext::resetContext() (for now). The "timestamp" that is used
to implement this has been moved from GrGpu to GrGLGpu as there were
no other use cases.

Change-Id: Ic24e00488fad254a29d5eec6890278b67df6efae
Bug: skia:7966
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/217385
Auto-Submit: Brian Salomon <bsalomon@google.com>
Commit-Queue: Greg Daniel <egdaniel@google.com>
Reviewed-by: Greg Daniel <egdaniel@google.com>
diff --git a/src/gpu/GrBackendSurface.cpp b/src/gpu/GrBackendSurface.cpp
index dc58250..7420ab3 100644
--- a/src/gpu/GrBackendSurface.cpp
+++ b/src/gpu/GrBackendSurface.cpp
@@ -187,6 +187,28 @@
         : fIsValid(false) {}
 #endif
 
+#ifdef SK_GL
+GrBackendTexture::GrBackendTexture(int width,
+                                   int height,
+                                   GrMipMapped mipMapped,
+                                   const GrGLTextureInfo glInfo,
+                                   sk_sp<GrGLTextureParameters> params)
+        : fIsValid(true)
+        , fWidth(width)
+        , fHeight(height)
+        , fConfig(kUnknown_GrPixelConfig)
+        , fMipMapped(mipMapped)
+        , fBackend(GrBackendApi::kOpenGL)
+        , fGLInfo(glInfo, params.release()) {}
+
+sk_sp<GrGLTextureParameters> GrBackendTexture::getGLTextureParams() const {
+    if (fBackend != GrBackendApi::kOpenGL) {
+        return nullptr;
+    }
+    return fGLInfo.refParameters();
+}
+#endif
+
 #ifdef SK_VULKAN
 GrBackendTexture::GrBackendTexture(int width,
                                    int height,
@@ -220,13 +242,10 @@
                                    int height,
                                    GrMipMapped mipMapped,
                                    const GrGLTextureInfo& glInfo)
-        : fIsValid(true)
-        , fWidth(width)
-        , fHeight(height)
-        , fConfig(kUnknown_GrPixelConfig)
-        , fMipMapped(mipMapped)
-        , fBackend(GrBackendApi::kOpenGL)
-        , fGLInfo(glInfo) {}
+        : GrBackendTexture(width, height, mipMapped, glInfo, sk_make_sp<GrGLTextureParameters>()) {
+    // Make no assumptions about client's texture's parameters.
+    this->glTextureParametersModified();
+}
 
 GrBackendTexture::GrBackendTexture(int width,
                                    int height,
@@ -245,6 +264,11 @@
 }
 
 void GrBackendTexture::cleanup() {
+#ifdef SK_GL
+    if (this->isValid() && GrBackendApi::kOpenGL == fBackend) {
+        fGLInfo.cleanup();
+    }
+#endif
 #ifdef SK_VULKAN
     if (this->isValid() && GrBackendApi::kVulkan == fBackend) {
         fVkInfo.cleanup();
@@ -261,6 +285,9 @@
         this->cleanup();
         fIsValid = false;
         return *this;
+    } else if (this->fBackend != that.fBackend) {
+        this->cleanup();
+        fIsValid = false;
     }
     fWidth = that.fWidth;
     fHeight = that.fHeight;
@@ -269,14 +296,16 @@
     fBackend = that.fBackend;
 
     switch (that.fBackend) {
+#ifdef SK_GL
         case GrBackendApi::kOpenGL:
-            fGLInfo = that.fGLInfo;
+            fGLInfo.assign(that.fGLInfo, this->isValid());
             break;
-        case GrBackendApi::kVulkan:
-#ifdef SK_VULKAN
-            fVkInfo.assign(that.fVkInfo, this->isValid());
 #endif
+#ifdef SK_VULKAN
+        case GrBackendApi::kVulkan:
+            fVkInfo.assign(that.fVkInfo, this->isValid());
             break;
+#endif
 #ifdef SK_METAL
         case GrBackendApi::kMetal:
             fMtlInfo = that.fMtlInfo;
@@ -288,7 +317,7 @@
         default:
             SK_ABORT("Unknown GrBackend");
     }
-    fIsValid = that.fIsValid;
+    fIsValid = true;
     return *this;
 }
 
@@ -310,21 +339,14 @@
 #endif
 }
 
-// We need a stubbed version of GrVkImageLayout for non vulkan builds
-#ifndef SK_VULKAN
-class GrVkImageLayout : public SkRefCnt {
-    GrVkImageLayout(VkImageLayout layout) {}
-};
-#endif
-
-sk_sp<GrVkImageLayout> GrBackendTexture::getGrVkImageLayout() const {
 #ifdef SK_VULKAN
+sk_sp<GrVkImageLayout> GrBackendTexture::getGrVkImageLayout() const {
     if (this->isValid() && GrBackendApi::kVulkan == fBackend) {
         return fVkInfo.getGrVkImageLayout();
     }
-#endif
     return nullptr;
 }
+#endif
 
 #ifdef SK_METAL
 bool GrBackendTexture::getMtlTextureInfo(GrMtlTextureInfo* outInfo) const {
@@ -337,8 +359,9 @@
 #endif
 
 bool GrBackendTexture::getGLTextureInfo(GrGLTextureInfo* outInfo) const {
+#ifdef SK_GL
     if (this->isValid() && GrBackendApi::kOpenGL == fBackend) {
-        *outInfo = fGLInfo;
+        *outInfo = fGLInfo.info();
         return true;
     } else if (this->isValid() && GrBackendApi::kMock == fBackend) {
         // Hack! This allows some blink unit tests to work when using the Mock GrContext.
@@ -349,9 +372,18 @@
                                     GR_GL_RGBA8 };
         return true;
     }
+#endif
     return false;
 }
 
+void GrBackendTexture::glTextureParametersModified() {
+#ifdef SK_GL
+    if (this->isValid() && fBackend == GrBackendApi::kOpenGL) {
+        fGLInfo.parameters()->invalidate();
+    }
+#endif
+}
+
 bool GrBackendTexture::getMockTextureInfo(GrMockTextureInfo* outInfo) const {
     if (this->isValid() && GrBackendApi::kMock == fBackend) {
         *outInfo = fMockInfo;
@@ -368,8 +400,10 @@
         return false;
     }
     switch (fBackend) {
+#ifdef SK_GL
         case GrBackendApi::kOpenGL:
-            return fGLInfo.fID == that.fGLInfo.fID;
+            return fGLInfo.info().fID == that.fGLInfo.info().fID;
+#endif
 #ifdef SK_VULKAN
         case GrBackendApi::kVulkan:
             return fVkInfo.snapImageInfo().fImage == that.fVkInfo.snapImageInfo().fImage;
@@ -390,8 +424,10 @@
         return GrBackendFormat();
     }
     switch (fBackend) {
+#ifdef SK_GL
         case GrBackendApi::kOpenGL:
-            return GrBackendFormat::MakeGL(fGLInfo.fFormat, fGLInfo.fTarget);
+            return GrBackendFormat::MakeGL(fGLInfo.info().fFormat, fGLInfo.info().fTarget);
+#endif
 #ifdef SK_VULKAN
         case GrBackendApi::kVulkan: {
             auto info = fVkInfo.snapImageInfo();
@@ -431,28 +467,23 @@
     }
 
     switch (t0.fBackend) {
-    case GrBackendApi::kOpenGL:
-        return t0.fGLInfo == t1.fGLInfo;
-    case GrBackendApi::kMock:
-        return t0.fMockInfo == t1.fMockInfo;
-    case GrBackendApi::kVulkan:
+#ifdef SK_GL
+        case GrBackendApi::kOpenGL:
+            return t0.fGLInfo.info() == t1.fGLInfo.info();
+#endif
+        case GrBackendApi::kMock:
+            return t0.fMockInfo == t1.fMockInfo;
 #ifdef SK_VULKAN
-        return t0.fVkInfo == t1.fVkInfo;
-#else
-        // fall through
+        case GrBackendApi::kVulkan:
+            return t0.fVkInfo == t1.fVkInfo;
 #endif
-    case GrBackendApi::kMetal:
 #ifdef SK_METAL
-        return t0.fMtlInfo == t1.fMtlInfo;
-#else
-        // fall through
+        case GrBackendApi::kMetal:
+            return t0.fMtlInfo == t1.fMtlInfo;
 #endif
-    default:
-        return false;
+        default:
+            return false;
     }
-
-    SkASSERT(0);
-    return false;
 }
 #endif
 
@@ -559,6 +590,9 @@
         this->cleanup();
         fIsValid = false;
         return *this;
+    } else if (this->fBackend != that.fBackend) {
+        this->cleanup();
+        fIsValid = false;
     }
     fWidth = that.fWidth;
     fHeight = that.fHeight;
@@ -609,14 +643,14 @@
 #endif
 }
 
-sk_sp<GrVkImageLayout> GrBackendRenderTarget::getGrVkImageLayout() const {
 #ifdef SK_VULKAN
+sk_sp<GrVkImageLayout> GrBackendRenderTarget::getGrVkImageLayout() const {
     if (this->isValid() && GrBackendApi::kVulkan == fBackend) {
         return fVkInfo.getGrVkImageLayout();
     }
-#endif
     return nullptr;
 }
+#endif
 
 #ifdef SK_METAL
 bool GrBackendRenderTarget::getMtlTextureInfo(GrMtlTextureInfo* outInfo) const {
@@ -661,24 +695,22 @@
     }
 
     switch (r0.fBackend) {
-    case GrBackendApi::kOpenGL:
-        return r0.fGLInfo == r1.fGLInfo;
-    case GrBackendApi::kMock:
-        return r0.fMockInfo == r1.fMockInfo;
-    case GrBackendApi::kVulkan:
+#ifdef SK_GL
+        case GrBackendApi::kOpenGL:
+            return r0.fGLInfo == r1.fGLInfo;
+#endif
+        case GrBackendApi::kMock:
+            return r0.fMockInfo == r1.fMockInfo;
 #ifdef SK_VULKAN
-        return r0.fVkInfo == r1.fVkInfo;
-#else
-        // fall through
+        case GrBackendApi::kVulkan:
+            return r0.fVkInfo == r1.fVkInfo;
 #endif
-    case GrBackendApi::kMetal:
 #ifdef SK_METAL
-        return r0.fMtlInfo == r1.fMtlInfo;
-#else
-        // fall through
+        case GrBackendApi::kMetal:
+            return r0.fMtlInfo == r1.fMtlInfo;
 #endif
-    default:
-        return false;
+        default:
+            return false;
     }
 
     SkASSERT(0);
diff --git a/src/gpu/GrGpu.cpp b/src/gpu/GrGpu.cpp
index 23d8218..ce151b1 100644
--- a/src/gpu/GrGpu.cpp
+++ b/src/gpu/GrGpu.cpp
@@ -32,11 +32,7 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-GrGpu::GrGpu(GrContext* context)
-    : fResetTimestamp(kExpiredTimestamp+1)
-    , fResetBits(kAll_GrBackendState)
-    , fContext(context) {
-}
+GrGpu::GrGpu(GrContext* context) : fResetBits(kAll_GrBackendState), fContext(context) {}
 
 GrGpu::~GrGpu() {}
 
diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h
index 630644a..467e1db 100644
--- a/src/gpu/GrGpu.h
+++ b/src/gpu/GrGpu.h
@@ -249,20 +249,6 @@
                             GrColorType bufferColorType, GrGpuBuffer* transferBuffer,
                             size_t offset);
 
-    // After the client interacts directly with the 3D context state the GrGpu
-    // must resync its internal state and assumptions about 3D context state.
-    // Each time this occurs the GrGpu bumps a timestamp.
-    // state of the 3D context
-    // At 10 resets / frame and 60fps a 64bit timestamp will overflow in about
-    // a billion years.
-    typedef uint64_t ResetTimestamp;
-
-    // This timestamp is always older than the current timestamp
-    static const ResetTimestamp kExpiredTimestamp = 0;
-    // Returns a timestamp based on the number of times the context was reset.
-    // This timestamp can be used to lazily detect when cached 3D context state
-    // is dirty.
-    ResetTimestamp getResetTimestamp() const { return fResetTimestamp; }
 
     // Called to perform a surface to surface copy. Fallbacks to issuing a draw from the src to dst
     // take place at the GrOpList level and this function implement faster copy paths. The rect
@@ -566,10 +552,8 @@
     void resetContext() {
         this->onResetContext(fResetBits);
         fResetBits = 0;
-        ++fResetTimestamp;
     }
 
-    ResetTimestamp fResetTimestamp;
     uint32_t fResetBits;
     // The context owns us, not vice-versa, so this ptr is not ref'ed by Gpu.
     GrContext* fContext;
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index 2a9b0f4..8edb3fa 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -655,6 +655,7 @@
         fHWProgramID = 0;
         fHWProgram.reset();
     }
+    ++fResetTimestampForTextureParameters;
 }
 
 static bool check_backend_texture(const GrBackendTexture& backendTex, const GrGLCaps& caps,
@@ -706,8 +707,8 @@
     GrMipMapsStatus mipMapsStatus = backendTex.hasMipMaps() ? GrMipMapsStatus::kValid
                                                             : GrMipMapsStatus::kNotAllocated;
 
-    auto texture =
-            GrGLTexture::MakeWrapped(this, surfDesc, mipMapsStatus, idDesc, cacheable, ioType);
+    auto texture = GrGLTexture::MakeWrapped(this, surfDesc, mipMapsStatus, idDesc,
+                                            backendTex.getGLTextureParams(), cacheable, ioType);
     // We don't know what parameters are already set on wrapped textures.
     texture->textureParamsModified();
     return std::move(texture);
@@ -755,7 +756,8 @@
                                                             : GrMipMapsStatus::kNotAllocated;
 
     sk_sp<GrGLTextureRenderTarget> texRT(GrGLTextureRenderTarget::MakeWrapped(
-            this, surfDesc, idDesc, rtIDDesc, cacheable, mipMapsStatus));
+            this, surfDesc, idDesc, backendTex.getGLTextureParams(), rtIDDesc, cacheable,
+            mipMapsStatus));
     texRT->baseLevelWasBoundToFBO();
     // We don't know what parameters are already set on wrapped textures.
     texRT->textureParamsModified();
@@ -1564,21 +1566,21 @@
     return nullptr;
 }
 
-static GrGLTexture::SamplerParams set_initial_texture_params(const GrGLInterface* interface,
-                                                             const GrGLTextureInfo& info) {
+static GrGLTextureParameters::SamplerOverriddenState set_initial_texture_params(
+        const GrGLInterface* interface, const GrGLTextureInfo& info) {
     // Some drivers like to know filter/wrap before seeing glTexImage2D. Some
     // drivers have a bug where an FBO won't be complete if it includes a
     // texture that is not mipmap complete (considering the filter in use).
-    GrGLTexture::SamplerParams params;
-    params.fMinFilter = GR_GL_NEAREST;
-    params.fMagFilter = GR_GL_NEAREST;
-    params.fWrapS = GR_GL_CLAMP_TO_EDGE;
-    params.fWrapT = GR_GL_CLAMP_TO_EDGE;
-    GR_GL_CALL(interface, TexParameteri(info.fTarget, GR_GL_TEXTURE_MAG_FILTER, params.fMagFilter));
-    GR_GL_CALL(interface, TexParameteri(info.fTarget, GR_GL_TEXTURE_MIN_FILTER, params.fMinFilter));
-    GR_GL_CALL(interface, TexParameteri(info.fTarget, GR_GL_TEXTURE_WRAP_S, params.fWrapS));
-    GR_GL_CALL(interface, TexParameteri(info.fTarget, GR_GL_TEXTURE_WRAP_T, params.fWrapT));
-    return params;
+    GrGLTextureParameters::SamplerOverriddenState state;
+    state.fMinFilter = GR_GL_NEAREST;
+    state.fMagFilter = GR_GL_NEAREST;
+    state.fWrapS = GR_GL_CLAMP_TO_EDGE;
+    state.fWrapT = GR_GL_CLAMP_TO_EDGE;
+    GR_GL_CALL(interface, TexParameteri(info.fTarget, GR_GL_TEXTURE_MAG_FILTER, state.fMagFilter));
+    GR_GL_CALL(interface, TexParameteri(info.fTarget, GR_GL_TEXTURE_MIN_FILTER, state.fMinFilter));
+    GR_GL_CALL(interface, TexParameteri(info.fTarget, GR_GL_TEXTURE_WRAP_S, state.fWrapS));
+    GR_GL_CALL(interface, TexParameteri(info.fTarget, GR_GL_TEXTURE_WRAP_T, state.fWrapT));
+    return state;
 }
 
 size_t GLBytesPerPixel(GrGLenum glFormat) {
@@ -1654,10 +1656,10 @@
     GrGLTexture::IDDesc idDesc;
     idDesc.fOwnership = GrBackendObjectOwnership::kOwned;
     GrMipMapsStatus mipMapsStatus;
-    GrGLTexture::SamplerParams initialTexParams;
+    GrGLTextureParameters::SamplerOverriddenState initialState;
     if (!this->createTextureImpl(desc, &idDesc.fInfo,
                                  isRenderTarget ? GrRenderable::kYes : GrRenderable::kNo,
-                                 &initialTexParams, texels, mipLevelCount, &mipMapsStatus)) {
+                                 &initialState, texels, mipLevelCount, &mipMapsStatus)) {
         return return_null_texture();
     }
 
@@ -1677,9 +1679,9 @@
     } else {
         tex = sk_make_sp<GrGLTexture>(this, budgeted, desc, idDesc, mipMapsStatus);
     }
-
-    tex->setCachedParams(&initialTexParams, tex->getCachedNonSamplerParams(),
-                         this->getResetTimestamp());
+    // The non-sampler params are still at their default values.
+    tex->parameters()->set(&initialState, GrGLTextureParameters::NonsamplerState(),
+                           fResetTimestampForTextureParameters);
 #ifdef TRACE_TEXTURE_CREATION
     SkDebugf("--- new texture [%d] size=(%d %d) config=%d\n",
              idDesc.fInfo.fID, desc.fWidth, desc.fHeight, desc.fConfig);
@@ -1842,7 +1844,7 @@
 
 bool GrGLGpu::createTextureImpl(const GrSurfaceDesc& desc, GrGLTextureInfo* info,
                                 GrRenderable renderable,
-                                GrGLTexture::SamplerParams* initialTexParams,
+                                GrGLTextureParameters::SamplerOverriddenState* initialState,
                                 const GrMipLevel texels[], int mipLevelCount,
                                 GrMipMapsStatus* mipMapsStatus) {
     info->fID = 0;
@@ -1864,7 +1866,7 @@
                               GR_GL_FRAMEBUFFER_ATTACHMENT));
     }
 
-    *initialTexParams = set_initial_texture_params(this->glInterface(), *info);
+    *initialState = set_initial_texture_params(this->glInterface(), *info);
 
     bool success = false;
     if (GrGLFormatIsCompressed(info->fFormat)) {
@@ -3052,71 +3054,73 @@
     }
 #endif
 
-    ResetTimestamp timestamp = texture->getCachedParamsTimestamp();
-    bool setAll = timestamp < this->getResetTimestamp();
+    auto timestamp = texture->parameters()->resetTimestamp();
+    bool setAll = timestamp < fResetTimestampForTextureParameters;
 
-    const GrGLTexture::SamplerParams* samplerParamsToRecord = nullptr;
-    GrGLTexture::SamplerParams newSamplerParams;
+    const GrGLTextureParameters::SamplerOverriddenState* samplerStateToRecord = nullptr;
+    GrGLTextureParameters::SamplerOverriddenState newSamplerState;
     if (fSamplerObjectCache) {
         fSamplerObjectCache->bindSampler(unitIdx, samplerState);
     } else {
-        const GrGLTexture::SamplerParams& oldSamplerParams = texture->getCachedSamplerParams();
-        samplerParamsToRecord = &newSamplerParams;
+        const GrGLTextureParameters::SamplerOverriddenState& oldSamplerState =
+                texture->parameters()->samplerOverriddenState();
+        samplerStateToRecord = &newSamplerState;
 
-        newSamplerParams.fMinFilter = filter_to_gl_min_filter(samplerState.filter());
-        newSamplerParams.fMagFilter = filter_to_gl_mag_filter(samplerState.filter());
+        newSamplerState.fMinFilter = filter_to_gl_min_filter(samplerState.filter());
+        newSamplerState.fMagFilter = filter_to_gl_mag_filter(samplerState.filter());
 
-        newSamplerParams.fWrapS = wrap_mode_to_gl_wrap(samplerState.wrapModeX(), this->glCaps());
-        newSamplerParams.fWrapT = wrap_mode_to_gl_wrap(samplerState.wrapModeY(), this->glCaps());
+        newSamplerState.fWrapS = wrap_mode_to_gl_wrap(samplerState.wrapModeX(), this->glCaps());
+        newSamplerState.fWrapT = wrap_mode_to_gl_wrap(samplerState.wrapModeY(), this->glCaps());
 
         // These are the OpenGL default values.
-        newSamplerParams.fMinLOD = -1000.f;
-        newSamplerParams.fMaxLOD = 1000.f;
+        newSamplerState.fMinLOD = -1000.f;
+        newSamplerState.fMaxLOD = 1000.f;
 
-        if (setAll || newSamplerParams.fMagFilter != oldSamplerParams.fMagFilter) {
+        if (setAll || newSamplerState.fMagFilter != oldSamplerState.fMagFilter) {
             this->setTextureUnit(unitIdx);
-            GL_CALL(TexParameteri(target, GR_GL_TEXTURE_MAG_FILTER, newSamplerParams.fMagFilter));
+            GL_CALL(TexParameteri(target, GR_GL_TEXTURE_MAG_FILTER, newSamplerState.fMagFilter));
         }
-        if (setAll || newSamplerParams.fMinFilter != oldSamplerParams.fMinFilter) {
+        if (setAll || newSamplerState.fMinFilter != oldSamplerState.fMinFilter) {
             this->setTextureUnit(unitIdx);
-            GL_CALL(TexParameteri(target, GR_GL_TEXTURE_MIN_FILTER, newSamplerParams.fMinFilter));
+            GL_CALL(TexParameteri(target, GR_GL_TEXTURE_MIN_FILTER, newSamplerState.fMinFilter));
         }
         if (this->glCaps().mipMapLevelAndLodControlSupport()) {
-            if (setAll || newSamplerParams.fMinLOD != oldSamplerParams.fMinLOD) {
+            if (setAll || newSamplerState.fMinLOD != oldSamplerState.fMinLOD) {
                 this->setTextureUnit(unitIdx);
-                GL_CALL(TexParameterf(target, GR_GL_TEXTURE_MIN_LOD, newSamplerParams.fMinLOD));
+                GL_CALL(TexParameterf(target, GR_GL_TEXTURE_MIN_LOD, newSamplerState.fMinLOD));
             }
-            if (setAll || newSamplerParams.fMaxLOD != oldSamplerParams.fMaxLOD) {
+            if (setAll || newSamplerState.fMaxLOD != oldSamplerState.fMaxLOD) {
                 this->setTextureUnit(unitIdx);
-                GL_CALL(TexParameterf(target, GR_GL_TEXTURE_MAX_LOD, newSamplerParams.fMaxLOD));
+                GL_CALL(TexParameterf(target, GR_GL_TEXTURE_MAX_LOD, newSamplerState.fMaxLOD));
             }
         }
-        if (setAll || newSamplerParams.fWrapS != oldSamplerParams.fWrapS) {
+        if (setAll || newSamplerState.fWrapS != oldSamplerState.fWrapS) {
             this->setTextureUnit(unitIdx);
-            GL_CALL(TexParameteri(target, GR_GL_TEXTURE_WRAP_S, newSamplerParams.fWrapS));
+            GL_CALL(TexParameteri(target, GR_GL_TEXTURE_WRAP_S, newSamplerState.fWrapS));
         }
-        if (setAll || newSamplerParams.fWrapT != oldSamplerParams.fWrapT) {
+        if (setAll || newSamplerState.fWrapT != oldSamplerState.fWrapT) {
             this->setTextureUnit(unitIdx);
-            GL_CALL(TexParameteri(target, GR_GL_TEXTURE_WRAP_T, newSamplerParams.fWrapT));
+            GL_CALL(TexParameteri(target, GR_GL_TEXTURE_WRAP_T, newSamplerState.fWrapT));
         }
         if (this->glCaps().clampToBorderSupport()) {
             // Make sure the border color is transparent black (the default)
-            if (setAll || oldSamplerParams.fBorderColorInvalid) {
+            if (setAll || oldSamplerState.fBorderColorInvalid) {
                 this->setTextureUnit(unitIdx);
                 static const GrGLfloat kTransparentBlack[4] = {0.f, 0.f, 0.f, 0.f};
                 GL_CALL(TexParameterfv(target, GR_GL_TEXTURE_BORDER_COLOR, kTransparentBlack));
             }
         }
     }
-    GrGLTexture::NonSamplerParams newNonSamplerParams;
-    newNonSamplerParams.fBaseMipMapLevel = 0;
-    newNonSamplerParams.fMaxMipMapLevel = texture->texturePriv().maxMipMapLevel();
+    GrGLTextureParameters::NonsamplerState newNonsamplerState;
+    newNonsamplerState.fBaseMipMapLevel = 0;
+    newNonsamplerState.fMaxMipMapLevel = texture->texturePriv().maxMipMapLevel();
 
-    const GrGLTexture::NonSamplerParams& oldNonSamplerParams = texture->getCachedNonSamplerParams();
+    const GrGLTextureParameters::NonsamplerState& oldNonsamplerState =
+            texture->parameters()->nonsamplerState();
     if (this->glCaps().textureSwizzleSupport()) {
         auto swizzle = this->glCaps().configSwizzle(texture->config());
-        newNonSamplerParams.fSwizzleKey = swizzle.asKey();
-        if (setAll || swizzle.asKey() != oldNonSamplerParams.fSwizzleKey) {
+        newNonsamplerState.fSwizzleKey = swizzle.asKey();
+        if (setAll || swizzle.asKey() != oldNonsamplerState.fSwizzleKey) {
             GrGLenum glValues[4];
             get_gl_swizzle_values(swizzle, glValues);
             this->setTextureUnit(unitIdx);
@@ -3137,18 +3141,19 @@
     if (this->glCaps().mipMapLevelAndLodControlSupport() &&
         (texture->texturePriv().textureType() != GrTextureType::kExternal ||
          !this->glCaps().dontSetBaseOrMaxLevelForExternalTextures())) {
-        if (newNonSamplerParams.fBaseMipMapLevel != oldNonSamplerParams.fBaseMipMapLevel) {
+        if (newNonsamplerState.fBaseMipMapLevel != oldNonsamplerState.fBaseMipMapLevel) {
             this->setTextureUnit(unitIdx);
             GL_CALL(TexParameteri(target, GR_GL_TEXTURE_BASE_LEVEL,
-                                  newNonSamplerParams.fBaseMipMapLevel));
+                                  newNonsamplerState.fBaseMipMapLevel));
         }
-        if (newNonSamplerParams.fMaxMipMapLevel != oldNonSamplerParams.fMaxMipMapLevel) {
+        if (newNonsamplerState.fMaxMipMapLevel != oldNonsamplerState.fMaxMipMapLevel) {
             this->setTextureUnit(unitIdx);
             GL_CALL(TexParameteri(target, GR_GL_TEXTURE_MAX_LEVEL,
-                                  newNonSamplerParams.fMaxMipMapLevel));
+                                  newNonsamplerState.fMaxMipMapLevel));
         }
     }
-    texture->setCachedParams(samplerParamsToRecord, newNonSamplerParams, this->getResetTimestamp());
+    texture->parameters()->set(samplerStateToRecord, newNonsamplerState,
+                               fResetTimestampForTextureParameters);
 }
 
 void GrGLGpu::onResetTextureBindings() {
@@ -3952,9 +3957,10 @@
                                  GR_GL_TEXTURE_2D, 0, 0));
 
     // We modified the base level param.
-    GrGLTexture::NonSamplerParams params = glTex->getCachedNonSamplerParams();
-    params.fBaseMipMapLevel = levelCount - 2; // we drew the 2nd to last level into the last level.
-    glTex->setCachedParams(nullptr, params, this->getResetTimestamp());
+    GrGLTextureParameters::NonsamplerState nonsamplerState = glTex->parameters()->nonsamplerState();
+    // We drew the 2nd to last level into the last level.
+    nonsamplerState.fBaseMipMapLevel = levelCount - 2;
+    glTex->parameters()->set(nullptr, nonsamplerState, fResetTimestampForTextureParameters);
 
     return true;
 }
@@ -4146,17 +4152,20 @@
     desc.fConfig = config;
 
     GrGLTextureInfo info;
-    GrGLTexture::SamplerParams initialTexParams;
+    GrGLTextureParameters::SamplerOverriddenState initialState;
 
-    if (!this->createTextureImpl(desc, &info, renderable, &initialTexParams,
-                                 texels.get(), mipLevelCount, nullptr)) {
+    if (!this->createTextureImpl(desc, &info, renderable, &initialState, texels.get(),
+                                 mipLevelCount, nullptr)) {
         return GrBackendTexture();  // invalid
     }
 
     // unbind the texture from the texture unit to avoid asserts
     GL_CALL(BindTexture(info.fTarget, 0));
 
-    GrBackendTexture beTex = GrBackendTexture(w, h, mipMapped, info);
+    auto parameters = sk_make_sp<GrGLTextureParameters>();
+    parameters->set(&initialState, GrGLTextureParameters::NonsamplerState(),
+                    fResetTimestampForTextureParameters);
+    GrBackendTexture beTex = GrBackendTexture(w, h, mipMapped, info, std::move(parameters));
 #if GR_TEST_UTILS
     // Lots of tests don't go through Skia's public interface, which will set the config, so for
     // testing we make sure we set a config here.
diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h
index 869a3f2..5a6c6d8 100644
--- a/src/gpu/gl/GrGLGpu.h
+++ b/src/gpu/gl/GrGLGpu.h
@@ -216,8 +216,9 @@
     // The texture is populated with |texels|, if it exists.
     // The texture parameters are cached in |initialTexParams|.
     bool createTextureImpl(const GrSurfaceDesc& desc, GrGLTextureInfo* info, GrRenderable,
-                           GrGLTexture::SamplerParams* initialTexParams, const GrMipLevel texels[],
-                           int mipLevelCount, GrMipMapsStatus* mipMapsStatus);
+                           GrGLTextureParameters::SamplerOverriddenState* initialState,
+                           const GrMipLevel texels[], int mipLevelCount,
+                           GrMipMapsStatus* mipMapsStatus);
 
     // Checks whether glReadPixels can be called to get pixel values in readConfig from the
     // render target.
@@ -665,6 +666,8 @@
 
     GrPrimitiveType fLastPrimitiveType;
 
+    GrGLTextureParameters::ResetTimestamp fResetTimestampForTextureParameters = 0;
+
     class SamplerObjectCache;
     std::unique_ptr<SamplerObjectCache> fSamplerObjectCache;
 
diff --git a/src/gpu/gl/GrGLTexture.cpp b/src/gpu/gl/GrGLTexture.cpp
index 942f125..899ff8c 100644
--- a/src/gpu/gl/GrGLTexture.cpp
+++ b/src/gpu/gl/GrGLTexture.cpp
@@ -45,7 +45,8 @@
 GrGLTexture::GrGLTexture(GrGLGpu* gpu, SkBudgeted budgeted, const GrSurfaceDesc& desc,
                          const IDDesc& idDesc, GrMipMapsStatus mipMapsStatus)
         : GrSurface(gpu, desc)
-        , INHERITED(gpu, desc, TextureTypeFromTarget(idDesc.fInfo.fTarget), mipMapsStatus) {
+        , INHERITED(gpu, desc, TextureTypeFromTarget(idDesc.fInfo.fTarget), mipMapsStatus)
+        , fParameters(sk_make_sp<GrGLTextureParameters>()) {
     this->init(desc, idDesc);
     this->registerWithCache(budgeted);
     if (GrPixelConfigIsCompressed(desc.fConfig)) {
@@ -54,9 +55,12 @@
 }
 
 GrGLTexture::GrGLTexture(GrGLGpu* gpu, const GrSurfaceDesc& desc, GrMipMapsStatus mipMapsStatus,
-                         const IDDesc& idDesc, GrWrapCacheable cacheable, GrIOType ioType)
+                         const IDDesc& idDesc, sk_sp<GrGLTextureParameters> parameters,
+                         GrWrapCacheable cacheable, GrIOType ioType)
         : GrSurface(gpu, desc)
-        , INHERITED(gpu, desc, TextureTypeFromTarget(idDesc.fInfo.fTarget), mipMapsStatus) {
+        , INHERITED(gpu, desc, TextureTypeFromTarget(idDesc.fInfo.fTarget), mipMapsStatus)
+        , fParameters(std::move(parameters)) {
+    SkASSERT(fParameters);
     this->init(desc, idDesc);
     this->registerWithCacheWrapped(cacheable);
     if (ioType == kRead_GrIOType) {
@@ -65,16 +69,17 @@
 }
 
 GrGLTexture::GrGLTexture(GrGLGpu* gpu, const GrSurfaceDesc& desc, const IDDesc& idDesc,
-                         GrMipMapsStatus mipMapsStatus)
+                         sk_sp<GrGLTextureParameters> parameters, GrMipMapsStatus mipMapsStatus)
         : GrSurface(gpu, desc)
         , INHERITED(gpu, desc, TextureTypeFromTarget(idDesc.fInfo.fTarget), mipMapsStatus) {
+    SkASSERT(parameters || idDesc.fOwnership == GrBackendObjectOwnership::kOwned);
+    fParameters = parameters ? std::move(parameters) : sk_make_sp<GrGLTextureParameters>();
     this->init(desc, idDesc);
 }
 
 void GrGLTexture::init(const GrSurfaceDesc& desc, const IDDesc& idDesc) {
     SkASSERT(0 != idDesc.fInfo.fID);
     SkASSERT(0 != idDesc.fInfo.fFormat);
-    fParamsTimestamp = GrGpu::kExpiredTimestamp;
     fID = idDesc.fInfo.fID;
     fFormat = idDesc.fInfo.fFormat;
     fTextureIDOwnership = idDesc.fOwnership;
@@ -104,7 +109,8 @@
     info.fTarget = target_from_texture_type(this->texturePriv().textureType());
     info.fID = fID;
     info.fFormat = fFormat;
-    return GrBackendTexture(this->width(), this->height(), this->texturePriv().mipMapped(), info);
+    return GrBackendTexture(this->width(), this->height(), this->texturePriv().mipMapped(), info,
+                            fParameters);
 }
 
 GrBackendFormat GrGLTexture::backendFormat() const {
@@ -114,8 +120,10 @@
 
 sk_sp<GrGLTexture> GrGLTexture::MakeWrapped(GrGLGpu* gpu, const GrSurfaceDesc& desc,
                                             GrMipMapsStatus mipMapsStatus, const IDDesc& idDesc,
+                                            sk_sp<GrGLTextureParameters> parameters,
                                             GrWrapCacheable cacheable, GrIOType ioType) {
-    return sk_sp<GrGLTexture>(new GrGLTexture(gpu, desc, mipMapsStatus, idDesc, cacheable, ioType));
+    return sk_sp<GrGLTexture>(new GrGLTexture(gpu, desc, mipMapsStatus, idDesc,
+                                              std::move(parameters), cacheable, ioType));
 }
 
 bool GrGLTexture::onStealBackendTexture(GrBackendTexture* backendTexture,
diff --git a/src/gpu/gl/GrGLTexture.h b/src/gpu/gl/GrGLTexture.h
index 54d419c..cb3f158 100644
--- a/src/gpu/gl/GrGLTexture.h
+++ b/src/gpu/gl/GrGLTexture.h
@@ -10,6 +10,7 @@
 #define GrGLTexture_DEFINED
 
 #include "include/gpu/GrTexture.h"
+#include "include/private/GrGLTypesPriv.h"
 #include "src/gpu/GrGpu.h"
 #include "src/gpu/gl/GrGLUtil.h"
 
@@ -17,44 +18,6 @@
 
 class GrGLTexture : public GrTexture {
 public:
-    // Texture state that overlaps with sampler object state. We don't need to track this if we
-    // are using sampler objects.
-    struct SamplerParams {
-        // These are the OpenGL defaults.
-        GrGLenum fMinFilter = GR_GL_NEAREST_MIPMAP_LINEAR;
-        GrGLenum fMagFilter = GR_GL_LINEAR;
-        GrGLenum fWrapS = GR_GL_REPEAT;
-        GrGLenum fWrapT = GR_GL_REPEAT;
-        GrGLfloat fMinLOD = -1000.f;
-        GrGLfloat fMaxLOD = 1000.f;
-        // We always want the border color to be transparent black, so no need to store 4 floats.
-        // Just track if it's been invalidated and no longer the default
-        bool fBorderColorInvalid = false;
-
-        void invalidate() {
-            fMinFilter = ~0U;
-            fMagFilter = ~0U;
-            fWrapS = ~0U;
-            fWrapT = ~0U;
-            fMinLOD = SK_ScalarNaN;
-            fMaxLOD = SK_ScalarNaN;
-            fBorderColorInvalid = true;
-        }
-    };
-
-    // Texture state that does not overlap with sampler object state.
-    struct NonSamplerParams {
-        // These are the OpenGL defaults.
-        uint32_t fSwizzleKey = GrSwizzle::RGBA().asKey();
-        GrGLint fBaseMipMapLevel = 0;
-        GrGLint fMaxMipMapLevel = 1000;
-        void invalidate() {
-            fSwizzleKey = ~0U;
-            fBaseMipMapLevel = ~0;
-            fMaxMipMapLevel = ~0;
-        }
-    };
-
     struct IDDesc {
         GrGLTextureInfo             fInfo;
         GrBackendObjectOwnership    fOwnership;
@@ -70,25 +33,10 @@
 
     GrBackendFormat backendFormat() const override;
 
-    void textureParamsModified() override {
-        fSamplerParams.invalidate();
-        fNonSamplerParams.invalidate();
-    }
+    // TODO: Remove once clients are no longer calling this.
+    void textureParamsModified() override { fParameters->invalidate(); }
 
-    // These functions are used to track the texture parameters associated with the texture.
-    GrGpu::ResetTimestamp getCachedParamsTimestamp() const { return fParamsTimestamp; }
-    const SamplerParams& getCachedSamplerParams() const { return fSamplerParams; }
-    const NonSamplerParams& getCachedNonSamplerParams() const { return fNonSamplerParams; }
-
-    void setCachedParams(const SamplerParams* samplerParams,
-                         const NonSamplerParams& nonSamplerParams,
-                         GrGpu::ResetTimestamp currTimestamp) {
-        if (samplerParams) {
-            fSamplerParams = *samplerParams;
-        }
-        fNonSamplerParams = nonSamplerParams;
-        fParamsTimestamp = currTimestamp;
-    }
+    GrGLTextureParameters* parameters() { return fParameters.get(); }
 
     GrGLuint textureID() const { return fID; }
 
@@ -98,17 +46,19 @@
     void baseLevelWasBoundToFBO() { fBaseLevelHasBeenBoundToFBO = true; }
 
     static sk_sp<GrGLTexture> MakeWrapped(GrGLGpu*, const GrSurfaceDesc&, GrMipMapsStatus,
-                                          const IDDesc&, GrWrapCacheable, GrIOType);
+                                          const IDDesc&, sk_sp<GrGLTextureParameters>,
+                                          GrWrapCacheable, GrIOType);
 
     void dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const override;
 
 protected:
     // Constructor for subclasses.
-    GrGLTexture(GrGLGpu*, const GrSurfaceDesc&, const IDDesc&, GrMipMapsStatus);
+    GrGLTexture(GrGLGpu*, const GrSurfaceDesc&, const IDDesc&, sk_sp<GrGLTextureParameters>,
+                GrMipMapsStatus);
 
     // Constructor for instances wrapping backend objects.
-    GrGLTexture(GrGLGpu*, const GrSurfaceDesc&, GrMipMapsStatus, const IDDesc&, GrWrapCacheable,
-                GrIOType);
+    GrGLTexture(GrGLGpu*, const GrSurfaceDesc&, GrMipMapsStatus, const IDDesc&,
+                sk_sp<GrGLTextureParameters>, GrWrapCacheable, GrIOType);
 
     void init(const GrSurfaceDesc&, const IDDesc&);
 
@@ -118,9 +68,7 @@
     bool onStealBackendTexture(GrBackendTexture*, SkImage::BackendTextureReleaseProc*) override;
 
 private:
-    SamplerParams fSamplerParams;
-    NonSamplerParams fNonSamplerParams;
-    GrGpu::ResetTimestamp fParamsTimestamp;
+    sk_sp<GrGLTextureParameters> fParameters;
     GrGLuint fID;
     GrGLenum fFormat;
     GrBackendObjectOwnership fTextureIDOwnership;
diff --git a/src/gpu/gl/GrGLTextureRenderTarget.cpp b/src/gpu/gl/GrGLTextureRenderTarget.cpp
index ab86162..6739b79 100644
--- a/src/gpu/gl/GrGLTextureRenderTarget.cpp
+++ b/src/gpu/gl/GrGLTextureRenderTarget.cpp
@@ -19,7 +19,7 @@
                                                  const GrGLRenderTarget::IDDesc& rtIDDesc,
                                                  GrMipMapsStatus mipMapsStatus)
         : GrSurface(gpu, desc)
-        , GrGLTexture(gpu, desc, texIDDesc, mipMapsStatus)
+        , GrGLTexture(gpu, desc, texIDDesc, nullptr, mipMapsStatus)
         , GrGLRenderTarget(gpu, desc, texIDDesc.fInfo.fFormat, rtIDDesc) {
     this->registerWithCache(budgeted);
 }
@@ -27,11 +27,12 @@
 GrGLTextureRenderTarget::GrGLTextureRenderTarget(GrGLGpu* gpu,
                                                  const GrSurfaceDesc& desc,
                                                  const GrGLTexture::IDDesc& texIDDesc,
+                                                 sk_sp<GrGLTextureParameters> parameters,
                                                  const GrGLRenderTarget::IDDesc& rtIDDesc,
                                                  GrWrapCacheable cacheable,
                                                  GrMipMapsStatus mipMapsStatus)
         : GrSurface(gpu, desc)
-        , GrGLTexture(gpu, desc, texIDDesc, mipMapsStatus)
+        , GrGLTexture(gpu, desc, texIDDesc, std::move(parameters), mipMapsStatus)
         , GrGLRenderTarget(gpu, desc, texIDDesc.fInfo.fFormat, rtIDDesc) {
     this->registerWithCacheWrapped(cacheable);
 }
@@ -58,14 +59,13 @@
 
 sk_sp<GrGLTextureRenderTarget> GrGLTextureRenderTarget::MakeWrapped(
         GrGLGpu* gpu, const GrSurfaceDesc& desc, const GrGLTexture::IDDesc& texIDDesc,
-        const GrGLRenderTarget::IDDesc& rtIDDesc, GrWrapCacheable cacheable,
-        GrMipMapsStatus mipMapsStatus) {
-    return sk_sp<GrGLTextureRenderTarget>(
-            new GrGLTextureRenderTarget(gpu, desc, texIDDesc, rtIDDesc, cacheable, mipMapsStatus));
+        sk_sp<GrGLTextureParameters> parameters, const GrGLRenderTarget::IDDesc& rtIDDesc,
+        GrWrapCacheable cacheable, GrMipMapsStatus mipMapsStatus) {
+    return sk_sp<GrGLTextureRenderTarget>(new GrGLTextureRenderTarget(
+            gpu, desc, texIDDesc, std::move(parameters), rtIDDesc, cacheable, mipMapsStatus));
 }
 
 size_t GrGLTextureRenderTarget::onGpuMemorySize() const {
     return GrSurface::ComputeSize(this->config(), this->width(), this->height(),
-                                    this->numSamplesOwnedPerPixel(),
-                                    this->texturePriv().mipMapped());
+                                  this->numSamplesOwnedPerPixel(), this->texturePriv().mipMapped());
 }
diff --git a/src/gpu/gl/GrGLTextureRenderTarget.h b/src/gpu/gl/GrGLTextureRenderTarget.h
index 75cdf01..c386822 100644
--- a/src/gpu/gl/GrGLTextureRenderTarget.h
+++ b/src/gpu/gl/GrGLTextureRenderTarget.h
@@ -37,6 +37,7 @@
 
     static sk_sp<GrGLTextureRenderTarget> MakeWrapped(GrGLGpu* gpu, const GrSurfaceDesc& desc,
                                                       const GrGLTexture::IDDesc& texIDDesc,
+                                                      sk_sp<GrGLTextureParameters> parameters,
                                                       const GrGLRenderTarget::IDDesc& rtIDDesc,
                                                       GrWrapCacheable cacheble, GrMipMapsStatus);
 
@@ -61,6 +62,7 @@
     GrGLTextureRenderTarget(GrGLGpu* gpu,
                             const GrSurfaceDesc& desc,
                             const GrGLTexture::IDDesc& texIDDesc,
+                            sk_sp<GrGLTextureParameters> parameters,
                             const GrGLRenderTarget::IDDesc& rtIDDesc,
                             GrWrapCacheable,
                             GrMipMapsStatus);
diff --git a/src/gpu/gl/GrGLTypesPriv.cpp b/src/gpu/gl/GrGLTypesPriv.cpp
new file mode 100644
index 0000000..debb408
--- /dev/null
+++ b/src/gpu/gl/GrGLTypesPriv.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "include/private/GrGLTypesPriv.h"
+#include "include/core/SkScalar.h"
+#include "src/gpu/GrSwizzle.h"
+#include "src/gpu/gl/GrGLDefines.h"
+
+GrGLTextureParameters::SamplerOverriddenState::SamplerOverriddenState()
+        // These are the OpenGL defaults.
+        : fMinFilter(GR_GL_NEAREST_MIPMAP_LINEAR)
+        , fMagFilter(GR_GL_LINEAR)
+        , fWrapS(GR_GL_REPEAT)
+        , fWrapT(GR_GL_REPEAT)
+        , fMinLOD(-1000.f)
+        , fMaxLOD(1000.f)
+        , fBorderColorInvalid(false) {}
+
+void GrGLTextureParameters::SamplerOverriddenState::invalidate() {
+    fMinFilter = ~0U;
+    fMagFilter = ~0U;
+    fWrapS = ~0U;
+    fWrapT = ~0U;
+    fMinLOD = SK_ScalarNaN;
+    fMaxLOD = SK_ScalarNaN;
+    fBorderColorInvalid = true;
+}
+
+GrGLTextureParameters::NonsamplerState::NonsamplerState()
+        // These are the OpenGL defaults.
+        : fSwizzleKey(GrSwizzle::RGBA().asKey()), fBaseMipMapLevel(0), fMaxMipMapLevel(1000) {}
+
+void GrGLTextureParameters::NonsamplerState::invalidate() {
+    fSwizzleKey = ~0U;
+    fBaseMipMapLevel = ~0;
+    fMaxMipMapLevel = ~0;
+}
+
+void GrGLTextureParameters::invalidate() {
+    fSamplerOverriddenState.invalidate();
+    fNonsamplerState.invalidate();
+}
+
+void GrGLTextureParameters::set(const SamplerOverriddenState* samplerState,
+                                const NonsamplerState& nonsamplerState,
+                                ResetTimestamp currTimestamp) {
+    if (samplerState) {
+        fSamplerOverriddenState = *samplerState;
+    }
+    fNonsamplerState = nonsamplerState;
+    fResetTimestamp = currTimestamp;
+}
+
+void GrGLBackendTextureInfo::assign(const GrGLBackendTextureInfo& that, bool thisIsValid) {
+    fInfo = that.fInfo;
+    SkSafeRef(that.fParams);
+    if (thisIsValid) {
+        SkSafeUnref(fParams);
+    }
+    fParams = that.fParams;
+}
+
+void GrGLBackendTextureInfo::cleanup() { SkSafeUnref(fParams); }