Implement separate sampler and textures in SkSL.

Dawn doesn't support old-style combined texturesampler uniforms, so
they must be expressed as separate samplers and texture uniforms.

At the SkSL Type level, I've added a Texture2D type, and expressed
the Sampler2D (combined) type in terms of it. This ensures that we
emit only a single OpTypeImage for it in the SPIRV.

Eventually, all of the Texture types (1D, 3D, Rect) could be defined
and SamplerX could simply contain a reference to TextureX. I wanted to
float this idea with a single example for now (and since it's all that
the Dawn backend needs).

This also required adding a new "makeSampler2D" function to combine
them, which maps to OpSampledImage at the SPIR-V level.

Change-Id: Iaf33a6e7d339da415be6ea9a017340cb0ef3c1eb
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/229417
Commit-Queue: Stephen White <senorblanco@chromium.org>
Reviewed-by: Ethan Nicholas <ethannicholas@google.com>
diff --git a/include/private/GrTypesPriv.h b/include/private/GrTypesPriv.h
index 1402c4e..fd08219 100644
--- a/include/private/GrTypesPriv.h
+++ b/include/private/GrTypesPriv.h
@@ -371,8 +371,10 @@
     kTexture2DSampler_GrSLType,
     kTextureExternalSampler_GrSLType,
     kTexture2DRectSampler_GrSLType,
+    kTexture2D_GrSLType,
+    kSampler_GrSLType,
 
-    kLast_GrSLType = kTexture2DRectSampler_GrSLType
+    kLast_GrSLType = kSampler_GrSLType
 };
 static const int kGrSLTypeCount = kLast_GrSLType + 1;
 
@@ -474,6 +476,8 @@
         case kInt4_GrSLType:
         case kUint_GrSLType:
         case kUint2_GrSLType:
+        case kTexture2D_GrSLType:
+        case kSampler_GrSLType:
             return false;
     }
     SkUNREACHABLE;
@@ -531,6 +535,8 @@
         case kTexture2DSampler_GrSLType:
         case kTextureExternalSampler_GrSLType:
         case kTexture2DRectSampler_GrSLType:
+        case kTexture2D_GrSLType:
+        case kSampler_GrSLType:
             return -1;
     }
     SkUNREACHABLE;
@@ -612,6 +618,8 @@
         case kUShort2_GrSLType:
         case kUShort3_GrSLType:
         case kUShort4_GrSLType:
+        case kTexture2D_GrSLType:
+        case kSampler_GrSLType:
             return false;
     }
     SkUNREACHABLE;
diff --git a/src/gpu/glsl/GrGLSL.cpp b/src/gpu/glsl/GrGLSL.cpp
index e8fa694..a506c43 100644
--- a/src/gpu/glsl/GrGLSL.cpp
+++ b/src/gpu/glsl/GrGLSL.cpp
@@ -92,6 +92,10 @@
             return "ubyte3";
         case kUByte4_GrSLType:
             return "ubyte4";
+        case kTexture2D_GrSLType:
+            return "texture2D";
+        case kSampler_GrSLType:
+            return "sampler";
     }
     SK_ABORT("Unknown shader var type.");
     return ""; // suppress warning
diff --git a/src/gpu/mtl/GrMtlUniformHandler.mm b/src/gpu/mtl/GrMtlUniformHandler.mm
index 346f9a3..73eeecf 100644
--- a/src/gpu/mtl/GrMtlUniformHandler.mm
+++ b/src/gpu/mtl/GrMtlUniformHandler.mm
@@ -85,6 +85,8 @@
         case kTexture2DSampler_GrSLType:
         case kTextureExternalSampler_GrSLType:
         case kTexture2DRectSampler_GrSLType:
+        case kSampler_GrSLType:
+        case kTexture2D_GrSLType:
             break;
     }
     SK_ABORT("Unexpected type");
@@ -166,6 +168,8 @@
         case kTexture2DSampler_GrSLType:
         case kTextureExternalSampler_GrSLType:
         case kTexture2DRectSampler_GrSLType:
+        case kSampler_GrSLType:
+        case kTexture2D_GrSLType:
             break;
     }
     SK_ABORT("Unexpected type");
diff --git a/src/gpu/vk/GrVkUniformHandler.cpp b/src/gpu/vk/GrVkUniformHandler.cpp
index 2613550..32210d6 100644
--- a/src/gpu/vk/GrVkUniformHandler.cpp
+++ b/src/gpu/vk/GrVkUniformHandler.cpp
@@ -84,6 +84,8 @@
         case kTexture2DSampler_GrSLType:
         case kTextureExternalSampler_GrSLType:
         case kTexture2DRectSampler_GrSLType:
