diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp
index c317b7420abd22c1688e9278b5ea4b1cdc8004ab..9720bcdfc51407eacb694ab60518adaa24aaa5a6 100644
--- a/indra/llrender/llimagegl.cpp
+++ b/indra/llrender/llimagegl.cpp
@@ -1099,15 +1099,9 @@ void LLImageGL::postAddToAtlas()
 	stop_glerror();	
 }
 
-// Equivalent to calling glSetSubImage2D(target, miplevel, x_offset, y_offset, width, height, pixformat, pixtype, src)
-// However, instead there are multiple calls to glSetSubImage2D on smaller slices of the image
-void subImageLines(U32 target, S32 miplevel, S32 x_offset, S32 y_offset, S32 width, S32 height, U32 pixformat, U32 pixtype, const U8* src)
+U32 type_width_from_pixtype(U32 pixtype)
 {
-    LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
-
-    U32 components = LLImageGL::dataFormatComponents(pixformat);
     U32 type_width = 0;
-
     switch (pixtype)
     {
     case GL_UNSIGNED_BYTE:
@@ -1127,12 +1121,22 @@ void subImageLines(U32 target, S32 miplevel, S32 x_offset, S32 y_offset, S32 wid
     default:
         LL_ERRS() << "Unknown type: " << pixtype << LL_ENDL;
     }
+    return type_width;
+}
+
+// Equivalent to calling glSetSubImage2D(target, miplevel, x_offset, y_offset, width, height, pixformat, pixtype, src), assuming the total width of the image is data_width
+// However, instead there are multiple calls to glSetSubImage2D on smaller slices of the image
+void subImageLines(U32 target, S32 miplevel, S32 x_offset, S32 y_offset, S32 width, S32 height, U32 pixformat, U32 pixtype, const U8* src, S32 data_width)
+{
+    LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
 
-    const U32 line_width = width * components * type_width;
+    U32 components = LLImageGL::dataFormatComponents(pixformat);
+    U32 type_width = type_width_from_pixtype(pixtype);
+
+    const U32 line_width = data_width * components * type_width;
     const U32 y_offset_end = y_offset + height;
-    for (U32 y = y_offset; y < y_offset_end; ++y)
+    for (U32 y_pos = y_offset; y_pos < y_offset_end; ++y_pos)
     {
-        const S32 y_pos = y + y_offset;
         glTexSubImage2D(target, miplevel, x_offset, y_pos, width, 1, pixformat, pixtype, src);
         src += line_width;
     }
@@ -1212,29 +1216,29 @@ BOOL LLImageGL::setSubImage(const U8* datap, S32 data_width, S32 data_height, S3
 			stop_glerror();
 		}
 
-		datap += (y_pos * data_width + x_pos) * getComponents();
+		const U8* sub_datap = datap + (y_pos * data_width + x_pos) * getComponents();
 		// Update the GL texture
 		BOOL res = gGL.getTexUnit(0)->bindManual(mBindTarget, tex_name);
 		if (!res) LL_ERRS() << "LLImageGL::setSubImage(): bindTexture failed" << LL_ENDL;
 		stop_glerror();
 
-//#if LL_DARWIN
-//        const bool use_sub_image = false;
-//#else
-//        const bool use_sub_image = !isCompressed();
-//#endif
-        //if (!use_sub_image)
+#if LL_DARWIN
+        const bool use_sub_image = false;
+#else
+        const bool use_sub_image = !isCompressed();
+#endif
+        if (!use_sub_image)
         {
             // *TODO: Why does this work here, in setSubImage, but not in
             // setManualImage? Maybe because it only gets called with the
             // dimensions of the full image?  Or because the image is never
             // compressed?
-            glTexSubImage2D(mTarget, 0, x_pos, y_pos, width, height, mFormatPrimary, mFormatType, datap);
+            glTexSubImage2D(mTarget, 0, x_pos, y_pos, width, height, mFormatPrimary, mFormatType, sub_datap);
+        }
+        else
+        {
+            subImageLines(mTarget, 0, x_pos, y_pos, width, height, mFormatPrimary, mFormatType, sub_datap, data_width);
         }
-        //else
-        //{
-        //    subImageLines(mTarget, 0, x_pos, y_pos, width, height, mFormatPrimary, mFormatType, datap);
-        //}
 		gGL.getTexUnit(0)->disable();
 		stop_glerror();
 
@@ -1511,7 +1515,7 @@ void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 widt
             if (src)
             {
                 LL_PROFILE_ZONE_NAMED("glTexImage2D copy");
-                subImageLines(target, miplevel, 0, 0, width, height, pixformat, pixtype, src);
+                subImageLines(target, miplevel, 0, 0, width, height, pixformat, pixtype, src, width);
             }
         }
         alloc_tex_image(width, height, pixformat);
diff --git a/indra/newview/llmaterialeditor.cpp b/indra/newview/llmaterialeditor.cpp
index aa7cfbf26dfc98157a1266619c5c0a858a7ccb07..32547589bd8ff674fb9d133e1e26d2008c8a76f3 100644
--- a/indra/newview/llmaterialeditor.cpp
+++ b/indra/newview/llmaterialeditor.cpp
@@ -2046,6 +2046,15 @@ void LLMaterialEditor::loadMaterial(const tinygltf::Model &model_in, const std::
 
     setFromGltfMetaData(filename, model_in, index);
 
+    if (getDoubleSided())
+    {
+        // SL-19392 Double sided materials double the number of pixels that must be rasterized,
+        // and a great many tools that export GLTF simply leave double sided enabled whether
+        // or not it is necessary.
+        LL_DEBUGS("MaterialEditor") << "Defaulting Double Sided to disabled on import" << LL_ENDL;
+        setDoubleSided(false);
+    }
+
     markChangesUnsaved(U32_MAX);
 
     if (open_floater)