Texture function refactoring

To make it easier to branch on the different texture
fetching options, a new TextureFunction class is
introduced here, which performs the string comparisons
and identifies the different options.

I also had to add a 5th argument for textureGradOffset
and textureProjGradOffset.

I added function stubs (with the UNIMPLEMENTED markers)
for all new texture functions.

Change-Id: I58cde91a2bacb0012bdc34ec85b0befa19a85326
Reviewed-on: https://swiftshader-review.googlesource.com/4116
Tested-by: Alexis Hétu <sugoi@google.com>
Reviewed-by: Nicolas Capens <capn@google.com>
diff --git a/src/OpenGL/compiler/OutputASM.cpp b/src/OpenGL/compiler/OutputASM.cpp
index bbfadf4..3245b67 100644
--- a/src/OpenGL/compiler/OutputASM.cpp
+++ b/src/OpenGL/compiler/OutputASM.cpp
@@ -113,6 +113,86 @@
 		return 0;

 	}

 

+	OutputASM::TextureFunction::TextureFunction(const TString& nodeName) : method(IMPLICIT), proj(false), offset(false)

+	{

+		TString name = TFunction::unmangleName(nodeName);

+

+		if(name == "texture2D" || name == "textureCube" || name == "texture" || name == "texture3D")

+		{

+			method = IMPLICIT;

+		}

+		else if(name == "texture2DProj" || name == "textureProj")

+		{

+			method = IMPLICIT;

+			proj = true;

+		}

+		else if(name == "texture2DLod" || name == "textureCubeLod" || name == "textureLod")

+		{

+			method = LOD;

+		}

+		else if(name == "texture2DProjLod" || name == "textureProjLod")

+		{

+			method = LOD;

+			proj = true;

+		}

+		else if(name == "textureSize")

+		{

+			method = SIZE;

+		}

+		else if(name == "textureOffset")

+		{

+			method = IMPLICIT;

+			offset = true;

+		}

+		else if(name == "textureProjOffset")

+		{

+			method = IMPLICIT;

+			offset = true;

+			proj = true;

+		}

+		else if(name == "textureLodOffset")

+		{

+			method = LOD;

+			offset = true;

+		}

+		else if(name == "textureProjLodOffset")

+		{

+			method = LOD;

+			proj = true;

+			offset = true;

+		}

+		else if(name == "texelFetch")

+		{

+			method = FETCH;

+		}

+		else if(name == "texelFetchOffset")

+		{

+			method = FETCH;

+			offset = true;

+		}

+		else if(name == "textureGrad")

+		{

+			method = GRAD;

+		}

+		else if(name == "textureGradOffset")

+		{

+			method = GRAD;

+			offset = true;

+		}

+		else if(name == "textureProjGrad")

+		{

+			method = GRAD;

+			proj = true;

+		}

+		else if(name == "textureProjGradOffset")

+		{

+			method = GRAD;

+			proj = true;

+			offset = true;

+		}

+		else UNREACHABLE(0);

+	}