+        case kSampler_GrSLType:
+        case kTexture2D_GrSLType:
             break;
     }
     SK_ABORT("Unexpected type");
@@ -166,6 +168,8 @@
         case kTexture2DSampler_GrSLType:
         case kTextureExternalSampler_GrSLType:
         case kTexture2DRectSampler_GrSLType:
+        case kSampler_GrSLType:
+        case kTexture2D_GrSLType:
             break;
     }
     SK_ABORT("Unexpected type");
diff --git a/src/gpu/vk/GrVkVaryingHandler.cpp b/src/gpu/vk/GrVkVaryingHandler.cpp
index a4b9350..1a38e8f 100644
--- a/src/gpu/vk/GrVkVaryingHandler.cpp
+++ b/src/gpu/vk/GrVkVaryingHandler.cpp
@@ -55,6 +55,8 @@
         case kHalf4x4_GrSLType:
             return 4;
         case kTexture2DSampler_GrSLType:
+        case kSampler_GrSLType:
+        case kTexture2D_GrSLType:
             return 0;
         case kTextureExternalSampler_GrSLType:
              return 0;
diff --git a/src/sksl/SkSLCompiler.cpp b/src/sksl/SkSLCompiler.cpp
index 8476396..36df201 100644
--- a/src/sksl/SkSLCompiler.cpp
+++ b/src/sksl/SkSLCompiler.cpp
@@ -211,6 +211,8 @@
     ADD_TYPE(GSamplerCubeArrayShadow);
     ADD_TYPE(FragmentProcessor);
     ADD_TYPE(SkRasterPipeline);
