Add SwiftShader source to repo

Oct 6 code drop from Transgaming
Review URL: https://chromereviews.googleplex.com/3846015
diff --git a/src/Shader/SamplerCore.cpp b/src/Shader/SamplerCore.cpp
new file mode 100644
index 0000000..b7055c4
--- /dev/null
+++ b/src/Shader/SamplerCore.cpp
@@ -0,0 +1,1877 @@
+// SwiftShader Software Renderer
+//
+// Copyright(c) 2005-2011 TransGaming Inc.
+//
+// All rights reserved. No part of this software may be copied, distributed, transmitted,
+// transcribed, stored in a retrieval system, translated into any human or computer
+// language by any means, or disclosed to third parties without the explicit written
+// agreement of TransGaming Inc. Without such an agreement, no rights or licenses, express
+// or implied, including but not limited to any patent rights, are granted to you.
+//
+
+#include "SamplerCore.hpp"
+
+#include "Constants.hpp"
+#include "Debug.hpp"
+
+namespace sw
+{
+	SamplerCore::SamplerCore(Pointer<Byte> &constants, const Sampler::State &state) : constants(constants), state(state)
+	{
+	}
+
+	void SamplerCore::sampleTexture(Pointer<Byte> &texture, Color4i &c, Float4 &u, Float4 &v, Float4 &w, Float4 &q, Color4f &dsx, Color4f &dsy, bool bias, bool fixed12, bool gradients, bool lodProvided)
+	{
+		#if PERF_PROFILE
+			AddAtomic(Pointer<Long>(&profiler.texOperations), Long(4));
+
+			if(state.compressedFormat)
+			{
+				AddAtomic(Pointer<Long>(&profiler.compressedTex), Long(4));
+			}
+		#endif
+
+		bool cubeTexture = state.textureType == TEXTURE_CUBE;
+		bool volumeTexture = state.textureType == TEXTURE_3D;
+
+		Float4 uuuu = u;
+		Float4 vvvv = v;
+		Float4 wwww = w;
+
+		if(state.textureType == TEXTURE_NULL)
+		{
+			c.r = Short4(0x0000, 0x0000, 0x0000, 0x0000);
+			c.g = Short4(0x0000, 0x0000, 0x0000, 0x0000);
+			c.b = Short4(0x0000, 0x0000, 0x0000, 0x0000);
+
+			if(fixed12)   // FIXME: Convert to fixed12 at higher level, when required
+			{
+				c.a = Short4(0x1000, 0x1000, 0x1000, 0x1000);
+			}
+			else
+			{
+				c.a = Short4((short)0xFFFF, (short)0xFFFF, (short)0xFFFF, (short)0xFFFF);   // FIXME
+			}
+		}
+		else
+		{
+			Int face[4];
+			Float4 lodU;
+			Float4 lodV;
+
+			if(cubeTexture)
+			{
+				cubeFace(face, uuuu, vvvv, lodU, lodV, u, v, w);
+			}
+
+			Float lod;
+			Float anisotropy;
+			Float4 uDelta;
+			Float4 vDelta;
+
+			if(!volumeTexture)
+			{
+				if(!cubeTexture)
+				{
+					computeLod(texture, lod, anisotropy, uDelta, vDelta, uuuu, vvvv, Float(q.x), dsx, dsy, bias, gradients, lodProvided);
+				}
+				else
+				{
+					computeLod(texture, lod, anisotropy, uDelta, vDelta, lodU, lodV, Float(q.x), dsx, dsy, bias, gradients, lodProvided);
+				}
+			}
+			else
+			{
+				computeLod3D(texture, lod, uuuu, vvvv, wwww, Float(q.x), dsx, dsy, bias, gradients, lodProvided);
+			}
+
+			if(cubeTexture)
+			{
+				uuuu += Float4(0.5f, 0.5f, 0.5f, 0.5f);
+				vvvv += Float4(0.5f, 0.5f, 0.5f, 0.5f);
+			}
+
+			if(!hasFloatTexture())
+			{
+				sampleFilter(texture, c, uuuu, vvvv, wwww, lod, anisotropy, uDelta, vDelta, face, lodProvided);
+			}
+			else
+			{
+				Color4f cf;
+
+				sampleFloatFilter(texture, cf, uuuu, vvvv, wwww, lod, anisotropy, uDelta, vDelta, face, lodProvided);
+
+				convertFixed12(c, cf);
+			}
+
+			if(fixed12 && !hasFloatTexture())
+			{
+				for(int component = 0; component < textureComponentCount(); component++)
+				{
+					if(state.sRGB && isRGBComponent(component))
+					{
+						sRGBtoLinear16_12(c[component]);   // FIXME: Perform linearization at surface level for read-only textures
+					}
+					else
+					{
+						if(hasUnsignedTextureComponent(component))
+						{
+							c[component] = As<UShort4>(c[component]) >> 4;
+						}
+						else
+						{
+							c[component] = c[component] >> 3;
+						}
+					}
+				}
+			}
+
+			if(fixed12 && state.textureFilter != FILTER_GATHER)
+			{
+				int componentCount = textureComponentCount();
+
+				switch(state.textureFormat)
+				{
+				case FORMAT_R8:
+				case FORMAT_X8R8G8B8:
+				case FORMAT_A8R8G8B8:
+				case FORMAT_V8U8:
+				case FORMAT_Q8W8V8U8:
+				case FORMAT_X8L8V8U8:
+				case FORMAT_V16U16:
+				case FORMAT_A16W16V16U16:
+				case FORMAT_Q16W16V16U16:
+				case FORMAT_G8R8:
+				case FORMAT_G16R16:
+				case FORMAT_A16B16G16R16:
+					if(componentCount < 2) c.g = Short4(0x1000, 0x1000, 0x1000, 0x1000);
+					if(componentCount < 3) c.b = Short4(0x1000, 0x1000, 0x1000, 0x1000);
+					if(componentCount < 4) c.a = Short4(0x1000, 0x1000, 0x1000, 0x1000);
+					break;
+				case FORMAT_A8:
+					c.a = c.r;
+					c.r = Short4(0x0000, 0x0000, 0x0000, 0x0000);
+					c.g = Short4(0x0000, 0x0000, 0x0000, 0x0000);
+					c.b = Short4(0x0000, 0x0000, 0x0000, 0x0000);
+					break;
+				case FORMAT_L8:
+				case FORMAT_L16:
+					c.g = c.r;
+					c.b = c.r;
+					c.a = Short4(0x1000, 0x1000, 0x1000, 0x1000);
+					break;
+				case FORMAT_A8L8:
+					c.a = c.g;
+					c.g = c.r;
+					c.b = c.r;
+					break;
+				case FORMAT_R32F:
+					c.g = Short4(0x1000, 0x1000, 0x1000, 0x1000);
+				case FORMAT_G32R32F:
+					c.b = Short4(0x1000, 0x1000, 0x1000, 0x1000);
+					c.a = Short4(0x1000, 0x1000, 0x1000, 0x1000);
+				case FORMAT_A32B32G32R32F:
+					break;
+				case FORMAT_D32F_LOCKABLE:
+				case FORMAT_D32F_TEXTURE:
+				case FORMAT_D32F_SHADOW:
+					c.g = c.r;
+					c.b = c.r;
+					c.a = c.r;
+					break;
+				default:
+					ASSERT(false);
+				}
+			}
+		}
+	}
+
+	void SamplerCore::sampleTexture(Pointer<Byte> &texture, Color4f &c, Float4 &u, Float4 &v, Float4 &w, Float4 &q, Color4f &dsx, Color4f &dsy, bool bias, bool gradients, bool lodProvided)
+	{
+		#if PERF_PROFILE
+			AddAtomic(Pointer<Long>(&profiler.texOperations), Long(4));
+
+			if(state.compressedFormat)
+			{
+				AddAtomic(Pointer<Long>(&profiler.compressedTex), Long(4));
+			}
+		#endif
+
+		bool cubeTexture = state.textureType == TEXTURE_CUBE;
+		bool volumeTexture = state.textureType == TEXTURE_3D;
+
+		if(state.textureType == TEXTURE_NULL)
+		{
+			c.r = Float4(0.0f, 0.0f, 0.0f, 0.0f);
+			c.g = Float4(0.0f, 0.0f, 0.0f, 0.0f);
+			c.b = Float4(0.0f, 0.0f, 0.0f, 0.0f);
+			c.a = Float4(1.0f, 1.0f, 1.0f, 1.0f);
+		}
+		else
+		{
+			if(hasFloatTexture())   // FIXME: Mostly identical to integer sampling
+			{
+				Float4 uuuu = u;
+				Float4 vvvv = v;
+				Float4 wwww = w;
+
+				Int face[4];
+				Float4 lodU;
+				Float4 lodV;
+
+				if(cubeTexture)
+				{
+					cubeFace(face, uuuu, vvvv, lodU, lodV, u, v, w);
+				}
+
+				Float lod;
+				Float anisotropy;
+				Float4 uDelta;
+				Float4 vDelta;
+
+				if(!volumeTexture)
+				{
+					if(!cubeTexture)
+					{
+						computeLod(texture, lod, anisotropy, uDelta, vDelta, uuuu, vvvv, Float(q.x), dsx, dsy, bias, gradients, lodProvided);
+					}
+					else
+					{
+						computeLod(texture, lod, anisotropy, uDelta, vDelta, lodU, lodV, Float(q.x), dsx, dsy, bias, gradients, lodProvided);
+					}
+				}
+				else
+				{
+					computeLod3D(texture, lod, uuuu, vvvv, wwww, Float(q.x), dsx, dsy, bias, gradients, lodProvided);
+				}
+
+				if(cubeTexture)
+				{
+					uuuu += Float4(0.5f, 0.5f, 0.5f, 0.5f);
+					vvvv += Float4(0.5f, 0.5f, 0.5f, 0.5f);
+				}
+
+				sampleFloatFilter(texture, c, uuuu, vvvv, wwww, lod, anisotropy, uDelta, vDelta, face, lodProvided);
+			}
+			else
+			{
+				Color4i ci;
+
+				sampleTexture(texture, ci, u, v, w, q, dsx, dsy, bias, false, gradients, lodProvided);
+
+				for(int component = 0; component < textureComponentCount(); component++)
+				{
+					if(state.sRGB && isRGBComponent(component))
+					{
+						sRGBtoLinear16_12(ci[component]);   // FIXME: Perform linearization at surface level for read-only textures
+						convertSigned12(c[component], ci[component]);
+					}
+					else
+					{
+						if(hasUnsignedTextureComponent(component))
+						{
+							convertUnsigned16(c[component], ci[component]);
+						}
+						else
+						{
+							convertSigned15(c[component], ci[component]);
+						}
+					}
+				}
+			}
+
+			int componentCount = textureComponentCount();
+
+			if(state.textureFilter != FILTER_GATHER)
+			{
+				switch(state.textureFormat)
+				{
+				case FORMAT_R8:
+				case FORMAT_X8R8G8B8:
+				case FORMAT_A8R8G8B8:
+				case FORMAT_V8U8:
+				case FORMAT_Q8W8V8U8:
+				case FORMAT_X8L8V8U8:
+				case FORMAT_V16U16:
+				case FORMAT_A16W16V16U16:
+				case FORMAT_Q16W16V16U16:
+				case FORMAT_G8R8:
+				case FORMAT_G16R16:
+				case FORMAT_A16B16G16R16:
+					if(componentCount < 2) c.g = Float4(1.0f, 1.0f, 1.0f, 1.0f);
+					if(componentCount < 3) c.b = Float4(1.0f, 1.0f, 1.0f, 1.0f);
+					if(componentCount < 4) c.a = Float4(1.0f, 1.0f, 1.0f, 1.0f);
+					break;
+				case FORMAT_A8:
+					c.a = c.r;
+					c.r = Float4(0.0f, 0.0f, 0.0f, 0.0f);
+					c.g = Float4(0.0f, 0.0f, 0.0f, 0.0f);
+					c.b = Float4(0.0f, 0.0f, 0.0f, 0.0f);
+					break;
+				case FORMAT_L8:
+				case FORMAT_L16:
+					c.g = c.r;
+					c.b = c.r;
+					c.a = Float4(1.0f, 1.0f, 1.0f, 1.0f);
+					break;
+				case FORMAT_A8L8:
+					c.a = c.g;
+					c.g = c.r;
+					c.b = c.r;
+					break;
+				case FORMAT_R32F:
+					c.g = Float4(1.0f, 1.0f, 1.0f, 1.0f);
+				case FORMAT_G32R32F:
+					c.b = Float4(1.0f, 1.0f, 1.0f, 1.0f);
+					c.a = Float4(1.0f, 1.0f, 1.0f, 1.0f);
+				case FORMAT_A32B32G32R32F:
+					break;
+				case FORMAT_D32F_LOCKABLE:
+				case FORMAT_D32F_TEXTURE:
+				case FORMAT_D32F_SHADOW:
+					c.g = c.r;
+					c.b = c.r;
+					c.a = c.r;
+					break;
+				default:
+					ASSERT(false);
+				}
+			}
+		}
+	}
+
+	void SamplerCore::border(Short4 &mask, Float4 &coordinates)
+	{
+		Int4 border = As<Int4>(CmpLT(Abs(coordinates - Float4(0.5f)), Float4(0.5f)));
+		mask = As<Short4>(Int2(As<Int4>(Pack(border, border))));
+	}
+
+	void SamplerCore::border(Int4 &mask, Float4 &coordinates)
+	{
+		mask = As<Int4>(CmpLT(Abs(coordinates - Float4(0.5f)), Float4(0.5f)));
+	}
+
+	Short4 SamplerCore::offsetSample(Short4 &uvw, Pointer<Byte> &mipmap, int halfOffset, bool wrap, int count)
+	{
+		if(wrap)
+		{
+			switch(count)
+			{
+			case -1: return uvw - *Pointer<Short4>(mipmap + halfOffset);
+			case  0: return uvw;
+			case +1: return uvw + *Pointer<Short4>(mipmap + halfOffset);
+			case  2: return uvw + *Pointer<Short4>(mipmap + halfOffset) + *Pointer<Short4>(mipmap + halfOffset);
+			}
+		}
+		else   // Clamp or mirror
+		{
+			switch(count)
+			{
+			case -1: return SubSat(As<UShort4>(uvw), *Pointer<UShort4>(mipmap + halfOffset));
+			case  0: return uvw;
+			case +1: return AddSat(As<UShort4>(uvw), *Pointer<UShort4>(mipmap + halfOffset));
+			case  2: return AddSat(AddSat(As<UShort4>(uvw), *Pointer<UShort4>(mipmap + halfOffset)), *Pointer<UShort4>(mipmap + halfOffset));
+			}
+		}
+
+		return uvw;
+	}
+
+	void SamplerCore::sampleFilter(Pointer<Byte> &texture, Color4i &c, Float4 &u, Float4 &v, Float4 &w, Float &lod, Float &anisotropy, Float4 &uDelta, Float4 &vDelta, Int face[4], bool lodProvided)
+	{
+		bool volumeTexture = state.textureType == TEXTURE_3D;
+
+		sampleAniso(texture, c, u, v, w, lod, anisotropy, uDelta, vDelta, face, false, lodProvided);
+
+		if(state.mipmapFilter > MIPMAP_POINT)
+		{
+			Color4i cc;
+
+			sampleAniso(texture, cc, u, v, w, lod, anisotropy, uDelta, vDelta, face, true, lodProvided);
+
+			lod *= Float(1 << 16);
+
+			UShort4 utri = UShort4(Float4(lod));   // FIXME: Optimize
+			Short4 stri = utri >> 1;   // FIXME: Optimize
+
+			if(hasUnsignedTextureComponent(0)) cc.r = MulHigh(As<UShort4>(cc.r), utri); else cc.r = MulHigh(cc.r, stri);
+			if(hasUnsignedTextureComponent(1)) cc.g = MulHigh(As<UShort4>(cc.g), utri); else cc.g = MulHigh(cc.g, stri);
+			if(hasUnsignedTextureComponent(2)) cc.b = MulHigh(As<UShort4>(cc.b), utri); else cc.b = MulHigh(cc.b, stri);
+			if(hasUnsignedTextureComponent(3)) cc.a = MulHigh(As<UShort4>(cc.a), utri); else cc.a = MulHigh(cc.a, stri);
+
+			utri = ~utri;
+			stri = Short4(0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF) - stri;
+
+			if(hasUnsignedTextureComponent(0)) c.r = MulHigh(As<UShort4>(c.r), utri); else c.r = MulHigh(c.r, stri);
+			if(hasUnsignedTextureComponent(1)) c.g = MulHigh(As<UShort4>(c.g), utri); else c.g = MulHigh(c.g, stri);
+			if(hasUnsignedTextureComponent(2)) c.b = MulHigh(As<UShort4>(c.b), utri); else c.b = MulHigh(c.b, stri);
+			if(hasUnsignedTextureComponent(3)) c.a = MulHigh(As<UShort4>(c.a), utri); else c.a = MulHigh(c.a, stri);
+			
+			c.r += cc.r;
+			c.g += cc.g;
+			c.b += cc.b;
+			c.a += cc.a;
+			
+			if(!hasUnsignedTextureComponent(0)) c.r += c.r;
+			if(!hasUnsignedTextureComponent(1)) c.g += c.g;
+			if(!hasUnsignedTextureComponent(2)) c.b += c.b;
+			if(!hasUnsignedTextureComponent(3)) c.a += c.a;
+		}
+
+		Short4 borderMask;
+
+		if((AddressingMode)state.addressingModeU == ADDRESSING_BORDER)
+		{
+			Short4 u0;
+			
+			border(u0, u);
+
+			borderMask = u0;
+		}
+
+		if((AddressingMode)state.addressingModeV == ADDRESSING_BORDER)
+		{
+			Short4 v0;
+			
+			border(v0, v);
+
+			if((AddressingMode)state.addressingModeU == ADDRESSING_BORDER)
+			{
+				borderMask &= v0;
+			}
+			else
+			{
+				borderMask = v0;
+			}
+		}
+
+		if((AddressingMode)state.addressingModeW == ADDRESSING_BORDER && volumeTexture)
+		{
+			Short4 s0;
+
+			border(s0, w);
+
+			if((AddressingMode)state.addressingModeU == ADDRESSING_BORDER ||
+			   (AddressingMode)state.addressingModeV == ADDRESSING_BORDER)
+			{
+				borderMask &= s0;
+			}
+			else
+			{
+				borderMask = s0;
+			}
+		}
+
+		if((AddressingMode)state.addressingModeU == ADDRESSING_BORDER ||
+		   (AddressingMode)state.addressingModeV == ADDRESSING_BORDER ||
+		   ((AddressingMode)state.addressingModeW == ADDRESSING_BORDER && volumeTexture))
+		{
+			Short4 b;
+
+			c.r = borderMask & c.r | ~borderMask & (*Pointer<Short4>(texture + OFFSET(Texture,borderColor4[0])) >> (hasUnsignedTextureComponent(0) ? 0 : 1));
+			c.g = borderMask & c.g | ~borderMask & (*Pointer<Short4>(texture + OFFSET(Texture,borderColor4[1])) >> (hasUnsignedTextureComponent(1) ? 0 : 1));
+			c.b = borderMask & c.b | ~borderMask & (*Pointer<Short4>(texture + OFFSET(Texture,borderColor4[2])) >> (hasUnsignedTextureComponent(2) ? 0 : 1));
+			c.a = borderMask & c.a | ~borderMask & (*Pointer<Short4>(texture + OFFSET(Texture,borderColor4[3])) >> (hasUnsignedTextureComponent(3) ? 0 : 1));
+		}
+	}
+
+	void SamplerCore::sampleAniso(Pointer<Byte> &texture, Color4i &c, Float4 &u, Float4 &v, Float4 &w, Float &lod, Float &anisotropy, Float4 &uDelta, Float4 &vDelta, Int face[4], bool secondLOD, bool lodProvided)
+	{
+		if(state.textureFilter != FILTER_ANISOTROPIC || lodProvided)
+		{
+			sampleQuad(texture, c, u, v, w, lod, face, secondLOD);
+		}
+		else
+		{
+			Int a = RoundInt(anisotropy);
+
+			Color4i cSum;
+
+			cSum.r = Short4(0, 0, 0, 0);
+			cSum.g = Short4(0, 0, 0, 0);
+			cSum.b = Short4(0, 0, 0, 0);
+			cSum.a = Short4(0, 0, 0, 0);
+
+			Float4 A = *Pointer<Float4>(constants + OFFSET(Constants,uvWeight) + 16 * a);
+			Float4 B = *Pointer<Float4>(constants + OFFSET(Constants,uvStart) + 16 * a);
+			UShort4 cw = *Pointer<UShort4>(constants + OFFSET(Constants,cWeight) + 8 * a);
+			Short4 sw = Short4(cw >> 1);
+
+			Float4 du = uDelta;
+			Float4 dv = vDelta;
+
+			Float4 u0 = u + B * du;
+			Float4 v0 = v + B * dv;
+
+			du *= A;
+			dv *= A;
+
+			Int i = 0;
+
+			Do
+			{
+				sampleQuad(texture, c, u0, v0, w, lod, face, secondLOD);
+
+				u0 += du;
+				v0 += dv;
+
+				if(hasUnsignedTextureComponent(0)) cSum.r += As<Short4>(MulHigh(As<UShort4>(c.r), cw)); else cSum.r += MulHigh(c.r, sw);
+				if(hasUnsignedTextureComponent(1)) cSum.g += As<Short4>(MulHigh(As<UShort4>(c.g), cw)); else cSum.g += MulHigh(c.g, sw);
+				if(hasUnsignedTextureComponent(2)) cSum.b += As<Short4>(MulHigh(As<UShort4>(c.b), cw)); else cSum.b += MulHigh(c.b, sw);
+				if(hasUnsignedTextureComponent(3)) cSum.a += As<Short4>(MulHigh(As<UShort4>(c.a), cw)); else cSum.a += MulHigh(c.a, sw);
+
+				i++;
+			}
+			Until(i >= a)
+
+			if(hasUnsignedTextureComponent(0)) c.r = cSum.r; else c.r = AddSat(cSum.r, cSum.r);
+			if(hasUnsignedTextureComponent(1)) c.g = cSum.g; else c.g = AddSat(cSum.g, cSum.g);
+			if(hasUnsignedTextureComponent(2)) c.b = cSum.b; else c.b = AddSat(cSum.b, cSum.b);
+			if(hasUnsignedTextureComponent(3)) c.a = cSum.a; else c.a = AddSat(cSum.a, cSum.a);
+		}
+	}
+
+	void SamplerCore::sampleQuad(Pointer<Byte> &texture, Color4i &c, Float4 &u, Float4 &v, Float4 &w, Float &lod, Int face[4], bool secondLOD)
+	{
+		if(state.textureType != TEXTURE_3D)
+		{
+			sampleQuad2D(texture, c, u, v, lod, face, secondLOD);
+		}
+		else
+		{
+			sample3D(texture, c, u, v, w, lod, secondLOD);
+		}
+	}
+
+	void SamplerCore::sampleQuad2D(Pointer<Byte> &texture, Color4i &c, Float4 &u, Float4 &v, Float &lod, Int face[4], bool secondLOD)
+	{
+		int componentCount = textureComponentCount();
+		bool gather = state.textureFilter == FILTER_GATHER;
+
+		Pointer<Byte> mipmap;
+		Pointer<Byte> buffer[4];
+
+		selectMipmap(texture, buffer, mipmap, lod, face, secondLOD);
+
+		Short4 uuuu;
+		Short4 vvvv;
+
+		address(uuuu, u, (AddressingMode)state.addressingModeU);
+		address(vvvv, v, (AddressingMode)state.addressingModeV);
+
+		if(state.textureFilter == FILTER_POINT)
+		{
+			sampleTexel(c, uuuu, vvvv, vvvv, mipmap, buffer);
+		}
+		else
+		{
+			Color4i c0;
+			Color4i c1;
+			Color4i c2;
+			Color4i c3;
+
+			Short4 uuuu0 = offsetSample(uuuu, mipmap, OFFSET(Mipmap,uHalf), (AddressingMode)state.addressingModeU == ADDRESSING_WRAP, gather ? 0 : -1);
+			Short4 vvvv0 = offsetSample(vvvv, mipmap, OFFSET(Mipmap,vHalf), (AddressingMode)state.addressingModeV == ADDRESSING_WRAP, gather ? 0 : -1);
+			Short4 uuuu1 = offsetSample(uuuu, mipmap, OFFSET(Mipmap,uHalf), (AddressingMode)state.addressingModeU == ADDRESSING_WRAP, gather ? 2 : +1);
+			Short4 vvvv1 = offsetSample(vvvv, mipmap, OFFSET(Mipmap,vHalf), (AddressingMode)state.addressingModeV == ADDRESSING_WRAP, gather ? 2 : +1);
+
+			sampleTexel(c0, uuuu0, vvvv0, vvvv0, mipmap, buffer);
+			sampleTexel(c1, uuuu1, vvvv0, vvvv0, mipmap, buffer);
+			sampleTexel(c2, uuuu0, vvvv1, vvvv1, mipmap, buffer);
+			sampleTexel(c3, uuuu1, vvvv1, vvvv1, mipmap, buffer);
+	
+			if(!gather)   // Blend
+			{
+				// Fractions
+				UShort4 f0u = uuuu0;
+				UShort4 f0v = vvvv0;
+			
+				if(!state.hasNPOTTexture)
+				{
+					f0u = f0u << *Pointer<Long1>(mipmap + OFFSET(Mipmap,uInt));   // .u
+					f0v = f0v << *Pointer<Long1>(mipmap + OFFSET(Mipmap,vInt));   // .v
+				}
+				else
+				{
+					f0u = f0u * *Pointer<UShort4>(mipmap + OFFSET(Mipmap,width));
+					f0v = f0v * *Pointer<UShort4>(mipmap + OFFSET(Mipmap,height));
+				}
+
+				Short4 f1u = ~f0u;
+				Short4 f1v = ~f0v;
+			
+				Short4 f0u0v = MulHigh(As<UShort4>(f0u), As<UShort4>(f0v));
+				Short4 f1u0v = MulHigh(As<UShort4>(f1u), As<UShort4>(f0v));
+				Short4 f0u1v = MulHigh(As<UShort4>(f0u), As<UShort4>(f1v));
+				Short4 f1u1v = MulHigh(As<UShort4>(f1u), As<UShort4>(f1v));
+
+				// Signed fractions
+				Short4 f1u1vs;
+				Short4 f0u1vs;
+				Short4 f1u0vs;
+				Short4 f0u0vs;
+
+				if(!hasUnsignedTextureComponent(0) || !hasUnsignedTextureComponent(1) || !hasUnsignedTextureComponent(2) || !hasUnsignedTextureComponent(3))
+				{
+					f1u1vs = As<UShort4>(f1u1v) >> 1;
+					f0u1vs = As<UShort4>(f0u1v) >> 1;
+					f1u0vs = As<UShort4>(f1u0v) >> 1;
+					f0u0vs = As<UShort4>(f0u0v) >> 1;
+				}
+
+				// Bilinear interpolation
+				if(componentCount >= 1)
+				{
+					if(has16bitTexture() && hasUnsignedTextureComponent(0))
+					{
+						c0.r = As<UShort4>(c0.r) - MulHigh(As<UShort4>(c0.r), f0u) + MulHigh(As<UShort4>(c1.r), f0u);
+						c2.r = As<UShort4>(c2.r) - MulHigh(As<UShort4>(c2.r), f0u) + MulHigh(As<UShort4>(c3.r), f0u);
+						c.r  = As<UShort4>(c0.r) - MulHigh(As<UShort4>(c0.r), f0v) + MulHigh(As<UShort4>(c2.r), f0v);
+					}
+					else
+					{
+						if(hasUnsignedTextureComponent(0))
+						{
+							c0.r = MulHigh(As<UShort4>(c0.r), As<UShort4>(f1u1v));
+							c1.r = MulHigh(As<UShort4>(c1.r), As<UShort4>(f0u1v));
+							c2.r = MulHigh(As<UShort4>(c2.r), As<UShort4>(f1u0v));
+							c3.r = MulHigh(As<UShort4>(c3.r), As<UShort4>(f0u0v));
+						}
+						else
+						{
+							c0.r = MulHigh(c0.r, f1u1vs);
+							c1.r = MulHigh(c1.r, f0u1vs);
+							c2.r = MulHigh(c2.r, f1u0vs);
+							c3.r = MulHigh(c3.r, f0u0vs);
+						}
+
+						c.r = (c0.r + c1.r) + (c2.r + c3.r);
+						if(!hasUnsignedTextureComponent(0)) c.r = AddSat(c.r, c.r);   // Correct for signed fractions
+					}
+				}
+
+				if(componentCount >= 2)
+				{
+					if(has16bitTexture() && hasUnsignedTextureComponent(1))
+					{
+						c0.g = As<UShort4>(c0.g) - MulHigh(As<UShort4>(c0.g), f0u) + MulHigh(As<UShort4>(c1.g), f0u);
+						c2.g = As<UShort4>(c2.g) - MulHigh(As<UShort4>(c2.g), f0u) + MulHigh(As<UShort4>(c3.g), f0u);
+						c.g  = As<UShort4>(c0.g) - MulHigh(As<UShort4>(c0.g), f0v) + MulHigh(As<UShort4>(c2.g), f0v);
+					}
+					else
+					{
+						if(hasUnsignedTextureComponent(1))
+						{
+							c0.g = MulHigh(As<UShort4>(c0.g), As<UShort4>(f1u1v));
+							c1.g = MulHigh(As<UShort4>(c1.g), As<UShort4>(f0u1v));
+							c2.g = MulHigh(As<UShort4>(c2.g), As<UShort4>(f1u0v));
+							c3.g = MulHigh(As<UShort4>(c3.g), As<UShort4>(f0u0v));
+						}
+						else
+						{
+							c0.g = MulHigh(c0.g, f1u1vs);
+							c1.g = MulHigh(c1.g, f0u1vs);
+							c2.g = MulHigh(c2.g, f1u0vs);
+							c3.g = MulHigh(c3.g, f0u0vs);
+						}
+
+						c.g = (c0.g + c1.g) + (c2.g + c3.g);
+						if(!hasUnsignedTextureComponent(1)) c.g = AddSat(c.g, c.g);   // Correct for signed fractions
+					}
+				}
+
+				if(componentCount >= 3)
+				{
+					if(has16bitTexture() && hasUnsignedTextureComponent(2))
+					{
+						c0.b = As<UShort4>(c0.b) - MulHigh(As<UShort4>(c0.b), f0u) + MulHigh(As<UShort4>(c1.b), f0u);
+						c2.b = As<UShort4>(c2.b) - MulHigh(As<UShort4>(c2.b), f0u) + MulHigh(As<UShort4>(c3.b), f0u);
+						c.b  = As<UShort4>(c0.b) - MulHigh(As<UShort4>(c0.b), f0v) + MulHigh(As<UShort4>(c2.b), f0v);
+					}
+					else
+					{
+						if(hasUnsignedTextureComponent(2))
+						{
+							c0.b = MulHigh(As<UShort4>(c0.b), As<UShort4>(f1u1v));
+							c1.b = MulHigh(As<UShort4>(c1.b), As<UShort4>(f0u1v));
+							c2.b = MulHigh(As<UShort4>(c2.b), As<UShort4>(f1u0v));
+							c3.b = MulHigh(As<UShort4>(c3.b), As<UShort4>(f0u0v));
+						}
+						else
+						{
+							c0.b = MulHigh(c0.b, f1u1vs);
+							c1.b = MulHigh(c1.b, f0u1vs);
+							c2.b = MulHigh(c2.b, f1u0vs);
+							c3.b = MulHigh(c3.b, f0u0vs);
+						}
+
+						c.b = (c0.b + c1.b) + (c2.b + c3.b);
+						if(!hasUnsignedTextureComponent(2)) c.b = AddSat(c.b, c.b);   // Correct for signed fractions
+					}
+				}
+
+				if(componentCount >= 4)
+				{
+					if(has16bitTexture() && hasUnsignedTextureComponent(3))
+					{
+						c0.a = As<UShort4>(c0.a) - MulHigh(As<UShort4>(c0.a), f0u) + MulHigh(As<UShort4>(c1.a), f0u);
+						c2.a = As<UShort4>(c2.a) - MulHigh(As<UShort4>(c2.a), f0u) + MulHigh(As<UShort4>(c3.a), f0u);
+						c.a  = As<UShort4>(c0.a) - MulHigh(As<UShort4>(c0.a), f0v) + MulHigh(As<UShort4>(c2.a), f0v);
+					}
+					else
+					{
+						if(hasUnsignedTextureComponent(3))
+						{
+							c0.a = MulHigh(As<UShort4>(c0.a), As<UShort4>(f1u1v));
+							c1.a = MulHigh(As<UShort4>(c1.a), As<UShort4>(f0u1v));
+							c2.a = MulHigh(As<UShort4>(c2.a), As<UShort4>(f1u0v));
+							c3.a = MulHigh(As<UShort4>(c3.a), As<UShort4>(f0u0v));
+						}
+						else
+						{
+							c0.a = MulHigh(c0.a, f1u1vs);
+							c1.a = MulHigh(c1.a, f0u1vs);
+							c2.a = MulHigh(c2.a, f1u0vs);
+							c3.a = MulHigh(c3.a, f0u0vs);
+						}
+
+						c.a = (c0.a + c1.a) + (c2.a + c3.a);
+						if(!hasUnsignedTextureComponent(3)) c.a = AddSat(c.a, c.a);   // Correct for signed fractions
+					}
+				}
+			}
+			else
+			{
+				c.r = c1.r;
+				c.g = c2.r;
+				c.b = c3.r;
+				c.a = c0.r;
+			}
+		}
+	}
+
+	void SamplerCore::sample3D(Pointer<Byte> &texture, Color4i &c_, Float4 &u_, Float4 &v_, Float4 &w_, Float &lod, bool secondLOD)
+	{
+		int componentCount = textureComponentCount();
+
+		Pointer<Byte> mipmap;
+		Pointer<Byte> buffer[4];
+		Int face[4];
+		
+		selectMipmap(texture, buffer, mipmap, lod, face, secondLOD);
+
+		Short4 uuuu;
+		Short4 vvvv;
+		Short4 wwww;
+
+		address(uuuu, u_, (AddressingMode)state.addressingModeU);
+		address(vvvv, v_, (AddressingMode)state.addressingModeV);
+		address(wwww, w_, (AddressingMode)state.addressingModeW);
+
+		if(state.textureFilter <= FILTER_POINT)
+		{
+			sampleTexel(c_, uuuu, vvvv, wwww, mipmap, buffer);
+		}
+		else
+		{
+			Color4i c[2][2][2];
+
+			Short4 u[2][2][2];
+			Short4 v[2][2][2];
+			Short4 s[2][2][2];
+
+			for(int i = 0; i < 2; i++)
+			{
+				for(int j = 0; j < 2; j++)
+				{
+					for(int k = 0; k < 2; k++)
+					{
+						u[i][j][k] = offsetSample(uuuu, mipmap, OFFSET(Mipmap,uHalf), (AddressingMode)state.addressingModeU == ADDRESSING_WRAP, i * 2 - 1);
+						v[i][j][k] = offsetSample(vvvv, mipmap, OFFSET(Mipmap,vHalf), (AddressingMode)state.addressingModeV == ADDRESSING_WRAP, j * 2 - 1);
+						s[i][j][k] = offsetSample(wwww, mipmap, OFFSET(Mipmap,wHalf), (AddressingMode)state.addressingModeW == ADDRESSING_WRAP, k * 2 - 1);
+					}
+				}
+			}
+
+			Short4 f[2][2][2];
+			Short4 fs[2][2][2];
+			Short4 f0u;
+			Short4 f0v;
+			Short4 f0s;
+			Short4 f1u;
+			Short4 f1v;
+			Short4 f1s;
+
+			// Fractions
+			f0u = u[0][0][0];
+			f0v = v[0][0][0];
+			f0s = s[0][0][0];
+
+			if(!state.hasNPOTTexture)
+			{
+				f0u = f0u << *Pointer<Long1>(mipmap + OFFSET(Mipmap,uInt));
+				f0v = f0v << *Pointer<Long1>(mipmap + OFFSET(Mipmap,vInt));
+				f0s = f0s << *Pointer<Long1>(mipmap + OFFSET(Mipmap,wInt));
+			}
+			else
+			{
+				f0u *= *Pointer<Short4>(mipmap + OFFSET(Mipmap,width));
+				f0v *= *Pointer<Short4>(mipmap + OFFSET(Mipmap,height));
+				f0s *= *Pointer<Short4>(mipmap + OFFSET(Mipmap,depth));
+			}
+
+			f1u = ~f0u;
+			f1v = ~f0v;
+			f1s = ~f0s;
+
+			f[1][1][1] = MulHigh(As<UShort4>(f1u), As<UShort4>(f1v));
+			f[0][1][1] = MulHigh(As<UShort4>(f0u), As<UShort4>(f1v));
+			f[1][0][1] = MulHigh(As<UShort4>(f1u), As<UShort4>(f0v));
+			f[0][0][1] = MulHigh(As<UShort4>(f0u), As<UShort4>(f0v));
+			f[1][1][0] = MulHigh(As<UShort4>(f1u), As<UShort4>(f1v));
+			f[0][1][0] = MulHigh(As<UShort4>(f0u), As<UShort4>(f1v));
+			f[1][0][0] = MulHigh(As<UShort4>(f1u), As<UShort4>(f0v));
+			f[0][0][0] = MulHigh(As<UShort4>(f0u), As<UShort4>(f0v));
+
+			f[1][1][1] = MulHigh(As<UShort4>(f[1][1][1]), As<UShort4>(f1s));
+			f[0][1][1] = MulHigh(As<UShort4>(f[0][1][1]), As<UShort4>(f1s));
+			f[1][0][1] = MulHigh(As<UShort4>(f[1][0][1]), As<UShort4>(f1s));
+			f[0][0][1] = MulHigh(As<UShort4>(f[0][0][1]), As<UShort4>(f1s));
+			f[1][1][0] = MulHigh(As<UShort4>(f[1][1][0]), As<UShort4>(f0s));
+			f[0][1][0] = MulHigh(As<UShort4>(f[0][1][0]), As<UShort4>(f0s));
+			f[1][0][0] = MulHigh(As<UShort4>(f[1][0][0]), As<UShort4>(f0s));
+			f[0][0][0] = MulHigh(As<UShort4>(f[0][0][0]), As<UShort4>(f0s));
+
+			// Signed fractions
+			if(!hasUnsignedTextureComponent(0) || !hasUnsignedTextureComponent(1) || !hasUnsignedTextureComponent(2) || !hasUnsignedTextureComponent(3))
+			{
+				fs[0][0][0] = As<UShort4>(f[0][0][0]) >> 1;
+				fs[0][0][1] = As<UShort4>(f[0][0][1]) >> 1;
+				fs[0][1][0] = As<UShort4>(f[0][1][0]) >> 1;
+				fs[0][1][1] = As<UShort4>(f[0][1][1]) >> 1;
+				fs[1][0][0] = As<UShort4>(f[1][0][0]) >> 1;
+				fs[1][0][1] = As<UShort4>(f[1][0][1]) >> 1;
+				fs[1][1][0] = As<UShort4>(f[1][1][0]) >> 1;
+				fs[1][1][1] = As<UShort4>(f[1][1][1]) >> 1;
+			}
+
+			for(int i = 0; i < 2; i++)
+			{
+				for(int j = 0; j < 2; j++)
+				{
+					for(int k = 0; k < 2; k++)
+					{
+						sampleTexel(c[i][j][k], u[i][j][k], v[i][j][k], s[i][j][k], mipmap, buffer);
+
+						if(componentCount >= 1) if(hasUnsignedTextureComponent(0)) c[i][j][k].r = MulHigh(As<UShort4>(c[i][j][k].r), As<UShort4>(f[1 - i][1 - j][1 - k])); else c[i][j][k].r = MulHigh(c[i][j][k].r, fs[1 - i][1 - j][1 - k]);
+						if(componentCount >= 2) if(hasUnsignedTextureComponent(1)) c[i][j][k].g = MulHigh(As<UShort4>(c[i][j][k].g), As<UShort4>(f[1 - i][1 - j][1 - k])); else c[i][j][k].g = MulHigh(c[i][j][k].g, fs[1 - i][1 - j][1 - k]);
+						if(componentCount >= 3) if(hasUnsignedTextureComponent(2)) c[i][j][k].b = MulHigh(As<UShort4>(c[i][j][k].b), As<UShort4>(f[1 - i][1 - j][1 - k])); else c[i][j][k].b = MulHigh(c[i][j][k].b, fs[1 - i][1 - j][1 - k]);
+						if(componentCount >= 4) if(hasUnsignedTextureComponent(3)) c[i][j][k].a = MulHigh(As<UShort4>(c[i][j][k].a), As<UShort4>(f[1 - i][1 - j][1 - k])); else c[i][j][k].a = MulHigh(c[i][j][k].a, fs[1 - i][1 - j][1 - k]);
+
+						if(i != 0 || j != 0 || k != 0)
+						{
+							if(componentCount >= 1) c[0][0][0].r += c[i][j][k].r;
+							if(componentCount >= 2) c[0][0][0].g += c[i][j][k].g;
+							if(componentCount >= 3) c[0][0][0].b += c[i][j][k].b;
+							if(componentCount >= 4) c[0][0][0].a += c[i][j][k].a;
+						}
+					}
+				}
+			}
+
+			if(componentCount >= 1) c_.r = c[0][0][0].r;
+			if(componentCount >= 2) c_.g = c[0][0][0].g;
+			if(componentCount >= 3) c_.b = c[0][0][0].b;
+			if(componentCount >= 4) c_.a = c[0][0][0].a;
+
+			// Correct for signed fractions
+			if(componentCount >= 1) if(!hasUnsignedTextureComponent(0)) c_.r = AddSat(c_.r, c_.r);
+			if(componentCount >= 2) if(!hasUnsignedTextureComponent(1)) c_.g = AddSat(c_.g, c_.g);
+			if(componentCount >= 3) if(!hasUnsignedTextureComponent(2)) c_.b = AddSat(c_.b, c_.b);
+			if(componentCount >= 4) if(!hasUnsignedTextureComponent(3)) c_.a = AddSat(c_.a, c_.a);
+		}
+	}
+
+	void SamplerCore::sampleFloatFilter(Pointer<Byte> &texture, Color4f &c, Float4 &u, Float4 &v, Float4 &w, Float &lod, Float &anisotropy, Float4 &uDelta, Float4 &vDelta, Int face[4], bool lodProvided)
+	{
+		bool volumeTexture = state.textureType == TEXTURE_3D;
+
+		sampleFloatAniso(texture, c, u, v, w, lod, anisotropy, uDelta, vDelta, face, false, lodProvided);
+
+		if(state.mipmapFilter > MIPMAP_POINT)
+		{
+			Color4f cc;
+
+			sampleFloatAniso(texture, cc, u, v, w, lod, anisotropy, uDelta, vDelta, face, true, lodProvided);
+
+			Float4 lod4 = Float4(Fraction(lod));
+
+			c.r = (cc.r - c.r) * lod4 + c.r;
+			c.g = (cc.g - c.g) * lod4 + c.g;
+			c.b = (cc.b - c.b) * lod4 + c.b;
+			c.a = (cc.a - c.a) * lod4 + c.a;
+		}
+
+		Int4 borderMask;
+
+		if((AddressingMode)state.addressingModeU == ADDRESSING_BORDER)
+		{
+			Int4 u0;
+			
+			border(u0, u);
+
+			borderMask = u0;
+		}
+
+		if((AddressingMode)state.addressingModeV == ADDRESSING_BORDER)
+		{
+			Int4 v0;
+			
+			border(v0, v);
+
+			if((AddressingMode)state.addressingModeU == ADDRESSING_BORDER)
+			{
+				borderMask &= v0;
+			}
+			else
+			{
+				borderMask = v0;
+			}
+		}
+
+		if((AddressingMode)state.addressingModeW == ADDRESSING_BORDER && volumeTexture)
+		{
+			Int4 s0;
+
+			border(s0, w);
+
+			if((AddressingMode)state.addressingModeU == ADDRESSING_BORDER ||
+			   (AddressingMode)state.addressingModeV == ADDRESSING_BORDER)
+			{
+				borderMask &= s0;
+			}
+			else
+			{
+				borderMask = s0;
+			}
+		}
+
+		if((AddressingMode)state.addressingModeU == ADDRESSING_BORDER ||
+		   (AddressingMode)state.addressingModeV == ADDRESSING_BORDER ||
+		   ((AddressingMode)state.addressingModeW == ADDRESSING_BORDER && volumeTexture))
+		{
+			Int4 b;
+
+			c.r = As<Float4>(borderMask & As<Int4>(c.r) | ~borderMask & *Pointer<Int4>(texture + OFFSET(Texture,borderColorF[0])));
+			c.g = As<Float4>(borderMask & As<Int4>(c.g) | ~borderMask & *Pointer<Int4>(texture + OFFSET(Texture,borderColorF[1])));
+			c.b = As<Float4>(borderMask & As<Int4>(c.b) | ~borderMask & *Pointer<Int4>(texture + OFFSET(Texture,borderColorF[2])));
+			c.a = As<Float4>(borderMask & As<Int4>(c.a) | ~borderMask & *Pointer<Int4>(texture + OFFSET(Texture,borderColorF[3])));
+		}
+	}
+
+	void SamplerCore::sampleFloatAniso(Pointer<Byte> &texture, Color4f &c, Float4 &u, Float4 &v, Float4 &w, Float &lod, Float &anisotropy, Float4 &uDelta, Float4 &vDelta, Int face[4], bool secondLOD, bool lodProvided)
+	{
+		if(state.textureFilter != FILTER_ANISOTROPIC || lodProvided)
+		{
+			sampleFloat(texture, c, u, v, w, lod, face, secondLOD);
+		}
+		else
+		{
+			Int a = RoundInt(anisotropy);
+
+			Color4f cSum;
+
+			cSum.r = Float4(0, 0, 0, 0);
+			cSum.g = Float4(0, 0, 0, 0);
+			cSum.b = Float4(0, 0, 0, 0);
+			cSum.a = Float4(0, 0, 0, 0);
+
+			Float4 A = *Pointer<Float4>(constants + OFFSET(Constants,uvWeight) + 16 * a);
+			Float4 B = *Pointer<Float4>(constants + OFFSET(Constants,uvStart) + 16 * a);
+
+			Float4 du = uDelta;
+			Float4 dv = vDelta;
+
+			Float4 u0 = u + B * du;
+			Float4 v0 = v + B * dv;
+
+			du *= A;
+			dv *= A;
+
+			Int i = 0;
+
+			Do
+			{
+				sampleFloat(texture, c, u0, v0, w, lod, face, secondLOD);
+
+				u0 += du;
+				v0 += dv;
+
+				cSum.r += c.r * A;
+				cSum.g += c.g * A;
+				cSum.b += c.b * A;
+				cSum.a += c.a * A;
+
+				i++;
+			}
+			Until(i >= a)
+
+			c.r = cSum.r;
+			c.g = cSum.g;
+			c.b = cSum.b;
+			c.a = cSum.a;
+		}
+	}
+
+	void SamplerCore::sampleFloat(Pointer<Byte> &texture, Color4f &c, Float4 &u, Float4 &v, Float4 &w, Float &lod, Int face[4], bool secondLOD)
+	{
+		if(state.textureType != TEXTURE_3D)
+		{
+			sampleFloat2D(texture, c, u, v, w, lod, face, secondLOD);
+		}
+		else
+		{
+			sampleFloat3D(texture, c, u, v, w, lod, secondLOD);
+		}
+	}
+	
+	void SamplerCore::sampleFloat2D(Pointer<Byte> &texture, Color4f &c, Float4 &u, Float4 &v, Float4 &z, Float &lod, Int face[4], bool secondLOD)
+	{
+		int componentCount = textureComponentCount();
+		bool gather = state.textureFilter == FILTER_GATHER;
+
+		Pointer<Byte> mipmap;
+		Pointer<Byte> buffer[4];
+		
+		selectMipmap(texture, buffer, mipmap, lod, face, secondLOD);
+
+		Short4 uuuu;
+		Short4 vvvv;
+
+		address(uuuu, u, (AddressingMode)state.addressingModeU);
+		address(vvvv, v, (AddressingMode)state.addressingModeV);
+
+		if(state.textureFilter == FILTER_POINT)
+		{
+			sampleTexel(c, uuuu, vvvv, vvvv, z, mipmap, buffer);
+		}
+		else
+		{
+			Color4f c0;
+			Color4f c1;
+			Color4f c2;
+			Color4f c3;
+
+			Short4 uuuu0 = offsetSample(uuuu, mipmap, OFFSET(Mipmap,uHalf), (AddressingMode)state.addressingModeU == ADDRESSING_WRAP, gather ? 0 : -1);
+			Short4 vvvv0 = offsetSample(vvvv, mipmap, OFFSET(Mipmap,vHalf), (AddressingMode)state.addressingModeV == ADDRESSING_WRAP, gather ? 0 : -1);
+			Short4 uuuu1 = offsetSample(uuuu, mipmap, OFFSET(Mipmap,uHalf), (AddressingMode)state.addressingModeU == ADDRESSING_WRAP, gather ? 2 : +1);
+			Short4 vvvv1 = offsetSample(vvvv, mipmap, OFFSET(Mipmap,vHalf), (AddressingMode)state.addressingModeV == ADDRESSING_WRAP, gather ? 2 : +1);
+
+			sampleTexel(c0, uuuu0, vvvv0, vvvv0, z, mipmap, buffer);		
+			sampleTexel(c1, uuuu1, vvvv0, vvvv0, z, mipmap, buffer);
+			sampleTexel(c2, uuuu0, vvvv1, vvvv1, z, mipmap, buffer);
+			sampleTexel(c3, uuuu1, vvvv1, vvvv1, z, mipmap, buffer);
+
+			if(!gather)   // Blend
+			{
+				// Fractions
+				Float4 fu = Fraction(Float4(As<UShort4>(uuuu0)) * *Pointer<Float4>(mipmap + OFFSET(Mipmap,fWidth)));
+				Float4 fv = Fraction(Float4(As<UShort4>(vvvv0)) * *Pointer<Float4>(mipmap + OFFSET(Mipmap,fHeight)));
+
+				if(componentCount >= 1) c0.r = c0.r + fu * (c1.r - c0.r);
+				if(componentCount >= 2) c0.g = c0.g + fu * (c1.g - c0.g);
+				if(componentCount >= 3) c0.b = c0.b + fu * (c1.b - c0.b);
+				if(componentCount >= 4) c0.a = c0.a + fu * (c1.a - c0.a);
+
+				if(componentCount >= 1) c2.r = c2.r + fu * (c3.r - c2.r);
+				if(componentCount >= 2) c2.g = c2.g + fu * (c3.g - c2.g);
+				if(componentCount >= 3) c2.b = c2.b + fu * (c3.b - c2.b);
+				if(componentCount >= 4) c2.a = c2.a + fu * (c3.a - c2.a);
+
+				if(componentCount >= 1) c.r = c0.r + fv * (c2.r - c0.r);
+				if(componentCount >= 2) c.g = c0.g + fv * (c2.g - c0.g);
+				if(componentCount >= 3) c.b = c0.b + fv * (c2.b - c0.b);
+				if(componentCount >= 4) c.a = c0.a + fv * (c2.a - c0.a);
+			}
+			else
+			{
+				c.r = c1.r;
+				c.g = c2.r;
+				c.b = c3.r;
+				c.a = c0.r;
+			}
+		}
+	}
+
+	void SamplerCore::sampleFloat3D(Pointer<Byte> &texture, Color4f &c, Float4 &u, Float4 &v, Float4 &w, Float &lod, bool secondLOD)
+	{
+		int componentCount = textureComponentCount();
+
+		Pointer<Byte> mipmap;
+		Pointer<Byte> buffer[4];
+		Int face[4];
+
+		selectMipmap(texture, buffer, mipmap, lod, face, secondLOD);
+
+		Short4 uuuu;
+		Short4 vvvv;
+		Short4 wwww;
+
+		address(uuuu, u, (AddressingMode)state.addressingModeU);
+		address(vvvv, v, (AddressingMode)state.addressingModeV);
+		address(wwww, w, (AddressingMode)state.addressingModeW);
+
+		if(state.textureFilter <= FILTER_POINT)
+		{
+			sampleTexel(c, uuuu, vvvv, wwww, w, mipmap, buffer);
+		}
+		else
+		{
+			Color4f &c0 = c;
+			Color4f c1;
+			Color4f c2;
+			Color4f c3;
+			Color4f c4;
+			Color4f c5;
+			Color4f c6;
+			Color4f c7;
+
+			Short4 uuuu0 = offsetSample(uuuu, mipmap, OFFSET(Mipmap,uHalf), (AddressingMode)state.addressingModeU == ADDRESSING_WRAP, -1);
+			Short4 vvvv0 = offsetSample(vvvv, mipmap, OFFSET(Mipmap,vHalf), (AddressingMode)state.addressingModeV == ADDRESSING_WRAP, -1);
+			Short4 wwww0 = offsetSample(wwww, mipmap, OFFSET(Mipmap,wHalf), (AddressingMode)state.addressingModeW == ADDRESSING_WRAP, -1);
+			Short4 uuuu1 = offsetSample(uuuu, mipmap, OFFSET(Mipmap,uHalf), (AddressingMode)state.addressingModeU == ADDRESSING_WRAP, +1);
+			Short4 vvvv1 = offsetSample(vvvv, mipmap, OFFSET(Mipmap,vHalf), (AddressingMode)state.addressingModeV == ADDRESSING_WRAP, +1);
+			Short4 wwww1 = offsetSample(wwww, mipmap, OFFSET(Mipmap,wHalf), (AddressingMode)state.addressingModeW == ADDRESSING_WRAP, +1);
+
+			sampleTexel(c0, uuuu0, vvvv0, wwww0, w, mipmap, buffer);
+			sampleTexel(c1, uuuu1, vvvv0, wwww0, w, mipmap, buffer);
+			sampleTexel(c2, uuuu0, vvvv1, wwww0, w, mipmap, buffer);
+			sampleTexel(c3, uuuu1, vvvv1, wwww0, w, mipmap, buffer);
+			sampleTexel(c4, uuuu0, vvvv0, wwww1, w, mipmap, buffer);
+			sampleTexel(c5, uuuu1, vvvv0, wwww1, w, mipmap, buffer);
+			sampleTexel(c6, uuuu0, vvvv1, wwww1, w, mipmap, buffer);
+			sampleTexel(c7, uuuu1, vvvv1, wwww1, w, mipmap, buffer);
+
+			// Fractions
+			Float4 fu = Fraction(Float4(As<UShort4>(uuuu0)) * *Pointer<Float4>(mipmap + OFFSET(Mipmap,fWidth)));
+			Float4 fv = Fraction(Float4(As<UShort4>(vvvv0)) * *Pointer<Float4>(mipmap + OFFSET(Mipmap,fHeight)));
+			Float4 fw = Fraction(Float4(As<UShort4>(wwww0)) * *Pointer<Float4>(mipmap + OFFSET(Mipmap,fDepth)));
+
+			// Blend first slice
+			if(componentCount >= 1) c0.r = c0.r + fu * (c1.r - c0.r);
+			if(componentCount >= 2) c0.g = c0.g + fu * (c1.g - c0.g);
+			if(componentCount >= 3) c0.b = c0.b + fu * (c1.b - c0.b);
+			if(componentCount >= 4) c0.a = c0.a + fu * (c1.a - c0.a);
+
+			if(componentCount >= 1) c2.r = c2.r + fu * (c3.r - c2.r);
+			if(componentCount >= 2) c2.g = c2.g + fu * (c3.g - c2.g);
+			if(componentCount >= 3) c2.b = c2.b + fu * (c3.b - c2.b);
+			if(componentCount >= 4) c2.a = c2.a + fu * (c3.a - c2.a);
+
+			if(componentCount >= 1) c0.r = c0.r + fv * (c2.r - c0.r);
+			if(componentCount >= 2) c0.g = c0.g + fv * (c2.g - c0.g);
+			if(componentCount >= 3) c0.b = c0.b + fv * (c2.b - c0.b);
+			if(componentCount >= 4) c0.a = c0.a + fv * (c2.a - c0.a);
+
+			// Blend second slice
+			if(componentCount >= 1) c4.r = c4.r + fu * (c5.r - c4.r);
+			if(componentCount >= 2) c4.g = c4.g + fu * (c5.g - c4.g);
+			if(componentCount >= 3) c4.b = c4.b + fu * (c5.b - c4.b);
+			if(componentCount >= 4) c4.a = c4.a + fu * (c5.a - c4.a);
+
+			if(componentCount >= 1) c6.r = c6.r + fu * (c7.r - c6.r);
+			if(componentCount >= 2) c6.g = c6.g + fu * (c7.g - c6.g);
+			if(componentCount >= 3) c6.b = c6.b + fu * (c7.b - c6.b);
+			if(componentCount >= 4) c6.a = c6.a + fu * (c7.a - c6.a);
+
+			if(componentCount >= 1) c4.r = c4.r + fv * (c6.r - c4.r);
+			if(componentCount >= 2) c4.g = c4.g + fv * (c6.g - c4.g);
+			if(componentCount >= 3) c4.b = c4.b + fv * (c6.b - c4.b);
+			if(componentCount >= 4) c4.a = c4.a + fv * (c6.a - c4.a);
+
+			// Blend slices
+			if(componentCount >= 1) c0.r = c0.r + fw * (c4.r - c0.r);
+			if(componentCount >= 2) c0.g = c0.g + fw * (c4.g - c0.g);
+			if(componentCount >= 3) c0.b = c0.b + fw * (c4.b - c0.b);
+			if(componentCount >= 4) c0.a = c0.a + fw * (c4.a - c0.a);
+		}
+	}
+
+	void SamplerCore::computeLod(Pointer<Byte> &texture, Float &lod, Float &anisotropy, Float4 &uDelta, Float4 &vDelta, Float4 &uuuu, Float4 &vvvv, Float &lodBias, Color4f &dsx, Color4f &dsy, bool bias, bool gradients, bool lodProvided)
+	{
+		if(!lodProvided)
+		{
+			Float4 duvdxy;
+
+			if(!gradients)
+			{
+				duvdxy = Float4(uuuu.yz, vvvv.yz) - Float4(uuuu.xx, vvvv.xx);
+			}
+			else
+			{
+				Float4 dudxy = Float4(dsx.x.xx, dsy.x.xx);
+				Float4 dvdxy = Float4(dsx.y.xx, dsy.y.xx);
+				
+				duvdxy = Float4(dudxy.xz, dvdxy.xz);
+			}
+
+			// Scale by texture dimensions and LOD
+			Float4 dUVdxy = duvdxy * *Pointer<Float4>(texture + OFFSET(Texture,widthHeightLOD));
+
+			Float4 dUV2dxy = dUVdxy * dUVdxy;
+			Float4 dUV2 = dUV2dxy.xy + dUV2dxy.zw;
+
+			lod = Max(Float(dUV2.x), Float(dUV2.y));   // Square length of major axis
+
+			if(state.textureFilter == FILTER_ANISOTROPIC)
+			{
+				Float det = Abs(Float(dUVdxy.x) * Float(dUVdxy.w) - Float(dUVdxy.y) * Float(dUVdxy.z));
+
+				Float4 dudx = duvdxy.xxxx;
+				Float4 dudy = duvdxy.yyyy;
+				Float4 dvdx = duvdxy.zzzz;
+				Float4 dvdy = duvdxy.wwww;
+
+				Int4 mask = As<Int4>(CmpNLT(dUV2.x, dUV2.y));
+				uDelta = As<Float4>(As<Int4>(dudx) & mask | As<Int4>(dudy) & ~mask);
+				vDelta = As<Float4>(As<Int4>(dvdx) & mask | As<Int4>(dvdy) & ~mask);
+
+				anisotropy = lod * Rcp_pp(det);
+				anisotropy = Min(anisotropy, *Pointer<Float>(texture + OFFSET(Texture,maxAnisotropy)));
+
+				lod *= Rcp_pp(anisotropy * anisotropy);
+			}
+
+			// log2(sqrt(lod))
+			lod = Float(As<Int>(lod));
+			lod -= Float(0x3F800000);
+			lod *= As<Float>(Int(0x33800000));
+
+			if(bias)
+			{
+				lod += lodBias;
+			}
+
+			// FIXME: Hack to satisfy WHQL
+			if(state.textureType == TEXTURE_CUBE)
+			{
+				lod += Float(-0.15f);
+			}
+		}
+		else
+		{
+			lod = lodBias + *Pointer<Float>(texture + OFFSET(Texture,LOD));
+		}
+
+		lod = Max(lod, Float(0));
+		lod = Min(lod, Float(MIPMAP_LEVELS - 2));   // Trilinear accesses lod+1
+	}
+
+	void SamplerCore::computeLod3D(Pointer<Byte> &texture, Float &lod, Float4 &uuuu, Float4 &vvvv, Float4 &wwww, Float &lodBias, Color4f &dsx, Color4f &dsy, bool bias, bool gradients, bool lodProvided)
+	{
+		if(state.mipmapFilter == MIPMAP_NONE)
+		{
+		}
+		else   // Point and linear filter
+		{
+			if(!lodProvided)
+			{
+				Float4 dudxy;
+				Float4 dvdxy;
+				Float4 dsdxy;
+
+				if(!gradients)
+				{
+					dudxy = uuuu.ywyw - uuuu;
+					dvdxy = vvvv.ywyw - vvvv;
+					dsdxy = wwww.ywyw - wwww;
+				}
+				else
+				{
+					dudxy = dsx.x;
+					dvdxy = dsx.y;
+					dsdxy = dsx.z;
+
+					dudxy = Float4(dudxy.xx, dsy.x.xx);
+					dvdxy = Float4(dvdxy.xx, dsy.y.xx);
+					dsdxy = Float4(dsdxy.xx, dsy.z.xx);
+
+					dudxy = Float4(dudxy.xz, dudxy.xz);
+					dvdxy = Float4(dvdxy.xz, dvdxy.xz);
+					dsdxy = Float4(dsdxy.xz, dsdxy.xz);
+				}
+
+				// Scale by texture dimensions and LOD
+				dudxy *= *Pointer<Float4>(texture + OFFSET(Texture,widthLOD));
+				dvdxy *= *Pointer<Float4>(texture + OFFSET(Texture,heightLOD));
+				dsdxy *= *Pointer<Float4>(texture + OFFSET(Texture,depthLOD));
+
+				dudxy *= dudxy;
+				dvdxy *= dvdxy;
+				dsdxy *= dsdxy;
+
+				dudxy += dvdxy;
+				dudxy += dsdxy;
+
+				lod = Max(Float(dudxy.x), Float(dudxy.y));   // FIXME: Max(dudxy.x, dudxy.y);
+
+				// log2(sqrt(lod))
+				lod = Float(As<Int>(lod));
+				lod -= Float(0x3F800000);
+				lod *= As<Float>(Int(0x33800000));
+
+				if(bias)
+				{
+					lod += lodBias;
+				}
+			}
+			else
+			{
+				lod = lodBias + *Pointer<Float>(texture + OFFSET(Texture,LOD));
+			}
+
+			lod = Max(lod, Float(0.0f));    // FIXME
+			lod = Min(lod, Float(MIPMAP_LEVELS - 2));   // Trilinear accesses lod+1
+		}
+	}
+
+	void SamplerCore::cubeFace(Int face[4], Float4 &U, Float4 &V, Float4 &lodU, Float4 &lodV, Float4 &x, Float4 &y, Float4 &z)
+	{
+		Int4 xp = CmpNLE(x, Float4(0.0f, 0.0f, 0.0f, 0.0f));   // x > 0
+		Int4 yp = CmpNLE(y, Float4(0.0f, 0.0f, 0.0f, 0.0f));   // y > 0
+		Int4 zp = CmpNLE(z, Float4(0.0f, 0.0f, 0.0f, 0.0f));   // z > 0
+
+		Float4 absX = Abs(x);
+		Float4 absY = Abs(y);
+		Float4 absZ = Abs(z);
+		Int4 xy = CmpNLE(absX, absY);   // abs(x) > abs(y)
+		Int4 yz = CmpNLE(absY, absZ);   // abs(y) > abs(z)
+		Int4 zx = CmpNLE(absZ, absX);   // abs(z) > abs(x)
+
+		Int4 xyz = ~zx & xy;   // abs(x) > abs(y) && abs(x) > abs(z)
+		Int4 yzx = ~xy & yz;   // abs(y) > abs(z) && abs(y) > abs(x)
+		Int4 zxy = ~yz & zx;   // abs(z) > abs(x) && abs(z) > abs(y)
+
+		// FACE_POSITIVE_X = 000b
+		// FACE_NEGATIVE_X = 001b
+		// FACE_POSITIVE_Y = 010b
+		// FACE_NEGATIVE_Y = 011b
+		// FACE_POSITIVE_Z = 100b
+		// FACE_NEGATIVE_Z = 101b
+
+		Int yAxis = SignMask(yzx);
+		Int zAxis = SignMask(zxy);
+
+		Int4 n = (~xp & xyz) | (~yp & yzx) | (~zp & zxy);
+		Int negative = SignMask(n);
+
+		face[0] = *Pointer<Int>(constants + OFFSET(Constants,transposeBit0) + negative * 4);
+		face[0] |= *Pointer<Int>(constants + OFFSET(Constants,transposeBit1) + yAxis * 4);
+		face[0] |= *Pointer<Int>(constants + OFFSET(Constants,transposeBit2) + zAxis * 4);
+		face[1] = (face[0] >> 4)  & 0x7;
+		face[2] = (face[0] >> 8)  & 0x7;
+		face[3] = (face[0] >> 12) & 0x7;
+		face[0] &= 0x7;
+
+		// U = xyz * -z + ~xyz * (yzx * ~yp * -x + (yzx * ~yp) * x)
+		U = As<Float4>((xyz & As<Int4>(-z)) | (~xyz & (((yzx & ~yp) & Int4(0x80000000, 0x80000000, 0x80000000, 0x80000000)) ^ As<Int4>(x))));
+
+		// V = yzx * z + ~yzx * (~neg * -y + neg * y)
+		V = As<Float4>((~yzx & ((~n & Int4(0x80000000, 0x80000000, 0x80000000, 0x80000000)) ^ As<Int4>(y))) | (yzx & As<Int4>(z)));
+
+		// M = xyz * x + yzx * y + zxy * z
+		Float4 M = As<Float4>((xyz & As<Int4>(x)) | (yzx & As<Int4>(y)) | (zxy & As<Int4>(z)));
+		
+		M = reciprocal(M);
+		U *= M * Float4(0.5f, 0.5f, 0.5f, 0.5f);
+		V *= M * Float4(0.5f, 0.5f, 0.5f, 0.5f);
+
+		// Project coordinates onto one face for consistent LOD calculation
+		{
+			yp = Swizzle(yp, 0);
+			n = Swizzle(n, 0);
+			xyz = Swizzle(xyz, 0);
+			yzx = Swizzle(yzx, 0);
+			zxy = Swizzle(zxy, 0);
+
+			// U = xyz * -z + ~xyz * (yzx * ~yp * -x + (yzx * ~yp) * x)
+			lodU = As<Float4>((xyz & As<Int4>(-z)) | (~xyz & (((yzx & ~yp) & Int4(0x80000000, 0x80000000, 0x80000000, 0x80000000)) ^ As<Int4>(x))));
+
+			// V = yzx * z + ~yzx * (~neg * -y + neg * y)
+			lodV = As<Float4>((~yzx & ((~n & Int4(0x80000000, 0x80000000, 0x80000000, 0x80000000)) ^ As<Int4>(y))) | (yzx & As<Int4>(z)));
+
+			// M = xyz * x + yzx * y + zxy * z
+			Float4 M = As<Float4>((xyz & As<Int4>(x)) | (yzx & As<Int4>(y)) | (zxy & As<Int4>(z)));
+			
+			M = Rcp_pp(M);
+			lodU *= M * Float4(0.5f, 0.5f, 0.5f, 0.5f);
+			lodV *= M * Float4(0.5f, 0.5f, 0.5f, 0.5f);
+		}
+	}
+
+	void SamplerCore::computeIndices(Int index[4], Short4 uuuu, Short4 vvvv, Short4 wwww, const Pointer<Byte> &mipmap)
+	{
+		Short4 uuu2;
+
+		if(!state.hasNPOTTexture && !hasFloatTexture())
+		{
+			vvvv = As<UShort4>(vvvv) >> *Pointer<Long1>(mipmap + OFFSET(Mipmap,vFrac));
+			uuu2 = uuuu;
+			uuuu = As<Short4>(UnpackLow(uuuu, vvvv));
+			uuu2 = As<Short4>(UnpackHigh(uuu2, vvvv));
+			uuuu = As<Short4>(As<UInt2>(uuuu) >> *Pointer<Long1>(mipmap + OFFSET(Mipmap,uFrac)));
+			uuu2 = As<Short4>(As<UInt2>(uuu2) >> *Pointer<Long1>(mipmap + OFFSET(Mipmap,uFrac)));
+
+			if(state.textureType == TEXTURE_3D)
+			{
+				wwww = As<UShort4>(wwww) >> *Pointer<Long1>(mipmap + OFFSET(Mipmap,wFrac));
+				Short4 www2 = wwww;
+				wwww = As<Short4>(UnpackLow(wwww, wwww));
+				www2 = As<Short4>(UnpackHigh(www2, www2));
+				wwww = As<Short4>(As<UInt2>(wwww) >> 16);
+				www2 = As<Short4>(As<UInt2>(www2) >> 16);
+				wwww = As<Short4>(As<Int2>(wwww) << *Pointer<Long1>(mipmap + OFFSET(Mipmap,uInt)));
+				www2 = As<Short4>(As<Int2>(www2) << *Pointer<Long1>(mipmap + OFFSET(Mipmap,uInt)));
+				wwww = As<Short4>(As<Int2>(wwww) << *Pointer<Long1>(mipmap + OFFSET(Mipmap,vInt)));   // FIXME: Combine uInt and vInt shift
+				www2 = As<Short4>(As<Int2>(www2) << *Pointer<Long1>(mipmap + OFFSET(Mipmap,vInt)));
+				uuuu = As<Short4>(As<Int2>(uuuu) + As<Int2>(wwww));
+				uuu2 = As<Short4>(As<Int2>(uuu2) + As<Int2>(www2));
+			}
+		}
+		else
+		{
+			uuuu = MulHigh(As<UShort4>(uuuu), *Pointer<UShort4>(mipmap + OFFSET(Mipmap,width)));
+			vvvv = MulHigh(As<UShort4>(vvvv), *Pointer<UShort4>(mipmap + OFFSET(Mipmap,height)));
+			uuu2 = uuuu;
+			uuuu = As<Short4>(UnpackLow(uuuu, vvvv));
+			uuu2 = As<Short4>(UnpackHigh(uuu2, vvvv));
+			uuuu = As<Short4>(MulAdd(uuuu, *Pointer<Short4>(mipmap + OFFSET(Mipmap,onePitchP))));
+			uuu2 = As<Short4>(MulAdd(uuu2, *Pointer<Short4>(mipmap + OFFSET(Mipmap,onePitchP))));
+
+			if(state.textureType == TEXTURE_3D)
+			{
+				wwww = MulHigh(As<UShort4>(wwww), *Pointer<UShort4>(mipmap + OFFSET(Mipmap,depth)));
+				Short4 www2 = wwww;
+				wwww = As<Short4>(UnpackLow(wwww, Short4(0x0000, 0x0000, 0x0000, 0x0000)));
+				www2 = As<Short4>(UnpackHigh(www2, Short4(0x0000, 0x0000, 0x0000, 0x0000)));
+				wwww = As<Short4>(MulAdd(wwww, *Pointer<Short4>(mipmap + OFFSET(Mipmap,sliceP))));
+				www2 = As<Short4>(MulAdd(www2, *Pointer<Short4>(mipmap + OFFSET(Mipmap,sliceP))));
+				uuuu = As<Short4>(As<Int2>(uuuu) + As<Int2>(wwww));
+				uuu2 = As<Short4>(As<Int2>(uuu2) + As<Int2>(www2));
+			}
+		}
+
+		index[0] = Extract(As<Int2>(uuuu), 0);
+		index[1] = Extract(As<Int2>(uuuu), 1);
+		index[2] = Extract(As<Int2>(uuu2), 0);
+		index[3] = Extract(As<Int2>(uuu2), 1);
+	}
+
+	void SamplerCore::sampleTexel(Color4i &c, Short4 &uuuu, Short4 &vvvv, Short4 &wwww, Pointer<Byte> &mipmap, Pointer<Byte> buffer[4])
+	{
+		Int index[4];
+
+		computeIndices(index, uuuu, vvvv, wwww, mipmap);
+
+		int f0 = state.textureType == TEXTURE_CUBE ? 0 : 0;
+		int f1 = state.textureType == TEXTURE_CUBE ? 1 : 0;
+		int f2 = state.textureType == TEXTURE_CUBE ? 2 : 0;
+		int f3 = state.textureType == TEXTURE_CUBE ? 3 : 0;
+
+		if(!has16bitTexture())
+		{
+			Int c0;
+			Int c1;
+			Int c2;
+			Int c3;
+
+			switch(textureComponentCount())
+			{
+			case 4:
+				{
+					Byte8 c0 = *Pointer<Byte8>(buffer[f0] + 4 * index[0]);
+					Byte8 c1 = *Pointer<Byte8>(buffer[f1] + 4 * index[1]);
+					Byte8 c2 = *Pointer<Byte8>(buffer[f2] + 4 * index[2]);
+					Byte8 c3 = *Pointer<Byte8>(buffer[f3] + 4 * index[3]);
+					c.r = UnpackLow(c0, c1);
+					c.g = UnpackLow(c2, c3);
+					
+					switch(state.textureFormat)
+					{
+					case FORMAT_A8R8G8B8:
+						c.b = c.r;
+						c.b = As<Short4>(UnpackLow(c.b, c.g));
+						c.r = As<Short4>(UnpackHigh(c.r, c.g));
+						c.g = c.b;
+						c.a = c.r;
+						c.b = UnpackLow(As<Byte8>(c.b), As<Byte8>(c.b));
+						c.g = UnpackHigh(As<Byte8>(c.g), As<Byte8>(c.g));
+						c.r = UnpackLow(As<Byte8>(c.r), As<Byte8>(c.r));
+						c.a = UnpackHigh(As<Byte8>(c.a), As<Byte8>(c.a));
+						break;
+					case FORMAT_Q8W8V8U8:
+						c.b = c.r;
+						c.r = As<Short4>(UnpackLow(c.r, c.g));
+						c.b = As<Short4>(UnpackHigh(c.b, c.g));
+						c.g = c.r;
+						c.a = c.b;
+						c.r = UnpackLow(As<Byte8>(c.r), As<Byte8>(c.r));
+						c.g = UnpackHigh(As<Byte8>(c.g), As<Byte8>(c.g));
+						c.b = UnpackLow(As<Byte8>(c.b), As<Byte8>(c.b));
+						c.a = UnpackHigh(As<Byte8>(c.a), As<Byte8>(c.a));
+						break;
+					default:
+						ASSERT(false);
+					}
+				}
+				break;
+			case 3:
+				{
+					Byte8 c0 = *Pointer<Byte8>(buffer[f0] + 4 * index[0]);
+					Byte8 c1 = *Pointer<Byte8>(buffer[f1] + 4 * index[1]);
+					Byte8 c2 = *Pointer<Byte8>(buffer[f2] + 4 * index[2]);
+					Byte8 c3 = *Pointer<Byte8>(buffer[f3] + 4 * index[3]);
+					c.r = UnpackLow(c0, c1);
+					c.g = UnpackLow(c2, c3);
+
+					switch(state.textureFormat)
+					{
+					case FORMAT_X8R8G8B8:
+						c.b = c.r;
+						c.b = As<Short4>(UnpackLow(c.b, c.g));
+						c.r = As<Short4>(UnpackHigh(c.r, c.g));
+						c.g = c.b;
+						c.b = UnpackLow(As<Byte8>(c.b), As<Byte8>(c.b));
+						c.g = UnpackHigh(As<Byte8>(c.g), As<Byte8>(c.g));
+						c.r = UnpackLow(As<Byte8>(c.r), As<Byte8>(c.r));
+						break;
+					case FORMAT_X8L8V8U8:
+						c.b = c.r;
+						c.r = As<Short4>(UnpackLow(c.r, c.g));
+						c.b = As<Short4>(UnpackHigh(c.b, c.g));
+						c.g = c.r;
+						c.r = UnpackLow(As<Byte8>(c.r), As<Byte8>(Short4(0x0000, 0x0000, 0x0000, 0x0000)));
+						c.r = c.r << 8;
+						c.g = UnpackHigh(As<Byte8>(c.g), As<Byte8>(Short4(0x0000, 0x0000, 0x0000, 0x0000)));
+						c.g = c.g << 8;
+						c.b = UnpackLow(As<Byte8>(c.b), As<Byte8>(c.b));
+						break;
+					default:
+						ASSERT(false);
+					}
+				}
+				break;
+			case 2:
+				c.r = Insert(c.r, *Pointer<Short>(buffer[f0] + 2 * index[0]), 0);
+				c.r = Insert(c.r, *Pointer<Short>(buffer[f1] + 2 * index[1]), 1);
+				c.r = Insert(c.r, *Pointer<Short>(buffer[f2] + 2 * index[2]), 2);
+				c.r = Insert(c.r, *Pointer<Short>(buffer[f3] + 2 * index[3]), 3);
+
+				switch(state.textureFormat)
+				{
+				case FORMAT_G8R8:
+				case FORMAT_V8U8:
+				case FORMAT_A8L8:
+					// FIXME: Unpack properly to 0.16 format
+					c.g = c.r;
+					c.r = c.r << 8;
+					break;
+				default:
+					ASSERT(false);
+				}
+				break;
+			case 1:
+				c0 = Int(*Pointer<Byte>(buffer[f0] + index[0]));
+				c1 = Int(*Pointer<Byte>(buffer[f1] + index[1]));
+				c2 = Int(*Pointer<Byte>(buffer[f2] + index[2]));
+				c3 = Int(*Pointer<Byte>(buffer[f3] + index[3]));
+				c0 = c0 | (c1 << 8) | (c2 << 16) | (c3 << 24);
+				c.r = As<Short4>(Int2(c0));
+				c.r = UnpackLow(As<Byte8>(c.r), As<Byte8>(c.r));
+				break;
+			default:
+				ASSERT(false);
+			}
+		}
+		else
+		{
+			switch(textureComponentCount())
+			{
+			case 4:
+				c.r = *Pointer<Short4>(buffer[f0] + 8 * index[0]);
+				c.g = *Pointer<Short4>(buffer[f1] + 8 * index[1]);
+				c.b = *Pointer<Short4>(buffer[f2] + 8 * index[2]);
+				c.a = *Pointer<Short4>(buffer[f3] + 8 * index[3]);
+				transpose4x4(c.r, c.g, c.b, c.a);
+				break;
+			case 2:
+				c.r = *Pointer<Short4>(buffer[f0] + 4 * index[0]);
+				c.r = As<Short4>(UnpackLow(c.r, *Pointer<Short4>(buffer[f1] + 4 * index[1])));
+				c.b = *Pointer<Short4>(buffer[f2] + 4 * index[2]);
+				c.b = As<Short4>(UnpackLow(c.b, *Pointer<Short4>(buffer[f3] + 4 * index[3])));
+				c.g = c.r;
+				c.r = As<Short4>(UnpackLow(As<Int2>(c.r), As<Int2>(c.b)));
+				c.g = As<Short4>(UnpackHigh(As<Int2>(c.g), As<Int2>(c.b)));
+				break;
+			case 1:
+				c.r = Insert(c.r, *Pointer<Short>(buffer[f0] + 2 * index[0]), 0);
+				c.r = Insert(c.r, *Pointer<Short>(buffer[f1] + 2 * index[1]), 1);
+				c.r = Insert(c.r, *Pointer<Short>(buffer[f2] + 2 * index[2]), 2);
+				c.r = Insert(c.r, *Pointer<Short>(buffer[f3] + 2 * index[3]), 3);
+				break;
+			default:
+				ASSERT(false);
+			}
+		}
+	}
+
+	void SamplerCore::sampleTexel(Color4f &c, Short4 &uuuu, Short4 &vvvv, Short4 &wwww, Float4 &z, Pointer<Byte> &mipmap, Pointer<Byte> buffer[4])
+	{
+		Int index[4];
+
+		computeIndices(index, uuuu, vvvv, wwww, mipmap);
+
+		int f0 = state.textureType == TEXTURE_CUBE ? 0 : 0;
+		int f1 = state.textureType == TEXTURE_CUBE ? 1 : 0;
+		int f2 = state.textureType == TEXTURE_CUBE ? 2 : 0;
+		int f3 = state.textureType == TEXTURE_CUBE ? 3 : 0;
+
+		// Read texels
+		switch(textureComponentCount())
+		{
+		case 4:
+			c.r = *Pointer<Float4>(buffer[f0] + index[0] * 16, 16);
+			c.g = *Pointer<Float4>(buffer[f1] + index[1] * 16, 16);
+			c.b = *Pointer<Float4>(buffer[f2] + index[2] * 16, 16);
+			c.a = *Pointer<Float4>(buffer[f3] + index[3] * 16, 16);
+			transpose4x4(c.r, c.g, c.b, c.a);
+			break;
+		case 2:
+			// FIXME: Optimal shuffling?
+			c.r.xy = *Pointer<Float4>(buffer[f0] + index[0] * 8);
+			c.r.zw = *Pointer<Float4>(buffer[f1] + index[1] * 8 - 8);
+			c.b.xy = *Pointer<Float4>(buffer[f2] + index[2] * 8);
+			c.b.zw = *Pointer<Float4>(buffer[f3] + index[3] * 8 - 8);
+			c.g = c.r;
+			c.r = Float4(c.r.xz, c.b.xz);
+			c.g = Float4(c.g.yw, c.b.yw);
+			break;
+		case 1:
+			// FIXME: Optimal shuffling?
+			c.r.x = *Pointer<Float>(buffer[f0] + index[0] * 4);
+			c.r.y = *Pointer<Float>(buffer[f1] + index[1] * 4);
+			c.r.z = *Pointer<Float>(buffer[f2] + index[2] * 4);
+			c.r.w = *Pointer<Float>(buffer[f3] + index[3] * 4);
+
+			if(state.textureFormat == FORMAT_D32F_SHADOW && state.textureFilter != FILTER_GATHER)
+			{
+				Float4 d = Min(Max(z, Float4(0.0f)), Float4(1.0f));
+
+				c.r = As<Float4>(As<Int4>(CmpNLT(c.r, d)) & As<Int4>(Float4(1.0f, 1.0f, 1.0f, 1.0f)));   // FIXME: Only less-equal?
+			}
+			break;
+		default:
+			ASSERT(false);
+		}
+	}
+
+	void SamplerCore::selectMipmap(Pointer<Byte> &texture, Pointer<Byte> buffer[4], Pointer<Byte> &mipmap, Float &lod, Int face[4], bool secondLOD)
+	{
+		if(state.mipmapFilter < MIPMAP_POINT)
+		{
+			mipmap = texture + OFFSET(Texture,mipmap[0]);
+		}
+		else
+		{
+			Int ilod;
+
+			if(state.mipmapFilter == MIPMAP_POINT)
+			{
+				ilod = RoundInt(lod);
+			}
+			else   // Linear
+			{
+				ilod = Int(lod);
+			}
+
+			mipmap = texture + OFFSET(Texture,mipmap) + ilod * sizeof(Mipmap) + secondLOD * sizeof(Mipmap);
+		}
+
+		if(state.textureType != TEXTURE_CUBE)
+		{
+			buffer[0] = *Pointer<Pointer<Byte>>(mipmap + OFFSET(Mipmap,buffer[0]));
+		}
+		else
+		{
+			for(int i = 0; i < 4; i++)
+			{
+				buffer[i] = *Pointer<Pointer<Byte>>(mipmap + OFFSET(Mipmap,buffer) + face[i] * sizeof(void*));
+			}
+		}
+	}
+
+	void SamplerCore::address(Short4 &uuuu, Float4 &uw, AddressingMode addressingMode)
+	{
+		if(addressingMode == ADDRESSING_CLAMP)
+		{
+			Float4 clamp = Min(Max(uw, Float4(0.0f)), Float4(65535.0f / 65536.0f));
+
+			uuuu = Short4(Int4(clamp * Float4(1 << 16)));
+		}
+		else if(addressingMode == ADDRESSING_MIRROR)
+		{
+			Int4 convert = Int4(uw * Float4(1 << 16));
+			Int4 mirror = (convert << 15) >> 31;
+
+			convert ^= mirror;
+
+			uuuu = Short4(convert);
+		}
+		else if(addressingMode == ADDRESSING_MIRRORONCE)
+		{
+			// Absolute value
+			Int4 convert = Int4(Abs(uw * Float4(1 << 16)));
+
+			// Clamp
+			convert -= Int4(0x00008000, 0x00008000, 0x00008000, 0x00008000);
+			convert = As<Int4>(Pack(convert, convert));
+
+			uuuu = As<Short4>(Int2(convert)) + Short4((short)0x8000, (short)0x8000, (short)0x8000, (short)0x8000);
+		}
+		else   // Wrap (or border)
+		{
+			uuuu = Short4(Int4(uw * Float4(1 << 16)));
+		}
+	}
+
+	void SamplerCore::convertFixed12(Short4 &ci, Float4 &cf)
+	{
+		ci = RoundShort4(cf * Float4(0x1000, 0x1000, 0x1000, 0x1000));
+	}
+
+	void SamplerCore::convertFixed12(Color4i &ci, Color4f &cf)
+	{
+		convertFixed12(ci.r, cf.r);
+		convertFixed12(ci.g, cf.g);
+		convertFixed12(ci.b, cf.b);
+		convertFixed12(ci.a, cf.a);
+	}
+
+	void SamplerCore::convertSigned12(Float4 &cf, Short4 &ci)
+	{
+		cf = Float4(ci) * Float4(1.0f / 0x0FFE);
+	}
+
+//	void SamplerCore::convertSigned12(Color4f &cf, Color4i &ci)
+//	{
+//		convertSigned12(cf.r, ci.r);
+//		convertSigned12(cf.g, ci.g);
+//		convertSigned12(cf.b, ci.b);
+//		convertSigned12(cf.a, ci.a);
+//	}
+
+	void SamplerCore::convertSigned15(Float4 &cf, Short4 &ci)
+	{
+		cf = Float4(ci) * Float4(1.0f / 0x7FFF, 1.0f / 0x7FFF, 1.0f / 0x7FFF, 1.0f / 0x7FFF);
+	}
+
+	void SamplerCore::convertUnsigned16(Float4 &cf, Short4 &ci)
+	{
+		cf = Float4(As<UShort4>(ci)) * Float4(1.0f / 0xFFFF, 1.0f / 0xFFFF, 1.0f / 0xFFFF, 1.0f / 0xFFFF);
+	}
+
+	void SamplerCore::sRGBtoLinear16_12(Short4 &c)
+	{
+		c = As<UShort4>(c) >> 8;
+
+		Pointer<Byte> LUT = Pointer<Byte>(constants + OFFSET(Constants,sRGBtoLinear8));
+
+		c = Insert(c, *Pointer<Short>(LUT + 2 * Int(Extract(c, 0))), 0);
+		c = Insert(c, *Pointer<Short>(LUT + 2 * Int(Extract(c, 1))), 1);
+		c = Insert(c, *Pointer<Short>(LUT + 2 * Int(Extract(c, 2))), 2);
+		c = Insert(c, *Pointer<Short>(LUT + 2 * Int(Extract(c, 3))), 3);
+	}
+
+	bool SamplerCore::hasFloatTexture() const
+	{
+		return Surface::isFloatFormat((Format)state.textureFormat);
+	}
+
+	bool SamplerCore::hasUnsignedTextureComponent(int component) const
+	{
+		return Surface::isUnsignedComponent((Format)state.textureFormat, component);
+	}
+
+	int SamplerCore::textureComponentCount() const
+	{
+		return Surface::componentCount((Format)state.textureFormat);
+	}
+
+	bool SamplerCore::has16bitTexture() const
+	{
+		switch(state.textureFormat)
+		{
+		case FORMAT_G8R8:
+		case FORMAT_X8R8G8B8:
+		case FORMAT_A8R8G8B8:
+		case FORMAT_V8U8:
+		case FORMAT_Q8W8V8U8:
+		case FORMAT_X8L8V8U8:
+		case FORMAT_R32F:
+		case FORMAT_G32R32F:
+		case FORMAT_A32B32G32R32F:
+		case FORMAT_A8:
+		case FORMAT_R8:
+		case FORMAT_L8:
+		case FORMAT_A8L8:
+		case FORMAT_D32F_LOCKABLE:
+		case FORMAT_D32F_TEXTURE:
+		case FORMAT_D32F_SHADOW:
+			return false;
+		case FORMAT_L16:
+		case FORMAT_G16R16:
+		case FORMAT_A16B16G16R16:
+		case FORMAT_V16U16:
+		case FORMAT_A16W16V16U16:
+		case FORMAT_Q16W16V16U16:
+			return true;
+		default:
+			ASSERT(false);
+		}
+		
+		return false;
+	}
+
+	bool SamplerCore::isRGBComponent(int component) const
+	{
+		switch(state.textureFormat)
+		{
+		case FORMAT_G8R8:          return component < 2;
+		case FORMAT_X8R8G8B8:      return component < 3;
+		case FORMAT_A8R8G8B8:      return component < 3;
+		case FORMAT_V8U8:          return false;
+		case FORMAT_Q8W8V8U8:      return false;
+		case FORMAT_X8L8V8U8:      return false;
+		case FORMAT_R32F:          return component < 1;
+		case FORMAT_G32R32F:       return component < 2;
+		case FORMAT_A32B32G32R32F: return component < 3;
+		case FORMAT_A8:            return false;
+		case FORMAT_R8:            return component < 1;
+		case FORMAT_L8:            return component < 1;
+		case FORMAT_A8L8:          return component < 1;
+		case FORMAT_D32F_LOCKABLE: return false;
+		case FORMAT_D32F_TEXTURE:  return false;
+		case FORMAT_D32F_SHADOW:   return false;
+		case FORMAT_L16:           return component < 1;
+		case FORMAT_G16R16:        return component < 2;
+		case FORMAT_A16B16G16R16:  return component < 3;
+		case FORMAT_V16U16:        return false;
+		case FORMAT_A16W16V16U16:  return false;
+		case FORMAT_Q16W16V16U16:  return false;
+		default:
+			ASSERT(false);
+		}
+		
+		return false;
+	}
+}