Revert "[graphite] Move inverting atlas size into vertex shader"

This reverts commit bb0593cf8ae47594991be9e226288823bb9b5147.

Reason for revert: primary culprit for b/506238401

Original change's description:
> [graphite] Move inverting atlas size into vertex shader
>
> Bug: b/402405637
> Change-Id: I8c8c3505cc7038001d28560f111d0f59cfbb0790
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/1214557
> Commit-Queue: Thomas Smith <thomsmit@google.com>
> Reviewed-by: Michael Ludwig <michaelludwig@google.com>

Bug: b/402405637
Bug: b/506238401
Change-Id: I861945127a0e6a29eccff02306d5f59c45a8b1ae
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/1226976
Bot-Commit: rubber-stamper@appspot.gserviceaccount.com <rubber-stamper@appspot.gserviceaccount.com>
Commit-Queue: Noelle Scobie <nscobie@google.com>
diff --git a/src/gpu/graphite/ShaderInfo.cpp b/src/gpu/graphite/ShaderInfo.cpp
index 01368e5..94563c5 100644
--- a/src/gpu/graphite/ShaderInfo.cpp
+++ b/src/gpu/graphite/ShaderInfo.cpp
@@ -761,7 +761,6 @@
                         UniquePaintParamsID paintID,
                         const char* uniformSsboIndex)
             : fRootNodes(SkSpan<const ShaderNode*>())
-            , fTexturesAndSamplersSkSL("")
             , fHasStepUniforms(step->numUniforms() > 0) {
 
         // Decompress Root Nodes & Determine Local Coords
@@ -849,9 +848,6 @@
 
     // The base SkSL preamble (uniforms, varyings) shared by both stages
     std::string fSharedPreamble;
-    // Textures and samplers are also shared between fragment and vertex shaders.
-    // TODO (thomsmit): Possibly pull texturues and samplers into the shared preamble?
-    std::string fTexturesAndSamplersSkSL;
 
     // Shared calculated properties
     bool fNeedsLocalCoords;
@@ -901,7 +897,6 @@
                                      rpDesc.fColorAttachment.fFormat,
                                      rpDesc.fWriteSwizzle,
                                      outDescs,
-                                     &sharedData.fTexturesAndSamplersSkSL,
                                      sharedData);
     } else {
         result->fBlendInfo.fWritesColor = false;
@@ -965,7 +960,6 @@
                                       TextureFormat targetFormat,
                                       Swizzle writeSwizzle,
                                       skia_private::TArray<SamplerDesc>* outDescs,
-                                      std::string* textureAndSamplerSkSL,
                                       const SharedGeneratorData& sharedData) {
 #if defined(SK_DEBUG)
     // Validate the root node structure of the key.
@@ -1073,12 +1067,11 @@
                                fDstReadStrategy == DstReadStrategy::kTextureSample;
     {
         int binding = 0;
-        // Cache the texture and fragment sksl so we can add it to the vertex shader
-        *textureAndSamplerSkSL = emit_textures_and_samplers(bindingReqs, sharedData.fRootNodes,
-                                                            &binding, outDescs);
+        fsPreamble += emit_textures_and_samplers(bindingReqs, sharedData.fRootNodes, &binding,
+                                               outDescs);
         int paintTextureCount = binding;
         if (step->hasTextures()) {
-            *textureAndSamplerSkSL += step->texturesAndSamplersSkSL(bindingReqs, &binding);
+            fsPreamble += step->texturesAndSamplersSkSL(bindingReqs, &binding);
             if (outDescs) {
                 // Determine how many render step samplers were used by comparing the binding value
                 // against paintTextureCount, taking into account the binding requirements. We
@@ -1092,8 +1085,8 @@
             }
         }
         if (useDstSampler) {
-            *textureAndSamplerSkSL += EmitSamplerLayout(bindingReqs, &binding);
-            *textureAndSamplerSkSL += " sampler2D dstSampler;";
+            fsPreamble += EmitSamplerLayout(bindingReqs, &binding);
+            fsPreamble += " sampler2D dstSampler;";
             // Add default SamplerDesc for the intrinsic dstSampler to stay consistent with
             // `fNumFragmentTexturesAndSamplers`.
             if (outDescs) {
@@ -1101,8 +1094,6 @@
             }
         }
 
-        fsPreamble += *textureAndSamplerSkSL;
-
         // Record how many textures and samplers are used.
         fNumFragmentTexturesAndSamplers = binding;
     }
@@ -1296,11 +1287,6 @@
     // Fixed program header (intrinsics are always declared as an uniform interface block)
     const ResourceBindingRequirements& bindingReqs = caps->resourceBindingRequirements();
     vsPreamble = emit_intrinsic_constants(bindingReqs);
-
-    if (step->hasTextures()) {
-        vsPreamble += sharedData.fTexturesAndSamplersSkSL;
-    }
-
     // Varyings needed by RenderStep and potentially lifted expressions
     vsPreamble += emit_varyings(step, "out",
                                 sharedData.fLiftedExpr,
diff --git a/src/gpu/graphite/ShaderInfo.h b/src/gpu/graphite/ShaderInfo.h
index 2912cf6..49d5a2d 100644
--- a/src/gpu/graphite/ShaderInfo.h
+++ b/src/gpu/graphite/ShaderInfo.h
@@ -98,7 +98,6 @@
                               TextureFormat targetFormat,
                               skgpu::Swizzle writeSwizzle,
                               skia_private::TArray<SamplerDesc>* outDescs,
-                              std::string* textureAndSamplerSkSL,
                               const SharedGeneratorData&);
 
     void generateVertexSkSL(const Caps*,
diff --git a/src/gpu/graphite/dawn/DawnGraphicsPipeline.cpp b/src/gpu/graphite/dawn/DawnGraphicsPipeline.cpp
index 3a07126..3bcdcb6 100644
--- a/src/gpu/graphite/dawn/DawnGraphicsPipeline.cpp
+++ b/src/gpu/graphite/dawn/DawnGraphicsPipeline.cpp
@@ -517,7 +517,7 @@
 
                 for (int i = 0; i < numTexturesAndSamplers;) {
                     entries[i].binding = i;
-                    entries[i].visibility = wgpu::ShaderStage::Fragment | wgpu::ShaderStage::Vertex;
+                    entries[i].visibility = wgpu::ShaderStage::Fragment;
 #if !defined(__EMSCRIPTEN__)
                     // Index of sampler information = 1/2 of cumulative texture and sampler index.
                     // If we have a non-default-initialized SamplerDesc at that index,
@@ -554,7 +554,7 @@
                     ++i;
 
                     entries[i].binding = i;
-                    entries[i].visibility = wgpu::ShaderStage::Fragment | wgpu::ShaderStage::Vertex;
+                    entries[i].visibility = wgpu::ShaderStage::Fragment;
                     entries[i].texture.sampleType = wgpu::TextureSampleType::Float;
                     entries[i].texture.viewDimension = wgpu::TextureViewDimension::e2D;
                     entries[i].texture.multisampled = false;
diff --git a/src/gpu/graphite/dawn/DawnSharedContext.cpp b/src/gpu/graphite/dawn/DawnSharedContext.cpp
index 64232b5..36dcb22 100644
--- a/src/gpu/graphite/dawn/DawnSharedContext.cpp
+++ b/src/gpu/graphite/dawn/DawnSharedContext.cpp
@@ -153,11 +153,11 @@
     std::array<wgpu::BindGroupLayoutEntry, 2> entries;
 
     entries[0].binding = 0;
-    entries[0].visibility = wgpu::ShaderStage::Fragment | wgpu::ShaderStage::Vertex;
+    entries[0].visibility = wgpu::ShaderStage::Fragment;
     entries[0].sampler.type = wgpu::SamplerBindingType::Filtering;
 
     entries[1].binding = 1;
-    entries[1].visibility = wgpu::ShaderStage::Fragment | wgpu::ShaderStage::Vertex;
+    entries[1].visibility = wgpu::ShaderStage::Fragment;
     entries[1].texture.sampleType = wgpu::TextureSampleType::Float;
     entries[1].texture.viewDimension = wgpu::TextureViewDimension::e2D;
     entries[1].texture.multisampled = false;
diff --git a/src/gpu/graphite/mtl/MtlCommandBuffer.mm b/src/gpu/graphite/mtl/MtlCommandBuffer.mm
index 2de184f..ff62360 100644
--- a/src/gpu/graphite/mtl/MtlCommandBuffer.mm
+++ b/src/gpu/graphite/mtl/MtlCommandBuffer.mm
@@ -587,9 +587,8 @@
 
     id<MTLTexture> mtlTexture = ((const MtlTexture*)texture)->mtlTexture();
     id<MTLSamplerState> mtlSamplerState = ((const MtlSampler*)sampler)->mtlSamplerState();
-
-    fActiveRenderCommandEncoder->setTexture(mtlTexture, bindIndex);
-    fActiveRenderCommandEncoder->setSamplerState(mtlSamplerState, bindIndex);
+    fActiveRenderCommandEncoder->setFragmentTexture(mtlTexture, bindIndex);
+    fActiveRenderCommandEncoder->setFragmentSamplerState(mtlSamplerState, bindIndex);
 }
 
 void MtlCommandBuffer::setScissor(const Scissor& scissor) {
diff --git a/src/gpu/graphite/mtl/MtlRenderCommandEncoder.h b/src/gpu/graphite/mtl/MtlRenderCommandEncoder.h
index fdcb8d2..96a3c6a 100644
--- a/src/gpu/graphite/mtl/MtlRenderCommandEncoder.h
+++ b/src/gpu/graphite/mtl/MtlRenderCommandEncoder.h
@@ -108,23 +108,11 @@
             fCurrentTexture[index] = texture;
         }
     }
-    void setTexture(id<MTLTexture> texture, NSUInteger index) {
-        SkASSERT(index < kMaxExpectedTextures);
-        if (fCurrentTexture[index] != texture) {
-            [(*fCommandEncoder) setFragmentTexture:texture
-                                           atIndex:index];
-            [(*fCommandEncoder) setVertexTexture:texture
-                                           atIndex:index];
-            fCurrentTexture[index] = texture;
-        }
-    }
-    void setSamplerState(id<MTLSamplerState> sampler, NSUInteger index) {
+    void setFragmentSamplerState(id<MTLSamplerState> sampler, NSUInteger index) {
         SkASSERT(index < kMaxExpectedTextures);
         if (fCurrentSampler[index] != sampler) {
             [(*fCommandEncoder) setFragmentSamplerState:sampler
                                                 atIndex:index];
-            [(*fCommandEncoder) setVertexSamplerState:sampler
-                                                atIndex:index];
             fCurrentSampler[index] = sampler;
         }
     }
diff --git a/src/gpu/graphite/render/BitmapTextRenderStep.cpp b/src/gpu/graphite/render/BitmapTextRenderStep.cpp
index 724228e..16d804d 100644
--- a/src/gpu/graphite/render/BitmapTextRenderStep.cpp
+++ b/src/gpu/graphite/render/BitmapTextRenderStep.cpp
@@ -74,7 +74,7 @@
                       {"depth", VertexAttribType::kFloat, SkSLType::kFloat},
                       {"ssboIndex", VertexAttribType::kUInt, SkSLType::kUInt}}},
                      /*varyings=*/
