added support for sk_Dimensions to SkSL

Bug: skia:
Change-Id: I5f6a9941822b7b4a3ad85b22b1bcd31d58320f90
Reviewed-on: https://skia-review.googlesource.com/146640
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Ethan Nicholas <ethannicholas@google.com>
diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp
index 9e90642..f033ffd 100644
--- a/src/gpu/gl/GrGLProgram.cpp
+++ b/src/gpu/gl/GrGLProgram.cpp
@@ -127,10 +127,13 @@
 void GrGLProgram::setRenderTargetState(const GrPrimitiveProcessor& primProc,
                                        const GrRenderTargetProxy* proxy) {
     GrRenderTarget* rt = proxy->peekRenderTarget();
-    // Load the RT height uniform if it is needed to y-flip gl_FragCoord.
-    if (fBuiltinUniformHandles.fRTHeightUni.isValid() &&
-        fRenderTargetState.fRenderTargetSize.fHeight != rt->height()) {
-        fProgramDataManager.set1f(fBuiltinUniformHandles.fRTHeightUni, SkIntToScalar(rt->height()));
+    // Load the RT dimension uniform if it is needed
+    if (fBuiltinUniformHandles.fRTDimensionsUni.isValid() &&
+        (fRenderTargetState.fRenderTargetSize.fWidth != rt->width() ||
+         fRenderTargetState.fRenderTargetSize.fHeight != rt->height())) {
+        fProgramDataManager.set2f(fBuiltinUniformHandles.fRTDimensionsUni,
+                                  SkIntToScalar(rt->width()),
+                                  SkIntToScalar(rt->height()));
     }
 
     // set RT adjustment
diff --git a/src/gpu/gl/builders/GrGLProgramBuilder.cpp b/src/gpu/gl/builders/GrGLProgramBuilder.cpp
index ba290ac..c9c16bb 100644
--- a/src/gpu/gl/builders/GrGLProgramBuilder.cpp
+++ b/src/gpu/gl/builders/GrGLProgramBuilder.cpp
@@ -159,6 +159,12 @@
     SkASSERT(fInstanceStride == primProc.debugOnly_instanceStride());
 }
 
+void GrGLProgramBuilder::addInputVars(const SkSL::Program::Inputs& inputs) {
+    if (inputs.fRTDimensions) {
+        this->addRTDimensionsUniform(SKSL_RTDIMENSIONS_NAME);
+    }
+}
+
 GrGLProgram* GrGLProgramBuilder::finalize() {
     TRACE_EVENT0("skia", TRACE_FUNC);
 
@@ -204,9 +210,7 @@
         if (GR_GL_GET_ERROR(this->gpu()->glInterface()) == GR_GL_NO_ERROR) {
             cached = this->checkLinkStatus(programID);
             if (cached) {
-                if (inputs.fRTHeight) {
-                    this->addRTHeightUniform(SKSL_RTHEIGHT_NAME);
-                }
+                this->addInputVars(inputs);
                 this->computeCountsAndStrides(programID, primProc, false);
             }
         } else {
@@ -231,9 +235,7 @@
             return nullptr;
         }
         inputs = fs->fInputs;
-        if (inputs.fRTHeight) {
-            this->addRTHeightUniform(SKSL_RTHEIGHT_NAME);
-        }
+        this->addInputVars(inputs);
         if (!this->compileAndAttachShaders(glsl.c_str(), glsl.size(), programID,
                                            GR_GL_FRAGMENT_SHADER, &shadersToDelete, settings,
                                            inputs)) {
diff --git a/src/gpu/gl/builders/GrGLProgramBuilder.h b/src/gpu/gl/builders/GrGLProgramBuilder.h
index 64e4269..b601b6b 100644
--- a/src/gpu/gl/builders/GrGLProgramBuilder.h
+++ b/src/gpu/gl/builders/GrGLProgramBuilder.h
@@ -48,6 +48,7 @@
     GrGLProgramBuilder(GrGLGpu*, const GrPipeline&, const GrPrimitiveProcessor&,
                        GrProgramDesc*);
 
+    void addInputVars(const SkSL::Program::Inputs& inputs);
     bool compileAndAttachShaders(const char* glsl,
                                  int length,
                                  GrGLuint programId,
diff --git a/src/gpu/glsl/GrGLSLProgramBuilder.cpp b/src/gpu/glsl/GrGLSLProgramBuilder.cpp
index 66e333a..6206cf6 100644
--- a/src/gpu/glsl/GrGLSLProgramBuilder.cpp
+++ b/src/gpu/glsl/GrGLSLProgramBuilder.cpp
@@ -371,12 +371,12 @@
     this->uniformHandler()->appendUniformDecls(visibility, out);
 }
 
-void GrGLSLProgramBuilder::addRTHeightUniform(const char* name) {
-        SkASSERT(!fUniformHandles.fRTHeightUni.isValid());
+void GrGLSLProgramBuilder::addRTDimensionsUniform(const char* name) {
+        SkASSERT(!fUniformHandles.fRTDimensionsUni.isValid());
         GrGLSLUniformHandler* uniformHandler = this->uniformHandler();
-        fUniformHandles.fRTHeightUni =
+        fUniformHandles.fRTDimensionsUni =
             uniformHandler->internalAddUniformArray(kFragment_GrShaderFlag,
-                                                    kHalf_GrSLType, kDefault_GrSLPrecision,
+                                                    kFloat2_GrSLType, kDefault_GrSLPrecision,
                                                     name, false, 0, nullptr);
 }
 
diff --git a/src/gpu/glsl/GrGLSLProgramBuilder.h b/src/gpu/glsl/GrGLSLProgramBuilder.h
index 8d263b8..77593b5 100644
--- a/src/gpu/glsl/GrGLSLProgramBuilder.h
+++ b/src/gpu/glsl/GrGLSLProgramBuilder.h
@@ -49,9 +49,9 @@
         return this->uniformHandler()->samplerSwizzle(handle);
     }
 
-    // Used to add a uniform for the RenderTarget height (used for frag position) without mangling
-    // the name of the uniform inside of a stage.
-    void addRTHeightUniform(const char* name);
+    // Used to add a uniform for the RenderTarget height (used for sk_Dimensions and sk_FragCoord)
+    // without mangling the name of the uniform inside of a stage.
+    void addRTDimensionsUniform(const char* name);
 
     // Generates a name for a variable. The generated string will be name prefixed by the prefix
     // char (unless the prefix is '\0'). It also will mangle the name to be stage-specific unless
diff --git a/src/gpu/glsl/GrGLSLUniformHandler.h b/src/gpu/glsl/GrGLSLUniformHandler.h
index ae87faf..eb7a850 100644
--- a/src/gpu/glsl/GrGLSLUniformHandler.h
+++ b/src/gpu/glsl/GrGLSLUniformHandler.h
@@ -20,9 +20,9 @@
 // Handles for program uniforms (other than per-effect uniforms)
 struct GrGLSLBuiltinUniformHandles {
     GrGLSLProgramDataManager::UniformHandle fRTAdjustmentUni;
-    // We use the render target height to provide a y-down frag coord when specifying
+    // Render target dimensions, used to implement sk_Dimensions and to calculate sk_FragCoord when
     // origin_upper_left is not supported.
-    GrGLSLProgramDataManager::UniformHandle fRTHeightUni;
+    GrGLSLProgramDataManager::UniformHandle fRTDimensionsUni;
 };
 
 class GrGLSLUniformHandler {
diff --git a/src/gpu/mtl/GrMtlPipelineState.mm b/src/gpu/mtl/GrMtlPipelineState.mm
index b2bcf08..82dc743 100644
--- a/src/gpu/mtl/GrMtlPipelineState.mm
+++ b/src/gpu/mtl/GrMtlPipelineState.mm
@@ -145,9 +145,11 @@
     GrRenderTarget* rt = proxy->peekRenderTarget();
 
     // Load the RT height uniform if it is needed to y-flip gl_FragCoord.
-    if (fBuiltinUniformHandles.fRTHeightUni.isValid() &&
-        fRenderTargetState.fRenderTargetSize.fHeight != rt->height()) {
-        fDataManager.set1f(fBuiltinUniformHandles.fRTHeightUni, SkIntToScalar(rt->height()));
+    if (fBuiltinUniformHandles.fRTDimensionsUni.isValid() &&
+        (fRenderTargetState.fRenderTargetSize.fWidth != rt->width() ||
+         fRenderTargetState.fRenderTargetSize.fHeight != rt->height())) {
+        fDataManager.set2f(fBuiltinUniformHandles.fRTDimensionsUni, SkIntToScalar(rt->width()),
+                           SkIntToScalar(rt->height()));
     }
 
     // set RT adjustment
diff --git a/src/gpu/mtl/GrMtlPipelineStateBuilder.mm b/src/gpu/mtl/GrMtlPipelineStateBuilder.mm
index 269779f..3fe8ba4 100644
--- a/src/gpu/mtl/GrMtlPipelineStateBuilder.mm
+++ b/src/gpu/mtl/GrMtlPipelineStateBuilder.mm
@@ -70,8 +70,8 @@
     if (shaderLibrary == nil) {
         return nil;
     }
-    if (inputs.fRTHeight) {
-        this->addRTHeightUniform(SKSL_RTHEIGHT_NAME);
+    if (inputs.fRTDimensions) {
+        this->addRTDimensionsUniform(SKSL_RTDIMENSIONS_NAME);
     }
     if (inputs.fFlipY) {
         desc->setSurfaceOriginKey(GrGLSLFragmentShaderBuilder::KeyForSurfaceOrigin(
diff --git a/src/gpu/vk/GrVkPipelineState.cpp b/src/gpu/vk/GrVkPipelineState.cpp
index cd59eab..a18bdb1 100644
--- a/src/gpu/vk/GrVkPipelineState.cpp
+++ b/src/gpu/vk/GrVkPipelineState.cpp
@@ -350,9 +350,11 @@
     GrRenderTarget* rt = proxy->peekRenderTarget();
 
     // Load the RT height uniform if it is needed to y-flip gl_FragCoord.
-    if (fBuiltinUniformHandles.fRTHeightUni.isValid() &&
-        fRenderTargetState.fRenderTargetSize.fHeight != rt->height()) {
-        fDataManager.set1f(fBuiltinUniformHandles.fRTHeightUni, SkIntToScalar(rt->height()));
+    if (fBuiltinUniformHandles.fRTDimensionsUni.isValid() &&
+        (fRenderTargetState.fRenderTargetSize.fWidth != rt->width() ||
+        fRenderTargetState.fRenderTargetSize.fHeight != rt->height())) {
+        fDataManager.set2f(fBuiltinUniformHandles.fRTDimensionsUni, SkIntToScalar(rt->height()),
+                           SkIntToScalar(rt->width()));
     }
 
     // set RT adjustment
diff --git a/src/gpu/vk/GrVkPipelineStateBuilder.cpp b/src/gpu/vk/GrVkPipelineStateBuilder.cpp
index 91685c6..0533805 100644
--- a/src/gpu/vk/GrVkPipelineStateBuilder.cpp
+++ b/src/gpu/vk/GrVkPipelineStateBuilder.cpp
@@ -75,8 +75,8 @@
     if (!result) {
         return false;
     }
-    if (inputs.fRTHeight) {
-        this->addRTHeightUniform(SKSL_RTHEIGHT_NAME);
+    if (inputs.fRTDimensions) {
+        this->addRTDimensionsUniform(SKSL_RTDIMENSIONS_NAME);
     }
     if (inputs.fFlipY) {
         desc->setSurfaceOriginKey(GrGLSLFragmentShaderBuilder::KeyForSurfaceOrigin(
diff --git a/src/sksl/README b/src/sksl/README
index dd0af98..6256417 100644
--- a/src/sksl/README
+++ b/src/sksl/README
@@ -57,6 +57,7 @@
   Use swizzles instead.
 * Use texture() instead of textureProj(), e.g. texture(sampler2D, float3) is
   equivalent to GLSL's textureProj(sampler2D, float3)
+* Render target dimensions are available via sk_Dimensions
 * some built-in functions and one or two rarely-used language features are not
   yet supported (sorry!)
 
diff --git a/src/sksl/SkSLCPPCodeGenerator.cpp b/src/sksl/SkSLCPPCodeGenerator.cpp
index 9d39595..137403b 100644
--- a/src/sksl/SkSLCPPCodeGenerator.cpp
+++ b/src/sksl/SkSLCPPCodeGenerator.cpp
@@ -246,6 +246,9 @@
             this->write("%s");
             fFormatArgs.push_back(String("args.fOutputColor"));
             break;
+        case SK_DIMENSIONS_BUILTIN:
+            this->write("sk_Dimensions");
+            break;
         default:
             if (ref.fVariable.fType.kind() == Type::kSampler_Kind) {
                 this->write("%s");
@@ -446,6 +449,9 @@
     }
 }
 
+void CPPCodeGenerator::writeInputVars() {
+}
+
 void CPPCodeGenerator::writePrivateVars() {
     for (const auto& p : fProgram) {
         if (ProgramElement::kVar_Kind == p.fKind) {
diff --git a/src/sksl/SkSLCPPCodeGenerator.h b/src/sksl/SkSLCPPCodeGenerator.h
index 014f917..39255d6 100644
--- a/src/sksl/SkSLCPPCodeGenerator.h
+++ b/src/sksl/SkSLCPPCodeGenerator.h
@@ -68,6 +68,8 @@
 
     void writeVarInitializer(const Variable& var, const Expression& value) override;
 
+    void writeInputVars() override;
+
     void writePrivateVars();
 
     void writePrivateVarValues();
diff --git a/src/sksl/SkSLCompiler.h b/src/sksl/SkSLCompiler.h
index f902962..5918223 100644
--- a/src/sksl/SkSLCompiler.h
+++ b/src/sksl/SkSLCompiler.h
@@ -28,6 +28,7 @@
 #define SK_LASTFRAGCOLOR_BUILTIN       10008
 #define SK_MAIN_X_BUILTIN              10009
 #define SK_MAIN_Y_BUILTIN              10010
+#define SK_DIMENSIONS_BUILTIN          10011
 #define SK_FRAGCOORD_BUILTIN              15
 #define SK_CLOCKWISE_BUILTIN              17
 #define SK_VERTEXID_BUILTIN               42
diff --git a/src/sksl/SkSLGLSLCodeGenerator.cpp b/src/sksl/SkSLGLSLCodeGenerator.cpp
index 0e5aea3..9f49a69 100644
--- a/src/sksl/SkSLGLSLCodeGenerator.cpp
+++ b/src/sksl/SkSLGLSLCodeGenerator.cpp
@@ -693,7 +693,7 @@
     if (!fProgram.fSettings.fFlipY) {
         this->write("gl_FragCoord");
     } else if (const char* extension =
-               fProgram.fSettings.fCaps->fragCoordConventionsExtensionString()) {
+                                  fProgram.fSettings.fCaps->fragCoordConventionsExtensionString()) {
         if (!fSetupFragPositionGlobal) {
             if (fProgram.fSettings.fCaps->generation() < k150_GrGLSLGeneration) {
                 this->writeExtension(extension);
@@ -703,25 +703,18 @@
         }
         this->write("gl_FragCoord");
     } else {
-        if (!fSetupFragPositionGlobal) {
+        if (!fSetupFragPositionLocal) {
             // The Adreno compiler seems to be very touchy about access to "gl_FragCoord".
             // Accessing glFragCoord.zw can cause a program to fail to link. Additionally,
             // depending on the surrounding code, accessing .xy with a uniform involved can
             // do the same thing. Copying gl_FragCoord.xy into a temp float2 beforehand
             // (and only accessing .xy) seems to "fix" things.
             const char* precision = usesPrecisionModifiers() ? "highp " : "";
-            fGlobals.writeText("uniform ");
-            fGlobals.writeText(precision);
-            fGlobals.writeText("float " SKSL_RTHEIGHT_NAME ";\n");
-            fSetupFragPositionGlobal = true;
-        }
-        if (!fSetupFragPositionLocal) {
-            const char* precision = usesPrecisionModifiers() ? "highp " : "";
             fFunctionHeader += precision;
             fFunctionHeader += "    vec2 _sktmpCoord = gl_FragCoord.xy;\n";
             fFunctionHeader += precision;
-            fFunctionHeader += "    vec4 sk_FragCoord = vec4(_sktmpCoord.x, " SKSL_RTHEIGHT_NAME
-                               " - _sktmpCoord.y, 1.0, 1.0);\n";
+            fFunctionHeader += "    vec4 sk_FragCoord = vec4(_sktmpCoord.x, " SKSL_RTDIMENSIONS_NAME
+                               ".y - _sktmpCoord.y, 1.0, 1.0);\n";
             fSetupFragPositionLocal = true;
         }
         this->write("sk_FragCoord");
@@ -740,6 +733,9 @@
         case SK_FRAGCOORD_BUILTIN:
             this->writeFragCoord();
             break;
+        case SK_DIMENSIONS_BUILTIN:
+            this->write("u_skRTDimensions");
+            break;
         case SK_CLOCKWISE_BUILTIN:
             this->write(fProgram.fSettings.fFlipY ? "(!gl_FrontFacing)" : "gl_FrontFacing");
             break;
@@ -1368,6 +1364,15 @@
     }
 }
 
+void GLSLCodeGenerator::writeInputVars() {
+    if (fProgram.fInputs.fRTDimensions) {
+        const char* precision = usesPrecisionModifiers() ? "highp " : "";
+        fGlobals.writeText("uniform ");
+        fGlobals.writeText(precision);
+        fGlobals.writeText("vec2 " SKSL_RTDIMENSIONS_NAME ";\n");
+    }
+}
+
 bool GLSLCodeGenerator::generateCode() {
     fProgramKind = fProgram.fKind;
     if (fProgramKind != Program::kPipelineStage_Kind) {
@@ -1386,6 +1391,7 @@
     fOut = rawOut;
 
     write_stringstream(fExtensions, *rawOut);
+    this->writeInputVars();
     write_stringstream(fGlobals, *rawOut);
 
     if (!fProgram.fSettings.fCaps->canUseFragCoord()) {
diff --git a/src/sksl/SkSLGLSLCodeGenerator.h b/src/sksl/SkSLGLSLCodeGenerator.h
index f6d78aa..a26bbd6 100644
--- a/src/sksl/SkSLGLSLCodeGenerator.h
+++ b/src/sksl/SkSLGLSLCodeGenerator.h
@@ -118,7 +118,7 @@
 
     void writeModifiers(const Modifiers& modifiers, bool globalContext);
 
-    void writeGlobalVars(const VarDeclaration& vs);
+    virtual void writeInputVars();
 
     virtual void writeVarInitializer(const Variable& var, const Expression& value);
 
diff --git a/src/sksl/SkSLIRGenerator.cpp b/src/sksl/SkSLIRGenerator.cpp
index a793ddd..1b2d992 100644
--- a/src/sksl/SkSLIRGenerator.cpp
+++ b/src/sksl/SkSLIRGenerator.cpp
@@ -1025,16 +1025,22 @@
         }
         case Symbol::kVariable_Kind: {
             const Variable* var = (const Variable*) result;
+            switch (var->fModifiers.fLayout.fBuiltin) {
+                case SK_DIMENSIONS_BUILTIN:
+                    fInputs.fRTDimensions = true;
+                    break;
 #ifndef SKSL_STANDALONE
-            if (var->fModifiers.fLayout.fBuiltin == SK_FRAGCOORD_BUILTIN) {
-                fInputs.fFlipY = true;
-                if (fSettings->fFlipY &&
-                    (!fSettings->fCaps ||
-                     !fSettings->fCaps->fragCoordConventionsExtensionString())) {
-                    fInputs.fRTHeight = true;
-                }
-            }
+                case SK_FRAGCOORD_BUILTIN:
+                    if (var->fModifiers.fLayout.fBuiltin == SK_FRAGCOORD_BUILTIN) {
+                        fInputs.fFlipY = true;
+                        if (fSettings->fFlipY &&
+                            (!fSettings->fCaps ||
+                             !fSettings->fCaps->fragCoordConventionsExtensionString())) {
+                            fInputs.fRTDimensions = true;
+                        }
+                    }
 #endif
+            }
             // default to kRead_RefKind; this will be corrected later if the variable is written to
             return std::unique_ptr<VariableReference>(new VariableReference(
                                                                  identifier.fOffset,
diff --git a/src/sksl/SkSLSPIRVCodeGenerator.cpp b/src/sksl/SkSLSPIRVCodeGenerator.cpp
index 7986c40..271562f 100644
--- a/src/sksl/SkSLSPIRVCodeGenerator.cpp
+++ b/src/sksl/SkSLSPIRVCodeGenerator.cpp
@@ -1710,7 +1710,48 @@
     }
 }
 
+SpvId SPIRVCodeGenerator::loadDimensions(OutputStream& out) {
+    if (fRTDimensionsStructId == (SpvId) -1) {
+        // dimensions variable hasn't been written yet
+        std::shared_ptr<SymbolTable> st(new SymbolTable(&fErrors));
+        SkASSERT(fRTDimensionsFieldIndex == (SpvId) -1);
+        std::vector<Type::Field> fields;
+        fields.emplace_back(Modifiers(), SKSL_RTDIMENSIONS_NAME, fContext.fFloat2_Type.get());
+        StringFragment name("sksl_synthetic_uniforms");
+        Type intfStruct(-1, name, fields);
+        Layout layout(0, -1, -1, 1, -1, -1, -1, -1, Layout::Format::kUnspecified,
+                      Layout::kUnspecified_Primitive, -1, -1, "", Layout::kNo_Key,
+                      StringFragment());
+        Variable* intfVar = new Variable(-1,
+                                         Modifiers(layout, Modifiers::kUniform_Flag),
+                                         name,
+                                         intfStruct,
+                                         Variable::kGlobal_Storage);
+        fSynthetics.takeOwnership(intfVar);
+        InterfaceBlock intf(-1, intfVar, name, String(""),
+                            std::vector<std::unique_ptr<Expression>>(), st);
+        fRTDimensionsStructId = this->writeInterfaceBlock(intf);
+        fRTDimensionsFieldIndex = 0;
+    }
+    SkASSERT(fRTDimensionsFieldIndex != (SpvId) -1);
+    IntLiteral fieldIndex(fContext, -1, fRTDimensionsFieldIndex);
+    SpvId fieldIndexId = this->writeIntLiteral(fieldIndex);
+    SpvId dimensionsPtr = this->nextId();
+    this->writeOpCode(SpvOpAccessChain, 5, out);
+    this->writeWord(this->getPointerType(*fContext.fFloat2_Type, SpvStorageClassUniform), out);
+    this->writeWord(dimensionsPtr, out);
+    this->writeWord(fRTDimensionsStructId, out);
+    this->writeWord(fieldIndexId, out);
+    SpvId result = this->nextId();
+    this->writeInstruction(SpvOpLoad, this->getType(*fContext.fFloat2_Type), result, dimensionsPtr,
+                           out);
+    return result;
+}
+
 SpvId SPIRVCodeGenerator::writeVariableReference(const VariableReference& ref, OutputStream& out) {
+    if (ref.fVariable.fModifiers.fLayout.fBuiltin == SK_DIMENSIONS_BUILTIN) {
+        return this->loadDimensions(out);
+    }
     SpvId result = this->nextId();
     auto entry = fVariableMap.find(&ref.fVariable);
     SkASSERT(entry != fVariableMap.end());
@@ -1719,50 +1760,20 @@
     if (ref.fVariable.fModifiers.fLayout.fBuiltin == SK_FRAGCOORD_BUILTIN &&
         fProgram.fSettings.fFlipY) {
         // need to remap to a top-left coordinate system
-        if (fRTHeightStructId == (SpvId) -1) {
-            // height variable hasn't been written yet
-            std::shared_ptr<SymbolTable> st(new SymbolTable(&fErrors));
-            SkASSERT(fRTHeightFieldIndex == (SpvId) -1);
-            std::vector<Type::Field> fields;
-            fields.emplace_back(Modifiers(), SKSL_RTHEIGHT_NAME, fContext.fFloat_Type.get());
-            StringFragment name("sksl_synthetic_uniforms");
-            Type intfStruct(-1, name, fields);
-            Layout layout(0, -1, -1, 1, -1, -1, -1, -1, Layout::Format::kUnspecified,
-                          Layout::kUnspecified_Primitive, -1, -1, "", Layout::kNo_Key,
-                          StringFragment());
-            Variable* intfVar = new Variable(-1,
-                                             Modifiers(layout, Modifiers::kUniform_Flag),
-                                             name,
-                                             intfStruct,
-                                             Variable::kGlobal_Storage);
-            fSynthetics.takeOwnership(intfVar);
-            InterfaceBlock intf(-1, intfVar, name, String(""),
-                                std::vector<std::unique_ptr<Expression>>(), st);
-            fRTHeightStructId = this->writeInterfaceBlock(intf);
-            fRTHeightFieldIndex = 0;
-        }
-        SkASSERT(fRTHeightFieldIndex != (SpvId) -1);
-        // write float4(gl_FragCoord.x, u_skRTHeight - gl_FragCoord.y, 0.0, 1.0)
+        // write float4(gl_FragCoord.x, u_skRTDimensions.y - gl_FragCoord.y, 0.0, 1.0)
         SpvId xId = this->nextId();
         this->writeInstruction(SpvOpCompositeExtract, this->getType(*fContext.fFloat_Type), xId,
                                result, 0, out);
-        IntLiteral fieldIndex(fContext, -1, fRTHeightFieldIndex);
-        SpvId fieldIndexId = this->writeIntLiteral(fieldIndex);
-        SpvId heightPtr = this->nextId();
-        this->writeOpCode(SpvOpAccessChain, 5, out);
-        this->writeWord(this->getPointerType(*fContext.fFloat_Type, SpvStorageClassUniform), out);
-        this->writeWord(heightPtr, out);
-        this->writeWord(fRTHeightStructId, out);
-        this->writeWord(fieldIndexId, out);
-        SpvId heightRead = this->nextId();
-        this->writeInstruction(SpvOpLoad, this->getType(*fContext.fFloat_Type), heightRead,
-                               heightPtr, out);
+        SpvId dimensions = this->loadDimensions(out);
+        SpvId height = this->nextId();
+        this->writeInstruction(SpvOpCompositeExtract, this->getType(*fContext.fFloat_Type), height,
+                               dimensions, 1, out);
         SpvId rawYId = this->nextId();
         this->writeInstruction(SpvOpCompositeExtract, this->getType(*fContext.fFloat_Type), rawYId,
                                result, 1, out);
         SpvId flippedYId = this->nextId();
         this->writeInstruction(SpvOpFSub, this->getType(*fContext.fFloat_Type), flippedYId,
-                               heightRead, rawYId, out);
+                               height, rawYId, out);
         FloatLiteral zero(fContext, -1, 0.0);
         SpvId zeroId = writeFloatLiteral(zero);
         FloatLiteral one(fContext, -1, 1.0);
@@ -2551,7 +2562,8 @@
         fCapabilities |= (((uint64_t) 1) << SpvCapabilityInputAttachment);
     }
     if (layout.fBuiltin >= 0 && layout.fBuiltin != SK_FRAGCOLOR_BUILTIN &&
-        layout.fBuiltin != SK_IN_BUILTIN && layout.fBuiltin != SK_OUT_BUILTIN) {
+        layout.fBuiltin != SK_IN_BUILTIN && layout.fBuiltin != SK_OUT_BUILTIN &&
+        layout.fBuiltin != SK_DIMENSIONS_BUILTIN) {
         this->writeInstruction(SpvOpDecorate, target, SpvDecorationBuiltIn, layout.fBuiltin,
                                fDecorationBuffer);
     }
@@ -2615,13 +2627,14 @@
                                 fDefaultLayout;
     SpvId result = this->nextId();
     const Type* type = &intf.fVariable.fType;
-    if (fProgram.fInputs.fRTHeight) {
-        SkASSERT(fRTHeightStructId == (SpvId) -1);
-        SkASSERT(fRTHeightFieldIndex == (SpvId) -1);
+    if (fProgram.fInputs.fRTDimensions) {
+        SkASSERT(fRTDimensionsStructId == (SpvId) -1);
+        SkASSERT(fRTDimensionsFieldIndex == (SpvId) -1);
         std::vector<Type::Field> fields = type->fields();
-        fRTHeightStructId = result;
-        fRTHeightFieldIndex = fields.size();
-        fields.emplace_back(Modifiers(), StringFragment(SKSL_RTHEIGHT_NAME), fContext.fFloat_Type.get());
+        fRTDimensionsStructId = result;
+        fRTDimensionsFieldIndex = fields.size();
+        fields.emplace_back(Modifiers(), StringFragment(SKSL_RTDIMENSIONS_NAME),
+                            fContext.fFloat2_Type.get());
         type = new Type(type->fOffset, type->name(), fields);
     }
     SpvId typeId;
@@ -2652,7 +2665,7 @@
     }
     this->writeLayout(layout, result);
     fVariableMap[&intf.fVariable] = result;
-    if (fProgram.fInputs.fRTHeight) {
+    if (fProgram.fInputs.fRTDimensions) {
         delete type;
     }
     return result;
diff --git a/src/sksl/SkSLSPIRVCodeGenerator.h b/src/sksl/SkSLSPIRVCodeGenerator.h
index 8b88c75..ada0f20 100644
--- a/src/sksl/SkSLSPIRVCodeGenerator.h
+++ b/src/sksl/SkSLSPIRVCodeGenerator.h
@@ -143,6 +143,8 @@
 
     void writeVarDeclarations(const VarDeclarations& decl, OutputStream& out);
 
+    SpvId loadDimensions(OutputStream& out);
+
     SpvId writeVariableReference(const VariableReference& ref, OutputStream& out);
 
     std::unique_ptr<LValue> getLValue(const Expression& value, OutputStream& out);
@@ -341,8 +343,8 @@
     SpvId fCurrentBlock;
     std::stack<SpvId> fBreakTarget;
     std::stack<SpvId> fContinueTarget;
-    SpvId fRTHeightStructId = (SpvId) -1;
-    SpvId fRTHeightFieldIndex = (SpvId) -1;
+    SpvId fRTDimensionsStructId = (SpvId) -1;
+    SpvId fRTDimensionsFieldIndex = (SpvId) -1;
     // holds variables synthesized during output, for lifetime purposes
     SymbolTable fSynthetics;
     int fSkInCount = 1;
diff --git a/src/sksl/ir/SkSLProgram.h b/src/sksl/ir/SkSLProgram.h
index 59c9122..4b18391 100644
--- a/src/sksl/ir/SkSLProgram.h
+++ b/src/sksl/ir/SkSLProgram.h
@@ -18,8 +18,8 @@
 #include "SkSLProgramElement.h"
 #include "SkSLSymbolTable.h"
 
-// name of the render target height uniform
-#define SKSL_RTHEIGHT_NAME "u_skRTHeight"
+// name of the render target dimensions uniform
+#define SKSL_RTDIMENSIONS_NAME "u_skRTDimensions"
 
 namespace SkSL {
 
@@ -88,20 +88,20 @@
     };
 
     struct Inputs {
-        // if true, this program requires the render target height uniform to be defined
-        bool fRTHeight;
+        // if true, this program requires the render target dimensions uniform to be defined
+        bool fRTDimensions;
 
         // if true, this program must be recompiled if the flipY setting changes. If false, the
         // program will compile to the same code regardless of the flipY setting.
         bool fFlipY;
 
         void reset() {
-            fRTHeight = false;
+            fRTDimensions = false;
             fFlipY = false;
         }
 
         bool isEmpty() {
-            return !fRTHeight && !fFlipY;
+            return !fRTDimensions && !fFlipY;
         }
     };
 
diff --git a/src/sksl/sksl_fp.inc b/src/sksl/sksl_fp.inc
index d407fd0..d9173b5 100644
--- a/src/sksl/sksl_fp.inc
+++ b/src/sksl/sksl_fp.inc
@@ -20,6 +20,8 @@
 layout(builtin=10004) out half4 sk_OutColor;
 layout(builtin=10005) float2[] sk_TransformedCoords2D;
 layout(builtin=10006) sampler2D[] sk_TextureSamplers;
+layout(builtin=10011) float2 sk_Dimensions;
 
 half4 process(fragmentProcessor fp);
+
 )
diff --git a/src/sksl/sksl_frag.inc b/src/sksl/sksl_frag.inc
index 202747a..73cc58a 100644
--- a/src/sksl/sksl_frag.inc
+++ b/src/sksl/sksl_frag.inc
@@ -17,5 +17,6 @@
 
 layout(location=0,index=0,builtin=10001) out half4 sk_FragColor;
 layout(builtin=10008) half4 sk_LastFragColor;
+layout(builtin=10011) float2 sk_Dimensions;
 
 )
diff --git a/tests/SkSLGLSLTest.cpp b/tests/SkSLGLSLTest.cpp
index 5b083fa..6c946c5 100644
--- a/tests/SkSLGLSLTest.cpp
+++ b/tests/SkSLGLSLTest.cpp
@@ -1070,7 +1070,7 @@
          "    gl_FragColor.xy = gl_FragCoord.xy;\n"
          "}\n",
          &inputs);
-    REPORTER_ASSERT(r, !inputs.fRTHeight);
+    REPORTER_ASSERT(r, !inputs.fRTDimensions);
 
     caps = SkSL::ShaderCapsFactory::FragCoordsNew();
     settings.fCaps = caps.get();
@@ -1084,7 +1084,7 @@
          "    sk_FragColor.xy = gl_FragCoord.xy;\n"
          "}\n",
          &inputs);
-    REPORTER_ASSERT(r, !inputs.fRTHeight);
+    REPORTER_ASSERT(r, !inputs.fRTDimensions);
 
     caps = SkSL::ShaderCapsFactory::Default();
     settings.fCaps = caps.get();
@@ -1092,15 +1092,16 @@
          "void main() { sk_FragColor.xy = sk_FragCoord.xy; }",
          settings,
          "#version 400\n"
-         "uniform float u_skRTHeight;\n"
+         "uniform vec2 u_skRTDimensions;\n"
          "out vec4 sk_FragColor;\n"
          "void main() {\n"
          "    vec2 _sktmpCoord = gl_FragCoord.xy;\n"
-         "    vec4 sk_FragCoord = vec4(_sktmpCoord.x, u_skRTHeight - _sktmpCoord.y, 1.0, 1.0);\n"
+         "    vec4 sk_FragCoord = vec4(_sktmpCoord.x, u_skRTDimensions.y - _sktmpCoord.y, 1.0,"
+                                     " 1.0);\n"
          "    sk_FragColor.xy = sk_FragCoord.xy;\n"
          "}\n",
          &inputs);
-    REPORTER_ASSERT(r, inputs.fRTHeight);
+    REPORTER_ASSERT(r, inputs.fRTDimensions);
 
     settings.fFlipY = false;
     test(r,
@@ -1112,7 +1113,7 @@
          "    sk_FragColor.xy = gl_FragCoord.xy;\n"
          "}\n",
          &inputs);
-    REPORTER_ASSERT(r, !inputs.fRTHeight);
+    REPORTER_ASSERT(r, !inputs.fRTDimensions);
 
     test(r,
          "in float4 pos; void main() { sk_Position = pos; }",
@@ -1154,6 +1155,24 @@
          "}\n");
 }
 
+DEF_TEST(SkSLDimensions, r) {
+    SkSL::Program::Settings settings;
+    sk_sp<GrShaderCaps> caps = SkSL::ShaderCapsFactory::Default();
+    settings.fCaps = caps.get();
+    SkSL::Program::Inputs inputs;
+    test(r,
+         "void main() { sk_FragColor.r = sk_FragCoord.x / sk_Dimensions.x; }",
+         settings,
+         "#version 400\n"
+         "uniform vec2 u_skRTDimensions;\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    sk_FragColor.x = gl_FragCoord.x / u_skRTDimensions.x;\n"
+         "}\n",
+         &inputs);
+    REPORTER_ASSERT(r, inputs.fRTDimensions);
+}
+
 DEF_TEST(SkSLClockwise, r) {
     test(r,
          "void main() { sk_FragColor = half4(sk_Clockwise ? +1 : -1); }",