+    ADD_TYPE(Sampler);
+    ADD_TYPE(Texture2D);
 
     StringFragment skCapsName("sk_Caps");
     Variable* skCaps = new Variable(-1, Modifiers(), skCapsName,
diff --git a/src/sksl/SkSLContext.h b/src/sksl/SkSLContext.h
index abe7322..b566a5e 100644
--- a/src/sksl/SkSLContext.h
+++ b/src/sksl/SkSLContext.h
@@ -91,8 +91,9 @@
     , fDouble4x2_Type(new Type("double4x2", *fDouble_Type, 4, 2))
     , fDouble4x3_Type(new Type("double4x3", *fDouble_Type, 4, 3))
     , fDouble4x4_Type(new Type("double4x4", *fDouble_Type, 4, 4))
+    , fTexture2D_Type(new Type("texture2D", Type::kTexture_Kind))
     , fSampler1D_Type(new Type("sampler1D", SpvDim1D, false, false, false, true))
-    , fSampler2D_Type(new Type("sampler2D", SpvDim2D, false, false, false, true))
+    , fSampler2D_Type(new Type("sampler2D", SpvDim2D, false, false, false, true, &*fTexture2D_Type))
     , fSampler3D_Type(new Type("sampler3D", SpvDim3D, false, false, false, true))
     , fSamplerExternalOES_Type(new Type("samplerExternalOES", SpvDim2D, false, false,
                                         false, true))
@@ -116,6 +117,7 @@
     // Related to below FIXME, gsampler*s don't currently expand to cover integer case.
     , fISampler2D_Type(new Type("isampler2D", SpvDim2D, false, false, false, true))
 
+    , fSampler_Type(new Type("sampler", Type::kSeparateSampler_Kind))
     // FIXME express these as "gimage2D" that expand to image2D, iimage2D, and uimage2D.
     , fImage2D_Type(new Type("image2D", SpvDim2D, false, false, false, true))
     , fIImage2D_Type(new Type("iimage2D", SpvDim2D, false, false, false, true))
@@ -293,6 +295,8 @@
     const std::unique_ptr<Type> fDouble4x3_Type;
     const std::unique_ptr<Type> fDouble4x4_Type;
 
+    const std::unique_ptr<Type> fTexture2D_Type;
+
     const std::unique_ptr<Type> fSampler1D_Type;
     const std::unique_ptr<Type> fSampler2D_Type;
     const std::unique_ptr<Type> fSampler3D_Type;
@@ -314,6 +318,7 @@
     const std::unique_ptr<Type> fSamplerCubeArrayShadow_Type;
 
     const std::unique_ptr<Type> fISampler2D_Type;
+    const std::unique_ptr<Type> fSampler_Type;
 
     const std::unique_ptr<Type> fImage2D_Type;
     const std::unique_ptr<Type> fIImage2D_Type;
diff --git a/src/sksl/SkSLSPIRVCodeGenerator.cpp b/src/sksl/SkSLSPIRVCodeGenerator.cpp
index 08c0fdb..6f1ae03 100644
--- a/src/sksl/SkSLSPIRVCodeGenerator.cpp
+++ b/src/sksl/SkSLSPIRVCodeGenerator.cpp
@@ -101,6 +101,8 @@
     fIntrinsicMap[String("dFdy")]        = SPECIAL(DFdy);
     fIntrinsicMap[String("fwidth")]      = std::make_tuple(kSPIRV_IntrinsicKind, SpvOpFwidth,
                                                            SpvOpUndef, SpvOpUndef, SpvOpUndef);
+    fIntrinsicMap[String("makeSampler2D")] = SPECIAL(SampledImage);
+
     fIntrinsicMap[String("texture")]     = SPECIAL(Texture);
     fIntrinsicMap[String("subpassLoad")] = SPECIAL(SubpassLoad);
 
@@ -206,6 +208,7 @@
         case SpvOpTypeStruct:        // fall through
         case SpvOpTypeImage:         // fall through
         case SpvOpTypeSampledImage:  // fall through
+        case SpvOpTypeSampler:       // fall through
         case SpvOpVariable:          // fall through
         case SpvOpFunction:          // fall through
         case SpvOpFunctionParameter: // fall through
@@ -526,22 +529,42 @@
             case Type::kSampler_Kind: {
                 SpvId image = result;
                 if (SpvDimSubpassData != type.dimensions()) {
-                    image = this->nextId();
+                    if (type.textureType()) {
+                        image = this->getType(*type.textureType(), layout);
+                    } else {
+                        image = nextId();
+                    }
                 }
                 if (SpvDimBuffer == type.dimensions()) {
                     fCapabilities |= (((uint64_t) 1) << SpvCapabilitySampledBuffer);
                 }
-                this->writeInstruction(SpvOpTypeImage, image,
-                                       this->getType(*fContext.fFloat_Type, layout),
-                                       type.dimensions(), type.isDepth(), type.isArrayed(),
-                                       type.isMultisampled(), type.isSampled() ? 1 : 2,
-                                       SpvImageFormatUnknown, fConstantBuffer);
-                fImageTypeMap[key] = image;
+                if (!type.textureType()) {
+                    this->writeInstruction(SpvOpTypeImage, image,
+                                           this->getType(*fContext.fFloat_Type, layout),
+                                           type.dimensions(), type.isDepth(), type.isArrayed(),
+                                           type.isMultisampled(), type.isSampled() ? 1 : 2,
+                                           SpvImageFormatUnknown, fConstantBuffer);
+                    fImageTypeMap[key] = image;
+                }
                 if (SpvDimSubpassData != type.dimensions()) {
                     this->writeInstruction(SpvOpTypeSampledImage, result, image, fConstantBuffer);
                 }
                 break;
             }
+            case Type::kSeparateSampler_Kind: {
+                this->writeInstruction(SpvOpTypeSampler, result, fConstantBuffer);
+                break;
+            }
+            case Type::kTexture_Kind: {
+                // FIXME: should support more than 2D
+                this->writeInstruction(SpvOpTypeImage, result,
+                                       this->getType(*fContext.fFloat_Type, layout),
+                                       SpvDim2D, type.isDepth(), type.isArrayed(),
+                                       type.isMultisampled(), 1,
+                                       SpvImageFormatUnknown, fConstantBuffer);
+                fImageTypeMap[key] = result;
+                break;
+            }
             default:
                 if (type == *fContext.fVoid_Type) {
                     this->writeInstruction(SpvOpTypeVoid, result, fConstantBuffer);
@@ -819,6 +842,18 @@
             }
             break;
         }