-                     {{{"textureCoords", SkSLType::kFloat2},
+                     {{{"unormTexCoords", SkSLType::kFloat2},
                       {"texIndex", SkSLType::kHalf},
                       {"maskFormat", SkSLType::kHalf}}}) {}
 
@@ -99,9 +99,7 @@
     // must write to an already-defined float2 stepLocalCoords variable.
     return "texIndex = half(indexAndFlags.x);"
            "maskFormat = half(indexAndFlags.y);"
-           "float2 unormTexCoords;" // unused
            "float4 devPosition = text_vertex_fn(float2(sk_VertexID >> 1, sk_VertexID & 1), "
-                                               "text_atlas_0, "
                                                "maskToDevice, "
                                                "localToDevice, "
                                                "float2(size), "
@@ -110,7 +108,6 @@
                                                "strikeToSourceScale, "
                                                "depth, "
                                                "unormTexCoords, "
-                                               "textureCoords, "
                                                "stepLocalCoords);";
 }
 
@@ -131,7 +128,7 @@
     // The returned SkSL must write its color into a 'half4 primitiveColor' variable
     // (defined in the calling code).
     static_assert(kNumTextAtlasTextures == 4);
-    return "primitiveColor = sample_indexed_atlas(textureCoords, "
+    return "primitiveColor = sample_indexed_atlas(unormTexCoords, "
                                                  "int(texIndex), "
                                                  "text_atlas_0, "
                                                  "text_atlas_1, "
@@ -143,7 +140,7 @@
     // The returned SkSL must write its coverage into a 'half4 outputCoverage' variable (defined in
     // the calling code) with the actual coverage splatted out into all four channels.
     static_assert(kNumTextAtlasTextures == 4);
-    return "outputCoverage = bitmap_text_coverage_fn(sample_indexed_atlas(textureCoords, "
+    return "outputCoverage = bitmap_text_coverage_fn(sample_indexed_atlas(unormTexCoords, "
                                                                          "int(texIndex), "
                                                                          "text_atlas_0, "
                                                                          "text_atlas_1, "
diff --git a/src/gpu/graphite/render/SDFTextLCDRenderStep.cpp b/src/gpu/graphite/render/SDFTextLCDRenderStep.cpp
index 4cdd6b1..7086d79 100644
--- a/src/gpu/graphite/render/SDFTextLCDRenderStep.cpp
+++ b/src/gpu/graphite/render/SDFTextLCDRenderStep.cpp
@@ -70,8 +70,7 @@
                       {"ssboIndex", VertexAttribType::kUInt, SkSLType::kUInt}}},
                      /*varyings=*/
                      {{{"unormTexCoords", SkSLType::kFloat2},
-                       {"textureCoords", SkSLType::kFloat2},
-                       {"texIndex", SkSLType::kFloat}}}) {}
+                      {"texIndex", SkSLType::kFloat}}}) {}
 
 SDFTextLCDRenderStep::~SDFTextLCDRenderStep() {}
 
@@ -80,7 +79,6 @@
     // must write to an already-defined float2 stepLocalCoords variable.
     return "texIndex = half(indexAndFlags.x);"
            "float4 devPosition = text_vertex_fn(float2(sk_VertexID >> 1, sk_VertexID & 1), "
-                                               "sdf_atlas_0, "
                                                "maskToDevice, "
                                                "localToDevice, "
                                                "float2(size), "
@@ -89,7 +87,6 @@
                                                "strikeToSourceScale, "
                                                "depth, "
                                                "unormTexCoords, "
-                                               "textureCoords, "
                                                "stepLocalCoords);";
 }
 
@@ -118,7 +115,6 @@
     return "outputCoverage = sdf_text_lcd_coverage_fn(pixelGeometryDelta, "
                                                      "gammaParams, "
                                                      "unormTexCoords, "
-                                                     "textureCoords, "
                                                      "texIndex, "
                                                      "sdf_atlas_0, "
                                                      "sdf_atlas_1, "
diff --git a/src/gpu/graphite/render/SDFTextRenderStep.cpp b/src/gpu/graphite/render/SDFTextRenderStep.cpp
index ebf5c50..f8eed48 100644
--- a/src/gpu/graphite/render/SDFTextRenderStep.cpp
+++ b/src/gpu/graphite/render/SDFTextRenderStep.cpp
@@ -72,8 +72,7 @@
                       {"ssboIndex", VertexAttribType::kUInt, SkSLType::kUInt}}},
                      /*varyings=*/
                      {{{"unormTexCoords", SkSLType::kFloat2},
-                       {"textureCoords", SkSLType::kFloat2},
-                       {"texIndex", SkSLType::kFloat}}}) {}
+                      {"texIndex", SkSLType::kFloat}}}) {}
 
 SDFTextRenderStep::~SDFTextRenderStep() {}
 
