Supply device and local coords to SkShader_Base::onProgram

Use that to add support for sk_FragCoord in SkRuntimeEffect.

Change-Id: I587ad97057c13ec8a4052c7c20f655eae88786ba
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/298504
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Mike Klein <mtklein@google.com>
diff --git a/src/core/SkRuntimeEffect.cpp b/src/core/SkRuntimeEffect.cpp
index acab69c..6934dcd 100644
--- a/src/core/SkRuntimeEffect.cpp
+++ b/src/core/SkRuntimeEffect.cpp
@@ -415,7 +415,7 @@
                                          std::vector<skvm::F32> stack,
                                          /*these parameters are used to call program() on children*/
                                          const std::vector<sk_sp<SkShader>>& children,
-                                         skvm::F32 x, skvm::F32 y, skvm::Color paint,
+                                         skvm::Coord device, skvm::Color paint,
                                          SkFilterQuality quality, const SkColorInfo& dst,
                                          skvm::Uniforms* uniforms, SkArenaAlloc* alloc) {
     auto push = [&](skvm::F32 x) { stack.push_back(x); };
@@ -483,7 +483,7 @@
                           x = pop();
 
                 SkOverrideDeviceMatrixProvider mats{matrices, SkMatrix::I()};
-                skvm::Color c = as_SB(children[ix])->program(p, x,y,paint,
+                skvm::Color c = as_SB(children[ix])->program(p, device, {x,y},paint,
                                                              mats, nullptr,
                                                              quality, dst,
                                                              uniforms, alloc);
@@ -548,6 +548,14 @@
                 push(uniform[ix + 3]);
             } break;
 
+            case Inst::kLoadFragCoord: {
+                // TODO: Actually supply Z and 1/W from the rasterizer?
+                push(device.x);
+                push(device.y);
+                push(p->splat(0.0f));  // Z
+                push(p->splat(1.0f));  // 1/W
+            } break;
+
             case Inst::kStore: {
                 int ix = u8();
                 stack[ix + 0] = pop();
@@ -828,7 +836,7 @@
         std::vector<skvm::F32> stack =
             program_fn(p, *fn, uniform, SkSimpleMatrixProvider{SkMatrix::I()}, {c.r, c.g, c.b, c.a},
                        /* the remaining parameters are for shaders only and won't be used here */
-                       {},{},{},{},{},{},{},{});
+                       {},{},{},{},{},{},{});
 
         if (stack.size() == 4) {
             return {stack[0], stack[1], stack[2], stack[3]};
@@ -1017,7 +1025,8 @@
         return true;
     }
 
-    skvm::Color onProgram(skvm::Builder* p, skvm::F32 x, skvm::F32 y, skvm::Color paint,
+    skvm::Color onProgram(skvm::Builder* p,
+                          skvm::Coord device, skvm::Coord local, skvm::Color paint,
                           const SkMatrixProvider& matrices, const SkMatrix* localM,
                           SkFilterQuality quality, const SkColorInfo& dst,
                           skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const override {
@@ -1047,12 +1056,13 @@
         if (!this->computeTotalInverse(matrices.localToDevice(), localM, &inv)) {
             return {};
         }
-        SkShaderBase::ApplyMatrix(p,inv, &x,&y,uniforms);
+        SkShaderBase::ApplyMatrix(p,inv, &local,uniforms);
 
         std::vector<skvm::F32> stack =
-            program_fn(p, *fn, uniform, matrices, {x,y, paint.r, paint.g, paint.b, paint.a},
+            program_fn(p, *fn, uniform, matrices,
+                       {local.x,local.y, paint.r, paint.g, paint.b, paint.a},
                        /*parameters for calling program() on children*/
-                       fChildren, x,y,paint, quality,dst, uniforms,alloc);
+                       fChildren, device,paint, quality,dst, uniforms,alloc);
 
         if (stack.size() == 6) {
             return {stack[2], stack[3], stack[4], stack[5]};
diff --git a/src/core/SkVM.h b/src/core/SkVM.h
index 609c01e..db5b273 100644
--- a/src/core/SkVM.h
+++ b/src/core/SkVM.h
@@ -476,6 +476,12 @@
         Builder* operator->()    const { return a.operator->(); }
     };
 
+    struct Coord {
+        F32 x,y;
+        explicit operator bool() const { return x && y; }
+        Builder* operator->()    const { return x.operator->(); }
+    };
+
     struct Uniform {
         Arg ptr;
         int offset;
diff --git a/src/core/SkVMBlitter.cpp b/src/core/SkVMBlitter.cpp
index 8a8d42e..5469c4b 100644
--- a/src/core/SkVMBlitter.cpp
+++ b/src/core/SkVMBlitter.cpp
@@ -117,8 +117,9 @@
             skvm::I32 dx = p.uniform32(uniforms->base, offsetof(BlitterUniforms, right))
                          - p.index(),
                       dy = p.uniform32(uniforms->base, offsetof(BlitterUniforms, y));
-            skvm::F32 x = to_f32(dx) + 0.5f,
-                      y = to_f32(dy) + 0.5f;
+            skvm::Coord device = {to_f32(dx) + 0.5f,
+                                  to_f32(dy) + 0.5f},
+                        local = device;
 
             skvm::Color paint = {
                 p.uniformF(uniforms->base, offsetof(BlitterUniforms, paint.fR)),
@@ -129,7 +130,7 @@
 
             uint64_t hash = 0;
             if (auto c = sb->program(&p,
-                                     x,y, paint,
+                                     device,local, paint,
                                      params.matrices, /*localM=*/nullptr,
                                      params.quality, params.dst,
                                      uniforms,alloc)) {
@@ -197,8 +198,9 @@
         skvm::I32 dx = p->uniform32(uniforms->base, offsetof(BlitterUniforms, right))
                      - p->index(),
                   dy = p->uniform32(uniforms->base, offsetof(BlitterUniforms, y));
-        skvm::F32 x = to_f32(dx) + 0.5f,
-                  y = to_f32(dy) + 0.5f;
+        skvm::Coord device = {to_f32(dx) + 0.5f,
+                              to_f32(dy) + 0.5f},
+                    local = device;
 
         skvm::Color paint = {
             p->uniformF(uniforms->base, offsetof(BlitterUniforms, paint.fR)),
@@ -207,7 +209,7 @@
             p->uniformF(uniforms->base, offsetof(BlitterUniforms, paint.fA)),
         };
 
-        skvm::Color src = as_SB(params.shader)->program(p, x,y, paint,
+        skvm::Color src = as_SB(params.shader)->program(p, device,local, paint,
                                                         params.matrices, /*localM=*/nullptr,
                                                         params.quality, params.dst,
                                                         uniforms, alloc);
@@ -274,7 +276,7 @@
             }
 
             if (params.clip) {
-                skvm::Color clip = as_SB(params.clip)->program(p, x,y, paint,
+                skvm::Color clip = as_SB(params.clip)->program(p, device,local, paint,
                                                                params.matrices, /*localM=*/nullptr,
                                                                params.quality, params.dst,
                                                                uniforms, alloc);
@@ -428,12 +430,13 @@
 
         bool isOpaque() const override { return fShader->isOpaque(); }
 
-        skvm::Color onProgram(skvm::Builder* p, skvm::F32 x, skvm::F32 y, skvm::Color paint,
+        skvm::Color onProgram(skvm::Builder* p,
+                              skvm::Coord device, skvm::Coord local, skvm::Color paint,
                               const SkMatrixProvider& matrices, const SkMatrix* localM,
                               SkFilterQuality quality, const SkColorInfo& dst,
                               skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const override {
             // Run our wrapped shader.
-            skvm::Color c = as_SB(fShader)->program(p, x,y, paint,
+            skvm::Color c = as_SB(fShader)->program(p, device,local, paint,
                                                     matrices,localM, quality,dst, uniforms,alloc);
             if (!c) {
                 return {};
@@ -467,8 +470,10 @@
 
             // See SkRasterPipeline dither stage.
             // This is 8x8 ordered dithering.  From here we'll only need dx and dx^dy.
-            skvm::I32 X =     trunc(x - 0.5f),
-                      Y = X ^ trunc(y - 0.5f);
+            SkASSERT(local.x.id == device.x.id);
+            SkASSERT(local.y.id == device.y.id);
+            skvm::I32 X =     trunc(device.x - 0.5f),
+                      Y = X ^ trunc(device.y - 0.5f);
 
             // If X's low bits are abc and Y's def, M is fcebda,
             // 6 bits producing all values [0,63] shuffled over an 8x8 grid.
diff --git a/src/core/SkVM_fwd.h b/src/core/SkVM_fwd.h
index 1442a9f..e8dc43f 100644
--- a/src/core/SkVM_fwd.h
+++ b/src/core/SkVM_fwd.h
@@ -16,6 +16,7 @@
     struct I32;
     struct F32;
     struct Color;
+    struct Coord;
     struct Uniforms;
 }
 
diff --git a/src/shaders/SkColorFilterShader.cpp b/src/shaders/SkColorFilterShader.cpp
index eb8b4e8..15b9a16 100644
--- a/src/shaders/SkColorFilterShader.cpp
+++ b/src/shaders/SkColorFilterShader.cpp
@@ -62,12 +62,12 @@
 }
 
 skvm::Color SkColorFilterShader::onProgram(skvm::Builder* p,
-                                           skvm::F32 x, skvm::F32 y, skvm::Color paint,
+                                           skvm::Coord device, skvm::Coord local, skvm::Color paint,
                                            const SkMatrixProvider& matrices, const SkMatrix* localM,
                                            SkFilterQuality quality, const SkColorInfo& dst,
                                            skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const {
     // Run the shader.
-    skvm::Color c = as_SB(fShader)->program(p, x,y, paint,
+    skvm::Color c = as_SB(fShader)->program(p, device,local, paint,
                                             matrices,localM,
                                             quality,dst,
                                             uniforms,alloc);
diff --git a/src/shaders/SkColorFilterShader.h b/src/shaders/SkColorFilterShader.h
index 348e437..e20af83 100644
--- a/src/shaders/SkColorFilterShader.h
+++ b/src/shaders/SkColorFilterShader.h
@@ -26,7 +26,7 @@
     void flatten(SkWriteBuffer&) const override;
     bool onAppendStages(const SkStageRec&) const override;
 
-    skvm::Color onProgram(skvm::Builder*, skvm::F32 x, skvm::F32 y, skvm::Color paint,
+    skvm::Color onProgram(skvm::Builder*, skvm::Coord device, skvm::Coord local, skvm::Color paint,
                           const SkMatrixProvider&, const SkMatrix* localM,
                           SkFilterQuality quality, const SkColorInfo& dst,
                           skvm::Uniforms* uniforms, SkArenaAlloc*) const override;
diff --git a/src/shaders/SkColorShader.cpp b/src/shaders/SkColorShader.cpp
index 7fbd6ad..8afdda1 100644
--- a/src/shaders/SkColorShader.cpp
+++ b/src/shaders/SkColorShader.cpp
@@ -92,7 +92,8 @@
 }
 
 skvm::Color SkColorShader::onProgram(skvm::Builder* p,
-                                     skvm::F32 /*x*/, skvm::F32 /*y*/, skvm::Color /*paint*/,
+                                     skvm::Coord /*device*/, skvm::Coord /*local*/,
+                                     skvm::Color /*paint*/,
                                      const SkMatrixProvider&, const SkMatrix* /*localM*/,
                                      SkFilterQuality /*quality*/, const SkColorInfo& dst,
                                      skvm::Uniforms* uniforms, SkArenaAlloc*) const {
@@ -100,7 +101,8 @@
                             uniforms, dst.colorSpace());
 }
 skvm::Color SkColor4Shader::onProgram(skvm::Builder* p,
-                                      skvm::F32 /*x*/, skvm::F32 /*y*/, skvm::Color /*paint*/,
+                                      skvm::Coord /*device*/, skvm::Coord /*local*/,
+                                      skvm::Color /*paint*/,
                                       const SkMatrixProvider&, const SkMatrix* /*localM*/,
                                       SkFilterQuality /*quality*/, const SkColorInfo& dst,
                                       skvm::Uniforms* uniforms, SkArenaAlloc*) const {
diff --git a/src/shaders/SkColorShader.h b/src/shaders/SkColorShader.h
index ecf305e..5b87665 100644
--- a/src/shaders/SkColorShader.h
+++ b/src/shaders/SkColorShader.h
@@ -44,7 +44,7 @@
 
     bool onAppendStages(const SkStageRec&) const override;
 
-    skvm::Color onProgram(skvm::Builder*, skvm::F32 x, skvm::F32 y, skvm::Color paint,
+    skvm::Color onProgram(skvm::Builder*, skvm::Coord device, skvm::Coord local, skvm::Color paint,
                           const SkMatrixProvider&, const SkMatrix* localM,
                           SkFilterQuality quality, const SkColorInfo& dst,
                           skvm::Uniforms* uniforms, SkArenaAlloc*) const override;
@@ -69,7 +69,7 @@
     void flatten(SkWriteBuffer&) const override;
     bool onAppendStages(const SkStageRec&) const override;
 
-    skvm::Color onProgram(skvm::Builder*, skvm::F32 x, skvm::F32 y, skvm::Color paint,
+    skvm::Color onProgram(skvm::Builder*, skvm::Coord device, skvm::Coord local, skvm::Color paint,
                           const SkMatrixProvider&, const SkMatrix* localM,
                           SkFilterQuality quality, const SkColorInfo& dst,
                           skvm::Uniforms* uniforms, SkArenaAlloc*) const override;
diff --git a/src/shaders/SkComposeShader.cpp b/src/shaders/SkComposeShader.cpp
index 9c508f0..77fbfeb 100644
--- a/src/shaders/SkComposeShader.cpp
+++ b/src/shaders/SkComposeShader.cpp
@@ -127,13 +127,14 @@
     return true;
 }
 
-skvm::Color SkShader_Blend::onProgram(skvm::Builder* p, skvm::F32 x, skvm::F32 y, skvm::Color paint,
-                                      const SkMatrixProvider& matrices, const SkMatrix* localM,
+skvm::Color SkShader_Blend::onProgram(skvm::Builder* p,
+                                      skvm::Coord device, skvm::Coord local, skvm::Color paint,
+                                      const SkMatrixProvider& mats, const SkMatrix* localM,
                                       SkFilterQuality q, const SkColorInfo& dst,
                                       skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const {
     skvm::Color d,s;
-    if ((d = as_SB(fDst)->program(p, x,y, paint, matrices,localM, q, dst, uniforms, alloc)) &&
-        (s = as_SB(fSrc)->program(p, x,y, paint, matrices,localM, q, dst, uniforms, alloc)))
+    if ((d = as_SB(fDst)->program(p, device,local, paint, mats,localM, q,dst, uniforms,alloc)) &&
+        (s = as_SB(fSrc)->program(p, device,local, paint, mats,localM, q,dst, uniforms,alloc)))
     {
         return p->blend(fMode, s,d);
     }
@@ -167,13 +168,14 @@
     return true;
 }
 
-skvm::Color SkShader_Lerp::onProgram(skvm::Builder* p, skvm::F32 x, skvm::F32 y, skvm::Color paint,
-                                     const SkMatrixProvider& matrices, const SkMatrix* localM,
+skvm::Color SkShader_Lerp::onProgram(skvm::Builder* p,
+                                     skvm::Coord device, skvm::Coord local, skvm::Color paint,
+                                     const SkMatrixProvider& mats, const SkMatrix* localM,
                                      SkFilterQuality q, const SkColorInfo& dst,
                                      skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const {
     skvm::Color d,s;
-    if ((d = as_SB(fDst)->program(p, x,y, paint, matrices,localM, q, dst, uniforms, alloc)) &&
-        (s = as_SB(fSrc)->program(p, x,y, paint, matrices,localM, q, dst, uniforms, alloc)))
+    if ((d = as_SB(fDst)->program(p, device,local, paint, mats,localM, q,dst, uniforms,alloc)) &&
+        (s = as_SB(fSrc)->program(p, device,local, paint, mats,localM, q,dst, uniforms,alloc)))
     {
         auto t = p->uniformF(uniforms->pushF(fWeight));
         return {
diff --git a/src/shaders/SkComposeShader.h b/src/shaders/SkComposeShader.h
index b19c4c4..96ae0a0 100644
--- a/src/shaders/SkComposeShader.h
+++ b/src/shaders/SkComposeShader.h
@@ -27,7 +27,7 @@
     SkShader_Blend(SkReadBuffer&);
     void flatten(SkWriteBuffer&) const override;
     bool onAppendStages(const SkStageRec&) const override;
-    skvm::Color onProgram(skvm::Builder*, skvm::F32 x, skvm::F32 y, skvm::Color paint,
+    skvm::Color onProgram(skvm::Builder*, skvm::Coord device, skvm::Coord local, skvm::Color paint,
                           const SkMatrixProvider&, const SkMatrix* localM,
                           SkFilterQuality, const SkColorInfo& dst,
                           skvm::Uniforms*, SkArenaAlloc*) const override;
@@ -60,7 +60,7 @@
     SkShader_Lerp(SkReadBuffer&);
     void flatten(SkWriteBuffer&) const override;
     bool onAppendStages(const SkStageRec&) const override;
-    skvm::Color onProgram(skvm::Builder*, skvm::F32 x, skvm::F32 y, skvm::Color paint,
+    skvm::Color onProgram(skvm::Builder*, skvm::Coord device, skvm::Coord local, skvm::Color paint,
                           const SkMatrixProvider&, const SkMatrix* localM,
                           SkFilterQuality, const SkColorInfo& dst,
                           skvm::Uniforms*, SkArenaAlloc*) const override;
diff --git a/src/shaders/SkEmptyShader.h b/src/shaders/SkEmptyShader.h
index d5d7950..414be6c 100644
--- a/src/shaders/SkEmptyShader.h
+++ b/src/shaders/SkEmptyShader.h
@@ -37,7 +37,7 @@
         return false;
     }
 
-    skvm::Color onProgram(skvm::Builder*, skvm::F32, skvm::F32, skvm::Color,
+    skvm::Color onProgram(skvm::Builder*, skvm::Coord, skvm::Coord, skvm::Color,
                           const SkMatrixProvider&, const SkMatrix*,
                           SkFilterQuality, const SkColorInfo&,
                           skvm::Uniforms*, SkArenaAlloc*) const override;
diff --git a/src/shaders/SkImageShader.cpp b/src/shaders/SkImageShader.cpp
index 82edbc5..bc1b36a 100755
--- a/src/shaders/SkImageShader.cpp
+++ b/src/shaders/SkImageShader.cpp
@@ -639,7 +639,8 @@
     return this->doStages(rec, updater) ? updater : nullptr;
 }
 
-skvm::Color SkImageShader::onProgram(skvm::Builder* p, skvm::F32 x, skvm::F32 y, skvm::Color paint,
+skvm::Color SkImageShader::onProgram(skvm::Builder* p,
+                                     skvm::Coord device, skvm::Coord local, skvm::Color paint,
                                      const SkMatrixProvider& matrices, const SkMatrix* localM,
                                      SkFilterQuality quality, const SkColorInfo& dst,
                                      skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const {
@@ -663,7 +664,7 @@
     inv.normalizePerspective();
 
     // Apply matrix to convert dst coords to sample center coords.
-    SkShaderBase::ApplyMatrix(p, inv, &x,&y,uniforms);
+    SkShaderBase::ApplyMatrix(p, inv, &local, uniforms);
 
     // Bail out if sample() can't yet handle our image's color type.
     switch (pm.colorType()) {
@@ -788,14 +789,14 @@
     skvm::Color c;
 
     if (quality == kNone_SkFilterQuality) {
-        c = sample(x,y);
+        c = sample(local.x,local.y);
     } else if (quality == kLow_SkFilterQuality) {
         // Our four sample points are the corners of a logical 1x1 pixel
         // box surrounding (x,y) at (0.5,0.5) off-center.
-        skvm::F32 left   = x - 0.5f,
-                  top    = y - 0.5f,
-                  right  = x + 0.5f,
-                  bottom = y + 0.5f;
+        skvm::F32 left   = local.x - 0.5f,
+                  top    = local.y - 0.5f,
+                  right  = local.x + 0.5f,
+                  bottom = local.y + 0.5f;
 
         // The fractional parts of right and bottom are our lerp factors in x and y respectively.
         skvm::F32 fx = fract(right ),
@@ -808,8 +809,8 @@
 
         // All bicubic samples have the same fractional offset (fx,fy) from the center.
         // They're either the 16 corners of a 3x3 grid/ surrounding (x,y) at (0.5,0.5) off-center.
-        skvm::F32 fx = fract(x + 0.5f),
-                  fy = fract(y + 0.5f);
+        skvm::F32 fx = fract(local.x + 0.5f),
+                  fy = fract(local.y + 0.5f);
 
         // See GrCubicEffect for details of these weights.
         // TODO: these maybe don't seem right looking at gm/bicubic and GrBicubicEffect.
@@ -836,9 +837,9 @@
 
         c.r = c.g = c.b = c.a = p->splat(0.0f);
 
-        skvm::F32 sy = y - 1.5f;
+        skvm::F32 sy = local.y - 1.5f;
         for (int j = 0; j < 4; j++, sy += 1.0f) {
-            skvm::F32 sx = x - 1.5f;
+            skvm::F32 sx = local.x - 1.5f;
             for (int i = 0; i < 4; i++, sx += 1.0f) {
                 skvm::Color s = sample(sx,sy);
                 skvm::F32   w = wx[i] * wy[j];
diff --git a/src/shaders/SkImageShader.h b/src/shaders/SkImageShader.h
index 3a6f197..a653a9f 100644
--- a/src/shaders/SkImageShader.h
+++ b/src/shaders/SkImageShader.h
@@ -58,7 +58,7 @@
     bool onAppendStages(const SkStageRec&) const override;
     SkStageUpdater* onAppendUpdatableStages(const SkStageRec&) const override;
 
-    skvm::Color onProgram(skvm::Builder*, skvm::F32 x, skvm::F32 y, skvm::Color paint,
+    skvm::Color onProgram(skvm::Builder*, skvm::Coord device, skvm::Coord local, skvm::Color paint,
                           const SkMatrixProvider&, const SkMatrix* localM,
                           SkFilterQuality quality, const SkColorInfo& dst,
                           skvm::Uniforms* uniforms, SkArenaAlloc*) const override;
diff --git a/src/shaders/SkLocalMatrixShader.cpp b/src/shaders/SkLocalMatrixShader.cpp
index 1caa035..c0ef126 100644
--- a/src/shaders/SkLocalMatrixShader.cpp
+++ b/src/shaders/SkLocalMatrixShader.cpp
@@ -78,7 +78,7 @@
 
 
 skvm::Color SkLocalMatrixShader::onProgram(skvm::Builder* p,
-                                           skvm::F32 x, skvm::F32 y, skvm::Color paint,
+                                           skvm::Coord device, skvm::Coord local, skvm::Color paint,
                                            const SkMatrixProvider& matrices, const SkMatrix* localM,
                                            SkFilterQuality quality, const SkColorInfo& dst,
                                            skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const {
@@ -86,7 +86,7 @@
     if (localM) {
         lm.writable()->preConcat(*localM);
     }
-    return as_SB(fProxyShader)->program(p, x,y, paint,
+    return as_SB(fProxyShader)->program(p, device,local, paint,
                                         matrices,lm.get(),
                                         quality,dst,
                                         uniforms,alloc);
@@ -156,12 +156,13 @@
         return as_SB(fProxyShader)->appendStages(newRec);
     }
 
-    skvm::Color onProgram(skvm::Builder* p, skvm::F32 x, skvm::F32 y, skvm::Color paint,
+    skvm::Color onProgram(skvm::Builder* p,
+                          skvm::Coord device, skvm::Coord local, skvm::Color paint,
                           const SkMatrixProvider& matrices, const SkMatrix* localM,
                           SkFilterQuality quality, const SkColorInfo& dst,
                           skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const override {
         SkOverrideDeviceMatrixProvider matrixProvider(matrices, fCTM);
-        return as_SB(fProxyShader)->program(p, x,y,paint,
+        return as_SB(fProxyShader)->program(p, device,local, paint,
                                             matrixProvider,localM,
                                             quality,dst,
                                             uniforms,alloc);
diff --git a/src/shaders/SkLocalMatrixShader.h b/src/shaders/SkLocalMatrixShader.h
index 845b0f4..b1fd882 100644
--- a/src/shaders/SkLocalMatrixShader.h
+++ b/src/shaders/SkLocalMatrixShader.h
@@ -48,7 +48,7 @@
 
     bool onAppendStages(const SkStageRec&) const override;
 
-    skvm::Color onProgram(skvm::Builder*, skvm::F32 x, skvm::F32 y, skvm::Color paint,
+    skvm::Color onProgram(skvm::Builder*, skvm::Coord device, skvm::Coord local, skvm::Color paint,
                           const SkMatrixProvider&, const SkMatrix* localM,
                           SkFilterQuality quality, const SkColorInfo& dst,
                           skvm::Uniforms* uniforms, SkArenaAlloc*) const override;
diff --git a/src/shaders/SkPictureShader.cpp b/src/shaders/SkPictureShader.cpp
index ab5db0b..6a77004 100644
--- a/src/shaders/SkPictureShader.cpp
+++ b/src/shaders/SkPictureShader.cpp
@@ -275,7 +275,7 @@
 }
 
 skvm::Color SkPictureShader::onProgram(skvm::Builder* p,
-                                       skvm::F32 x, skvm::F32 y, skvm::Color paint,
+                                       skvm::Coord device, skvm::Coord local, skvm::Color paint,
                                        const SkMatrixProvider& matrices, const SkMatrix* localM,
                                        SkFilterQuality quality, const SkColorInfo& dst,
                                        skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const {
@@ -289,7 +289,10 @@
         return {};
     }
 
-    return as_SB(bitmapShader)->program(p, x,y, paint, matrices, lm, quality, dst, uniforms, alloc);
+    return as_SB(bitmapShader)->program(p, device,local, paint,
+                                        matrices,lm,
+                                        quality,dst,
+                                        uniforms,alloc);
 }
 
 /////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/shaders/SkPictureShader.h b/src/shaders/SkPictureShader.h
index 98eb761..01ef4b4 100644
--- a/src/shaders/SkPictureShader.h
+++ b/src/shaders/SkPictureShader.h
@@ -37,7 +37,7 @@
     SkPictureShader(SkReadBuffer&);
     void flatten(SkWriteBuffer&) const override;
     bool onAppendStages(const SkStageRec&) const override;
-    skvm::Color onProgram(skvm::Builder*, skvm::F32 x, skvm::F32 y, skvm::Color paint,
+    skvm::Color onProgram(skvm::Builder*, skvm::Coord device, skvm::Coord local, skvm::Color paint,
                           const SkMatrixProvider&, const SkMatrix* localM,
                           SkFilterQuality quality, const SkColorInfo& dst,
                           skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const override;
diff --git a/src/shaders/SkShader.cpp b/src/shaders/SkShader.cpp
index 0514e8c..573fe50 100644
--- a/src/shaders/SkShader.cpp
+++ b/src/shaders/SkShader.cpp
@@ -195,7 +195,8 @@
     return false;
 }
 
-skvm::Color SkShaderBase::program(skvm::Builder* p, skvm::F32 x, skvm::F32 y, skvm::Color paint,
+skvm::Color SkShaderBase::program(skvm::Builder* p,
+                                  skvm::Coord device, skvm::Coord local, skvm::Color paint,
                                   const SkMatrixProvider& matrices, const SkMatrix* localM,
                                   SkFilterQuality quality, const SkColorInfo& dst,
                                   skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const {
@@ -211,7 +212,8 @@
     // shader program hash and blitter Key.  This makes it safe for us to use
     // that bit to make decisions when constructing an SkVMBlitter, like doing
     // SrcOver -> Src strength reduction.
-    if (auto color = this->onProgram(p, x,y, paint, matrices,localM, quality,dst, uniforms,alloc)) {
+    if (auto color = this->onProgram(p, device,local, paint, matrices,localM, quality,dst,
+                                     uniforms,alloc)) {
         if (this->isOpaque()) {
             color.a = p->splat(1.0f);
         }
@@ -220,11 +222,12 @@
     return {};
 }
 
-skvm::Color SkShaderBase::onProgram(skvm::Builder*, skvm::F32 x, skvm::F32 y, skvm::Color paint,
+skvm::Color SkShaderBase::onProgram(skvm::Builder*,
+                                    skvm::Coord device, skvm::Coord local, skvm::Color paint,
                                     const SkMatrixProvider&, const SkMatrix* localM,
                                     SkFilterQuality quality, const SkColorInfo& dst,
                                     skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const {
-    //SkDebugf("cannot onProgram %s\n", this->getTypeName());
+    // SkDebugf("cannot onProgram %s\n", this->getTypeName());
     return {};
 }
 
@@ -235,7 +238,9 @@
 
 
 void SkShaderBase::ApplyMatrix(skvm::Builder* p, const SkMatrix& m,
-                               skvm::F32* x, skvm::F32* y, skvm::Uniforms* uniforms) {
+                               skvm::Coord* coord, skvm::Uniforms* uniforms) {
+    skvm::F32 *x = &coord->x,
+              *y = &coord->y;
     if (m.isIdentity()) {
         // That was easy.
     } else if (m.isTranslate()) {
@@ -261,7 +266,7 @@
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-skvm::Color SkEmptyShader::onProgram(skvm::Builder*, skvm::F32, skvm::F32, skvm::Color,
+skvm::Color SkEmptyShader::onProgram(skvm::Builder*, skvm::Coord, skvm::Coord, skvm::Color,
                                      const SkMatrixProvider&, const SkMatrix*,
                                      SkFilterQuality, const SkColorInfo&,
                                      skvm::Uniforms*, SkArenaAlloc*) const {
diff --git a/src/shaders/SkShaderBase.h b/src/shaders/SkShaderBase.h
index 7ef46dd..e9283cd 100644
--- a/src/shaders/SkShaderBase.h
+++ b/src/shaders/SkShaderBase.h
@@ -211,7 +211,7 @@
         return this->onAppendUpdatableStages(rec);
     }
 
-    skvm::Color program(skvm::Builder*, skvm::F32 x, skvm::F32 y, skvm::Color paint,
+    skvm::Color program(skvm::Builder*, skvm::Coord device, skvm::Coord local, skvm::Color paint,
                         const SkMatrixProvider&, const SkMatrix* localM,
                         SkFilterQuality quality, const SkColorInfo& dst,
                         skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const;
@@ -241,13 +241,14 @@
     virtual SkStageUpdater* onAppendUpdatableStages(const SkStageRec&) const { return nullptr; }
 
 protected:
-    static void ApplyMatrix(skvm::Builder*, const SkMatrix&, skvm::F32* x, skvm::F32* y, skvm::Uniforms*);
+    static void ApplyMatrix(skvm::Builder*, const SkMatrix&, skvm::Coord*, skvm::Uniforms*);
 
 private:
     // This is essentially const, but not officially so it can be modified in constructors.
     SkMatrix fLocalMatrix;
 
-    virtual skvm::Color onProgram(skvm::Builder*, skvm::F32 x, skvm::F32 y, skvm::Color paint,
+    virtual skvm::Color onProgram(skvm::Builder*,
+                                  skvm::Coord device, skvm::Coord local, skvm::Color paint,
                                   const SkMatrixProvider&, const SkMatrix* localM,
                                   SkFilterQuality quality, const SkColorInfo& dst,
                                   skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const;
diff --git a/src/shaders/gradients/SkGradientShader.cpp b/src/shaders/gradients/SkGradientShader.cpp
index 61e9b3a..6a495b5 100644
--- a/src/shaders/gradients/SkGradientShader.cpp
+++ b/src/shaders/gradients/SkGradientShader.cpp
@@ -420,7 +420,8 @@
 }
 
 skvm::Color SkGradientShaderBase::onProgram(skvm::Builder* p,
-                                            skvm::F32 x, skvm::F32 y, skvm::Color /*paint*/,
+                                            skvm::Coord device, skvm::Coord local,
+                                            skvm::Color /*paint*/,
                                             const SkMatrixProvider& mats, const SkMatrix* localM,
                                             SkFilterQuality quality, const SkColorInfo& dstInfo,
                                             skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const {
@@ -431,10 +432,10 @@
     inv.postConcat(fPtsToUnit);
     inv.normalizePerspective();
 
-    SkShaderBase::ApplyMatrix(p, inv, &x,&y,uniforms);
+    SkShaderBase::ApplyMatrix(p, inv, &local, uniforms);
 
     skvm::I32 mask = p->splat(~0);
-    skvm::F32 t = this->transformT(p,uniforms, x,y, &mask);
+    skvm::F32 t = this->transformT(p,uniforms, local, &mask);
 
     // Perhaps unexpectedly, clamping is handled naturally by our search, so we
     // don't explicitly clamp t to [0,1].  That clamp would break hard stops
diff --git a/src/shaders/gradients/SkGradientShaderPriv.h b/src/shaders/gradients/SkGradientShaderPriv.h
index b38b1e8..f33c444 100644
--- a/src/shaders/gradients/SkGradientShaderPriv.h
+++ b/src/shaders/gradients/SkGradientShaderPriv.h
@@ -79,7 +79,7 @@
 
     bool onAppendStages(const SkStageRec&) const override;
 
-    skvm::Color onProgram(skvm::Builder* p, skvm::F32 x, skvm::F32 y, skvm::Color paint,
+    skvm::Color onProgram(skvm::Builder*, skvm::Coord device, skvm::Coord local, skvm::Color paint,
                           const SkMatrixProvider&, const SkMatrix* localM,
                           SkFilterQuality quality, const SkColorInfo& dstCS,
                           skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const override;
@@ -89,7 +89,7 @@
 
     // Produce t from (x,y), modifying mask if it should be anything other than ~0.
     virtual skvm::F32 transformT(skvm::Builder*, skvm::Uniforms*,
-                                 skvm::F32 x, skvm::F32 y, skvm::I32* mask) const = 0;
+                                 skvm::Coord coord, skvm::I32* mask) const = 0;
 
     template <typename T, typename... Args>
     static Context* CheckedMakeContext(SkArenaAlloc* alloc, Args&&... args) {
diff --git a/src/shaders/gradients/SkLinearGradient.cpp b/src/shaders/gradients/SkLinearGradient.cpp
index ad5129c..347fd17 100644
--- a/src/shaders/gradients/SkLinearGradient.cpp
+++ b/src/shaders/gradients/SkLinearGradient.cpp
@@ -76,9 +76,9 @@
 }
 
 skvm::F32 SkLinearGradient::transformT(skvm::Builder* p, skvm::Uniforms*,
-                                       skvm::F32 x, skvm::F32 y, skvm::I32* mask) const {
+                                       skvm::Coord coord, skvm::I32* mask) const {
     // We've baked getting t in x into the matrix, so this is pretty trivial.
-    return x;
+    return coord.x;
 }
 
 SkShader::GradientType SkLinearGradient::asAGradient(GradientInfo* info) const {
diff --git a/src/shaders/gradients/SkLinearGradient.h b/src/shaders/gradients/SkLinearGradient.h
index c8f9bc6..063b9c5 100644
--- a/src/shaders/gradients/SkLinearGradient.h
+++ b/src/shaders/gradients/SkLinearGradient.h
@@ -30,7 +30,7 @@
                               SkRasterPipeline* postPipeline) const final;
 
     skvm::F32 transformT(skvm::Builder*, skvm::Uniforms*,
-                         skvm::F32 x, skvm::F32 y, skvm::I32* mask) const final;
+                         skvm::Coord coord, skvm::I32* mask) const final;
 
 private:
     SK_FLATTENABLE_HOOKS(SkLinearGradient)
diff --git a/src/shaders/gradients/SkRadialGradient.cpp b/src/shaders/gradients/SkRadialGradient.cpp
index aeefe15..fea9ca6 100644
--- a/src/shaders/gradients/SkRadialGradient.cpp
+++ b/src/shaders/gradients/SkRadialGradient.cpp
@@ -64,8 +64,8 @@
 }
 
 skvm::F32 SkRadialGradient::transformT(skvm::Builder* p, skvm::Uniforms*,
-                                       skvm::F32 x, skvm::F32 y, skvm::I32* mask) const {
-    return sqrt(x*x + y*y);
+                                       skvm::Coord coord, skvm::I32* mask) const {
+    return sqrt(coord.x*coord.x + coord.y*coord.y);
 }
 
 /////////////////////////////////////////////////////////////////////
diff --git a/src/shaders/gradients/SkRadialGradient.h b/src/shaders/gradients/SkRadialGradient.h
index 7568097..7396bb2 100644
--- a/src/shaders/gradients/SkRadialGradient.h
+++ b/src/shaders/gradients/SkRadialGradient.h
@@ -27,7 +27,7 @@
                               SkRasterPipeline* postPipeline) const override;
 
     skvm::F32 transformT(skvm::Builder*, skvm::Uniforms*,
-                         skvm::F32 x, skvm::F32 y, skvm::I32* mask) const final;
+                         skvm::Coord coord, skvm::I32* mask) const final;
 
 private:
     SK_FLATTENABLE_HOOKS(SkRadialGradient)
diff --git a/src/shaders/gradients/SkSweepGradient.cpp b/src/shaders/gradients/SkSweepGradient.cpp
index 1cd7a05..6256cc6 100644
--- a/src/shaders/gradients/SkSweepGradient.cpp
+++ b/src/shaders/gradients/SkSweepGradient.cpp
@@ -68,9 +68,9 @@
 }
 
 skvm::F32 SkSweepGradient::transformT(skvm::Builder* p, skvm::Uniforms* uniforms,
-                                      skvm::F32 x, skvm::F32 y, skvm::I32* mask) const {
-    skvm::F32 xabs = abs(x),
-              yabs = abs(y),
+                                      skvm::Coord coord, skvm::I32* mask) const {
+    skvm::F32 xabs = abs(coord.x),
+              yabs = abs(coord.y),
              slope = min(xabs, yabs) / max(xabs, yabs);
     skvm::F32 s = slope * slope;
 
@@ -82,9 +82,9 @@
                                     +2.476101927459239959716796875e-2f,
                                     -5.185396969318389892578125e-2f,
                                     +0.15912117063999176025390625f);
-    phi = select(xabs < yabs, (1/4.0f) - phi, phi);
-    phi = select(   x < 0.0f, (1/2.0f) - phi, phi);
-    phi = select(   y < 0.0f, (1/1.0f) - phi, phi);
+    phi = select(   xabs < yabs, (1/4.0f) - phi, phi);
+    phi = select(coord.x < 0.0f, (1/2.0f) - phi, phi);
+    phi = select(coord.y < 0.0f, (1/1.0f) - phi, phi);
 
     skvm::F32 t = select(is_NaN(phi), p->splat(0.0f)
                                     , phi);
diff --git a/src/shaders/gradients/SkSweepGradient.h b/src/shaders/gradients/SkSweepGradient.h
index e7beae2..86ca372 100644
--- a/src/shaders/gradients/SkSweepGradient.h
+++ b/src/shaders/gradients/SkSweepGradient.h
@@ -31,7 +31,7 @@
                               SkRasterPipeline* postPipeline) const override;
 
     skvm::F32 transformT(skvm::Builder*, skvm::Uniforms*,
-                         skvm::F32 x, skvm::F32 y, skvm::I32* mask) const final;
+                         skvm::Coord coord, skvm::I32* mask) const final;
 private:
     SK_FLATTENABLE_HOOKS(SkSweepGradient)
 
diff --git a/src/shaders/gradients/SkTwoPointConicalGradient.cpp b/src/shaders/gradients/SkTwoPointConicalGradient.cpp
index 42b3d6f..d3ec755 100644
--- a/src/shaders/gradients/SkTwoPointConicalGradient.cpp
+++ b/src/shaders/gradients/SkTwoPointConicalGradient.cpp
@@ -234,11 +234,13 @@
 }
 
 skvm::F32 SkTwoPointConicalGradient::transformT(skvm::Builder* p, skvm::Uniforms* uniforms,
-                                                skvm::F32 x, skvm::F32 y, skvm::I32* mask) const {
+                                                skvm::Coord coord, skvm::I32* mask) const {
     // See https://skia.org/dev/design/conical, and onAppendStages() above.
     // There's a lot going on here, and I'm not really sure what's independent
     // or disjoint, what can be reordered, simplified, etc.  Tweak carefully.
 
+    const skvm::F32 x = coord.x,
+                    y = coord.y;
     if (fType == Type::kRadial) {
         float denom = 1.0f / (fRadius2 - fRadius1),
               scale = std::max(fRadius1, fRadius2) * denom,
diff --git a/src/shaders/gradients/SkTwoPointConicalGradient.h b/src/shaders/gradients/SkTwoPointConicalGradient.h
index a8893ce..d362ba0 100644
--- a/src/shaders/gradients/SkTwoPointConicalGradient.h
+++ b/src/shaders/gradients/SkTwoPointConicalGradient.h
@@ -70,7 +70,7 @@
                               SkRasterPipeline* postPipeline) const override;
 
     skvm::F32 transformT(skvm::Builder*, skvm::Uniforms*,
-                         skvm::F32 x, skvm::F32 y, skvm::I32* mask) const final;
+                         skvm::Coord coord, skvm::I32* mask) const final;
 
 private:
     SK_FLATTENABLE_HOOKS(SkTwoPointConicalGradient)
diff --git a/src/sksl/SkSLByteCode.cpp b/src/sksl/SkSLByteCode.cpp
index b734db5..132dc6d 100644
--- a/src/sksl/SkSLByteCode.cpp
+++ b/src/sksl/SkSLByteCode.cpp
@@ -110,6 +110,7 @@
             break;
         case ByteCodeInstruction::kLoadExtendedUniform: printf("loadextendeduniform %d", READ8());
             break;
+        case ByteCodeInstruction::kLoadFragCoord: printf("loadfragcoord"); break;
         case ByteCodeInstruction::kMatrixToMatrix: {
             int srcCols = READ8();
             int srcRows = READ8();
@@ -926,11 +927,6 @@
                 }
             }
 
-            case ByteCodeInstruction::kSampleExplicit:
-            case ByteCodeInstruction::kSampleMatrix:
-                // TODO: Support these?
-                return false;
-
             case ByteCodeInstruction::kScalarToMatrix: {
                 int cols = READ8();
                 int rows = READ8();
@@ -1105,6 +1101,14 @@
                 *loopPtr &= ~m;
                 continue;
             }
+
+            case ByteCodeInstruction::kLoadFragCoord:
+            case ByteCodeInstruction::kSampleExplicit:
+            case ByteCodeInstruction::kSampleMatrix:
+            default:
+                // TODO: Support these?
+                SkASSERT(false);
+                return false;
         }
     }
 }
diff --git a/src/sksl/SkSLByteCode.h b/src/sksl/SkSLByteCode.h
index a4b0918..79f2bef 100644
--- a/src/sksl/SkSLByteCode.h
+++ b/src/sksl/SkSLByteCode.h
@@ -78,6 +78,8 @@
     kLoadExtended,
     kLoadExtendedGlobal,
     kLoadExtendedUniform,
+    // Loads "sk_FragCoord" [X, Y, Z, 1/W]
+    kLoadFragCoord,
     // Followed by four bytes: srcCols, srcRows, dstCols, dstRows. Consumes the src matrix from the
     // stack, and replaces it with the dst matrix. Per GLSL rules, there are no restrictions on
     // dimensions. Any overlapping values are copied, and any other values are filled in with the
@@ -275,7 +277,7 @@
      * Some byte code programs can't be executed by the interpreter, due to unsupported features.
      * They may still be used to convert to other formats, or for reflection of uniforms.
      */
-    bool canRun() const { return fChildFPCount == 0; }
+    bool canRun() const { return fChildFPCount == 0 && !fUsesFragCoord; }
 
 private:
     ByteCode(const ByteCode&) = delete;
@@ -287,6 +289,7 @@
     int fGlobalSlotCount = 0;
     int fUniformSlotCount = 0;
     int fChildFPCount = 0;
+    bool fUsesFragCoord = false;
     std::vector<Uniform> fUniforms;
 
     std::vector<std::unique_ptr<ByteCodeFunction>> fFunctions;
diff --git a/src/sksl/SkSLByteCodeGenerator.cpp b/src/sksl/SkSLByteCodeGenerator.cpp
index 1d8f87f..819a1a3 100644
--- a/src/sksl/SkSLByteCodeGenerator.cpp
+++ b/src/sksl/SkSLByteCodeGenerator.cpp
@@ -207,10 +207,27 @@
     return result;
 }
 
+// If the expression is a reference to a builtin global variable, return the builtin ID.
+// Otherwise, return -1.
+static int expression_as_builtin(const Expression& e) {
+    if (e.fKind == Expression::kVariableReference_Kind) {
+        const Variable& var(((VariableReference&)e).fVariable);
+        if (var.fStorage == Variable::kGlobal_Storage) {
+            return var.fModifiers.fLayout.fBuiltin;
+        }
+    }
+    return -1;
+}
+
 // A "simple" Swizzle is based on a variable (or a compound variable like a struct or array), and
 // that references consecutive values, such that it can be implemented using normal load/store ops
 // with an offset. Note that all single-component swizzles (of suitable base types) are simple.
 static bool swizzle_is_simple(const Swizzle& s) {
+    // Builtin variables use dedicated instructions that don't allow subset loads
+    if (expression_as_builtin(*s.fBase) >= 0) {
+        return false;
+    }
+
     switch (s.fBase->fKind) {
         case Expression::kFieldAccess_Kind:
         case Expression::kIndex_Kind:
@@ -366,6 +383,7 @@
         case ByteCodeInstruction::kLoadGlobal4:
         case ByteCodeInstruction::kLoadUniform4:
         case ByteCodeInstruction::kReadExternal4:
+        case ByteCodeInstruction::kLoadFragCoord:
             return 4;
 
         case ByteCodeInstruction::kDupN:
@@ -1015,6 +1033,19 @@
 }
 
 void ByteCodeGenerator::writeVariableExpression(const Expression& expr) {
+    if (int builtin = expression_as_builtin(expr); builtin >= 0) {
+        switch (builtin) {
+            case SK_FRAGCOORD_BUILTIN:
+                this->write(ByteCodeInstruction::kLoadFragCoord);
+                fOutput->fUsesFragCoord = true;
+                break;
+            default:
+                fErrors.error(expr.fOffset, "Unsupported builtin");
+                break;
+        }
+        return;
+    }
+
     Location location = this->getLocation(expr);
     int count = SlotCount(expr.fType);
     if (count == 0) {
diff --git a/tests/SkRuntimeEffectTest.cpp b/tests/SkRuntimeEffectTest.cpp
index 082a98a..7fdf421 100644
--- a/tests/SkRuntimeEffectTest.cpp
+++ b/tests/SkRuntimeEffectTest.cpp
@@ -76,7 +76,7 @@
     }
 
     void test(skiatest::Reporter* r, sk_sp<SkSurface> surface,
-              uint32_t TL, uint32_t TR, uint32_t BL, uint32_t BR) {
+              uint32_t TL, uint32_t TR, uint32_t BL, uint32_t BR, SkScalar rotate = 0.0f) {
         auto shader = fBuilder->makeShader(nullptr, false);
         if (!shader) {
             REPORT_FAILURE(r, "shader", SkString("Effect didn't produce a shader"));
@@ -86,6 +86,7 @@
         SkPaint paint;
         paint.setShader(std::move(shader));
         paint.setBlendMode(SkBlendMode::kSrc);
+        surface->getCanvas()->rotate(rotate);
         surface->getCanvas()->drawPaint(paint);
 
         uint32_t actual[4];
@@ -153,11 +154,8 @@
 
     // Test sk_FragCoord, which we output to color. Since the surface is 2x2, we should see
     // (0,0), (1,0), (0,1), (1,1), multiply by 0.498 to make sure we're not saturating unexpectedly.
-    // TODO: Remove this when sk_FragCoord is supported by interpreter.
-    if (context) {
-        TestEffect fragCoord(r, "", "color = half4(0.498 * (half2(sk_FragCoord.xy) - 0.5), 0, 1);");
-        fragCoord.test(r, surface, 0xFF000000, 0xFF00007F, 0xFF007F00, 0xFF007F7F);
-    }
+    TestEffect fragCoord(r, "", "color = half4(0.498 * (half2(sk_FragCoord.xy) - 0.5), 0, 1);");
+    fragCoord.test(r, surface, 0xFF000000, 0xFF00007F, 0xFF007F00, 0xFF007F7F, 45.0f);
 }
 
 DEF_TEST(SkRuntimeEffectSimple, r) {