Infer sampler precision from pixel config

Adds a "samplerPrecision" method to GrGLSLCaps and updates
GrGLSLProgramBuilder to infer a sampler's precision based on its
config and visibility.

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1846963004

Review URL: https://codereview.chromium.org/1846963004
diff --git a/include/gpu/GrTextureAccess.h b/include/gpu/GrTextureAccess.h
index 237485a..1b5de0c 100644
--- a/include/gpu/GrTextureAccess.h
+++ b/include/gpu/GrTextureAccess.h
@@ -30,30 +30,25 @@
     explicit GrTextureAccess(GrTexture*,
                              GrTextureParams::FilterMode = GrTextureParams::kNone_FilterMode,
                              SkShader::TileMode tileXAndY = SkShader::kClamp_TileMode,
-                             GrShaderFlags visibility = kFragment_GrShaderFlag,
-                             GrSLPrecision = kDefault_GrSLPrecision);
+                             GrShaderFlags visibility = kFragment_GrShaderFlag);
 
     void reset(GrTexture*, const GrTextureParams&,
-               GrShaderFlags visibility = kFragment_GrShaderFlag,
-               GrSLPrecision = kDefault_GrSLPrecision);
+               GrShaderFlags visibility = kFragment_GrShaderFlag);
     void reset(GrTexture*,
                GrTextureParams::FilterMode = GrTextureParams::kNone_FilterMode,
                SkShader::TileMode tileXAndY = SkShader::kClamp_TileMode,
-               GrShaderFlags visibility = kFragment_GrShaderFlag,
-               GrSLPrecision = kDefault_GrSLPrecision);
+               GrShaderFlags visibility = kFragment_GrShaderFlag);
 
     bool operator==(const GrTextureAccess& that) const {
         return this->getTexture() == that.getTexture() &&
                fParams == that.fParams &&
-               fVisibility == that.fVisibility &&
-               fPrecision == that.fPrecision;
+               fVisibility == that.fVisibility;
     }
 
     bool operator!=(const GrTextureAccess& other) const { return !(*this == other); }
 
     GrTexture* getTexture() const { return fTexture.get(); }
     GrShaderFlags getVisibility() const { return fVisibility; }