@@ -82,7 +81,6 @@
     // must write to an already-defined float2 stepLocalCoords variable.
     return "texIndex = half(indexAndFlags.x);"
            "float4 devPosition = text_vertex_fn(float2(sk_VertexID >> 1, sk_VertexID & 1), "
-                                               "sdf_atlas_0, "
                                                "maskToDevice, "
                                                "localToDevice, "
                                                "float2(size), "
@@ -91,7 +89,6 @@
                                                "strikeToSourceScale, "
                                                "depth, "
                                                "unormTexCoords, "
-                                               "textureCoords, "
                                                "stepLocalCoords);";
 }
 
@@ -117,7 +114,7 @@
     // TODO: Need to add 565 support.
     // TODO: Need aliased and possibly sRGB support.
     static_assert(kNumSDFAtlasTextures == 4);
-    return "outputCoverage = sdf_text_coverage_fn(sample_indexed_atlas(textureCoords, "
+    return "outputCoverage = sdf_text_coverage_fn(sample_indexed_atlas(unormTexCoords, "
                                                                       "int(texIndex), "
                                                                       "sdf_atlas_0, "
                                                                       "sdf_atlas_1, "
diff --git a/src/gpu/graphite/vk/VulkanCommandBuffer.cpp b/src/gpu/graphite/vk/VulkanCommandBuffer.cpp
index 1d2a591..1eb257c 100644
--- a/src/gpu/graphite/vk/VulkanCommandBuffer.cpp
+++ b/src/gpu/graphite/vk/VulkanCommandBuffer.cpp
@@ -618,16 +618,14 @@
         // Our current implementation of setting texture image layouts does not allow layout changes
         // once we have already begun a render pass, so prior to any other commands, set the layout
         // of all sampled textures from the drawpass so they can be sampled from the shader.
-
         for (const sk_sp<TextureProxy>& textureProxy : drawPass->sampledTextures()) {
             VulkanTexture* vulkanTexture = const_cast<VulkanTexture*>(
                                            static_cast<const VulkanTexture*>(
                                            textureProxy->texture()));
-            vulkanTexture->setImageLayout(
-                    this,
-                    VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
-                    VK_ACCESS_SHADER_READ_BIT,
-                    VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT);
+            vulkanTexture->setImageLayout(this,
+                                          VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
+                                          VK_ACCESS_SHADER_READ_BIT,
+                                          VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
         }
     }
     if (fDstCopy.first) {
@@ -1539,12 +1537,11 @@
                 // active graphics pipeline and can be indexed directly with `i`.
                 const Sampler* immutableSampler = fActiveGraphicsPipeline->immutableSampler(i);
                 SkASSERT(SkToBool(immutableSampler) == command->fSamplers[i].isImmutable());
-                descriptors.push_back(
-                        {DescriptorType::kCombinedTextureSampler,
-                         /*count=*/1,
-                         /*bindingIdx=*/i,
-                         PipelineStageFlags::kFragmentShader | PipelineStageFlags::kVertexShader,
-                         immutableSampler});
+                descriptors.push_back({DescriptorType::kCombinedTextureSampler,
+                                       /*count=*/1,
+                                       /*bindingIdx=*/i,
+                                       PipelineStageFlags::kFragmentShader,
+                                       immutableSampler});
             }
         }
         // If required the dst copy texture+sampler is the last one in the descriptor set
diff --git a/src/gpu/graphite/vk/VulkanGraphicsPipeline.cpp b/src/gpu/graphite/vk/VulkanGraphicsPipeline.cpp
index 4c2fec6..6e8f3ea 100644
--- a/src/gpu/graphite/vk/VulkanGraphicsPipeline.cpp
+++ b/src/gpu/graphite/vk/VulkanGraphicsPipeline.cpp
@@ -559,12 +559,11 @@
         if (!immutableSamplers.empty() && immutableSamplers[i]) {
             immutableSampler = immutableSamplers[i].get();
         }
-        textureSamplerDescs.push_back(
-                {DescriptorType::kCombinedTextureSampler,
-                 /*count=*/1,
-                 /*bindingIdx=*/i,
-                 PipelineStageFlags::kVertexShader | PipelineStageFlags::kFragmentShader,
-                 immutableSampler});
+        textureSamplerDescs.push_back({DescriptorType::kCombinedTextureSampler,
+                                       /*count=*/1,
+                                       /*bindingIdx=*/i,
+                                       PipelineStageFlags::kFragmentShader,
+                                       immutableSampler});
     }
 
     // If no texture/samplers are used, a mock VkDescriptorSetLayout handle by passing in the
diff --git a/src/sksl/generated/sksl_graphite_frag.minified.sksl b/src/sksl/generated/sksl_graphite_frag.minified.sksl
index b2cb6b6..4698add 100644
--- a/src/sksl/generated/sksl_graphite_frag.minified.sksl
+++ b/src/sksl/generated/sksl_graphite_frag.minified.sksl
@@ -195,66 +195,68 @@
 "float2(half2(c.z,.875))).x,1.);return d*sample(b,float2(half2(c.w,.125))).x"
 ";}$pure half4 sk_gaussian_colorfilter(half4 a){half b=1.-a.w;b=exp((-b*b)*4."
 ")-.018;return b.xxxx;}$pure half4 sample_indexed_atlas(float2 a,int b,sampler2D"