+        case kSampledImage_SpecialIntrinsic: {
+            SkASSERT(2 == c.fArguments.size());
+            SpvId img = this->writeExpression(*c.fArguments[0], out);
+            SpvId sampler = this->writeExpression(*c.fArguments[1], out);
+            this->writeInstruction(SpvOpSampledImage,
+                                   this->getType(c.fType),
+                                   result,
+                                   img,
+                                   sampler,
+                                   out);
+            break;
+        }
         case kSubpassLoad_SpecialIntrinsic: {
             SpvId img = this->writeExpression(*c.fArguments[0], out);
             std::vector<std::unique_ptr<Expression>> args;
@@ -2708,7 +2743,9 @@
         } else if (var->fModifiers.fFlags & Modifiers::kOut_Flag) {
             storageClass = SpvStorageClassOutput;
         } else if (var->fModifiers.fFlags & Modifiers::kUniform_Flag) {
-            if (var->fType.kind() == Type::kSampler_Kind) {
+            if (var->fType.kind() == Type::kSampler_Kind ||
+                var->fType.kind() == Type::kSeparateSampler_Kind ||
+                var->fType.kind() == Type::kTexture_Kind) {
                 storageClass = SpvStorageClassUniformConstant;
             } else {
                 storageClass = SpvStorageClassUniform;
diff --git a/src/sksl/SkSLSPIRVCodeGenerator.h b/src/sksl/SkSLSPIRVCodeGenerator.h
index aff2e7f..8f14b46 100644
--- a/src/sksl/SkSLSPIRVCodeGenerator.h
+++ b/src/sksl/SkSLSPIRVCodeGenerator.h
@@ -134,6 +134,7 @@
         kMod_SpecialIntrinsic,
         kDFdy_SpecialIntrinsic,
         kSaturate_SpecialIntrinsic,
+        kSampledImage_SpecialIntrinsic,
         kSubpassLoad_SpecialIntrinsic,
         kTexture_SpecialIntrinsic,
     };
diff --git a/src/sksl/ir/SkSLType.h b/src/sksl/ir/SkSLType.h
index 9c0fcc8..6b474b1 100644
--- a/src/sksl/ir/SkSLType.h
+++ b/src/sksl/ir/SkSLType.h
@@ -49,8 +49,10 @@
         kMatrix_Kind,
         kOther_Kind,
         kSampler_Kind,
+        kSeparateSampler_Kind,
         kScalar_Kind,
         kStruct_Kind,
+        kTexture_Kind,
         kVector_Kind
     };
 
@@ -194,7 +196,7 @@
 
     // Create a sampler type.
     Type(const char* name, SpvDim_ dimensions, bool isDepth, bool isArrayed, bool isMultisampled,
-         bool isSampled)
+         bool isSampled, Type* textureType = nullptr)
     : INHERITED(-1, kType_Kind, StringFragment())
     , fNameString(name)
     , fTypeKind(kSampler_Kind)
@@ -203,7 +205,9 @@
     , fIsDepth(isDepth)
     , fIsArrayed(isArrayed)
     , fIsMultisampled(isMultisampled)
-    , fIsSampled(isSampled) {
+    , fIsSampled(isSampled)
+    , fTextureType(textureType)
+    {
         fName.fChars = fNameString.c_str();
         fName.fLength = fNameString.size();
     }
@@ -305,6 +309,14 @@
     }
 
     /**
+     * For texturesamplers, returns the type of texture it samples (e.g., sampler2D has
+     * a texture type of texture2D).
+     */
+    const Type* textureType() const {
+        return fTextureType;
+    }
+
+    /**
      * For nullable types, returns the base type, otherwise returns the type itself.
      */
     const Type& nonnullable() const {
@@ -349,27 +361,27 @@
     }
 
     SpvDim_ dimensions() const {
-        SkASSERT(kSampler_Kind == fTypeKind);
+        SkASSERT(kSampler_Kind == fTypeKind || kTexture_Kind == fTypeKind);
         return fDimensions;
     }
 
     bool isDepth() const {
-        SkASSERT(kSampler_Kind == fTypeKind);
+        SkASSERT(kSampler_Kind == fTypeKind || kTexture_Kind == fTypeKind);
         return fIsDepth;
     }
 
     bool isArrayed() const {
-        SkASSERT(kSampler_Kind == fTypeKind);
+        SkASSERT(kSampler_Kind == fTypeKind || kTexture_Kind == fTypeKind);
         return fIsArrayed;
     }
 
     bool isMultisampled() const {
-        SkASSERT(kSampler_Kind == fTypeKind);
+        SkASSERT(kSampler_Kind == fTypeKind || kTexture_Kind == fTypeKind);
         return fIsMultisampled;
     }
 
     bool isSampled() const {
-        SkASSERT(kSampler_Kind == fTypeKind);
+        SkASSERT(kSampler_Kind == fTypeKind || kTexture_Kind == fTypeKind);
         return fIsSampled;
     }
 
@@ -405,6 +417,7 @@
     bool fIsMultisampled = false;
     bool fIsSampled = false;
     bool fHighPrecision = false;
+    const Type* fTextureType = nullptr;
 };
 
 } // namespace
diff --git a/src/sksl/sksl_gpu.inc b/src/sksl/sksl_gpu.inc
index 46fb124..dc76bda 100644
--- a/src/sksl/sksl_gpu.inc
+++ b/src/sksl/sksl_gpu.inc
@@ -254,6 +254,7 @@
 $genIType findMSB($genIType value);
 $genIType findMSB($genUType value);
 
+sampler2D makeSampler2D(texture2D texture, sampler sampler);
 int2 textureSize($gsampler2DRect sampler);
 
 half4 texture($gsampler1D sampler, float P);