diff --git a/indra/llrender/llglslshader.cpp b/indra/llrender/llglslshader.cpp
index 78fd4e98d7ba40bfe7251a5b92e4da5ad53b79e4..39ad9b9e9b4c6808d6034a53e08f82f5e98e83fe 100644
--- a/indra/llrender/llglslshader.cpp
+++ b/indra/llrender/llglslshader.cpp
@@ -88,7 +88,12 @@ LLShaderFeatures::LLShaderFeatures()
 // LLGLSL Shader implementation
 //===============================
 LLGLSLShader::LLGLSLShader()
-	: mProgramObject(0), mActiveTextureChannels(0), mShaderLevel(0), mShaderGroup(SG_DEFAULT), mUniformsDirty(FALSE)
+	: mProgramObject(0), 
+	  mAttributeMask(0),
+	  mActiveTextureChannels(0), 
+	  mShaderLevel(0), 
+	  mShaderGroup(SG_DEFAULT), 
+	  mUniformsDirty(FALSE)
 {
 
 }
@@ -285,6 +290,8 @@ BOOL LLGLSLShader::mapAttributes(const vector<string> * attributes)
 	if (res)
 	{ //read back channel locations
 
+		mAttributeMask = 0;
+
 		//read back reserved channels first
 		for (U32 i = 0; i < LLShaderMgr::instance()->mReservedAttribs.size(); i++)
 		{
@@ -293,6 +300,7 @@ BOOL LLGLSLShader::mapAttributes(const vector<string> * attributes)
 			if (index != -1)
 			{
 				mAttribute[i] = index;
+				mAttributeMask |= 1 << i;
 				LL_DEBUGS("ShaderLoading") << "Attribute " << name << " assigned to channel " << index << LL_ENDL;
 			}
 		}
diff --git a/indra/llrender/llglslshader.h b/indra/llrender/llglslshader.h
index 2af74c20ff30db5b6d65a15d1f73c1ee166d7fb5..725a7e2573f0425ecf4ab55d37dc61ecf4de4fa0 100644
--- a/indra/llrender/llglslshader.h
+++ b/indra/llrender/llglslshader.h
@@ -152,6 +152,7 @@ class LLGLSLShader
 
 	GLhandleARB mProgramObject;
 	std::vector<GLint> mAttribute; //lookup table of attribute enum to attribute channel
+	U32 mAttributeMask;  //mask of which reserved attributes are set (lines up with LLVertexBuffer::getTypeMask())
 	std::vector<GLint> mUniform;   //lookup table of uniform enum to uniform location
 	std::map<std::string, GLint> mUniformMap;  //lookup map of uniform name to uniform location
 	std::map<GLint, LLVector4> mValue; //lookup map of uniform location to last known value
diff --git a/indra/llrender/llvertexbuffer.cpp b/indra/llrender/llvertexbuffer.cpp
index 4e3cea9474a35102984724cadbc8f9ffa009fb7c..29fe5400a3b4597c8617aa8cd3007c6d161f3731 100644
--- a/indra/llrender/llvertexbuffer.cpp
+++ b/indra/llrender/llvertexbuffer.cpp
@@ -349,6 +349,25 @@ S32 LLVertexBuffer::sTypeSize[LLVertexBuffer::TYPE_MAX] =
 	sizeof(LLVector4), // TYPE_TEXTURE_INDEX (actually exists as position.w), no extra data, but stride is 16 bytes
 };
 