-" c,sampler2D d,sampler2D e,sampler2D f){switch(b){case 1:return sample(d,a)"
-";case 2:return sample(e,a);case 3:return sample(f,a);default:return sample("
-"c,a);}}$pure half3 $G(float2 a,int b,float2 c,sampler2D d,sampler2D e,sampler2D"
-" f,sampler2D g){half3 h=half3(1.);switch(b){case 1:h.x=sample(e,a-c).x;h.y="
-"sample(e,a).x;h.z=sample(e,a+c).x;break;case 2:h.x=sample(f,a-c).x;h.y=sample"
-"(f,a).x;h.z=sample(f,a+c).x;break;case 3:h.x=sample(g,a-c).x;h.y=sample(g,a"
-").x;h.z=sample(g,a+c).x;break;default:h.x=sample(d,a-c).x;h.y=sample(d,a).x"
-";h.z=sample(d,a+c).x;break;}return h;}$pure half4 bitmap_text_coverage_fn(half4"
-" a,int b){return b==0?a.xxxx:a;}$pure half4 sdf_text_coverage_fn(half a,float2"
-" b,half2 c){half d=7.96875*(a-.5019608);d-=c.x;half2 e=half2(dFdx(d),dFdy(d"
-"));half f=dot(e,e);e=f>=.0001?e*inversesqrt(f):half2(.7071);float2x2 g=float2x2"
-"(dFdx(b),dFdy(b));half2 h=half2(g*float2(e));half i=.65*length(h);if(c.y>0."
-")return saturate((d+i)/(2.*i)).xxxx;else return smoothstep(-i,i,d).xxxx;}$pure"
-" half4 sdf_text_lcd_coverage_fn(half2 a,half4 b,float2 c,float2 d,float e,sampler2D"
-" f,sampler2D g,sampler2D h,sampler2D i){float2x2 j=float2x2(dFdx(c),dFdy(c)"
-");float2 k=j*float2(a);half3 l=$G(d,int(e),k,f,g,h,i);half3 m=half3(7.96875"
-")*(l-half3(.5019608));m-=b.xyz;half2 n=half2(dFdx(m.y),dFdy(m.y));half o=dot"
-"(n,n);n=o>=.0001?n*inversesqrt(o):half2(.7071);half2 p=half2(j*float2(n));half3"
-" q=(.65*length(p)).xxx;if(b.w>0.)return half4(saturate(m+q/(2.*q)),1.);else"
-" return half4(smoothstep(-q,q,m),1.);}$pure float $H(float2 a,float2x2 b){float2"
-" c=a*b;return inversesqrt(dot(c,c));}$pure float2 $I(float2 a,float2 b,float"
-" c,float2x2 d){float2 e=1./(b*b+c*c);float2 g=e*a;float h=$H(g,d);float i=("
-".5*h)*(dot(a,g)-1.);float j=((b.x*c)*e.x)*h;return float2(j-i,j+i);}void $J"
-"(inout float2 a,float2x2 b,float2 c,float2 d,float2 e,float2 f){float2 g=f-"
-"d;if(all(greaterThan(g,0..xx)))if(all(greaterThan(f,0..xx))||c.x>0.&&c.y<0."
-"){float2 h=$I(g*e,f,c.x,b);h.y=f.x-c.x<=0.?1.:-h.y;a=min(a,h);}else if(c.y=="
-"0.){float h=((c.x-g.x)-g.y)*$H(e,b);a.x=min(a.x,h);}}void $K(inout float2 a"
-",float2x2 b,float2 c,float4 e,float4 f,float4 g){$J(a,b,c,e.xy,-1..xx,float2"
-"(f.x,g.x));$J(a,b,c,e.zy,float2(1.,-1.),float2(f.y,g.y));$J(a,b,c,e.zw,1..xx"
-",float2(f.z,g.z));$J(a,b,c,e.xw,float2(-1.,1.),float2(f.w,g.w));}$pure half4"
-" analytic_rrect_coverage_fn(float4 a,float4 b,float4 c,float4 d,float4 e,float2"
-" f,float2 g){if(g.x>0.)return half4(1.);else if(g.y>1.){float2 h=min(c.xy,c"
-".zw);float i=min(h.x,h.y)*a.w;float j=(g.y-1.)*a.w;float k=coverage_bias(j)"
-";return half(saturate(j*(i+k))).xxxx;}else{float2x2 h=float2x2(b)*(1./a.w);"
-"float2 i=float2($H(float2(1.,0.),h),$H(float2(0.,1.),h));float2 j=i*(f.x+min"
-"(c.xy,c.zw));float2 k=float2(min(j.x,j.y),-1.);float l;float m;if(g.x>-.95)"
-"{float2 n=i*((c.xy+c.zw)+2.*f.xx);l=min(min(n.x,n.y),1.);m=coverage_bias(l)"
-";}else{float2 n=(2.*f.x)*i;float2 o=n-j;k.y=-max(o.x,o.y);if(f.x>0.){float p"
-"=min(n.x,n.y);float2 q=mix(p.xx,n,greaterThanEqual(o,-.5.xx));l=saturate(max"
-"(q.x,q.y));m=coverage_bias(l);}else l=(m=1.);}$K(k,h,f,c,d,e);float n=min(g"
-".y,0.)*a.w;float o=l*(min(k.x+n,-k.y)+m);return half(saturate(o)).xxxx;}}$pure"
-" half4 per_edge_aa_quad_coverage_fn(float4 a,float4 b){float2 d=min(b.xy,b."
-"zw);float e=min(d.x,d.y)*a.w;return half(saturate(e)).xxxx;}$pure half4 circular_arc_coverage_fn"
-"(float4 a,float3 b,float3 c,float3 e,float f,float4 g){float h=length(a.xy)"
-";half i=half(a.z*(1.-h));half j=saturate(i);half k=half(a.z*(h-a.w));half l"
-"=saturate(k);j*=l;half m=half(saturate(a.z*dot(a.xy,b.xy)+b.z));m*=half(saturate"
-"(a.z*dot(a.xy,c.xy)+c.z));m=m+half(saturate(a.z*dot(a.xy,e.xy)+e.z));half n"
-"=half(a.z*(f-length(a.xy-g.xy)));half o=half(a.z*(f-length(a.xy-g.zw)));half"
-" p=max(n,0.)+max(o,0.);m=saturate(m+p);return(m*j).xxxx;}$pure half4 $L(float2"
-" a,float4 b,half c,half d,sampler2D e){half f;half g;if(c!=0.){half2 h=max("
-"half2(b.xy-a),half2(a-b.zw));f=sample(e,float2(float(d*h.x),.5)).x;g=sample"
-"(e,float2(float(d*h.y),.5)).x;}else{half4 h=half4(half2(b.xy-a),half2(a-b.zw"
-"));f=(1.-sample(e,float2(float(d*h.x),.5)).x)-sample(e,float2(float(d*h.z),"
-".5)).x;g=(1.-sample(e,float2(float(d*h.y),.5)).x)-sample(e,float2(float(d*h"
-".w),.5)).x;}return(f*g).xxxx;}$pure half4 $M(float2 a,float4 b,sampler2D c)"
-"{float d=b.z;float e=b.w;half2 f=half2((a-b.xy)*d);float g=float(length(f))"
-"-e;return sample(c,float2(g,.5)).xxxx;}$pure half4 $N(float2 a,float4 b,half"
-" c,sampler2D d){float2 e=a-b.xy;float2 f=(b.zw-b.xy)*.5;e-=f;half2 g=half2("
-"sign(e));e=abs(e);half2 h=half2(e-(f-float(c)));h=max(h,0.);h*=g;h+=c.xx;half2"
-" i=(2.*c).xx;half2 j=h/i;return sample(d,float2(j)).xxxx;}$pure half4 blur_coverage_fn"
-"(float2 a,float4 b,half2 c,int d,sampler2D e){switch(d){case 0:{return $L(a"
-",b,c.x,c.y,e);}case 2:{return $M(a,b,e);}case 1:{return $N(a,b,c.x,e);}}return"
-" half4(0.);}";
+" c,sampler2D d,sampler2D e,sampler2D f){switch(b){case 1:return sample(d,a/"
+"float2(textureSize(d)));case 2:return sample(e,a/float2(textureSize(e)));case"
+" 3:return sample(f,a/float2(textureSize(f)));default:return sample(c,a/float2"
+"(textureSize(c)));}}$pure half3 $G(float2 a,int b,float2 c,sampler2D d,sampler2D"
+" e,sampler2D f,sampler2D g){half3 h=half3(1.);float2 i;switch(b){case 1:i=a"
+"/float2(textureSize(e));h.x=sample(e,i-c).x;h.y=sample(e,i).x;h.z=sample(e,"
+"i+c).x;break;case 2:i=a/float2(textureSize(f));h.x=sample(f,i-c).x;h.y=sample"
+"(f,i).x;h.z=sample(f,i+c).x;break;case 3:i=a/float2(textureSize(g));h.x=sample"
+"(g,i-c).x;h.y=sample(g,i).x;h.z=sample(g,i+c).x;break;default:i=a/float2(textureSize"
+"(d));h.x=sample(d,i-c).x;h.y=sample(d,i).x;h.z=sample(d,i+c).x;break;}return"
+" h;}$pure half4 bitmap_text_coverage_fn(half4 a,int b){return b==0?a.xxxx:a"
+";}$pure half4 sdf_text_coverage_fn(half a,float2 b,half2 c){half d=7.96875*"
+"(a-.5019608);d-=c.x;half2 e=half2(dFdx(d),dFdy(d));half f=dot(e,e);e=f>=.0001"
+"?e*inversesqrt(f):half2(.7071);float2x2 g=float2x2(dFdx(b),dFdy(b));half2 h"
+"=half2(g*float2(e));half i=.65*length(h);if(c.y>0.)return saturate((d+i)/(2."
+"*i)).xxxx;else return smoothstep(-i,i,d).xxxx;}$pure half4 sdf_text_lcd_coverage_fn"
+"(half2 a,half4 b,float2 c,float d,sampler2D e,sampler2D f,sampler2D g,sampler2D"
+" h){float2x2 i=float2x2(dFdx(c),dFdy(c));float2 j=i*float2(a);half3 k=$G(c,"
+"int(d),j,e,f,g,h);half3 l=half3(7.96875)*(k-half3(.5019608));l-=b.xyz;half2"
+" m=half2(dFdx(l.y),dFdy(l.y));half n=dot(m,m);m=n>=.0001?m*inversesqrt(n):half2"
+"(.7071);half2 o=half2(i*float2(m));half3 p=(.65*length(o)).xxx;if(b.w>0.)return"
+" half4(saturate(l+p/(2.*p)),1.);else return half4(smoothstep(-p,p,l),1.);}$pure"
+" float $H(float2 a,float2x2 b){float2 c=a*b;return inversesqrt(dot(c,c));}$pure"
+" float2 $I(float2 a,float2 b,float c,float2x2 d){float2 e=1./(b*b+c*c);float2"
+" g=e*a;float h=$H(g,d);float i=(.5*h)*(dot(a,g)-1.);float j=((b.x*c)*e.x)*h"
+";return float2(j-i,j+i);}void $J(inout float2 a,float2x2 b,float2 c,float2 d"
+",float2 e,float2 f){float2 g=f-d;if(all(greaterThan(g,0..xx)))if(all(greaterThan"
+"(f,0..xx))||c.x>0.&&c.y<0.){float2 h=$I(g*e,f,c.x,b);h.y=f.x-c.x<=0.?1.:-h."
+"y;a=min(a,h);}else if(c.y==0.){float h=((c.x-g.x)-g.y)*$H(e,b);a.x=min(a.x,"
+"h);}}void $K(inout float2 a,float2x2 b,float2 c,float4 e,float4 f,float4 g)"
+"{$J(a,b,c,e.xy,-1..xx,float2(f.x,g.x));$J(a,b,c,e.zy,float2(1.,-1.),float2("
+"f.y,g.y));$J(a,b,c,e.zw,1..xx,float2(f.z,g.z));$J(a,b,c,e.xw,float2(-1.,1.)"
+",float2(f.w,g.w));}$pure half4 analytic_rrect_coverage_fn(float4 a,float4 b"
+",float4 c,float4 d,float4 e,float2 f,float2 g){if(g.x>0.)return half4(1.);else"
+" if(g.y>1.){float2 h=min(c.xy,c.zw);float i=min(h.x,h.y)*a.w;float j=(g.y-1."
+")*a.w;float k=coverage_bias(j);return half(saturate(j*(i+k))).xxxx;}else{float2x2"
+" h=float2x2(b)*(1./a.w);float2 i=float2($H(float2(1.,0.),h),$H(float2(0.,1."
+"),h));float2 j=i*(f.x+min(c.xy,c.zw));float2 k=float2(min(j.x,j.y),-1.);float"
+" l;float m;if(g.x>-.95){float2 n=i*((c.xy+c.zw)+2.*f.xx);l=min(min(n.x,n.y)"
+",1.);m=coverage_bias(l);}else{float2 n=(2.*f.x)*i;float2 o=n-j;k.y=-max(o.x"
+",o.y);if(f.x>0.){float p=min(n.x,n.y);float2 q=mix(p.xx,n,greaterThanEqual("
+"o,-.5.xx));l=saturate(max(q.x,q.y));m=coverage_bias(l);}else l=(m=1.);}$K(k"
+",h,f,c,d,e);float n=min(g.y,0.)*a.w;float o=l*(min(k.x+n,-k.y)+m);return half"
+"(saturate(o)).xxxx;}}$pure half4 per_edge_aa_quad_coverage_fn(float4 a,float4"
+" b){float2 d=min(b.xy,b.zw);float e=min(d.x,d.y)*a.w;return half(saturate(e"
+")).xxxx;}$pure half4 circular_arc_coverage_fn(float4 a,float3 b,float3 c,float3"
+" e,float f,float4 g){float h=length(a.xy);half i=half(a.z*(1.-h));half j=saturate"
+"(i);half k=half(a.z*(h-a.w));half l=saturate(k);j*=l;half m=half(saturate(a"
+".z*dot(a.xy,b.xy)+b.z));m*=half(saturate(a.z*dot(a.xy,c.xy)+c.z));m=m+half("
+"saturate(a.z*dot(a.xy,e.xy)+e.z));half n=half(a.z*(f-length(a.xy-g.xy)));half"
+" o=half(a.z*(f-length(a.xy-g.zw)));half p=max(n,0.)+max(o,0.);m=saturate(m+"
+"p);return(m*j).xxxx;}$pure half4 $L(float2 a,float4 b,half c,half d,sampler2D"
+" e){half f;half g;if(c!=0.){half2 h=max(half2(b.xy-a),half2(a-b.zw));f=sample"
+"(e,float2(float(d*h.x),.5)).x;g=sample(e,float2(float(d*h.y),.5)).x;}else{half4"
+" h=half4(half2(b.xy-a),half2(a-b.zw));f=(1.-sample(e,float2(float(d*h.x),.5"
+")).x)-sample(e,float2(float(d*h.z),.5)).x;g=(1.-sample(e,float2(float(d*h.y"
+"),.5)).x)-sample(e,float2(float(d*h.w),.5)).x;}return(f*g).xxxx;}$pure half4"
+" $M(float2 a,float4 b,sampler2D c){float d=b.z;float e=b.w;half2 f=half2((a"
+"-b.xy)*d);float g=float(length(f))-e;return sample(c,float2(g,.5)).xxxx;}$pure"
+" half4 $N(float2 a,float4 b,half c,sampler2D d){float2 e=a-b.xy;float2 f=(b"
+".zw-b.xy)*.5;e-=f;half2 g=half2(sign(e));e=abs(e);half2 h=half2(e-(f-float("
+"c)));h=max(h,0.);h*=g;h+=c.xx;half2 i=(2.*c).xx;half2 j=h/i;return sample(d"
+",float2(j)).xxxx;}$pure half4 blur_coverage_fn(float2 a,float4 b,half2 c,int"
+" d,sampler2D e){switch(d){case 0:{return $L(a,b,c.x,c.y,e);}case 2:{return $M"
+"(a,b,e);}case 1:{return $N(a,b,c.x,e);}}return half4(0.);}";
diff --git a/src/sksl/generated/sksl_graphite_frag.unoptimized.sksl b/src/sksl/generated/sksl_graphite_frag.unoptimized.sksl
index 1823bff..e3aa725 100644
--- a/src/sksl/generated/sksl_graphite_frag.unoptimized.sksl
+++ b/src/sksl/generated/sksl_graphite_frag.unoptimized.sksl
@@ -364,53 +364,57 @@
 ".z,.875))).x,1.);return color*sample(s,float2(half2(coords.w,.125))).x;}$pure"
 " half4 sk_gaussian_colorfilter(half4 inColor){half factor=1.-inColor.w;factor"
 "=exp((-factor*factor)*4.)-.018;return factor.xxxx;}$pure half4 sample_indexed_atlas"
