Quellcode durchsuchen

shaderc: Added ARB_shader_texture_lod extension.

Branimir Karadžić vor 12 Jahren
Ursprung
Commit
ad335be715
4 geänderte Dateien mit 322 neuen und 120 gelöschten Zeilen
  1. 1 1
      src/bgfx_shader.sh
  2. 158 106
      src/renderer_gl.cpp
  3. BIN
      tools/bin/windows/shaderc.exe
  4. 163 13
      tools/shaderc/shaderc.cpp

+ 1 - 1
src/bgfx_shader.sh

@@ -93,7 +93,7 @@ vec4 bgfxTextureCubeLod(BgfxSamplerCube _sampler, vec3 _coord, float _level)
 #	else
 #		define SAMPLER2D(_name, _reg) uniform sampler2D _name : register(s ## _reg)
 #		define texture2D(_sampler, _coord) tex2D(_sampler, _coord)
-#		define texture2DLod(_sampler, _coord, _level) tex2Dlod(_sampler, vec3( (_coord).xy, _level) )
+#		define texture2DLod(_sampler, _coord, _level) tex2Dlod(_sampler, vec4( (_coord).xy, _level, 0.0) )
 #		define SAMPLER3D(_name, _reg) uniform sampler3D _name : register(s ## _reg)
 #		define texture3D(_sampler, _coord) tex3D(_sampler, _coord)
 #		define texture3DLod(_sampler, _coord, _level) tex3Dlod(_sampler, vec4( (_coord).xyz, _level) )

+ 158 - 106
src/renderer_gl.cpp

@@ -228,6 +228,7 @@ namespace bgfx
 			ARB_multisample,
 			ARB_sampler_objects,
 			ARB_seamless_cube_map,
+			ARB_shader_texture_lod,
 			ARB_texture_compression_rgtc,
 			ARB_texture_float,
 			ARB_texture_multisample,
@@ -248,6 +249,7 @@ namespace bgfx
 			EXT_framebuffer_blit,
 			EXT_framebuffer_sRGB,
 			EXT_occlusion_query_boolean,
+			EXT_shadow_samplers,
 			EXT_texture_array,
 			EXT_texture_compression_dxt1,
 			EXT_texture_compression_latc,
@@ -314,6 +316,7 @@ namespace bgfx
 		{ "GL_ARB_multisample",                    false,                             true  },
 		{ "GL_ARB_sampler_objects",                BGFX_CONFIG_RENDERER_OPENGL >= 33, true  },
 		{ "GL_ARB_seamless_cube_map",              BGFX_CONFIG_RENDERER_OPENGL >= 32, true  },
+		{ "GL_ARB_shader_texture_lod",             BGFX_CONFIG_RENDERER_OPENGL >= 30, true  },
 		{ "GL_ARB_texture_compression_rgtc",       BGFX_CONFIG_RENDERER_OPENGL >= 30, true  },
 		{ "GL_ARB_texture_float",                  BGFX_CONFIG_RENDERER_OPENGL >= 30, true  },
 		{ "GL_ARB_texture_multisample",            BGFX_CONFIG_RENDERER_OPENGL >= 32, true  },
@@ -334,6 +337,7 @@ namespace bgfx
 		{ "GL_EXT_framebuffer_blit",               BGFX_CONFIG_RENDERER_OPENGL >= 30, true  },
 		{ "GL_EXT_framebuffer_sRGB",               BGFX_CONFIG_RENDERER_OPENGL >= 30, true  },
 		{ "GL_EXT_occlusion_query_boolean",        false,                             true  },
+		{ "GL_EXT_shadow_samplers",                false,                             true  },
 		{ "GL_EXT_texture_array",                  BGFX_CONFIG_RENDERER_OPENGL >= 30, true  },
 		{ "GL_EXT_texture_compression_dxt1",       false,                             true  },
 		{ "GL_EXT_texture_compression_latc",       false,                             true  },
@@ -377,6 +381,46 @@ namespace bgfx
 		{ "GL_OES_vertex_type_10_10_10_2",         false,                             true  },
 	};
 