+

 	OutputASM::OutputASM(TParseContext &context, Shader *shaderObject) : TIntermTraverser(true, true, true), mContext(context), shaderObject(shaderObject)

 	{

 		shader = 0;

@@ -1042,105 +1122,161 @@
 				}

 				else

 				{

-					TString name = TFunction::unmangleName(node->getName());

-

-					if(name == "texture" || name == "texture2D" || name == "textureCube" || name == "texture3D")

+					const TextureFunction textureFunction(node->getName());

+					switch(textureFunction.method)

 					{

-						if(argumentCount == 2)

+					case TextureFunction::IMPLICIT:

 						{

-							emit(sw::Shader::OPCODE_TEX, result, arg[1], arg[0]);

-						}

-						else if(argumentCount == 3)   // bias

-						{

-							Temporary uvwb(this);

-							emit(sw::Shader::OPCODE_MOV, &uvwb, arg[1]);

-							Instruction *bias = emit(sw::Shader::OPCODE_MOV, &uvwb, arg[2]);

-							bias->dst.mask = 0x8;

+							TIntermTyped *t = arg[1]->getAsTyped();

 

-							Instruction *tex = emit(sw::Shader::OPCODE_TEX, result, &uvwb, arg[0]);   // FIXME: Implement an efficient TEXLDB instruction

-							tex->bias = true;

-						}

-						else UNREACHABLE(argumentCount);

-					}

-					else if(name == "texture2DProj" || name == "textureProj")

-					{

-						TIntermTyped *t = arg[1]->getAsTyped();

+							TIntermNode* offset = textureFunction.offset ? arg[2] : 0;

 

-						if(argumentCount == 2)

-						{

-							Instruction *tex = emit(sw::Shader::OPCODE_TEX, result, arg[1], arg[0]);

-							tex->project = true;

-

-							if(t->getNominalSize() == 3)

+							if(argumentCount == 2 || (textureFunction.offset && argumentCount == 3))

 							{

-								tex->src[0].swizzle = 0xA4;

+								Instruction *tex = emit(textureFunction.offset ? sw::Shader::OPCODE_TEXOFFSET : sw::Shader::OPCODE_TEX,

+								                        result, arg[1], arg[0], offset);

+								if(textureFunction.proj)

+								{

+									tex->project = true;

+

+									switch(t->getNominalSize())

+									{

+									case 2: tex->src[0].swizzle = 0x54; break; // xyyy

+									case 3: tex->src[0].swizzle = 0xA4; break; // xyzz

+									case 4: break; // xyzw

+									default:

+										UNREACHABLE(t->getNominalSize());

+										break;

+									}

+								}

 							}

-							else ASSERT(t->getNominalSize() == 4);

+							else if(argumentCount == 3 || (textureFunction.offset && argumentCount == 4))   // bias

+							{

+								Temporary proj(this);

+								if(textureFunction.proj)

+								{

+									Instruction *div = emit(sw::Shader::OPCODE_DIV, &proj, arg[1], arg[1]);

+									div->dst.mask = 0x3;

+

+									switch(t->getNominalSize())

+									{

+									case 2:

+									case 3:

+									case 4:

+										div->src[1].swizzle = 0x55 * (t->getNominalSize() - 1);

+										break;

+									default:

+										UNREACHABLE(t->getNominalSize());

+										break;

+									}

+								}

+								else

+								{

+									emit(sw::Shader::OPCODE_MOV, &proj, arg[1]);

+								}

+

+								Instruction *bias = emit(sw::Shader::OPCODE_MOV, &proj, arg[textureFunction.offset ? 3 : 2]);

+								bias->dst.mask = 0x8;

+

+								Instruction *tex = emit(textureFunction.offset ? sw::Shader::OPCODE_TEXOFFSET : sw::Shader::OPCODE_TEX, 

+								                        result, &proj, arg[0], offset); // FIXME: Implement an efficient TEXLDB instruction

+								tex->bias = true;

+							}

+							else UNREACHABLE(argumentCount);

 						}

-						else if(argumentCount == 3)   // bias

+						break;

+					case TextureFunction::LOD:

 						{

+							TIntermTyped *t = arg[1]->getAsTyped();

 							Temporary proj(this);

 

-							if(t->getNominalSize() == 3)

+							if(textureFunction.proj)

 							{

-								Instruction *div = emit(sw::Shader::OPCODE_DIV, &proj, arg[1], arg[1]);

-								div->src[1].swizzle = 0xAA;

-								div->dst.mask = 0x3;

+									Instruction *div = emit(sw::Shader::OPCODE_DIV, &proj, arg[1], arg[1]);

+									div->dst.mask = 0x3;

+

+								switch(t->getNominalSize())

+								{

+								case 2:

+								case 3:

+								case 4:

+									div->src[1].swizzle = 0x55 * (t->getNominalSize() - 1);

+									break;

+								default:

+									UNREACHABLE(t->getNominalSize());

+									break;

+								}

 							}

-							else if(t->getNominalSize() == 4)

+							else

 							{

-								Instruction *div = emit(sw::Shader::OPCODE_DIV, &proj, arg[1], arg[1]);

-								div->src[1].swizzle = 0xFF;

-								div->dst.mask = 0x3;

+								emit(sw::Shader::OPCODE_MOV, &proj, arg[1]);

 							}

-							else UNREACHABLE(t->getNominalSize());

 

-							Instruction *bias = emit(sw::Shader::OPCODE_MOV, &proj, arg[2]);

-							bias->dst.mask = 0x8;

+							Instruction *lod = emit(sw::Shader::OPCODE_MOV, &proj, arg[2]);

+							lod->dst.mask = 0x8;

 

-							Instruction *tex = emit(sw::Shader::OPCODE_TEX, result, &proj, arg[0]);

-							tex->bias = true;

+							emit(textureFunction.offset ? sw::Shader::OPCODE_TEXLDLOFFSET : sw::Shader::OPCODE_TEXLDL,

+							     result, &proj, arg[0], textureFunction.offset ? arg[3] : 0);

 						}

-						else UNREACHABLE(argumentCount);

-					}

-					else if(name == "texture2DLod" || name == "textureCubeLod" || name == "textureLod")

-					{

-						Temporary uvwb(this);

-						emit(sw::Shader::OPCODE_MOV, &uvwb, arg[1]);

-						Instruction *lod = emit(sw::Shader::OPCODE_MOV, &uvwb, arg[2]);

-						lod->dst.mask = 0x8;

-

-						emit(sw::Shader::OPCODE_TEXLDL, result, &uvwb, arg[0]);

-					}

-					else if(name == "texture2DProjLod" || name == "textureProjLod")

-					{

-						TIntermTyped *t = arg[1]->getAsTyped();

-						Temporary proj(this);

-

-						if(t->getNominalSize() == 3)

+						break;

+					case TextureFunction::FETCH:

 						{

-							Instruction *div = emit(sw::Shader::OPCODE_DIV, &proj, arg[1], arg[1]);

-							div->src[1].swizzle = 0xAA;

-							div->dst.mask = 0x3;

+							TIntermTyped *t = arg[1]->getAsTyped();

+

+							if(argumentCount == 3 || (textureFunction.offset && argumentCount == 4))

+							{

+								TIntermNode* offset = textureFunction.offset ? arg[3] : 0;

+

+								emit(textureFunction.offset ? sw::Shader::OPCODE_TEXELFETCHOFFSET : sw::Shader::OPCODE_TEXELFETCH,

+								     result, arg[1], arg[0], arg[2], offset);

+							}

+							else UNREACHABLE(argumentCount);

 						}

-						else if(t->getNominalSize() == 4)

+						break;

+					case TextureFunction::GRAD:

 						{

-							Instruction *div = emit(sw::Shader::OPCODE_DIV, &proj, arg[1], arg[1]);

-							div->src[1].swizzle = 0xFF;

-							div->dst.mask = 0x3;

+							TIntermTyped *t = arg[1]->getAsTyped();

+

+							if(argumentCount == 4 || (textureFunction.offset && argumentCount == 5))

+							{

+								Temporary uvwb(this);

+

+								if(textureFunction.proj)

+								{

+									Instruction *div = emit(sw::Shader::OPCODE_DIV, &uvwb, arg[1], arg[1]);

+									div->dst.mask = 0x3;

+

+									switch(t->getNominalSize())

+									{

+									case 2:

+									case 3:

+									case 4:

+										div->src[1].swizzle = 0x55 * (t->getNominalSize() - 1);

+										break;

+									default:

+										UNREACHABLE(t->getNominalSize());

+										break;

+									}

+								}

+								else

+								{

+									emit(sw::Shader::OPCODE_MOV, &uvwb, arg[1]);

+								}

+

+								TIntermNode* offset = textureFunction.offset ? arg[4] : 0;

+

+								emit(textureFunction.offset ? sw::Shader::OPCODE_TEXGRADOFFSET : sw::Shader::OPCODE_TEXGRAD,

+								     result, &uvwb, arg[0], arg[2], arg[3], offset);

+							}

+							else UNREACHABLE(argumentCount);

 						}

-						else UNREACHABLE(t->getNominalSize());

-

-						Instruction *lod = emit(sw::Shader::OPCODE_MOV, &proj, arg[2]);

-						lod->dst.mask = 0x8;

-

-						emit(sw::Shader::OPCODE_TEXLDL, result, &proj, arg[0]);

-					}

-					else if(name == "textureSize")

-					{

+						break;

+					case TextureFunction::SIZE:

 						emit(sw::Shader::OPCODE_TEXSIZE, result, arg[1], arg[0]);

+						break;

+					default:

+						UNREACHABLE(textureFunction.method);

 					}

-					else UNREACHABLE(0);

 				}

 			}

 			break;