-"(float2 textureCoords,int atlasIndex,sampler2D atlas0,sampler2D atlas1,sampler2D"
-" atlas2,sampler2D atlas3){switch(atlasIndex){case 1:return sample(atlas1,textureCoords"
-");case 2:return sample(atlas2,textureCoords);case 3:return sample(atlas3,textureCoords"
-");default:return sample(atlas0,textureCoords);}}$pure half3 $sample_indexed_atlas_lcd"
-"(float2 textureCoords,int atlasIndex,float2 offset,sampler2D atlas0,sampler2D"
-" atlas1,sampler2D atlas2,sampler2D atlas3){half3 distance=half3(1.);switch("
-"atlasIndex){case 1:distance.x=sample(atlas1,textureCoords-offset).x;distance"
-".y=sample(atlas1,textureCoords).x;distance.z=sample(atlas1,textureCoords+offset"
-").x;break;case 2:distance.x=sample(atlas2,textureCoords-offset).x;distance."
-"y=sample(atlas2,textureCoords).x;distance.z=sample(atlas2,textureCoords+offset"
-").x;break;case 3:distance.x=sample(atlas3,textureCoords-offset).x;distance."
-"y=sample(atlas3,textureCoords).x;distance.z=sample(atlas3,textureCoords+offset"
-").x;break;default:distance.x=sample(atlas0,textureCoords-offset).x;distance"
-".y=sample(atlas0,textureCoords).x;distance.z=sample(atlas0,textureCoords+offset"
-").x;break;}return distance;}$pure half4 bitmap_text_coverage_fn(half4 texColor"
-",int maskFormat){return maskFormat==$kMaskFormatA8?texColor.xxxx:texColor;}"
-"$pure half4 sdf_text_coverage_fn(half texColor,float2 unormTexCoords,half2 gammaParams"
-"){half dist=7.96875*(texColor-.5019608);dist-=gammaParams.x;half2 dist_grad"
-"=half2(dFdx(dist),dFdy(dist));half dg_len2=dot(dist_grad,dist_grad);dist_grad"
-"=dg_len2>=.0001?dist_grad*inversesqrt(dg_len2):half2(.7071);float2x2 jacobian"
-"=float2x2(dFdx(unormTexCoords),dFdy(unormTexCoords));half2 grad=half2(jacobian"
-"*float2(dist_grad));half approxFragWidth=.65*length(grad);if(gammaParams.y>"
-"0.)return saturate((dist+approxFragWidth)/(2.*approxFragWidth)).xxxx;else return"
-" smoothstep(-approxFragWidth,approxFragWidth,dist).xxxx;}$pure half4 sdf_text_lcd_coverage_fn"
-"(half2 pixelGeometryDelta,half4 gammaParams,float2 unormTexCoords,float2 textureCoords"
-",float texIndex,sampler2D atlas0,sampler2D atlas1,sampler2D atlas2,sampler2D"
-" atlas3){float2x2 jacobian=float2x2(dFdx(unormTexCoords),dFdy(unormTexCoords"
-"));float2 offset=jacobian*float2(pixelGeometryDelta);half3 distance=$sample_indexed_atlas_lcd"
-"(textureCoords,int(texIndex),offset,atlas0,atlas1,atlas2,atlas3);half3 dist"
-"=half3(7.96875)*(distance-half3(.5019608));dist-=gammaParams.xyz;half2 dist_grad"
-"=half2(dFdx(dist.y),dFdy(dist.y));half dg_len2=dot(dist_grad,dist_grad);dist_grad"
-"=dg_len2>=.0001?dist_grad*inversesqrt(dg_len2):half2(.7071);half2 grad=half2"
-"(jacobian*float2(dist_grad));half3 approxFragWidth=(.65*length(grad)).xxx;if"
-"(gammaParams.w>0.)return half4(saturate(dist+approxFragWidth/(2.*approxFragWidth"
-")),1.);else return half4(smoothstep(-approxFragWidth,approxFragWidth,dist),"
-"1.);}$pure float $inverse_grad_len(float2 localGrad,float2x2 jacobian){float2"
-" devGrad=localGrad*jacobian;return inversesqrt(dot(devGrad,devGrad));}$pure"
-" float2 $elliptical_distance(float2 uv,float2 radii,float strokeRadius,float2x2"
-" jacobian){float2 invR2=1./(radii*radii+strokeRadius*strokeRadius);float2 normUV"
-"=invR2*uv;float invGradLength=$inverse_grad_len(normUV,jacobian);float f=(.5"
-"*invGradLength)*(dot(uv,normUV)-1.);float width=((radii.x*strokeRadius)*invR2"
-".x)*invGradLength;return float2(width-f,width+f);}void $corner_distance(inout"
-" float2 dist,float2x2 jacobian,float2 strokeParams,float2 cornerEdgeDist,float2"
-" xyFlip,float2 radii){float2 uv=radii-cornerEdgeDist;if(all(greaterThan(uv,"
-"0..xx)))if(all(greaterThan(radii,0..xx))||strokeParams.x>0.&&strokeParams.y"
-"<0.){float2 d=$elliptical_distance(uv*xyFlip,radii,strokeParams.x,jacobian)"
-";d.y=radii.x-strokeParams.x<=0.?1.:-d.y;dist=min(dist,d);}else if(strokeParams"
+"(float2 unormTexCoords,int atlasIndex,sampler2D atlas0,sampler2D atlas1,sampler2D"
+" atlas2,sampler2D atlas3){switch(atlasIndex){case 1:return sample(atlas1,unormTexCoords"
+"/float2(textureSize(atlas1)));case 2:return sample(atlas2,unormTexCoords/float2"
+"(textureSize(atlas2)));case 3:return sample(atlas3,unormTexCoords/float2(textureSize"
+"(atlas3)));default:return sample(atlas0,unormTexCoords/float2(textureSize(atlas0"
+")));}}$pure half3 $sample_indexed_atlas_lcd(float2 unormTexCoords,int atlasIndex"
+",float2 offset,sampler2D atlas0,sampler2D atlas1,sampler2D atlas2,sampler2D"
+" atlas3){half3 distance=half3(1.);float2 textureCoords;switch(atlasIndex){case"
+" 1:textureCoords=unormTexCoords/float2(textureSize(atlas1));distance.x=sample"
+"(atlas1,textureCoords-offset).x;distance.y=sample(atlas1,textureCoords).x;distance"
+".z=sample(atlas1,textureCoords+offset).x;break;case 2:textureCoords=unormTexCoords"
+"/float2(textureSize(atlas2));distance.x=sample(atlas2,textureCoords-offset)"
+".x;distance.y=sample(atlas2,textureCoords).x;distance.z=sample(atlas2,textureCoords"
+"+offset).x;break;case 3:textureCoords=unormTexCoords/float2(textureSize(atlas3"
+"));distance.x=sample(atlas3,textureCoords-offset).x;distance.y=sample(atlas3"
+",textureCoords).x;distance.z=sample(atlas3,textureCoords+offset).x;break;default"
+":textureCoords=unormTexCoords/float2(textureSize(atlas0));distance.x=sample"
+"(atlas0,textureCoords-offset).x;distance.y=sample(atlas0,textureCoords).x;distance"
+".z=sample(atlas0,textureCoords+offset).x;break;}return distance;}$pure half4"
+" bitmap_text_coverage_fn(half4 texColor,int maskFormat){return maskFormat=="
+"$kMaskFormatA8?texColor.xxxx:texColor;}$pure half4 sdf_text_coverage_fn(half"
+" texColor,float2 unormTexCoords,half2 gammaParams){half dist=7.96875*(texColor"
+"-.5019608);dist-=gammaParams.x;half2 dist_grad=half2(dFdx(dist),dFdy(dist))"
+";half dg_len2=dot(dist_grad,dist_grad);dist_grad=dg_len2>=.0001?dist_grad*inversesqrt"
+"(dg_len2):half2(.7071);float2x2 jacobian=float2x2(dFdx(unormTexCoords),dFdy"
+"(unormTexCoords));half2 grad=half2(jacobian*float2(dist_grad));half approxFragWidth"
+"=.65*length(grad);if(gammaParams.y>0.)return saturate((dist+approxFragWidth"
+")/(2.*approxFragWidth)).xxxx;else return smoothstep(-approxFragWidth,approxFragWidth"
+",dist).xxxx;}$pure half4 sdf_text_lcd_coverage_fn(half2 pixelGeometryDelta,"
+"half4 gammaParams,float2 unormTexCoords,float texIndex,sampler2D atlas0,sampler2D"
+" atlas1,sampler2D atlas2,sampler2D atlas3){float2x2 jacobian=float2x2(dFdx("
+"unormTexCoords),dFdy(unormTexCoords));float2 offset=jacobian*float2(pixelGeometryDelta"
+");half3 distance=$sample_indexed_atlas_lcd(unormTexCoords,int(texIndex),offset"
+",atlas0,atlas1,atlas2,atlas3);half3 dist=half3(7.96875)*(distance-half3(.5019608"
+"));dist-=gammaParams.xyz;half2 dist_grad=half2(dFdx(dist.y),dFdy(dist.y));half"
+" dg_len2=dot(dist_grad,dist_grad);dist_grad=dg_len2>=.0001?dist_grad*inversesqrt"
+"(dg_len2):half2(.7071);half2 grad=half2(jacobian*float2(dist_grad));half3 approxFragWidth"
+"=(.65*length(grad)).xxx;if(gammaParams.w>0.)return half4(saturate(dist+approxFragWidth"
+"/(2.*approxFragWidth)),1.);else return half4(smoothstep(-approxFragWidth,approxFragWidth"
+",dist),1.);}$pure float $inverse_grad_len(float2 localGrad,float2x2 jacobian"
+"){float2 devGrad=localGrad*jacobian;return inversesqrt(dot(devGrad,devGrad)"
+");}$pure float2 $elliptical_distance(float2 uv,float2 radii,float strokeRadius"
+",float2x2 jacobian){float2 invR2=1./(radii*radii+strokeRadius*strokeRadius)"
+";float2 normUV=invR2*uv;float invGradLength=$inverse_grad_len(normUV,jacobian"
+");float f=(.5*invGradLength)*(dot(uv,normUV)-1.);float width=((radii.x*strokeRadius"
+")*invR2.x)*invGradLength;return float2(width-f,width+f);}void $corner_distance"
+"(inout float2 dist,float2x2 jacobian,float2 strokeParams,float2 cornerEdgeDist"
+",float2 xyFlip,float2 radii){float2 uv=radii-cornerEdgeDist;if(all(greaterThan"
+"(uv,0..xx)))if(all(greaterThan(radii,0..xx))||strokeParams.x>0.&&strokeParams"
+".y<0.){float2 d=$elliptical_distance(uv*xyFlip,radii,strokeParams.x,jacobian"
+");d.y=radii.x-strokeParams.x<=0.?1.:-d.y;dist=min(dist,d);}else if(strokeParams"
 ".y==0.){float bevelDist=((strokeParams.x-uv.x)-uv.y)*$inverse_grad_len(xyFlip"
 ",jacobian);dist.x=min(dist.x,bevelDist);}}void $corner_distances(inout float2"
 " d,float2x2 J,float2 stroke,float4 edgeDists,float4 xRadii,float4 yRadii){$corner_distance"