+	static const char* s_ARB_shader_texture_lod[] =
+	{
+		"texture2DLod",
+		"texture2DProjLod",
+		"texture3DLod",
+		"texture3DProjLod",
+		"textureCubeLod",
+		"shadow2DLod",
+		"shadow2DProjLod",
+		NULL
+		// "texture1DLod",
+		// "texture1DProjLod",
+		// "shadow1DLod",
+		// "shadow1DProjLod",
+	};
+
+	static const char* s_EXT_shadow_samplers[] =
+	{
+		"shadow2D",
+		"shadow2DProj",
+		NULL
+	};
+
+	static const char* s_OES_standard_derivatives[] =
+	{
+		"dFdx",
+		"dFdy",
+		"fwidth",
+		NULL
+	};
+
+	static const char* s_OES_texture_3D[] =
+	{
+		"texture3D",
+		"texture3DProj",
+		"texture3DLod",
+		"texture3DProjLod",
+		NULL
+	};
+
 #if BGFX_CONFIG_RENDERER_OPENGLES3
 #	define s_vertexAttribDivisor glVertexAttribDivisor
 #	define s_drawArraysInstanced glDrawArraysInstanced
@@ -1786,33 +1830,6 @@ namespace bgfx
 		bx::write(_writer, _str, (int32_t)strlen(_str) );
 	}
 