-    GrSLPrecision getPrecision() const { return fPrecision; }
 
     /**
      * For internal use by GrProcessor.
@@ -69,7 +64,6 @@
     ProgramTexture                  fTexture;
     GrTextureParams                 fParams;
     GrShaderFlags                   fVisibility;
-    GrSLPrecision                   fPrecision;
 
     typedef SkNoncopyable INHERITED;
 };
diff --git a/src/gpu/GrTextureAccess.cpp b/src/gpu/GrTextureAccess.cpp
index a62a4dd..675bc20 100644
--- a/src/gpu/GrTextureAccess.cpp
+++ b/src/gpu/GrTextureAccess.cpp
@@ -18,30 +18,25 @@
 GrTextureAccess::GrTextureAccess(GrTexture* texture,
                                  GrTextureParams::FilterMode filterMode,
                                  SkShader::TileMode tileXAndY,
-                                 GrShaderFlags visibility,
-                                 GrSLPrecision precision) {
-    this->reset(texture, filterMode, tileXAndY, visibility, precision);
+                                 GrShaderFlags visibility) {
+    this->reset(texture, filterMode, tileXAndY, visibility);
 }
 
 void GrTextureAccess::reset(GrTexture* texture,
                             const GrTextureParams& params,
-                            GrShaderFlags visibility,
-                            GrSLPrecision precision) {
+                            GrShaderFlags visibility) {
     SkASSERT(texture);
     fTexture.set(SkRef(texture), kRead_GrIOType);
     fParams = params;
     fVisibility = visibility;
-    fPrecision = precision;
 }
 
 void GrTextureAccess::reset(GrTexture* texture,
                             GrTextureParams::FilterMode filterMode,
                             SkShader::TileMode tileXAndY,
-                            GrShaderFlags visibility,
-                            GrSLPrecision precision) {
+                            GrShaderFlags visibility) {
     SkASSERT(texture);
     fTexture.set(SkRef(texture), kRead_GrIOType);
     fParams.reset(tileXAndY, filterMode);
     fVisibility = visibility;
-    fPrecision = precision;
 }
diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp
index e850dca..fc2c8ce 100644
--- a/src/gpu/gl/GrGLCaps.cpp
+++ b/src/gpu/gl/GrGLCaps.cpp
@@ -1197,6 +1197,7 @@
                                                glslCaps->fFloatPrecisions[kVertex_GrShaderType][p];
         }
     }
+    glslCaps->initSamplerPrecisionTable();
 }
 
 bool GrGLCaps::bgraIsInternalFormat() const {
diff --git a/src/gpu/gl/GrGLProgramDesc.cpp b/src/gpu/gl/GrGLProgramDesc.cpp
index 68020a9..5e9fe5f 100644
--- a/src/gpu/gl/GrGLProgramDesc.cpp
+++ b/src/gpu/gl/GrGLProgramDesc.cpp
@@ -17,24 +17,27 @@
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
 #include "glsl/GrGLSLCaps.h"
 
-static uint8_t texture_target_key(GrGLenum target) {
-    switch (target) {
-        case GR_GL_TEXTURE_2D:
-            return 0;
-        case GR_GL_TEXTURE_EXTERNAL:
-            return 1;
-        case GR_GL_TEXTURE_RECTANGLE:
-            return 2;
-        default:
-            SkFAIL("Unexpected texture target.");
-            return 0;
-    }
+static uint16_t texture_key(GrSLType samplerType, GrPixelConfig config, GrShaderFlags visibility,
+                            const GrGLSLCaps& caps) {
+    enum {
+        kFirstSamplerType = kSampler2D_GrSLType,
+        kLastSamplerType = kSampler2DRect_GrSLType,
+        kSamplerTypeKeyBits = 4
+    };
+    GR_STATIC_ASSERT(kLastSamplerType - kFirstSamplerType < (1 << kSamplerTypeKeyBits));
+
+    SkASSERT((int)samplerType >= kFirstSamplerType && (int)samplerType <= kLastSamplerType);
+    int samplerTypeKey = samplerType - kFirstSamplerType;
+
+    return SkToU16(caps.configTextureSwizzle(config).asKey() |
+                   (samplerTypeKey << 8) |
+                   (caps.samplerPrecision(config, visibility) << (8 + kSamplerTypeKeyBits)));
 }
 
 static void add_texture_key(GrProcessorKeyBuilder* b, const GrProcessor& proc,
                             const GrGLSLCaps& caps) {
     int numTextures = proc.numTextures();
-    // Need two bytes per key (swizzle and target).
+    // Need two bytes per key (swizzle, sampler type, and precision).
     int word32Count = (proc.numTextures() + 1) / 2;
     if (0 == word32Count) {
         return;
@@ -42,9 +45,8 @@
     uint16_t* k16 = SkTCast<uint16_t*>(b->add32n(word32Count));
     for (int i = 0; i < numTextures; ++i) {
         const GrTextureAccess& access = proc.textureAccess(i);
-        GrGLTexture* texture = static_cast<GrGLTexture*>(access.getTexture());
-        k16[i] = SkToU16(caps.configTextureSwizzle(texture->config()).asKey() |
-                         (texture_target_key(texture->target()) << 8));
+        const GrTexture* tex = access.getTexture();
+        k16[i] = texture_key(tex->samplerType(), tex->config(), access.getVisibility(), caps);
     }
     // zero the last 16 bits if the number of textures is odd.
     if (numTextures & 0x1) {
diff --git a/src/gpu/glsl/GrGLSLCaps.cpp b/src/gpu/glsl/GrGLSLCaps.cpp
index c2404c6..60bea86 100755
--- a/src/gpu/glsl/GrGLSLCaps.cpp
+++ b/src/gpu/glsl/GrGLSLCaps.cpp
@@ -92,5 +92,60 @@
     return r;
 }
 
+void GrGLSLCaps::initSamplerPrecisionTable() {
+    // Determine the largest precision qualifiers that are effectively the same as lowp/mediump.
+    //   e.g. if lowp == mediump, then use mediump instead of lowp.
+    GrSLPrecision effectiveMediumP[kGrShaderTypeCount];
+    GrSLPrecision effectiveLowP[kGrShaderTypeCount];
+    for (int s = 0; s < kGrShaderTypeCount; ++s) {
+        const PrecisionInfo* info = fFloatPrecisions[s];
+        effectiveMediumP[s] = info[kHigh_GrSLPrecision] == info[kMedium_GrSLPrecision] ?
+                                  kHigh_GrSLPrecision : kMedium_GrSLPrecision;
+        effectiveLowP[s] = info[kMedium_GrSLPrecision] == info[kLow_GrSLPrecision] ?
+                               effectiveMediumP[s] : kLow_GrSLPrecision;
+    }
+
+    // Determine which precision qualifiers should be used with samplers.
+    for (int visibility = 0; visibility < (1 << kGrShaderTypeCount); ++visibility) {
+        GrSLPrecision mediump = kHigh_GrSLPrecision;
+        GrSLPrecision lowp = kHigh_GrSLPrecision;
+        for (int s = 0; s < kGrShaderTypeCount; ++s) {
+            if (visibility & (1 << s)) {
+                mediump = SkTMin(mediump, effectiveMediumP[s]);
+                lowp = SkTMin(lowp, effectiveLowP[s]);
+            }
+
+            GR_STATIC_ASSERT(0 == kLow_GrSLPrecision);
+            GR_STATIC_ASSERT(1 == kMedium_GrSLPrecision);
+            GR_STATIC_ASSERT(2 == kHigh_GrSLPrecision);
+
+            GR_STATIC_ASSERT((1 << kVertex_GrShaderType) == kVertex_GrShaderFlag);
+            GR_STATIC_ASSERT((1 << kGeometry_GrShaderType) == kGeometry_GrShaderFlag);
+            GR_STATIC_ASSERT((1 << kFragment_GrShaderType) == kFragment_GrShaderFlag);
+            GR_STATIC_ASSERT(3 == kGrShaderTypeCount);
+        }
+
+        uint8_t* table = fSamplerPrecisions[visibility];
+        table[kUnknown_GrPixelConfig]    = kDefault_GrSLPrecision;
+        table[kAlpha_8_GrPixelConfig]    = lowp;
+        table[kIndex_8_GrPixelConfig]    = lowp;
+        table[kRGB_565_GrPixelConfig]    = lowp;
+        table[kRGBA_4444_GrPixelConfig]  = lowp;
+        table[kRGBA_8888_GrPixelConfig]  = lowp;
+        table[kBGRA_8888_GrPixelConfig]  = lowp;
+        table[kSRGBA_8888_GrPixelConfig] = lowp;
+        table[kSBGRA_8888_GrPixelConfig] = lowp;
+        table[kETC1_GrPixelConfig]       = lowp;
+        table[kLATC_GrPixelConfig]       = lowp;
+        table[kR11_EAC_GrPixelConfig]    = lowp;
+        table[kASTC_12x12_GrPixelConfig] = lowp;
+        table[kRGBA_float_GrPixelConfig] = kHigh_GrSLPrecision;
+        table[kAlpha_half_GrPixelConfig] = mediump;
+        table[kRGBA_half_GrPixelConfig]  = mediump;
+
+        GR_STATIC_ASSERT(16 == kGrPixelConfigCnt);
+    }
+}
+
 void GrGLSLCaps::onApplyOptionsOverrides(const GrContextOptions& options) {
 }
diff --git a/src/gpu/glsl/GrGLSLCaps.h b/src/gpu/glsl/GrGLSLCaps.h
index 7b0868e..a1d458a 100755
--- a/src/gpu/glsl/GrGLSLCaps.h
+++ b/src/gpu/glsl/GrGLSLCaps.h
@@ -162,6 +162,11 @@
         return fConfigOutputSwizzle[config];
     }
 
+    /** Precision qualifier that should be used with a sampler, given its config and visibility. */
+    GrSLPrecision samplerPrecision(GrPixelConfig config, GrShaderFlags visibility) const {
+        return static_cast<GrSLPrecision>(fSamplerPrecisions[visibility][config]);
+    }
+
     GrGLSLGeneration generation() const { return fGLSLGeneration; }
 
     /**
@@ -170,6 +175,9 @@
     SkString dump() const override;
 
 private:
+    /** GrCaps subclasses must call this after filling in the shader precision table. */
+    void initSamplerPrecisionTable();
+
     void onApplyOptionsOverrides(const GrContextOptions& options) override;
 
     GrGLSLGeneration fGLSLGeneration;