diff --git a/src/sksl/generated/sksl_graphite_vert.minified.sksl b/src/sksl/generated/sksl_graphite_vert.minified.sksl
index e858ac9..a6bd6eb 100644
--- a/src/sksl/generated/sksl_graphite_vert.minified.sksl
+++ b/src/sksl/generated/sksl_graphite_vert.minified.sksl
@@ -117,16 +117,15 @@
 " s+=a.xy*b.w;float3 v=j*float3(s,1.);float3 w=j*float3(r,1.);float2 x=v.xy-"
 "w.xy;if(x!=0..xx){x=normalize(x);v.xy+=a.z*x;if(a.z>0.)x*=u;else x*=u*c.y;}"
 "k=float4(x,c.xy);if(c.z>0.){l=e;m=f;n=0..xxx;}else{l=e;m=float3(0.,0.,1.);n"
-"=f;}o=h;p=g;q=s;return float4(v.xy,i,1.);}float4 text_vertex_fn(float2 a,sampler2D"
-" b,float4x4 c,float4x4 d,float2 e,float2 f,float2 g,float h,float i,out float2"
-" j,out float2 k,out float2 l){a*=e;float2 m=h*a+g;float4 n=c*float4(m,0.,1."
-");l=(inverse(d)*n).xy;j=a+f;k=j/float2(textureSize(b));return float4(n.xy,i"
-"*n.w,n.w);}float4 coverage_mask_vertex_fn(float2 a,float3x3 b,float4 c,float4"
-" d,float2 e,float f,float3x3 g,out float4 h,out float2 i,out half j,out float2"
-" k){i=mix(c.xy,c.zw,a);float3 l=b*float3(i+e,1.);float3 m=inverse(g)*l;k=m."
-"xy/m.z;if(all(lessThanEqual(d.xy,d.zw))){h=d;j=0.;}else{h=d.zwxy;j=1.;}return"
-" float4(l.xy,f*l.z,l.z);}float4 cover_bounds_vertex_fn(float2 a,float4 b,float"
-" c,float3x3 d,out float2 e){if(all(lessThanEqual(b.xy,b.zw))){a=mix(b.xy,b."
-"zw,a);float3 f=d*float3(a,1.);e=a;return float4(f.xy,c*f.z,f.z);}else{a=mix"
-"(b.zw,b.xy,a);float3 f=inverse(d)*float3(a,1.);float g=1./f.z;e=f.xy*g;return"
-" float4(a*g,c*g,g);}}";
+"=f;}o=h;p=g;q=s;return float4(v.xy,i,1.);}float4 text_vertex_fn(float2 a,float4x4"
+" b,float4x4 c,float2 d,float2 e,float2 f,float g,float h,out float2 i,out float2"
+" j){a*=d;float2 k=g*a+f;float4 l=b*float4(k,0.,1.);j=(inverse(c)*l).xy;i=a+"
+"e;return float4(l.xy,h*l.w,l.w);}float4 coverage_mask_vertex_fn(float2 a,float3x3"
+" b,float4 c,float4 d,float2 e,float f,float3x3 g,out float4 h,out float2 i,"
+"out half j,out float2 k){i=mix(c.xy,c.zw,a);float3 l=b*float3(i+e,1.);float3"
+" m=inverse(g)*l;k=m.xy/m.z;if(all(lessThanEqual(d.xy,d.zw))){h=d;j=0.;}else"
+"{h=d.zwxy;j=1.;}return float4(l.xy,f*l.z,l.z);}float4 cover_bounds_vertex_fn"
+"(float2 a,float4 b,float c,float3x3 d,out float2 e){if(all(lessThanEqual(b."
+"xy,b.zw))){a=mix(b.xy,b.zw,a);float3 f=d*float3(a,1.);e=a;return float4(f.xy"
+",c*f.z,f.z);}else{a=mix(b.zw,b.xy,a);float3 f=inverse(d)*float3(a,1.);float"
+" g=1./f.z;e=f.xy*g;return float4(a*g,c*g,g);}}";
diff --git a/src/sksl/generated/sksl_graphite_vert.unoptimized.sksl b/src/sksl/generated/sksl_graphite_vert.unoptimized.sksl
index f4bdceb..f27487c 100644
--- a/src/sksl/generated/sksl_graphite_vert.unoptimized.sksl
+++ b/src/sksl/generated/sksl_graphite_vert.unoptimized.sksl
@@ -225,27 +225,25 @@
 ";unionPlane=0..xxx;}else{clipPlane=fragClipPlane0;isectPlane=float3(0.,0.,1."
 ");unionPlane=fragClipPlane1;}roundCapRadius=inRoundCapRadius;roundCapPos=inRoundCapPos"
 ";stepLocalCoords=localPos;return float4(devPos.xy,depth,1.);}float4 text_vertex_fn"