-	const char* findMatch(const char* _str, const char* _word)
-	{
-		size_t len = strlen(_word);
-		const char* ptr = strstr(_str, _word);
-		for (; NULL != ptr; ptr = strstr(ptr + len, _word) )
-		{
-			if (ptr != _str)
-			{
-				char ch = *(ptr - 1);
-				if (isalnum(ch) || '_' == ch)
-				{
-					continue;
-				}
-			}
-
-			char ch = ptr[len];
-			if (isalnum(ch) || '_' == ch)
-			{
-				continue;
-			}
-
-			return ptr;
-		}
-
-		return ptr;
-	}
-
 	void strins(char* _str, const char* _insert)
 	{
 		size_t len = strlen(_insert);
@@ -1838,111 +1855,146 @@ namespace bgfx
 
 		if (0 != m_id)
 		{
-#if BGFX_CONFIG_RENDERER_OPENGLES2
-			bool usesDerivatives = s_extension[Extension::OES_standard_derivatives].m_supported &&
-							(  findMatch(code, "dFdx")
-							|| findMatch(code, "dFdy")
-							|| findMatch(code, "fwidth")
-							);
+			if (BX_ENABLED(BGFX_CONFIG_RENDERER_OPENGLES2) )
+			{
+				bool usesDerivatives = s_extension[Extension::OES_standard_derivatives].m_supported 
+					&& bx::findIdentifierMatch(code, s_OES_standard_derivatives)
+					;
 
-			bool usesFragDepth = !!findMatch(code, "gl_FragDepth");
+				bool usesFragDepth = !!bx::findIdentifierMatch(code, "gl_FragDepth");
 
-			bool usesTexture3D = s_extension[Extension::OES_texture_3D].m_supported &&
-							(  findMatch(code, "texture3D")
-							|| findMatch(code, "texture3DProj")
-							|| findMatch(code, "texture3DLod")
-							|| findMatch(code, "texture3DProjLod")
-							);
+				bool usesShadowSamplers = s_extension[Extension::EXT_shadow_samplers].m_supported
+					&& bx::findIdentifierMatch(code, s_EXT_shadow_samplers)
+					;
 
-			if (usesDerivatives
-			||  usesFragDepth
-			||  usesTexture3D)
-			{
-				int32_t codeLen = (int32_t)strlen(code);
-				int32_t tempLen = codeLen + 1024;
-				char* temp = (char*)alloca(tempLen);
-				bx::StaticMemoryBlockWriter writer(temp, tempLen);
+				bool usesTexture3D = s_extension[Extension::OES_texture_3D].m_supported
+					&& bx::findIdentifierMatch(code, s_OES_texture_3D)
+					;
 
-				if (usesDerivatives)
+				if (usesDerivatives
+				||  usesFragDepth
+				||  usesShadowSamplers
+				||  usesTexture3D)
 				{
-					writeString(&writer, "#extension GL_OES_standard_derivatives : enable\n");
-				}
+					int32_t codeLen = (int32_t)strlen(code);
+					int32_t tempLen = codeLen + 1024;
+					char* temp = (char*)alloca(tempLen);
+					bx::StaticMemoryBlockWriter writer(temp, tempLen);
 
-				bool insertFragDepth = false;
-				if (usesFragDepth)
-				{
-					if (s_extension[Extension::EXT_frag_depth].m_supported)
+					if (usesDerivatives)
 					{
-						writeString(&writer
-							, "#extension GL_EXT_frag_depth : enable\n"
-							  "#define gl_FragDepth gl_FragDepthEXT\n"
-							);
-
-						char str[128];
-						bx::snprintf(str, BX_COUNTOF(str), "%s float gl_FragDepthEXT;\n"
-							, s_extension[Extension::OES_fragment_precision_high].m_supported ? "highp" : "mediump"
-							);
-						writeString(&writer, str);
+						writeString(&writer, "#extension GL_OES_standard_derivatives : enable\n");
 					}
-					else
+
+					bool insertFragDepth = false;
+					if (usesFragDepth)
 					{
-						insertFragDepth = true;
+						if (s_extension[Extension::EXT_frag_depth].m_supported)
+						{
+							writeString(&writer
+								, "#extension GL_EXT_frag_depth : enable\n"
+								  "#define gl_FragDepth gl_FragDepthEXT\n"
+								);
+
+							char str[128];
+							bx::snprintf(str, BX_COUNTOF(str), "%s float gl_FragDepthEXT;\n"
+								, s_extension[Extension::OES_fragment_precision_high].m_supported ? "highp" : "mediump"
+								);
+							writeString(&writer, str);
+						}
+						else
+						{
+							insertFragDepth = true;
+						}
 					}
-				}
 
-				if (usesTexture3D)
-				{
-					writeString(&writer, "#extension GL_OES_texture_3D : enable\n");
-				}
+					if (usesShadowSamplers)
+					{
+						writeString(&writer, "#extension GL_EXT_shadow_samplers : enable\n");
+					}
 
-				bx::write(&writer, code, codeLen);
-				bx::write(&writer, '\0');
+					if (usesTexture3D)
+					{
+						writeString(&writer, "#extension GL_OES_texture_3D : enable\n");
+					}
 
-				code = temp;
+					bx::write(&writer, code, codeLen);
+					bx::write(&writer, '\0');
 
-				if (insertFragDepth)
-				{
-					char* entry = strstr(temp, "void main ()");
-					if (NULL != entry)
+					code = temp;
+
+					if (insertFragDepth)
 					{
-						char* brace = strstr(entry, "{");
-						if (NULL != brace)
+						char* entry = strstr(temp, "void main ()");
+						if (NULL != entry)
 						{
-							const char* end = bx::strmb(brace, '{', '}');
-							if (NULL != end)
+							char* brace = strstr(entry, "{");
+							if (NULL != brace)
 							{
-								strins(brace+1, "\n  float gl_FragDepth = 0.0;\n");
+								const char* end = bx::strmb(brace, '{', '}');
+								if (NULL != end)
+								{
+									strins(brace+1, "\n  float gl_FragDepth = 0.0;\n");
+								}
 							}
 						}
 					}
 				}
 			}
-#elif BGFX_CONFIG_RENDERER_OPENGL >= 31
-			int32_t codeLen = (int32_t)strlen(code);
-			int32_t tempLen = codeLen + 1024;
-			char* temp = (char*)alloca(tempLen);
-			bx::StaticMemoryBlockWriter writer(temp, tempLen);
-
-			writeString(&writer, "#version 140\n");
-			if (_type == GL_FRAGMENT_SHADER)
+			else if (BX_ENABLED(BGFX_CONFIG_RENDERER_OPENGL == 21) )
 			{
-				writeString(&writer, "#define varying in\n");
-				writeString(&writer, "#define texture2D texture\n");
-				writeString(&writer, "#define texture3D texture\n");
-				writeString(&writer, "#define textureCube texture\n");
-				writeString(&writer, "out vec4 bgfx_FragColor;\n");
-				writeString(&writer, "#define gl_FragColor bgfx_FragColor\n");
+				bool usesTextureLod = s_extension[Extension::ARB_shader_texture_lod].m_supported
+					&& bx::findIdentifierMatch(code, s_ARB_shader_texture_lod)
+					;
+
+				if (usesTextureLod)
+				{
+					int32_t codeLen = (int32_t)strlen(code);
+					int32_t tempLen = codeLen + 1024;
+					char* temp = (char*)alloca(tempLen);
+					bx::StaticMemoryBlockWriter writer(temp, tempLen);
+
+					writeString(&writer, "#version 120\n");
+					if (_type == GL_FRAGMENT_SHADER)
+					{
+						writeString(&writer, "#extension GL_ARB_shader_texture_lod : enable\n");
+					}
+
+					bx::write(&writer, code, codeLen);
+					bx::write(&writer, '\0');
+					code = temp;
+				}
 			}
-			else
+			else if (BX_ENABLED(BGFX_CONFIG_RENDERER_OPENGL >= 31) )
 			{
-				writeString(&writer, "#define attribute in\n");
-				writeString(&writer, "#define varying out\n");
-			}
+				int32_t codeLen = (int32_t)strlen(code);
+				int32_t tempLen = codeLen + 1024;
+				char* temp = (char*)alloca(tempLen);
+				bx::StaticMemoryBlockWriter writer(temp, tempLen);
 
-			bx::write(&writer, code, codeLen);
-			bx::write(&writer, '\0');
-			code = temp;
-#endif // BGFX_CONFIG_RENDERER_OPENGLES2
+				writeString(&writer, "#version 140\n");
+				if (_type == GL_FRAGMENT_SHADER)
+				{
+					writeString(&writer, "#define varying in\n");
+					writeString(&writer, "#define texture2D texture\n");
+					writeString(&writer, "#define texture2DLod textureLod\n");
+					writeString(&writer, "#define texture3D texture\n");
+					writeString(&writer, "#define texture3DLod textureLod\n");
+					writeString(&writer, "#define textureCube texture\n");
+					writeString(&writer, "#define textureCubeLod textureLod\n");
+					writeString(&writer, "out vec4 bgfx_FragColor;\n");
+					writeString(&writer, "#define gl_FragColor bgfx_FragColor\n");
+				}
+				else
+				{
+					writeString(&writer, "#define attribute in\n");
+					writeString(&writer, "#define varying out\n");
+				}
+
+				bx::write(&writer, code, codeLen);
+				bx::write(&writer, '\0');
+				code = temp;
+			}
 
 			GL_CHECK(glShaderSource(m_id, 1, (const GLchar**)&code, NULL) );
 			GL_CHECK(glCompileShader(m_id) );

BIN
tools/bin/windows/shaderc.exe


+ 163 - 13
tools/shaderc/shaderc.cpp

@@ -7,8 +7,32 @@
 #	define SHADERC_DEBUG 0
 #endif // SHADERC_DEBUG
 
+#define _BX_TRACE(_format, ...) \
+				do { \
+					fprintf(stderr, BX_FILE_LINE_LITERAL "" _format "\n", ##__VA_ARGS__); \
+				} while(0)
+
+#define _BX_WARN(_condition, _format, ...) \
+				do { \
+					if (!(_condition) ) \
+					{ \
+						BX_TRACE("WARN " _format, ##__VA_ARGS__); \
+					} \
+				} while(0)
+
+#define _BX_CHECK(_condition, _format, ...) \
+				do { \
+					if (!(_condition) ) \
+					{ \
+						BX_TRACE("CHECK " _format, ##__VA_ARGS__); \
+						bx::debugBreak(); \
+					} \
+				} while(0)
+
 #if SHADERC_DEBUG
-#	define BX_TRACE(_format, ...) fprintf(stderr, "" _format "\n", ##__VA_ARGS__)
+#	define BX_TRACE _BX_TRACE
+#	define BX_WARN  _BX_WARN
+#	define BX_CHECK _BX_CHECK
 #endif // DEBUG
 
 #include <bx/bx.h>
@@ -83,6 +107,46 @@ struct Attrib
 	};
 };
 
+static const char* s_ARB_shader_texture_lod[] =
+{
+	"texture2DLod",
+	"texture2DProjLod",
+	"texture3DLod",
+	"texture3DProjLod",
+	"textureCubeLod",
+	"shadow2DLod",
+	"shadow2DProjLod",
+	NULL
+	// "texture1DLod",
+	// "texture1DProjLod",
+	// "shadow1DLod",
+	// "shadow1DProjLod",
+};
+
+static const char* s_EXT_shadow_samplers[] =
+{
+	"shadow2D",
+	"shadow2DProj",
+	NULL
+};
+
+static const char* s_OES_standard_derivatives[] =
+{
+	"dFdx",
+	"dFdy",
+	"fwidth",
+	NULL
+};
+
+static const char* s_OES_texture_3D[] =
+{
+	"texture3D",
+	"texture3DProj",
+	"texture3DLod",
+	"texture3DProjLod",
+	NULL
+};
+
 struct RemapInputSemantic
 {
 	Attrib::Enum m_attr;
@@ -445,9 +509,19 @@ void strins(char* _str, const char* _insert)
 void strreplace(char* _str, const char* _find, const char* _replace)
 {
 	const size_t len = strlen(_find);
+
+	char* replace = (char*)alloca(len+1);
+	bx::strlcpy(replace, _replace, len+1);
+	for (uint32_t ii = strlen(replace); ii < len; ++ii)
+	{
+		replace[ii] = ' ';
+	}
+	replace[len] = '\0';
+
+	BX_CHECK(len >= strlen(_replace), "");
 	for (char* ptr = strstr(_str, _find); NULL != ptr; ptr = strstr(ptr + len, _find) )
 	{
-		memcpy(ptr, _replace, len);
+		memcpy(ptr, replace, len);
 	}
 }
 
@@ -520,7 +594,7 @@ void writeFile(const char* _filePath, const void* _data, int32_t _size)
 	}
 }
 
-bool compileGLSLShader(bx::CommandLine& _cmdLine, const std::string& _code, bx::WriterI* _writer)
+bool compileGLSLShader(bx::CommandLine& _cmdLine, uint32_t _gles, const std::string& _code, bx::WriterI* _writer)
 {
 	const glslopt_shader_type type = tolower(_cmdLine.findOption('\0', "type")[0]) == 'f' ? kGlslOptShaderFragment : kGlslOptShaderVertex;
 
@@ -529,7 +603,7 @@ bool compileGLSLShader(bx::CommandLine& _cmdLine, const std::string& _code, bx::
 
 	glslopt_ctx* ctx = glslopt_initialize(gles);
 
-	glslopt_shader* shader = glslopt_optimize(ctx, type, _code.c_str(), kGlslOptionSkipPreprocessor); 
+	glslopt_shader* shader = glslopt_optimize(ctx, type, _code.c_str(), 0); 
 
 	if (!glslopt_get_status(shader) )
 	{
@@ -540,7 +614,8 @@ bool compileGLSLShader(bx::CommandLine& _cmdLine, const std::string& _code, bx::
 		int32_t start = 0;
 		int32_t end = INT32_MAX;
 
-		if (3 == sscanf(log, "%u:%u(%u):", &source, &line, &column) )
+		if (3 == sscanf(log, "%u:%u(%u):", &source, &line, &column)
+		&&  0 != line)
 		{
 			start = bx::uint32_imax(1, line-10);
 			end = start + 20;
@@ -554,11 +629,26 @@ bool compileGLSLShader(bx::CommandLine& _cmdLine, const std::string& _code, bx::
 
 	const char* optimizedShader = glslopt_get_output(shader);
 
-	const char* version = strstr(optimizedShader, "#version");
-	if (NULL != version)
+	// Trim all directives.
+	while ('#' == *optimizedShader)
 	{
-		// trim version line...
-		optimizedShader = bx::strnl(version);
+		optimizedShader = bx::strnl(optimizedShader);
+	}
+
+	if (0 != _gles)
+	{
+		char* shader = const_cast<char*>(optimizedShader);
+		strreplace(shader, "gl_FragDepthEXT", "gl_FragDepth");
+
+		strreplace(shader, "texture2DLodEXT", "texture2DLod");
+		strreplace(shader, "texture2DProjLodEXT", "texture2DProjLod");
+		strreplace(shader, "textureCubeLodEXT", "textureCubeLod");
+		strreplace(shader, "texture2DGradEXT", "texture2DGrad");
+		strreplace(shader, "texture2DProjGradEXT", "texture2DProjGrad");
+		strreplace(shader, "textureCubeGradEXT", "textureCubeGrad");
+
+		strreplace(shader, "shadow2DEXT", "shadow2D");
+		strreplace(shader, "shadow2DProjEXT", "shadow2DProj");
 	}
 
 	bx::write(_writer, optimizedShader, (int32_t)strlen(optimizedShader) );
@@ -658,7 +748,8 @@ bool compileHLSLShaderDx9(bx::CommandLine& _cmdLine, const std::string& _code, b
 		int32_t start = 0;
 		int32_t end = INT32_MAX;
 
-		if (3 == sscanf(log, "%[^(](%u,%u):", source, &line, &column) )
+		if (3 == sscanf(log, "%[^(](%u,%u):", source, &line, &column)
+		&&  0 != line)
 		{
 			start = bx::uint32_imax(1, line-10);
 			end = start + 20;
@@ -678,7 +769,7 @@ bool compileHLSLShaderDx9(bx::CommandLine& _cmdLine, const std::string& _code, b
 		return false;
 	}
 
-	BX_TRACE("Creator: %s 0x%08x", desc.Creator, desc.Version);
+	BX_TRACE("Creator: %s 0x%08x", desc.Creator, (uint32_t /*mingw warning*/)desc.Version);
 	BX_TRACE("Num constants: %d", desc.Constants);
 	BX_TRACE("#   cl ty RxC   S  By Name");
 
@@ -868,7 +959,8 @@ bool compileHLSLShaderDx11(bx::CommandLine& _cmdLine, const std::string& _code,
 		int32_t start = 0;
 		int32_t end = INT32_MAX;
 
-		if (2 == sscanf(log, "(%u,%u):", &line, &column) )
+		if (2 == sscanf(log, "(%u,%u):", &line, &column)
+		&&  0 != line)
 		{
 			start = bx::uint32_imax(1, line-10);
 			end = start + 20;
@@ -2016,12 +2108,70 @@ int main(int _argc, const char* _argv[])
 					if (glsl)
 					{
 						std::string code;
+
+						bool hasTextureLod = NULL != bx::findIdentifierMatch(data, s_ARB_shader_texture_lod /*EXT_shader_texture_lod*/);
+
 						if (0 == gles)
 						{
 							bx::stringPrintf(code, "#version %s\n", profile);
+							int32_t version = atoi(profile);
+
+							if (hasTextureLod
+							&&  130 > version)
+							{
+								bx::stringPrintf(code
+									, "#extension GL_ARB_shader_texture_lod : enable\n"
+									);
+							}
+						}
+						else
+						{
+							// Pretend that all extensions are available.
+							// This will be stripped later.
+							if (hasTextureLod)
+							{
+								bx::stringPrintf(code
+									, "#extension GL_EXT_shader_texture_lod : enable\n"
+									  "#define texture2DLod texture2DLodEXT\n"
+									  "#define texture2DProjLod texture2DProjLodEXT\n"
+									  "#define textureCubeLod textureCubeLodEXT\n"
+//									  "#define texture2DGrad texture2DGradEXT\n"
+//									  "#define texture2DProjGrad texture2DProjGradEXT\n"
+//									  "#define textureCubeGrad textureCubeGradEXT\n"
+									);
+							}
+
+							if (NULL != bx::findIdentifierMatch(data, s_OES_standard_derivatives) )
+							{
+								bx::stringPrintf(code, "#extension GL_OES_standard_derivatives : enable\n");
+							}
+
+							if (NULL != bx::findIdentifierMatch(data, s_OES_texture_3D) )
+							{
+								bx::stringPrintf(code, "#extension GL_OES_texture_3D : enable\n");
+							}
+
+							if (NULL != bx::findIdentifierMatch(data, s_EXT_shadow_samplers) )
+							{
+								bx::stringPrintf(code
+									, "#extension GL_EXT_shadow_samplers : enable\n"
+									  "#define shadow2D shadow2DEXT\n"
+									  "#define shadow2DProj shadow2DProjEXT\n"
+									);
+							}
+
+							if (NULL != bx::findIdentifierMatch(data, "gl_FragDepth") )
+							{
+								bx::stringPrintf(code
+									, "#extension GL_EXT_frag_depth : enable\n"
+									  "#define gl_FragDepth gl_FragDepthEXT\n"
+									);
+							}
+
+							bx::stringPrintf(code, "precision highp float;\n");
 						}
 						code += preprocessor.m_preprocessed;
-						compiled = compileGLSLShader(cmdLine, code, writer);
+						compiled = compileGLSLShader(cmdLine, gles, code, writer);
 					}
 					else
 					{