@@ -216,6 +224,8 @@
     GrSwizzle fConfigTextureSwizzle[kGrPixelConfigCnt];
     GrSwizzle fConfigOutputSwizzle[kGrPixelConfigCnt];
 
+    uint8_t fSamplerPrecisions[(1 << kGrShaderTypeCount)][kGrPixelConfigCnt];
+
     friend class GrGLCaps;  // For initialization.
     friend class GrVkCaps;
 
diff --git a/src/gpu/glsl/GrGLSLProgramBuilder.cpp b/src/gpu/glsl/GrGLSLProgramBuilder.cpp
index 512d9d1..4e90452 100644
--- a/src/gpu/glsl/GrGLSLProgramBuilder.cpp
+++ b/src/gpu/glsl/GrGLSLProgramBuilder.cpp
@@ -241,10 +241,12 @@
                              1 << GrGLSLShaderBuilder::kExternalTexture_GLSLPrivateFeature,
                              externalFeatureString);
         }
+        GrSLPrecision precision = this->glslCaps()->samplerPrecision(access.getTexture()->config(),
+                                                                     visibility);
         name.printf("Sampler%d", t);
-        localSamplerUniforms[t] = this->uniformHandler()->addUniform(access.getVisibility(),
+        localSamplerUniforms[t] = this->uniformHandler()->addUniform(visibility,
                                                                      samplerType,
-                                                                     access.getPrecision(),
+                                                                     precision,
                                                                      name.c_str());
         outSamplers->emplace_back(localSamplerUniforms[t], access);
     }