@@ -1483,7 +1619,7 @@
 		TIntermTyped *condition = node->getCondition();

 		TIntermTyped *expression = node->getExpression();

 		TIntermNode *body = node->getBody();

-

+		

 		if(node->getType() == ELoopDoWhile)

 		{

 			Temporary iterate(this);

@@ -1607,7 +1743,7 @@
 		return IsSampler(type.getBasicType()) && (type.getQualifier() == EvqUniform || type.getQualifier() == EvqTemporary);

 	}

 

-	Instruction *OutputASM::emit(sw::Shader::Opcode op, TIntermTyped *dst, TIntermNode *src0, TIntermNode *src1, TIntermNode *src2, TIntermNode *src3, int index)

+	Instruction *OutputASM::emit(sw::Shader::Opcode op, TIntermTyped *dst, TIntermNode *src0, TIntermNode *src1, TIntermNode *src2, TIntermNode *src3, TIntermNode *src4, int index)

 	{

 		if(isSamplerRegister(dst))

 		{

@@ -1628,6 +1764,7 @@
 		argument(instruction->src[1], src1, index);

 		argument(instruction->src[2], src2, index);

 		argument(instruction->src[3], src3, index);

+		argument(instruction->src[4], src4, index);

 

 		shader->append(instruction);

 

@@ -1683,7 +1820,7 @@
 	{

 		for(int index = 0; index < dst->elementRegisterCount(); index++)

 		{

-			emit(op, dst, src0, src1, src2, 0, index);

+			emit(op, dst, src0, src1, src2, 0, 0, index);

 		}

 	}

 

diff --git a/src/OpenGL/compiler/OutputASM.h b/src/OpenGL/compiler/OutputASM.h
index acb981b..47bd4d1 100644
--- a/src/OpenGL/compiler/OutputASM.h
+++ b/src/OpenGL/compiler/OutputASM.h
@@ -164,6 +164,24 @@
 			FUNCTION

 		};

 

+		struct TextureFunction

+		{

+			TextureFunction(const TString& name);

+

+			enum Method

+			{

+				IMPLICIT,   // Mipmap LOD determined implicitly (standard lookup)

+				LOD,

+				SIZE,   // textureSize()

+				FETCH,

+				GRAD

+			};

+

+			Method method;

+			bool proj;

+			bool offset;

+		};

+

 		void emitShader(Scope scope);

 

 		// Visit AST nodes and output their code to the body stream

@@ -176,7 +194,7 @@
 		virtual bool visitBranch(Visit visit, TIntermBranch*);

 

 		sw::Shader::Opcode getOpcode(sw::Shader::Opcode op, TIntermTyped *in) const;

-		Instruction *emit(sw::Shader::Opcode op, TIntermTyped *dst = 0, TIntermNode *src0 = 0, TIntermNode *src1 = 0, TIntermNode *src2 = 0, TIntermNode *src3 = 0, int index = 0);

+		Instruction *emit(sw::Shader::Opcode op, TIntermTyped *dst = 0, TIntermNode *src0 = 0, TIntermNode *src1 = 0, TIntermNode *src2 = 0, TIntermNode *src3 = 0, TIntermNode *src4 = 0, int index = 0);

 		Instruction *emitCast(TIntermTyped *dst, TIntermTyped *src);

 		void emitBinary(sw::Shader::Opcode op, TIntermTyped *dst = 0, TIntermNode *src0 = 0, TIntermNode *src1 = 0, TIntermNode *src2 = 0);

 		void emitAssign(sw::Shader::Opcode op, TIntermTyped *result, TIntermTyped *lhs, TIntermTyped *src0, TIntermTyped *src1 = 0);

diff --git a/src/Shader/PixelProgram.cpp b/src/Shader/PixelProgram.cpp
index 46ca81c..f889cfa 100644
--- a/src/Shader/PixelProgram.cpp
+++ b/src/Shader/PixelProgram.cpp
@@ -110,6 +110,7 @@
 			const Src &src1 = instruction->src[1];
 			const Src &src2 = instruction->src[2];
 			const Src &src3 = instruction->src[3];
+			const Src &src4 = instruction->src[4];
 
 			bool predicate = instruction->predicate;
 			Control control = instruction->control;
@@ -122,6 +123,7 @@
 			Vector4f s1;
 			Vector4f s2;
 			Vector4f s3;
+			Vector4f s4;
 
 			if(opcode == Shader::OPCODE_TEXKILL)   // Takes destination as input
 			{
@@ -142,6 +144,7 @@
 			if(src1.type != Shader::PARAMETER_VOID) s1 = fetchRegisterF(r, src1);
 			if(src2.type != Shader::PARAMETER_VOID) s2 = fetchRegisterF(r, src2);
 			if(src3.type != Shader::PARAMETER_VOID) s3 = fetchRegisterF(r, src3);
+			if(src4.type != Shader::PARAMETER_VOID) s4 = fetchRegisterF(r, src4);
 
 			switch(opcode)
 			{
@@ -275,6 +278,12 @@
 			case Shader::OPCODE_TEXLDL:     TEXLDL(r, d, s0, src1, project, bias);         break;
 			case Shader::OPCODE_TEXSIZE:    TEXSIZE(r, d, s0.x, src1);                     break;
 			case Shader::OPCODE_TEXKILL:    TEXKILL(cMask, d, dst.mask);                   break;
+			case Shader::OPCODE_TEXOFFSET:  TEXOFFSET(r, d, s0, src1, s2, s3, project, bias); break;
+			case Shader::OPCODE_TEXLDLOFFSET: TEXLDL(r, d, s0, src1, s2, project, bias);   break;
+			case Shader::OPCODE_TEXELFETCH: TEXELFETCH(r, d, s0, src1, s2);                break;
+			case Shader::OPCODE_TEXELFETCHOFFSET: TEXELFETCH(r, d, s0, src1, s2, s3);      break;
+			case Shader::OPCODE_TEXGRAD:    TEXGRAD(r, d, s0, src1, s2, s3);               break;
+			case Shader::OPCODE_TEXGRADOFFSET: TEXGRAD(r, d, s0, src1, s2, s3, s4);        break;
 			case Shader::OPCODE_DISCARD:    DISCARD(r, cMask, instruction);                break;
 			case Shader::OPCODE_DFDX:       DFDX(d, s0);                                   break;
 			case Shader::OPCODE_DFDY:       DFDY(d, s0);                                   break;
@@ -1030,6 +1039,36 @@
 		dst.w = tmp[(src1.swizzle >> 6) & 0x3];
 	}
 
+	void PixelProgram::TEXOFFSET(Registers &r, Vector4f &dst, Vector4f &src0, const Src& src1, Vector4f &src2, Vector4f &src3, bool project, bool bias)
+	{
+		UNIMPLEMENTED();
+	}
+
+	void PixelProgram::TEXLDL(Registers &r, Vector4f &dst, Vector4f &src0, const Src &src1, Vector4f &offset, bool project, bool bias)
+	{
+		UNIMPLEMENTED();
+	}
+
+	void PixelProgram::TEXELFETCH(Registers &r, Vector4f &dst, Vector4f &src0, const Src& src1, Vector4f &src2)
+	{
+		UNIMPLEMENTED();
+	}
+
+	void PixelProgram::TEXELFETCH(Registers &r, Vector4f &dst, Vector4f &src0, const Src& src1, Vector4f &src2, Vector4f &offset)
+	{
+		UNIMPLEMENTED();
+	}
+
+	void PixelProgram::TEXGRAD(Registers &r, Vector4f &dst, Vector4f &src0, const Src& src1, Vector4f &src2, Vector4f &src3)
+	{
+		UNIMPLEMENTED();
+	}
+
+	void PixelProgram::TEXGRAD(Registers &r, Vector4f &dst, Vector4f &src0, const Src& src1, Vector4f &src2, Vector4f &src3, Vector4f &offset)
+	{
+		UNIMPLEMENTED();
+	}
+
 	void PixelProgram::TEXLDD(Registers &r, Vector4f &dst, Vector4f &src0, const Src &src1, Vector4f &src2, Vector4f &src3, bool project, bool bias)
 	{
 		Vector4f tmp;
diff --git a/src/Shader/PixelProgram.hpp b/src/Shader/PixelProgram.hpp
index 850a7b1..41b9c4a 100644
--- a/src/Shader/PixelProgram.hpp
+++ b/src/Shader/PixelProgram.hpp
@@ -112,6 +112,12 @@
 		void TEXLDL(Registers &r, Vector4f &dst, Vector4f &src0, const Src &src1, bool project, bool bias);
 		void TEXSIZE(Registers &r, Vector4f &dst, Float4 &lod, const Src &src1);
 		void TEXKILL(Int cMask[4], Vector4f &src, unsigned char mask);
+		void TEXOFFSET(Registers &r, Vector4f &dst, Vector4f &src, const Src&, Vector4f &src2, Vector4f &src3, bool project, bool bias);
+		void TEXLDL(Registers &r, Vector4f &dst, Vector4f &src0, const Src &src1, Vector4f &src2, bool project, bool bias);
+		void TEXELFETCH(Registers &r, Vector4f &dst, Vector4f &src, const Src&, Vector4f &src2);
+		void TEXELFETCH(Registers &r, Vector4f &dst, Vector4f &src, const Src&, Vector4f &src2, Vector4f &src3);
+		void TEXGRAD(Registers &r, Vector4f &dst, Vector4f &src, const Src&, Vector4f &src2, Vector4f &src3);
+		void TEXGRAD(Registers &r, Vector4f &dst, Vector4f &src, const Src&, Vector4f &src2, Vector4f &src3, Vector4f &src4);
 		void DISCARD(Registers &r, Int cMask[4], const Shader::Instruction *instruction);
 		void DFDX(Vector4f &dst, Vector4f &src);
 		void DFDY(Vector4f &dst, Vector4f &src);
diff --git a/src/Shader/PixelShader.cpp b/src/Shader/PixelShader.cpp
index 9e4cff5..0cd6f1c 100644
--- a/src/Shader/PixelShader.cpp
+++ b/src/Shader/PixelShader.cpp
@@ -299,6 +299,12 @@
 						case Shader::OPCODE_TEX:
 						case Shader::OPCODE_TEXLDD:
 						case Shader::OPCODE_TEXLDL:
+						case Shader::OPCODE_TEXOFFSET:
+						case Shader::OPCODE_TEXLDLOFFSET:
+						case Shader::OPCODE_TEXELFETCH:
+						case Shader::OPCODE_TEXELFETCHOFFSET:
+						case Shader::OPCODE_TEXGRAD:
+						case Shader::OPCODE_TEXGRADOFFSET:
 							{
 								int sampler = instruction[i]->src[1].index;
 
diff --git a/src/Shader/Shader.cpp b/src/Shader/Shader.cpp
index 7767e79..6251359 100644
--- a/src/Shader/Shader.cpp
+++ b/src/Shader/Shader.cpp
@@ -865,6 +865,12 @@
 		case OPCODE_TEXLDD:			return "texldd";
 		case OPCODE_CMP:			return "cmp";
 		case OPCODE_TEXLDL:			return "texldl";
+		case OPCODE_TEXOFFSET:		return "texoffset";
+		case OPCODE_TEXLDLOFFSET:	return "texldloffset";
+		case OPCODE_TEXELFETCH:		return "texelfetch";
+		case OPCODE_TEXELFETCHOFFSET: return "texelfetchoffset";
+		case OPCODE_TEXGRAD:		return "texgrad";
+		case OPCODE_TEXGRADOFFSET:	return "texgradoffset";
 		case OPCODE_BREAKP:			return "breakp";
 		case OPCODE_TEXSIZE:        return "texsize";
 		case OPCODE_PHASE:			return "phase";
@@ -1772,6 +1778,12 @@
 			case OPCODE_TEXM3X2DEPTH:
 			case OPCODE_TEXLDD:
 			case OPCODE_TEXLDL:
+			case OPCODE_TEXOFFSET:
+			case OPCODE_TEXLDLOFFSET:
+			case OPCODE_TEXELFETCH:
+			case OPCODE_TEXELFETCHOFFSET:
+			case OPCODE_TEXGRAD:
+			case OPCODE_TEXGRADOFFSET:
 				{
 					Parameter &dst = instruction[i]->dst;
 					Parameter &src1 = instruction[i]->src[1];
diff --git a/src/Shader/Shader.hpp b/src/Shader/Shader.hpp
index 18c723b..588f637 100644
--- a/src/Shader/Shader.hpp
+++ b/src/Shader/Shader.hpp
@@ -205,6 +205,12 @@
 			OPCODE_SMOOTH,

 			OPCODE_ISNAN,

 			OPCODE_ISINF,

+			OPCODE_TEXOFFSET,

+			OPCODE_TEXLDLOFFSET,

+			OPCODE_TEXELFETCH,

+			OPCODE_TEXELFETCHOFFSET,

+			OPCODE_TEXGRAD,

+			OPCODE_TEXGRADOFFSET,

 			OPCODE_FLOATBITSTOINT,

 			OPCODE_FLOATBITSTOUINT,

 			OPCODE_INTBITSTOFLOAT,

@@ -511,7 +517,7 @@
 			unsigned char usageIndex;

 

 			DestinationParameter dst;

-			SourceParameter src[4];

+			SourceParameter src[5];

 

 			union

 			{

diff --git a/src/Shader/VertexProgram.cpp b/src/Shader/VertexProgram.cpp
index 916a7fd..40c0910 100644
--- a/src/Shader/VertexProgram.cpp
+++ b/src/Shader/VertexProgram.cpp
@@ -103,6 +103,7 @@
 			Src src1 = instruction->src[1];
 			Src src2 = instruction->src[2];
 			Src src3 = instruction->src[3];
+			Src src4 = instruction->src[4];
 
 			bool predicate = instruction->predicate;
 			Control control = instruction->control;
@@ -114,11 +115,13 @@
 			Vector4f s1;
 			Vector4f s2;
 			Vector4f s3;
+			Vector4f s4;
 
 			if(src0.type != Shader::PARAMETER_VOID) s0 = fetchRegisterF(r, src0);
 			if(src1.type != Shader::PARAMETER_VOID) s1 = fetchRegisterF(r, src1);
 			if(src2.type != Shader::PARAMETER_VOID) s2 = fetchRegisterF(r, src2);
 			if(src3.type != Shader::PARAMETER_VOID) s3 = fetchRegisterF(r, src3);
+			if(src4.type != Shader::PARAMETER_VOID) s4 = fetchRegisterF(r, src4);
 
 			switch(opcode)
 			{
@@ -289,6 +292,12 @@
 			case Shader::OPCODE_NE:         notEqual(d, s0, s1);            break;
 			case Shader::OPCODE_TEXLDL:		TEXLDL(r, d, s0, src1);			break;
 			case Shader::OPCODE_TEX:		TEX(r, d, s0, src1);			break;
+			case Shader::OPCODE_TEXOFFSET:  TEXOFFSET(r, d, s0, src1, s2, s3);        break;
+			case Shader::OPCODE_TEXLDLOFFSET: TEXLDL(r, d, s0, src1, s2);	break;
+			case Shader::OPCODE_TEXELFETCH: TEXELFETCH(r, d, s0, src1, s2);           break;
+			case Shader::OPCODE_TEXELFETCHOFFSET: TEXELFETCH(r, d, s0, src1, s2, s3); break;
+			case Shader::OPCODE_TEXGRAD:    TEXGRAD(r, d, s0, src1, s2, s3);          break;
+			case Shader::OPCODE_TEXGRADOFFSET: TEXGRAD(r, d, s0, src1, s2, s3, s4);   break;
 			case Shader::OPCODE_TEXSIZE:	TEXSIZE(r, d, s0.x, src1);		break;
 			case Shader::OPCODE_END:										break;
 			default:
@@ -1488,6 +1497,36 @@
 		dst.w = tmp[(src1.swizzle >> 6) & 0x3];
 	}
 
+	void VertexProgram::TEXOFFSET(Registers &r, Vector4f &dst, Vector4f &src0, const Src& src1, Vector4f &src2, Vector4f &src3)
+	{
+		UNIMPLEMENTED();
+	}
+
+	void VertexProgram::TEXLDL(Registers &r, Vector4f &dst, Vector4f &src, const Src&, Vector4f &offset)
+	{
+		UNIMPLEMENTED();
+	}
+
+	void VertexProgram::TEXELFETCH(Registers &r, Vector4f &dst, Vector4f &src0, const Src& src1, Vector4f &src2)
+	{
+		UNIMPLEMENTED();
+	}
+
+	void VertexProgram::TEXELFETCH(Registers &r, Vector4f &dst, Vector4f &src0, const Src& src1, Vector4f &src2, Vector4f &offset)
+	{
+		UNIMPLEMENTED();
+	}
+
+	void VertexProgram::TEXGRAD(Registers &r, Vector4f &dst, Vector4f &src0, const Src& src1, Vector4f &src2, Vector4f &src3)
+	{
+		UNIMPLEMENTED();
+	}
+
+	void VertexProgram::TEXGRAD(Registers &r, Vector4f &dst, Vector4f &src0, const Src& src1, Vector4f &src2, Vector4f &src3, Vector4f &offset)
+	{
+		UNIMPLEMENTED();
+	}
+
 	void VertexProgram::TEXSIZE(Registers &r, Vector4f &dst, Float4 &lod, const Src &src1)
 	{
 		Pointer<Byte> textureMipmap = r.data + OFFSET(DrawData, mipmap[16]) + src1.index * sizeof(Texture) + OFFSET(Texture, mipmap);
diff --git a/src/Shader/VertexProgram.hpp b/src/Shader/VertexProgram.hpp
index 4c384d7..53427a5 100644
--- a/src/Shader/VertexProgram.hpp
+++ b/src/Shader/VertexProgram.hpp
@@ -79,6 +79,12 @@
 		void LEAVE(Registers &r);
 		void TEXLDL(Registers &r, Vector4f &dst, Vector4f &src, const Src&);
 		void TEX(Registers &r, Vector4f &dst, Vector4f &src, const Src&);
+		void TEXOFFSET(Registers &r, Vector4f &dst, Vector4f &src, const Src&, Vector4f &src2, Vector4f &src3);
+		void TEXLDL(Registers &r, Vector4f &dst, Vector4f &src, const Src&, Vector4f &src2);
+		void TEXELFETCH(Registers &r, Vector4f &dst, Vector4f &src, const Src&, Vector4f &src2);
+		void TEXELFETCH(Registers &r, Vector4f &dst, Vector4f &src, const Src&, Vector4f &src2, Vector4f &src3);
+		void TEXGRAD(Registers &r, Vector4f &dst, Vector4f &src, const Src&, Vector4f &src2, Vector4f &src3);
+		void TEXGRAD(Registers &r, Vector4f &dst, Vector4f &src, const Src&, Vector4f &src2, Vector4f &src3, Vector4f &src4);
 		void TEXSIZE(Registers &r, Vector4f &dst, Float4 &lod, const Src&);
 
 		void sampleTexture(Registers &r, Vector4f &c, const Src &s, Float4 &u, Float4 &v, Float4 &w, Float4 &q);