-"(float2 baseCoords,sampler2D text_atlas_0,float4x4 maskToDevice,float4x4 localToDevice"
-",float2 size,float2 uvPos,float2 xyPos,float strikeToSourceScale,float depth"
-",out float2 unormTexCoords,out float2 textureCoords,out float2 stepLocalCoords"
-"){baseCoords*=size;float2 subRunCoords=strikeToSourceScale*baseCoords+xyPos"
-";float4 position=maskToDevice*float4(subRunCoords,0.,1.);stepLocalCoords=(inverse"
-"(localToDevice)*position).xy;unormTexCoords=baseCoords+uvPos;textureCoords="
-"unormTexCoords/float2(textureSize(text_atlas_0));return float4(position.xy,"
-"depth*position.w,position.w);}float4 coverage_mask_vertex_fn(float2 quadCoords"
-",float3x3 maskToDeviceRemainder,float4 drawBounds,float4 maskBoundsIn,float2"
-" deviceOrigin,float depth,float3x3 localToDevice,out float4 maskBounds,out float2"
-" textureCoords,out half invert,out float2 stepLocalCoords){textureCoords=mix"
-"(drawBounds.xy,drawBounds.zw,quadCoords);float3 drawCoords=maskToDeviceRemainder"
-"*float3(textureCoords+deviceOrigin,1.);float3 localCoords=inverse(localToDevice"
-")*drawCoords;stepLocalCoords=localCoords.xy/localCoords.z;if(all(lessThanEqual"
-"(maskBoundsIn.xy,maskBoundsIn.zw))){maskBounds=maskBoundsIn;invert=0.;}else"
-"{maskBounds=maskBoundsIn.zwxy;invert=1.;}return float4(drawCoords.xy,depth*"
-"drawCoords.z,drawCoords.z);}float4 cover_bounds_vertex_fn(float2 corner,float4"
-" bounds,float depth,float3x3 matrix,out float2 stepLocalCoords){if(all(lessThanEqual"
-"(bounds.xy,bounds.zw))){corner=mix(bounds.xy,bounds.zw,corner);float3 devCorner"
-"=matrix*float3(corner,1.);stepLocalCoords=corner;return float4(devCorner.xy"
-",depth*devCorner.z,devCorner.z);}else{corner=mix(bounds.zw,bounds.xy,corner"
-");float3 localCoords=inverse(matrix)*float3(corner,1.);float invW=1./localCoords"
-".z;stepLocalCoords=localCoords.xy*invW;return float4(corner*invW,depth*invW"
-",invW);}}";
+"(float2 baseCoords,float4x4 maskToDevice,float4x4 localToDevice,float2 size"
+",float2 uvPos,float2 xyPos,float strikeToSourceScale,float depth,out float2"
+" unormTexCoords,out float2 stepLocalCoords){baseCoords*=size;float2 subRunCoords"
+"=strikeToSourceScale*baseCoords+xyPos;float4 position=maskToDevice*float4(subRunCoords"
+",0.,1.);stepLocalCoords=(inverse(localToDevice)*position).xy;unormTexCoords"
+"=baseCoords+uvPos;return float4(position.xy,depth*position.w,position.w);}float4"
+" coverage_mask_vertex_fn(float2 quadCoords,float3x3 maskToDeviceRemainder,float4"
+" drawBounds,float4 maskBoundsIn,float2 deviceOrigin,float depth,float3x3 localToDevice"
+",out float4 maskBounds,out float2 textureCoords,out half invert,out float2 stepLocalCoords"
+"){textureCoords=mix(drawBounds.xy,drawBounds.zw,quadCoords);float3 drawCoords"
+"=maskToDeviceRemainder*float3(textureCoords+deviceOrigin,1.);float3 localCoords"
+"=inverse(localToDevice)*drawCoords;stepLocalCoords=localCoords.xy/localCoords"
+".z;if(all(lessThanEqual(maskBoundsIn.xy,maskBoundsIn.zw))){maskBounds=maskBoundsIn"
+";invert=0.;}else{maskBounds=maskBoundsIn.zwxy;invert=1.;}return float4(drawCoords"
+".xy,depth*drawCoords.z,drawCoords.z);}float4 cover_bounds_vertex_fn(float2 corner"
+",float4 bounds,float depth,float3x3 matrix,out float2 stepLocalCoords){if(all"
+"(lessThanEqual(bounds.xy,bounds.zw))){corner=mix(bounds.xy,bounds.zw,corner"
+");float3 devCorner=matrix*float3(corner,1.);stepLocalCoords=corner;return float4"
+"(devCorner.xy,depth*devCorner.z,devCorner.z);}else{corner=mix(bounds.zw,bounds"
+".xy,corner);float3 localCoords=inverse(matrix)*float3(corner,1.);float invW"
+"=1./localCoords.z;stepLocalCoords=localCoords.xy*invW;return float4(corner*"
+"invW,depth*invW,invW);}}";
diff --git a/src/sksl/sksl_graphite_frag.sksl b/src/sksl/sksl_graphite_frag.sksl
index c42f37e..078632e 100644
--- a/src/sksl/sksl_graphite_frag.sksl
+++ b/src/sksl/sksl_graphite_frag.sksl
@@ -1361,7 +1361,7 @@
     return half4(factor);
 }
 