diff --git a/src/gpu/vk/GrVkCaps.cpp b/src/gpu/vk/GrVkCaps.cpp
index b72a512..5b62a37 100644
--- a/src/gpu/vk/GrVkCaps.cpp
+++ b/src/gpu/vk/GrVkCaps.cpp
@@ -154,6 +154,21 @@
 
     glslCaps->fIntegerSupport = true;
 
+    // Assume the minimum precisions mandated by the SPIR-V spec.
+    glslCaps->fShaderPrecisionVaries = true;
+    for (int s = 0; s < kGrShaderTypeCount; ++s) {
+        auto& highp = glslCaps->fFloatPrecisions[s][kHigh_GrSLPrecision];
+        highp.fLogRangeLow = highp.fLogRangeHigh = 127;
+        highp.fBits = 23;
+
+        auto& mediump = glslCaps->fFloatPrecisions[s][kMedium_GrSLPrecision];
+        mediump.fLogRangeLow = mediump.fLogRangeHigh = 14;
+        mediump.fBits = 10;
+
+        glslCaps->fFloatPrecisions[s][kLow_GrSLPrecision] = mediump;
+    }
+    glslCaps->initSamplerPrecisionTable();
+
     glslCaps->fMaxVertexSamplers =
     glslCaps->fMaxGeometrySamplers =
     glslCaps->fMaxFragmentSamplers = SkTMin(properties.limits.maxPerStageDescriptorSampledImages,
diff --git a/src/gpu/vk/GrVkProgramDesc.cpp b/src/gpu/vk/GrVkProgramDesc.cpp
index 74e6bbb..f4bd2bf 100644
--- a/src/gpu/vk/GrVkProgramDesc.cpp
+++ b/src/gpu/vk/GrVkProgramDesc.cpp
@@ -22,7 +22,7 @@
 static void add_texture_key(GrProcessorKeyBuilder* b, const GrProcessor& proc,
                             const GrGLSLCaps& caps) {
     int numTextures = proc.numTextures();
-    // Need two bytes per key (swizzle and target).
+    // Need two bytes per key (swizzle, sampler type, and precision).
     int word32Count = (proc.numTextures() + 1) / 2;
     if (0 == word32Count) {
         return;
@@ -31,7 +31,8 @@
     for (int i = 0; i < numTextures; ++i) {
         const GrTextureAccess& access = proc.textureAccess(i);
         GrTexture* texture = access.getTexture();
-        k16[i] = SkToU16(caps.configTextureSwizzle(texture->config()).asKey());
+        k16[i] = SkToU16(caps.configTextureSwizzle(texture->config()).asKey() |
+                         (caps.samplerPrecision(texture->config(), access.getVisibility()) << 8));
     }
     // zero the last 16 bits if the number of textures is odd.
     if (numTextures & 0x1) {