+static std::string vb_type_name[] =
+{
+	"TYPE_VERTEX",
+	"TYPE_NORMAL",
+	"TYPE_TEXCOORD0",
+	"TYPE_TEXCOORD1",
+	"TYPE_TEXCOORD2",
+	"TYPE_TEXCOORD3",
+	"TYPE_COLOR",
+	"TYPE_EMISSIVE",
+	"TYPE_BINORMAL",
+	"TYPE_WEIGHT",
+	"TYPE_WEIGHT4",
+	"TYPE_CLOTHWEIGHT",
+	"TYPE_TEXTURE_INDEX",
+	"TYPE_MAX",
+	"TYPE_INDEX",	
+};
+
 U32 LLVertexBuffer::sGLMode[LLRender::NUM_MODES] = 
 {
 	GL_TRIANGLES,
@@ -2313,6 +2332,14 @@ void LLVertexBuffer::setupVertexBuffer(U32 data_mask)
 
 	if (gDebugGL && ((data_mask & mTypeMask) != data_mask))
 	{
+		for (U32 i = 0; i < LLVertexBuffer::TYPE_MAX; ++i)
+		{
+			U32 mask = 1 << i;
+			if (mask & data_mask && !(mask & mTypeMask))
+			{ //bit set in data_mask, but not set in mTypeMask
+				llwarns << "Missing required component " << vb_type_name[i] << llendl;
+			}
+		}
 		llerrs << "LLVertexBuffer::setupVertexBuffer missing required components for supplied data mask." << llendl;
 	}
 
diff --git a/indra/newview/lldrawpoolmaterials.cpp b/indra/newview/lldrawpoolmaterials.cpp
index d6cca9abe29fa05b298469f83e04362c064aac3b..3e0f9c9d4d995ad42283c6347fbcf05b19689f00 100644
--- a/indra/newview/lldrawpoolmaterials.cpp
+++ b/indra/newview/lldrawpoolmaterials.cpp
@@ -113,6 +113,9 @@ void LLDrawPoolMaterials::renderDeferred(S32 pass)
 	llassert(pass < sizeof(type_list)/sizeof(U32));
 
 	U32 type = type_list[pass];
+
+	U32 mask = mShader->mAttributeMask;
+
 	LLCullResult::drawinfo_iterator begin = gPipeline.beginRenderMap(type);
 	LLCullResult::drawinfo_iterator end = gPipeline.endRenderMap(type);
 	
@@ -137,7 +140,7 @@ void LLDrawPoolMaterials::renderDeferred(S32 pass)
 		
 		mShader->setMinimumAlpha(params.mAlphaMaskCutoff);
 
-		pushBatch(params, VERTEX_DATA_MASK, TRUE);
+		pushBatch(params, mask, TRUE);
 	}
 }
 
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index fd9f3dc48618434360b5cc4914b410291c770bd5..8a1f00aa0e950f561a46fedfcb013bf254656733 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -4771,10 +4771,10 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
 	genDrawInfo(group, simple_mask | LLVertexBuffer::MAP_TEXTURE_INDEX, simple_faces, FALSE, batch_textures);
 	genDrawInfo(group, fullbright_mask | LLVertexBuffer::MAP_TEXTURE_INDEX, fullbright_faces, FALSE, batch_textures);
 	genDrawInfo(group, alpha_mask | LLVertexBuffer::MAP_TEXTURE_INDEX, alpha_faces, TRUE, batch_textures);
-	genDrawInfo(group, bump_mask, bump_faces, FALSE, FALSE);
-	genDrawInfo(group, norm_mask, norm_faces, FALSE, FALSE);
-	genDrawInfo(group, spec_mask, spec_faces, FALSE, FALSE);
-	genDrawInfo(group, normspec_mask, normspec_faces, FALSE, FALSE);
+	genDrawInfo(group, bump_mask | LLVertexBuffer::MAP_TEXTURE_INDEX, bump_faces, FALSE, FALSE);
+	genDrawInfo(group, norm_mask | LLVertexBuffer::MAP_TEXTURE_INDEX, norm_faces, FALSE, FALSE);
+	genDrawInfo(group, spec_mask | LLVertexBuffer::MAP_TEXTURE_INDEX, spec_faces, FALSE, FALSE);
+	genDrawInfo(group, normspec_mask | LLVertexBuffer::MAP_TEXTURE_INDEX, normspec_faces, FALSE, FALSE);
 
 	if (!LLPipeline::sDelayVBUpdate)
 	{