-$pure half4 sample_indexed_atlas(float2 textureCoords,
+$pure half4 sample_indexed_atlas(float2 unormTexCoords, // unormalized texture coords
                                  int atlasIndex,
                                  sampler2D atlas0,
                                  sampler2D atlas1,
@@ -1369,17 +1369,17 @@
                                  sampler2D atlas3) {
     switch (atlasIndex) {
         case 1:
-            return sample(atlas1, textureCoords);
+            return sample(atlas1, unormTexCoords / float2(textureSize(atlas1)));
         case 2:
-            return sample(atlas2, textureCoords);
+            return sample(atlas2, unormTexCoords / float2(textureSize(atlas2)));
         case 3:
-            return sample(atlas3, textureCoords);
+            return sample(atlas3, unormTexCoords / float2(textureSize(atlas3)));
         default:
-            return sample(atlas0, textureCoords);
+            return sample(atlas0, unormTexCoords / float2(textureSize(atlas0)));
     }
 }
 
-$pure half3 $sample_indexed_atlas_lcd(float2 textureCoords,
+$pure half3 $sample_indexed_atlas_lcd(float2 unormTexCoords, // unormalized texture coords
                                       int atlasIndex,
                                       float2 offset,
                                       sampler2D atlas0,
@@ -1387,23 +1387,28 @@
                                       sampler2D atlas2,
                                       sampler2D atlas3) {
     half3 distance = half3(1);
+    float2 textureCoords;
     switch (atlasIndex) {
         case 1:
+            textureCoords = unormTexCoords / float2(textureSize(atlas1));
             distance.x = sample(atlas1, textureCoords - offset).r;
             distance.y = sample(atlas1, textureCoords).r;
             distance.z = sample(atlas1, textureCoords + offset).r;
             break;
         case 2:
+            textureCoords = unormTexCoords / float2(textureSize(atlas2));
             distance.x = sample(atlas2, textureCoords - offset).r;
             distance.y = sample(atlas2, textureCoords).r;
             distance.z = sample(atlas2, textureCoords + offset).r;
             break;
         case 3:
+            textureCoords = unormTexCoords / float2(textureSize(atlas3));
             distance.x = sample(atlas3, textureCoords - offset).r;
             distance.y = sample(atlas3, textureCoords).r;
             distance.z = sample(atlas3, textureCoords + offset).r;
             break;
         default:
+            textureCoords = unormTexCoords / float2(textureSize(atlas0));
             distance.x = sample(atlas0, textureCoords - offset).r;
             distance.y = sample(atlas0, textureCoords).r;
             distance.z = sample(atlas0, textureCoords + offset).r;
@@ -1468,7 +1473,6 @@
 $pure half4 sdf_text_lcd_coverage_fn(half2 pixelGeometryDelta,
                                      half4 gammaParams,
                                      float2 unormTexCoords, // unormalized texture coords
-                                     float2 textureCoords,
                                      float texIndex,
                                      sampler2D atlas0,
                                      sampler2D atlas1,
@@ -1481,7 +1485,7 @@
     float2x2 jacobian = float2x2(dFdx(unormTexCoords), dFdy(unormTexCoords));
     float2 offset = jacobian * float2(pixelGeometryDelta);
 
-    half3 distance = $sample_indexed_atlas_lcd(textureCoords,
+    half3 distance = $sample_indexed_atlas_lcd(unormTexCoords,
                                                int(texIndex),
                                                offset,
                                                atlas0,
diff --git a/src/sksl/sksl_graphite_vert.sksl b/src/sksl/sksl_graphite_vert.sksl
index 2d42359..ea92779 100644
--- a/src/sksl/sksl_graphite_vert.sksl
+++ b/src/sksl/sksl_graphite_vert.sksl
@@ -1092,7 +1092,6 @@
 
 float4 text_vertex_fn(float2 baseCoords,
                       // Uniforms
-                      sampler2D text_atlas_0,
                       float4x4 maskToDevice,
                       float4x4 localToDevice,
                       // Instance Attributes
@@ -1103,7 +1102,6 @@
                       float depth,
                       // Varyings
                       out float2 unormTexCoords,
-                      out float2 textureCoords,
                       // Render Step
                       out float2 stepLocalCoords) {
     baseCoords.xy *= float2(size);
@@ -1120,9 +1118,6 @@
     stepLocalCoords = (inverse(localToDevice) * position).xy;
 
     unormTexCoords = baseCoords + uvPos;
-    // Match the previous CPU-side behavior where it is assumed that all text atlases are of the
-    // same size.
-    textureCoords = unormTexCoords / float2(textureSize(text_atlas_0));
 
     return float4(position.xy, depth*position.w, position.w);
 }