Adding Texture3D support.

Bug 19126833

Added Texture3D argument verifications.
Added the basic API and functions. A few are still unimplemented:
- Image::loadCompressedData() (for depth other than 1)
- Texture3D::copyImage()
- Texture3D::generateMipmaps()
Added colour grading test for 3D texture

Change-Id: I9e52afa7213999f94c5916c2f301fc6fa4b42c0d
Reviewed-on: https://swiftshader-review.googlesource.com/1730
Tested-by: Alexis Hétu <sugoi@google.com>
Reviewed-by: Alexis Hétu <sugoi@google.com>
Reviewed-by: Nicolas Capens <capn@google.com>
diff --git a/src/OpenGL/libGLESv2/Context.cpp b/src/OpenGL/libGLESv2/Context.cpp
index da05a8a..d228d29 100644
--- a/src/OpenGL/libGLESv2/Context.cpp
+++ b/src/OpenGL/libGLESv2/Context.cpp
@@ -131,6 +131,7 @@
     // objects all of whose names are 0.

 

     mTexture2DZero.set(new Texture2D(0));

+	mTexture3DZero.set(new Texture3D(0));

     mTextureCubeMapZero.set(new TextureCubeMap(0));

     mTextureExternalZero.set(new TextureExternal(0));

 

@@ -212,6 +213,7 @@
     mState.renderbuffer.set(NULL);

 

     mTexture2DZero.set(NULL);

+	mTexture3DZero.set(NULL);

     mTextureCubeMapZero.set(NULL);

     mTextureExternalZero.set(NULL);

 

@@ -951,6 +953,13 @@
     mState.samplerTexture[TEXTURE_EXTERNAL][mState.activeSampler].set(getTexture(texture));

 }

 

+void Context::bindTexture3D(GLuint texture)

+{

+	mResourceManager->checkTextureAllocation(texture, TEXTURE_3D);

+

+	mState.samplerTexture[TEXTURE_3D][mState.activeSampler].set(getTexture(texture));

+}

+

 void Context::bindReadFramebuffer(GLuint framebuffer)

 {

     if(!getFramebuffer(framebuffer))

@@ -1162,7 +1171,12 @@
 

 Texture2D *Context::getTexture2D()

 {

-    return static_cast<Texture2D*>(getSamplerTexture(mState.activeSampler, TEXTURE_2D));

+	return static_cast<Texture2D*>(getSamplerTexture(mState.activeSampler, TEXTURE_2D));

+}

+

+Texture3D *Context::getTexture3D()

+{

+	return static_cast<Texture3D*>(getSamplerTexture(mState.activeSampler, TEXTURE_3D));

 }

 

 TextureCubeMap *Context::getTextureCubeMap()

@@ -1184,6 +1198,7 @@
         switch (type)

         {

         case TEXTURE_2D: return mTexture2DZero.get();

+		case TEXTURE_3D: return mTexture3DZero.get();

         case TEXTURE_CUBE: return mTextureCubeMapZero.get();

         case TEXTURE_EXTERNAL: return mTextureExternalZero.get();

         default: UNREACHABLE();

@@ -1486,7 +1501,18 @@
             *params = mState.samplerTexture[TEXTURE_EXTERNAL][mState.activeSampler].id();

         }

         break;

-    default:

+	case GL_TEXTURE_BINDING_3D_OES:

+		{

+			if(mState.activeSampler < 0 || mState.activeSampler > MAX_COMBINED_TEXTURE_IMAGE_UNITS - 1)

+			{

+				error(GL_INVALID_OPERATION);

+				return false;

+			}

+

+			*params = mState.samplerTexture[TEXTURE_3D][mState.activeSampler].id();

+	}

+		break;

+	default:

         return false;

     }

 

@@ -1576,6 +1602,7 @@
       case GL_TEXTURE_BINDING_2D:

       case GL_TEXTURE_BINDING_CUBE_MAP:

       case GL_TEXTURE_BINDING_EXTERNAL_OES:

+	  case GL_TEXTURE_BINDING_3D_OES:

         {

             *type = GL_INT;

             *numParams = 1;

@@ -2028,12 +2055,14 @@
             {

                 GLenum wrapS = texture->getWrapS();

                 GLenum wrapT = texture->getWrapT();

+				GLenum wrapR = texture->getWrapR();

                 GLenum texFilter = texture->getMinFilter();

                 GLenum magFilter = texture->getMagFilter();

 				GLfloat maxAnisotropy = texture->getMaxAnisotropy();

 

 				device->setAddressingModeU(samplerType, samplerIndex, es2sw::ConvertTextureWrap(wrapS));

-                device->setAddressingModeV(samplerType, samplerIndex, es2sw::ConvertTextureWrap(wrapT));

+				device->setAddressingModeV(samplerType, samplerIndex, es2sw::ConvertTextureWrap(wrapT));

+				device->setAddressingModeW(samplerType, samplerIndex, es2sw::ConvertTextureWrap(wrapR));

 

 				sw::FilterType minFilter;

 				sw::MipmapType mipFilter;

@@ -2109,6 +2138,27 @@
 				device->setTextureLevel(sampler, 0, mipmapLevel, surface, sw::TEXTURE_2D);

 			}

 		}

+		else if(baseTexture->getTarget() == GL_TEXTURE_3D_OES)

+		{

+			Texture3D *texture = static_cast<Texture3D*>(baseTexture);

+

+			for(int mipmapLevel = 0; mipmapLevel < MIPMAP_LEVELS; mipmapLevel++)

+			{

+				int surfaceLevel = mipmapLevel;

+

+				if(surfaceLevel < 0)

+				{

+					surfaceLevel = 0;

+				}

+				else if(surfaceLevel >= levelCount)

+				{

+					surfaceLevel = levelCount - 1;

+				}

+

+				egl::Image *surface = texture->getImage(surfaceLevel);

+				device->setTextureLevel(sampler, 0, mipmapLevel, surface, sw::TEXTURE_3D);

+			}

+		}

 		else if(baseTexture->getTarget() == GL_TEXTURE_CUBE_MAP)

 		{

 			for(int face = 0; face < 6; face++)

diff --git a/src/OpenGL/libGLESv2/Context.h b/src/OpenGL/libGLESv2/Context.h
index 42c8c19..5331af3 100644
--- a/src/OpenGL/libGLESv2/Context.h
+++ b/src/OpenGL/libGLESv2/Context.h
@@ -49,6 +49,7 @@
 class Program;

 class Texture;

 class Texture2D;

+class Texture3D;

 class TextureCubeMap;

 class TextureExternal;

 class Framebuffer;

@@ -370,6 +371,7 @@
     void bindTexture2D(GLuint texture);

     void bindTextureCubeMap(GLuint texture);

     void bindTextureExternal(GLuint texture);

+	void bindTexture3D(GLuint texture);

     void bindReadFramebuffer(GLuint framebuffer);

     void bindDrawFramebuffer(GLuint framebuffer);

     void bindRenderbuffer(GLuint renderbuffer);

@@ -397,7 +399,8 @@
     Buffer *getElementArrayBuffer();

     Program *getCurrentProgram();

     Texture2D *getTexture2D();

-    TextureCubeMap *getTextureCubeMap();

+	Texture3D *getTexture3D();

+	TextureCubeMap *getTextureCubeMap();

     TextureExternal *getTextureExternal();

     Texture *getSamplerTexture(unsigned int sampler, TextureType type);

     Framebuffer *getReadFramebuffer();

@@ -460,8 +463,9 @@
 

     State mState;

 

-    gl::BindingPointer<Texture2D> mTexture2DZero;

-    gl::BindingPointer<TextureCubeMap> mTextureCubeMapZero;

+	gl::BindingPointer<Texture2D> mTexture2DZero;

+	gl::BindingPointer<Texture3D> mTexture3DZero;

+	gl::BindingPointer<TextureCubeMap> mTextureCubeMapZero;

     gl::BindingPointer<TextureExternal> mTextureExternalZero;

 

     typedef std::map<GLint, Framebuffer*> FramebufferMap;

diff --git a/src/OpenGL/libGLESv2/Image.cpp b/src/OpenGL/libGLESv2/Image.cpp
index a78694c..6a36db5 100644
--- a/src/OpenGL/libGLESv2/Image.cpp
+++ b/src/OpenGL/libGLESv2/Image.cpp
@@ -22,28 +22,28 @@
 {
 	enum DataType
 	{
-		Alpha,

-		AlphaFloat,

-		AlphaHalfFloat,

-		Luminance,

-		LuminanceFloat,

-		LuminanceHalfFloat,

-		LuminanceAlpha,

-		LuminanceAlphaFloat,

-		LuminanceAlphaHalfFloat,

-		RGBUByte,

-		RGB565,

-		RGBFloat,

-		RGBHalfFloat,

-		RGBAUByte,

-		RGBA4444,

-		RGBA5551,

-		RGBAFloat,

-		RGBAHalfFloat,

-		BGRA,

-		D16,

-		D24,

-		D32,

+		Alpha,
+		AlphaFloat,
+		AlphaHalfFloat,
+		Luminance,
+		LuminanceFloat,
+		LuminanceHalfFloat,
+		LuminanceAlpha,
+		LuminanceAlphaFloat,
+		LuminanceAlphaHalfFloat,
+		RGBUByte,
+		RGB565,
+		RGBFloat,
+		RGBHalfFloat,
+		RGBAUByte,
+		RGBA4444,
+		RGBA5551,
+		RGBAFloat,
+		RGBAHalfFloat,
+		BGRA,
+		D16,
+		D24,
+		D32,
 		S8,
 	};
 
@@ -53,13 +53,13 @@
 		UNIMPLEMENTED();
 	}
 
-	template<>
+	template<>

 	void LoadImageRow<Alpha>(const unsigned char *source, unsigned char *dest, GLint xoffset, GLsizei width)
 	{
 		memcpy(dest + xoffset, source, width);
 	}
 
-	template<>
+	template<>

 	void LoadImageRow<AlphaFloat>(const unsigned char *source, unsigned char *dest, GLint xoffset, GLsizei width)
 	{
 		const float *sourceF = reinterpret_cast<const float*>(source);
@@ -74,7 +74,7 @@
 		}
 	}
 
-	template<>
+	template<>

 	void LoadImageRow<AlphaHalfFloat>(const unsigned char *source, unsigned char *dest, GLint xoffset, GLsizei width)
 	{
 		const unsigned short *sourceH = reinterpret_cast<const unsigned short*>(source);
@@ -89,13 +89,13 @@
 		}
 	}
 
-	template<>
+	template<>

 	void LoadImageRow<Luminance>(const unsigned char *source, unsigned char *dest, GLint xoffset, GLsizei width)
 	{
 		memcpy(dest + xoffset, source, width);
 	}
 
-	template<>
+	template<>

 	void LoadImageRow<LuminanceFloat>(const unsigned char *source, unsigned char *dest, GLint xoffset, GLsizei width)
 	{
 		const float *sourceF = reinterpret_cast<const float*>(source);
@@ -110,7 +110,7 @@
 		}
 	}
 
-	template<>
+	template<>

 	void LoadImageRow<LuminanceHalfFloat>(const unsigned char *source, unsigned char *dest, GLint xoffset, GLsizei width)
 	{
 		const unsigned short *sourceH = reinterpret_cast<const unsigned short*>(source);
@@ -125,13 +125,13 @@
 		}
 	}
 
-	template<>
+	template<>

 	void LoadImageRow<LuminanceAlpha>(const unsigned char *source, unsigned char *dest, GLint xoffset, GLsizei width)
 	{
 		memcpy(dest + xoffset * 2, source, width * 2);
 	}
 
-	template<>
+	template<>

 	void LoadImageRow<LuminanceAlphaFloat>(const unsigned char *source, unsigned char *dest, GLint xoffset, GLsizei width)
 	{
 		const float *sourceF = reinterpret_cast<const float*>(source);
@@ -146,7 +146,7 @@
 		}
 	}
 
-	template<>
+	template<>

 	void LoadImageRow<LuminanceAlphaHalfFloat>(const unsigned char *source, unsigned char *dest, GLint xoffset, GLsizei width)
 	{
 		const unsigned short *sourceH = reinterpret_cast<const unsigned short*>(source);
@@ -161,7 +161,7 @@
 		}
 	}
 
-	template<>
+	template<>

 	void LoadImageRow<RGBUByte>(const unsigned char *source, unsigned char *dest, GLint xoffset, GLsizei width)
 	{
 		unsigned char *destB = dest + xoffset * 4;
@@ -175,7 +175,7 @@
 		}
 	}
 
-	template<>
+	template<>

 	void LoadImageRow<RGB565>(const unsigned char *source, unsigned char *dest, GLint xoffset, GLsizei width)
 	{
 		const unsigned short *source565 = reinterpret_cast<const unsigned short*>(source);
@@ -191,7 +191,7 @@
 		}
 	}
 
-	template<>
+	template<>

 	void LoadImageRow<RGBFloat>(const unsigned char *source, unsigned char *dest, GLint xoffset, GLsizei width)
 	{
 		const float *sourceF = reinterpret_cast<const float*>(source);
@@ -206,7 +206,7 @@
 		}
 	}
 
-	template<>
+	template<>

 	void LoadImageRow<RGBHalfFloat>(const unsigned char *source, unsigned char *dest, GLint xoffset, GLsizei width)
 	{
 		const unsigned short *sourceH = reinterpret_cast<const unsigned short*>(source);
@@ -221,7 +221,7 @@
 		}
 	}
 
-	template<>
+	template<>

 	void LoadImageRow<RGBAUByte>(const unsigned char *source, unsigned char *dest, GLint xoffset, GLsizei width)
 	{
 		const unsigned int *sourceI = reinterpret_cast<const unsigned int*>(source);
@@ -234,7 +234,7 @@
 		}
 	}
 
-	template<>
+	template<>

 	void LoadImageRow<RGBA4444>(const unsigned char *source, unsigned char *dest, GLint xoffset, GLsizei width)
 	{
 		const unsigned short *source4444 = reinterpret_cast<const unsigned short*>(source);
@@ -250,7 +250,7 @@
 		}
 	}
 
-	template<>
+	template<>

 	void LoadImageRow<RGBA5551>(const unsigned char *source, unsigned char *dest, GLint xoffset, GLsizei width)
 	{
 		const unsigned short *source5551 = reinterpret_cast<const unsigned short*>(source);
@@ -266,25 +266,25 @@
 		}
 	}
 
-	template<>
+	template<>

 	void LoadImageRow<RGBAFloat>(const unsigned char *source, unsigned char *dest, GLint xoffset, GLsizei width)
 	{
 		memcpy(dest + xoffset * 16, source, width * 16);
 	}
 
-	template<>
+	template<>

 	void LoadImageRow<RGBAHalfFloat>(const unsigned char *source, unsigned char *dest, GLint xoffset, GLsizei width)
 	{
 		memcpy(dest + xoffset * 8, source, width * 8);
 	}
 
-	template<>
+	template<>

 	void LoadImageRow<BGRA>(const unsigned char *source, unsigned char *dest, GLint xoffset, GLsizei width)
 	{
 		memcpy(dest + xoffset * 4, source, width * 4);
 	}
 
-	template<>
+	template<>

 	void LoadImageRow<D16>(const unsigned char *source, unsigned char *dest, GLint xoffset, GLsizei width)
 	{
 		const unsigned short *sourceD16 = reinterpret_cast<const unsigned short*>(source);
@@ -296,7 +296,7 @@
 		}
 	}
 
-	template<>
+	template<>

 	void LoadImageRow<D24>(const unsigned char *source, unsigned char *dest, GLint xoffset, GLsizei width)
 	{
 		const unsigned int *sourceD24 = reinterpret_cast<const unsigned int*>(source);
@@ -308,7 +308,7 @@
 		}
 	}
 
-	template<>
+	template<>

 	void LoadImageRow<D32>(const unsigned char *source, unsigned char *dest, GLint xoffset, GLsizei width)
 	{
 		const unsigned int *sourceD32 = reinterpret_cast<const unsigned int*>(source);
@@ -320,7 +320,7 @@
 		}
 	}
 
-	template<>
+	template<>

 	void LoadImageRow<S8>(const unsigned char *source, unsigned char *dest, GLint xoffset, GLsizei width)
 	{
 		const unsigned int *sourceI = reinterpret_cast<const unsigned int*>(source);
@@ -339,7 +339,7 @@
 		{
 			const unsigned char *inputStart = static_cast<const unsigned char*>(input)+(z * inputPitch * height);
 			unsigned char *destStart = static_cast<unsigned char*>(buffer)+((zoffset + z) * destPitch * destHeight);
-			for(int y = 0; y < height; y++)
+			for(int y = 0; y < height; ++y)
 			{
 				const unsigned char *source = inputStart + y * inputPitch;
 				unsigned char *dest = destStart + (y + yoffset) * destPitch;
@@ -364,7 +364,14 @@
 
 	Image::Image(Texture *parentTexture, GLsizei width, GLsizei height, GLenum format, GLenum type)
 		: parentTexture(parentTexture)
-		, egl::Image(getParentResource(parentTexture), width, height, format, type, selectInternalFormat(format, type))
+		, egl::Image(getParentResource(parentTexture), width, height, 1, format, type, selectInternalFormat(format, type))
+	{
+		referenceCount = 1;
+	}
+
+	Image::Image(Texture *parentTexture, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type)
+		: parentTexture(parentTexture)
+		, egl::Image(getParentResource(parentTexture), width, height, depth, format, type, selectInternalFormat(format, type))
 	{
 		referenceCount = 1;
 	}
@@ -508,13 +515,11 @@
 		return sw::FORMAT_A8R8G8B8;
 	}
 
-	void Image::loadImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *input)
+	void Image::loadImageData(GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLint unpackAlignment, const void *input)
 	{
 		GLsizei inputPitch = ComputePitch(width, format, type, unpackAlignment);
 		void *buffer = lock(0, 0, sw::LOCK_WRITEONLY);
-		GLint zoffset = 0;
-		GLsizei depth = 1;
-
+		
 		if(buffer)
 		{
 			switch(type)
@@ -621,7 +626,7 @@
 				LoadImageData<D32>(xoffset, yoffset, zoffset, width, height, depth, inputPitch, getPitch(), getHeight(), input, buffer);
 				break;
 			case GL_UNSIGNED_INT_24_8_OES:
-				loadD24S8ImageData(xoffset, yoffset, width, height, inputPitch, input, buffer);
+				loadD24S8ImageData(xoffset, yoffset, zoffset, width, height, depth, inputPitch, input, buffer);
 				break;
 			default: UNREACHABLE();
 			}
@@ -630,22 +635,27 @@
 		unlock();
 	}
 
-	void Image::loadD24S8ImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, int inputPitch, const void *input, void *buffer)
+	void Image::loadD24S8ImageData(GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, int inputPitch, const void *input, void *buffer)
 	{
-		LoadImageData<D24>(xoffset, yoffset, 0, width, height, 1, inputPitch, getPitch(), getHeight(), input, buffer);
+		LoadImageData<D24>(xoffset, yoffset, zoffset, width, height, depth, inputPitch, getPitch(), getHeight(), input, buffer);
 
 		unsigned char *stencil = reinterpret_cast<unsigned char*>(lockStencil(0, sw::PUBLIC));
 
 		if(stencil)
 		{
-			LoadImageData<S8>(xoffset, yoffset, 0, width, height, 1, inputPitch, getStencilPitchB(), getHeight(), input, stencil);
+			LoadImageData<S8>(xoffset, yoffset, zoffset, width, height, depth, inputPitch, getStencilPitchB(), getHeight(), input, stencil);
 
 			unlockStencil();
 		}
 	}
 
-	void Image::loadCompressedData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)
+	void Image::loadCompressedData(GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei imageSize, const void *pixels)
 	{
+		if(zoffset != 0 || depth != 1)
+		{
+			UNIMPLEMENTED(); // FIXME
+		}
+
 		int inputPitch = ComputeCompressedPitch(width, format);
 		int rows = imageSize / inputPitch;
 		void *buffer = lock(xoffset, yoffset, sw::LOCK_WRITEONLY);
diff --git a/src/OpenGL/libGLESv2/Image.hpp b/src/OpenGL/libGLESv2/Image.hpp
index 4c1a1ac..3824a8f 100644
--- a/src/OpenGL/libGLESv2/Image.hpp
+++ b/src/OpenGL/libGLESv2/Image.hpp
@@ -26,10 +26,11 @@
 	{
 	public:
 		Image(Texture *parentTexture, GLsizei width, GLsizei height, GLenum format, GLenum type);
+		Image(Texture *parentTexture, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type);
 		Image(Texture *parentTexture, GLsizei width, GLsizei height, sw::Format internalFormat, int multiSampleDepth, bool lockable, bool renderTarget);
 
-		void loadImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *input);
-		void loadCompressedData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels);
+		void loadImageData(GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLint unpackAlignment, const void *input);
+		void loadCompressedData(GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei imageSize, const void *pixels);
 
 		virtual void addRef();
 		virtual void release();
@@ -40,7 +41,7 @@
 	private:
 		virtual ~Image();
 
-		void loadD24S8ImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, int inputPitch, const void *input, void *buffer);
+		void loadD24S8ImageData(GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, int inputPitch, const void *input, void *buffer);
 
 		egl::Texture *parentTexture;
 
diff --git a/src/OpenGL/libGLESv2/Program.cpp b/src/OpenGL/libGLESv2/Program.cpp
index 3ffb06b..7599be5 100644
--- a/src/OpenGL/libGLESv2/Program.cpp
+++ b/src/OpenGL/libGLESv2/Program.cpp
@@ -591,7 +591,8 @@
 		if(targetUniform->type == GL_INT ||

 		   targetUniform->type == GL_SAMPLER_2D ||

 		   targetUniform->type == GL_SAMPLER_CUBE ||

-           targetUniform->type == GL_SAMPLER_EXTERNAL_OES)

+           targetUniform->type == GL_SAMPLER_EXTERNAL_OES ||

+		   targetUniform->type == GL_SAMPLER_3D_OES)

 		{

 			memcpy(targetUniform->data + uniformIndex[location].element * sizeof(GLint),

 				   v, sizeof(GLint) * count);

@@ -924,7 +925,8 @@
 				  case GL_FLOAT_MAT4: applyUniformMatrix4fv(location, size, f); break;

 				  case GL_SAMPLER_2D:

 				  case GL_SAMPLER_CUBE:

-                  case GL_SAMPLER_EXTERNAL_OES:

+				  case GL_SAMPLER_EXTERNAL_OES:

+				  case GL_SAMPLER_3D_OES:

 				  case GL_INT:        applyUniform1iv(location, size, i);       break;

 				  case GL_INT_VEC2:   applyUniform2iv(location, size, i);       break;

 				  case GL_INT_VEC3:   applyUniform3iv(location, size, i);       break;

@@ -1315,7 +1317,7 @@
 

 	bool Program::defineUniform(GLenum shader, GLenum type, GLenum precision, const std::string &name, unsigned int arraySize, int registerIndex)

 	{

-		if(type == GL_SAMPLER_2D || type == GL_SAMPLER_CUBE || type == GL_SAMPLER_EXTERNAL_OES)

+		if(type == GL_SAMPLER_2D || type == GL_SAMPLER_CUBE || type == GL_SAMPLER_EXTERNAL_OES || type == GL_SAMPLER_3D_OES)

 	    {

 			int index = registerIndex;

 			

@@ -1326,7 +1328,17 @@
 					if(index < MAX_VERTEX_TEXTURE_IMAGE_UNITS)

 					{

 						samplersVS[index].active = true;

-						samplersVS[index].textureType = (type == GL_SAMPLER_CUBE) ? TEXTURE_CUBE : TEXTURE_2D;

+						switch(type) {

+						case GL_SAMPLER_CUBE:

+							samplersVS[index].textureType = TEXTURE_CUBE;

+							break;

+						case GL_SAMPLER_3D_OES:

+							samplersVS[index].textureType = TEXTURE_3D;

+							break;

+						default:

+							samplersVS[index].textureType = TEXTURE_2D;

+							break;

+						}

 						samplersVS[index].logicalTextureUnit = 0;

 					}

 					else

@@ -1340,7 +1352,17 @@
 					if(index < MAX_TEXTURE_IMAGE_UNITS)

 					{

 						samplersPS[index].active = true;

-						samplersPS[index].textureType = (type == GL_SAMPLER_CUBE) ? TEXTURE_CUBE : TEXTURE_2D;

+						switch(type) {

+						case GL_SAMPLER_CUBE:

+							samplersPS[index].textureType = TEXTURE_CUBE;

+							break;

+						case GL_SAMPLER_3D_OES:

+							samplersPS[index].textureType = TEXTURE_3D;

+							break;

+						default:

+							samplersPS[index].textureType = TEXTURE_2D;

+							break;

+						}

 						samplersPS[index].logicalTextureUnit = 0;

 					}

 					else

@@ -1737,7 +1759,8 @@
 		{

             if(targetUniform->type == GL_SAMPLER_2D ||

                targetUniform->type == GL_SAMPLER_CUBE ||

-               targetUniform->type == GL_SAMPLER_EXTERNAL_OES)

+			   targetUniform->type == GL_SAMPLER_EXTERNAL_OES ||

+			   targetUniform->type == GL_SAMPLER_3D_OES)

 			{

 				for(int i = 0; i < count; i++)

 				{

@@ -1760,7 +1783,8 @@
 		{

 			if(targetUniform->type == GL_SAMPLER_2D ||

                targetUniform->type == GL_SAMPLER_CUBE ||

-               targetUniform->type == GL_SAMPLER_EXTERNAL_OES)

+			   targetUniform->type == GL_SAMPLER_EXTERNAL_OES ||

+			   targetUniform->type == GL_SAMPLER_3D_OES)

 			{

 				for(int i = 0; i < count; i++)

 				{

diff --git a/src/OpenGL/libGLESv2/Renderbuffer.cpp b/src/OpenGL/libGLESv2/Renderbuffer.cpp
index a88d222..58f5f57 100644
--- a/src/OpenGL/libGLESv2/Renderbuffer.cpp
+++ b/src/OpenGL/libGLESv2/Renderbuffer.cpp
@@ -82,12 +82,12 @@
 // Renderbuffers acting as proxies. Here, we notify the texture of a reference.
 void RenderbufferTexture2D::addProxyRef(const Renderbuffer *proxy)
 {
-    mTexture2D->addProxyRef(proxy);
+	mTexture2D->addProxyRef(proxy);
 }
 
 void RenderbufferTexture2D::releaseProxy(const Renderbuffer *proxy)
 {
-    mTexture2D->releaseProxy(proxy);
+	mTexture2D->releaseProxy(proxy);
 }
 
 // Increments refcount on image.
@@ -101,12 +101,12 @@
 // caller must release() the returned image
 egl::Image *RenderbufferTexture2D::createSharedImage()
 {
-    return mTexture2D->createSharedImage(GL_TEXTURE_2D, 0);
+	return mTexture2D->createSharedImage(GL_TEXTURE_2D, 0);
 }
 
 bool RenderbufferTexture2D::isShared() const
 {
-    return mTexture2D->isShared(GL_TEXTURE_2D, 0);
+	return mTexture2D->isShared(GL_TEXTURE_2D, 0);
 }
 
 GLsizei RenderbufferTexture2D::getWidth() const
@@ -134,6 +134,74 @@
 	return 0;
 }
 
+///// RenderbufferTexture3D Implementation ////////
+
+RenderbufferTexture3D::RenderbufferTexture3D(Texture3D *texture)
+{
+	mTexture3D.set(texture);
+}
+
+RenderbufferTexture3D::~RenderbufferTexture3D()
+{
+	mTexture3D.set(NULL);
+}
+
+// Textures need to maintain their own reference count for references via
+// Renderbuffers acting as proxies. Here, we notify the texture of a reference.
+void RenderbufferTexture3D::addProxyRef(const Renderbuffer *proxy)
+{
+	mTexture3D->addProxyRef(proxy);
+}
+
+void RenderbufferTexture3D::releaseProxy(const Renderbuffer *proxy)
+{
+	mTexture3D->releaseProxy(proxy);
+}
+
+// Increments refcount on image.
+// caller must release() the returned image
+egl::Image *RenderbufferTexture3D::getRenderTarget()
+{
+	return mTexture3D->getRenderTarget(GL_TEXTURE_3D_OES, 0);
+}
+
+// Increments refcount on image.
+// caller must release() the returned image
+egl::Image *RenderbufferTexture3D::createSharedImage()
+{
+	return mTexture3D->createSharedImage(GL_TEXTURE_3D_OES, 0);
+}
+
+bool RenderbufferTexture3D::isShared() const
+{
+	return mTexture3D->isShared(GL_TEXTURE_3D_OES, 0);
+}
+
+GLsizei RenderbufferTexture3D::getWidth() const
+{
+	return mTexture3D->getWidth(GL_TEXTURE_3D_OES, 0);
+}
+
+GLsizei RenderbufferTexture3D::getHeight() const
+{
+	return mTexture3D->getHeight(GL_TEXTURE_3D_OES, 0);
+}
+
+GLenum RenderbufferTexture3D::getFormat() const
+{
+	return mTexture3D->getFormat(GL_TEXTURE_3D_OES, 0);
+}
+
+sw::Format RenderbufferTexture3D::getInternalFormat() const
+{
+	return mTexture3D->getInternalFormat(GL_TEXTURE_3D_OES, 0);
+}
+
+GLsizei RenderbufferTexture3D::getSamples() const
+{
+	return 0;
+}
+
 ///// RenderbufferTextureCubeMap Implementation ////////
 
 RenderbufferTextureCubeMap::RenderbufferTextureCubeMap(TextureCubeMap *texture, GLenum target) : mTarget(target)
@@ -361,7 +429,7 @@
 		mHeight = renderTarget->getHeight();
 		internalFormat = renderTarget->getInternalFormat();
 		format = sw2es::ConvertBackBufferFormat(internalFormat);
-		mSamples = renderTarget->getMultiSampleDepth() & ~1;
+		mSamples = renderTarget->getDepth() & ~1;
 	}
 }
 
@@ -438,7 +506,7 @@
 		mHeight = depthStencil->getHeight();
 		internalFormat = depthStencil->getInternalFormat();
 		format = sw2es::ConvertDepthStencilFormat(internalFormat);
-		mSamples = depthStencil->getMultiSampleDepth() & ~1;
+		mSamples = depthStencil->getDepth() & ~1;
 	}
 }
 
diff --git a/src/OpenGL/libGLESv2/Renderbuffer.h b/src/OpenGL/libGLESv2/Renderbuffer.h
index b8e9d0d..bd322e7 100644
--- a/src/OpenGL/libGLESv2/Renderbuffer.h
+++ b/src/OpenGL/libGLESv2/Renderbuffer.h
@@ -26,6 +26,7 @@
 namespace es2

 {

 class Texture2D;

+class Texture3D;

 class TextureCubeMap;

 class Renderbuffer;

 class Colorbuffer;

@@ -83,6 +84,30 @@
 	gl::BindingPointer<Texture2D> mTexture2D;

 };

 

+class RenderbufferTexture3D : public RenderbufferInterface

+{

+public:

+	RenderbufferTexture3D(Texture3D *texture);

+

+	virtual ~RenderbufferTexture3D();

+

+	virtual void addProxyRef(const Renderbuffer *proxy);

+	virtual void releaseProxy(const Renderbuffer *proxy);

+

+	virtual egl::Image *getRenderTarget();

+	virtual egl::Image *createSharedImage();

+	virtual bool isShared() const;

+

+	virtual GLsizei getWidth() const;

+	virtual GLsizei getHeight() const;

+	virtual GLenum getFormat() const;

+	virtual sw::Format getInternalFormat() const;

+	virtual GLsizei getSamples() const;

+

+private:

+	gl::BindingPointer<Texture3D> mTexture3D;

+};

+

 class RenderbufferTextureCubeMap : public RenderbufferInterface

 {

 public:

diff --git a/src/OpenGL/libGLESv2/ResourceManager.cpp b/src/OpenGL/libGLESv2/ResourceManager.cpp
index e3be9bc..2f6fd74 100644
--- a/src/OpenGL/libGLESv2/ResourceManager.cpp
+++ b/src/OpenGL/libGLESv2/ResourceManager.cpp
@@ -307,11 +307,15 @@
         {
             textureObject = new TextureCubeMap(texture);
         }
-        else if(type == TEXTURE_EXTERNAL)
-        {
-            textureObject = new TextureExternal(texture);
-        }
-        else
+		else if(type == TEXTURE_EXTERNAL)
+		{
+			textureObject = new TextureExternal(texture);
+		}
+		else if(type == TEXTURE_3D)
+		{
+			textureObject = new Texture3D(texture);
+		}
+		else
         {
             UNREACHABLE();
             return;
diff --git a/src/OpenGL/libGLESv2/ResourceManager.h b/src/OpenGL/libGLESv2/ResourceManager.h
index 699944f..d7d626d 100644
--- a/src/OpenGL/libGLESv2/ResourceManager.h
+++ b/src/OpenGL/libGLESv2/ResourceManager.h
@@ -33,6 +33,7 @@
 enum TextureType

 {

     TEXTURE_2D,

+	TEXTURE_3D,

     TEXTURE_CUBE,

     TEXTURE_EXTERNAL,

 

diff --git a/src/OpenGL/libGLESv2/Texture.cpp b/src/OpenGL/libGLESv2/Texture.cpp
index 4964bce..982c7c8 100644
--- a/src/OpenGL/libGLESv2/Texture.cpp
+++ b/src/OpenGL/libGLESv2/Texture.cpp
@@ -10,7 +10,7 @@
 //

 

 // Texture.cpp: Implements the Texture class and its derived classes

-// Texture2D and TextureCubeMap. Implements GL texture objects and related

+// Texture2D, TextureCubeMap and Texture3D. Implements GL texture objects and related

 // functionality. [OpenGL ES 2.0.24] section 3.7 page 63.

 

 #include "Texture.h"

@@ -34,6 +34,7 @@
     mMagFilter = GL_LINEAR;

     mWrapS = GL_REPEAT;

     mWrapT = GL_REPEAT;

+	mWrapR = GL_REPEAT;

 	mMaxAnisotropy = 1.0f;

 

 	resource = new sw::Resource(0);

@@ -109,21 +110,41 @@
 // Returns true on successful wrap state update (valid enum parameter)

 bool Texture::setWrapT(GLenum wrap)

 {

-    switch(wrap)

-    {

-    case GL_REPEAT:

-    case GL_MIRRORED_REPEAT:

-        if(getTarget() == GL_TEXTURE_EXTERNAL_OES)

-        {

-            return false;

-        }

-        // Fall through

-    case GL_CLAMP_TO_EDGE:

-         mWrapT = wrap;

-         return true;

-    default:

-        return false;

-    }

+	switch(wrap)

+	{

+	case GL_REPEAT:

+	case GL_MIRRORED_REPEAT:

+		if(getTarget() == GL_TEXTURE_EXTERNAL_OES)

+		{

+			return false;

+		}

+		// Fall through

+	case GL_CLAMP_TO_EDGE:

+		mWrapT = wrap;

+		return true;

+	default:

+		return false;

+	}

+}

+

+// Returns true on successful wrap state update (valid enum parameter)

+bool Texture::setWrapR(GLenum wrap)

+{

+	switch(wrap)

+	{

+	case GL_REPEAT:

+	case GL_MIRRORED_REPEAT:

+		if(getTarget() == GL_TEXTURE_EXTERNAL_OES)

+		{

+			return false;

+		}

+		// Fall through

+	case GL_CLAMP_TO_EDGE:

+		mWrapR = wrap;

+		return true;

+	default:

+		return false;

+	}

 }

 

 // Returns true on successful max anisotropy update (valid anisotropy value)

@@ -161,7 +182,12 @@
 

 GLenum Texture::getWrapT() const

 {

-    return mWrapT;

+	return mWrapT;

+}

+

+GLenum Texture::getWrapR() const

+{

+	return mWrapR;

 }

 

 GLfloat Texture::getMaxAnisotropy() const

@@ -169,6 +195,11 @@
     return mMaxAnisotropy;

 }

 

+GLsizei Texture::getDepth(GLenum target, GLint level) const

+{

+	return 1;

+}

+

 egl::Image *Texture::createSharedImage(GLenum target, unsigned int level)

 {

     egl::Image *image = getRenderTarget(target, level);   // Increments reference count

@@ -185,7 +216,8 @@
 {

     if(pixels && image)

     {

-		image->loadImageData(0, 0, image->getWidth(), image->getHeight(), format, type, unpackAlignment, pixels);

+		GLsizei depth = (getTarget() == GL_TEXTURE_3D_OES) ? image->getDepth() : 1;

+		image->loadImageData(0, 0, 0, image->getWidth(), image->getHeight(), depth, format, type, unpackAlignment, pixels);

     }

 }

 

@@ -193,18 +225,19 @@
 {

     if(pixels && image)

     {

-		image->loadCompressedData(0, 0, image->getWidth(), image->getHeight(), imageSize, pixels);

+		GLsizei depth = (getTarget() == GL_TEXTURE_3D_OES) ? image->getDepth() : 1;

+		image->loadCompressedData(0, 0, 0, image->getWidth(), image->getHeight(), depth, imageSize, pixels);

     }

 }

 

-void Texture::subImage(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels, egl::Image *image)

+void Texture::subImage(GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels, egl::Image *image)

 {

 	if(!image)

 	{

 		return error(GL_INVALID_OPERATION);

 	}

 

-    if(width + xoffset > image->getWidth() || height + yoffset > image->getHeight())

+	if(width + xoffset > image->getWidth() || height + yoffset > image->getHeight() || depth + zoffset > image->getDepth())

     {

         return error(GL_INVALID_VALUE);

     }

@@ -221,18 +254,18 @@
 

     if(pixels)

     {

-        image->loadImageData(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels);

+        image->loadImageData(xoffset, yoffset, zoffset, width, height, depth, format, type, unpackAlignment, pixels);

     }

 }

 

-void Texture::subImageCompressed(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels, egl::Image *image)

+void Texture::subImageCompressed(GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *pixels, egl::Image *image)

 {

 	if(!image)

 	{

 		return error(GL_INVALID_OPERATION);

 	}

 

-    if(width + xoffset > image->getWidth() || height + yoffset > image->getHeight())

+    if(width + xoffset > image->getWidth() || height + yoffset > image->getHeight() || depth + zoffset > image->getDepth())

     {

         return error(GL_INVALID_VALUE);

     }

@@ -244,7 +277,7 @@
 

     if(pixels)

     {

-		image->loadCompressedData(xoffset, yoffset, width, height, imageSize, pixels);

+		image->loadCompressedData(xoffset, yoffset, zoffset, width, height, depth, imageSize, pixels);

     }

 }

 

@@ -467,12 +500,12 @@
 

 void Texture2D::subImage(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)

 {

-	Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, image[level]);

+	Texture::subImage(xoffset, yoffset, 0, width, height, 1, format, type, unpackAlignment, pixels, image[level]);

 }

 

 void Texture2D::subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)

 {

-    Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, image[level]);

+    Texture::subImageCompressed(xoffset, yoffset, 0, width, height, 1, format, imageSize, pixels, image[level]);

 }

 

 void Texture2D::copyImage(GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)

@@ -508,7 +541,7 @@
 	renderTarget->release();

 }

 

-void Texture2D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)

+void Texture2D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)

 {

 	if(!image[level])

 	{

@@ -847,12 +880,12 @@
 

 void TextureCubeMap::subImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)

 {

-    Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, image[CubeFaceIndex(target)][level]);

+    Texture::subImage(xoffset, yoffset, 0, width, height, 1, format, type, unpackAlignment, pixels, image[CubeFaceIndex(target)][level]);

 }

 

 void TextureCubeMap::subImageCompressed(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)

 {

-    Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, image[CubeFaceIndex(target)][level]);

+    Texture::subImageCompressed(xoffset, yoffset, 0, width, height, 1, format, imageSize, pixels, image[CubeFaceIndex(target)][level]);

 }

 

 // Tests for cube map sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 86.

@@ -1031,7 +1064,7 @@
     return image[CubeFaceIndex(face)][level];

 }

 

-void TextureCubeMap::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)

+void TextureCubeMap::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)

 {

 	int face = CubeFaceIndex(target);

 

@@ -1140,12 +1173,402 @@
     return image[face][level]->isShared();

 }

 

+Texture3D::Texture3D(GLuint id) : Texture(id)

+{

+	for(int i = 0; i < MIPMAP_LEVELS; i++)

+	{

+		image[i] = 0;

+	}

+

+	mSurface = NULL;

+

+	mColorbufferProxy = NULL;

+	mProxyRefs = 0;

+}

+

+Texture3D::~Texture3D()

+{

+	resource->lock(sw::DESTRUCT);

+

+	for(int i = 0; i < MIPMAP_LEVELS; i++)

+	{

+		if(image[i])

+		{

+			image[i]->unbind(this);

+			image[i] = 0;

+		}

+	}

+

+	resource->unlock();

+

+	if(mSurface)

+	{

+		mSurface->setBoundTexture(NULL);

+		mSurface = NULL;

+	}

+

+	mColorbufferProxy = NULL;

+}

+

+// We need to maintain a count of references to renderbuffers acting as 

+// proxies for this texture, so that we do not attempt to use a pointer 

+// to a renderbuffer proxy which has been deleted.

+void Texture3D::addProxyRef(const Renderbuffer *proxy)

+{

+	mProxyRefs++;

+}

+

+void Texture3D::releaseProxy(const Renderbuffer *proxy)

+{

+	if(mProxyRefs > 0)

+	{

+		mProxyRefs--;

+	}

+

+	if(mProxyRefs == 0)

+	{

+		mColorbufferProxy = NULL;

+	}

+}

+

+GLenum Texture3D::getTarget() const

+{

+	return GL_TEXTURE_3D_OES;

+}

+

+GLsizei Texture3D::getWidth(GLenum target, GLint level) const

+{

+	ASSERT(target == GL_TEXTURE_3D_OES);

+	return image[level] ? image[level]->getWidth() : 0;

+}

+

+GLsizei Texture3D::getHeight(GLenum target, GLint level) const

+{

+	ASSERT(target == GL_TEXTURE_3D_OES);

+	return image[level] ? image[level]->getHeight() : 0;

+}

+

+GLsizei Texture3D::getDepth(GLenum target, GLint level) const

+{

+	ASSERT(target == GL_TEXTURE_3D_OES);

+	return image[level] ? image[level]->getDepth() : 0;

+}

+

+GLenum Texture3D::getFormat(GLenum target, GLint level) const

+{

+	ASSERT(target == GL_TEXTURE_3D_OES);

+	return image[level] ? image[level]->getFormat() : 0;

+}

+

+GLenum Texture3D::getType(GLenum target, GLint level) const

+{

+	ASSERT(target == GL_TEXTURE_3D_OES);

+	return image[level] ? image[level]->getType() : 0;

+}

+

+sw::Format Texture3D::getInternalFormat(GLenum target, GLint level) const

+{

+	ASSERT(target == GL_TEXTURE_3D_OES);

+	return image[level] ? image[level]->getInternalFormat() : sw::FORMAT_NULL;

+}

+

+int Texture3D::getLevelCount() const

+{

+	ASSERT(isSamplerComplete());

+	int levels = 0;

+

+	while(levels < MIPMAP_LEVELS && image[levels])

+	{

+		levels++;

+	}

+

+	return levels;

+}

+

+void Texture3D::setImage(GLint level, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)

+{

+	if(image[level])

+	{

+		image[level]->unbind(this);

+	}

+

+	image[level] = new Image(this, width, height, depth, format, type);

+

+	if(!image[level])

+	{

+		return error(GL_OUT_OF_MEMORY);

+	}

+

+	Texture::setImage(format, type, unpackAlignment, pixels, image[level]);

+}

+

+void Texture3D::bindTexImage(egl::Surface *surface)

+{

+	GLenum format;

+

+	switch(surface->getInternalFormat())

+	{

+	case sw::FORMAT_A8R8G8B8:

+		format = GL_RGBA;

+		break;

+	case sw::FORMAT_X8R8G8B8:

+		format = GL_RGB;

+		break;

+	default:

+		UNIMPLEMENTED();

+		return;

+	}

+

+	for(int level = 0; level < MIPMAP_LEVELS; level++)

+	{

+		if(image[level])

+		{

+			image[level]->unbind(this);

+			image[level] = 0;

+		}

+	}

+

+	image[0] = surface->getRenderTarget();

+

+	mSurface = surface;

+	mSurface->setBoundTexture(this);

+}

+

+void Texture3D::releaseTexImage()

+{

+	for(int level = 0; level < MIPMAP_LEVELS; level++)

+	{

+		if(image[level])

+		{

+			image[level]->unbind(this);

+			image[level] = 0;

+		}

+	}

+}

+

+void Texture3D::setCompressedImage(GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei depth, GLsizei imageSize, const void *pixels)

+{

+	if(image[level])

+	{

+		image[level]->unbind(this);

+	}

+

+	image[level] = new Image(this, width, height, depth, format, GL_UNSIGNED_BYTE);

+

+	if(!image[level])

+	{

+		return error(GL_OUT_OF_MEMORY);

+	}

+

+	Texture::setCompressedImage(imageSize, pixels, image[level]);

+}

+

+void Texture3D::subImage(GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)

+{

+	Texture::subImage(xoffset, yoffset, zoffset, width, height, format, depth, type, unpackAlignment, pixels, image[level]);

+}

+

+void Texture3D::subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *pixels)

+{

+	Texture::subImageCompressed(xoffset, yoffset, zoffset, width, height, depth, format, imageSize, pixels, image[level]);

+}

+

+void Texture3D::copyImage(GLint level, GLenum format, GLint x, GLint y, GLint z, GLsizei width, GLsizei height, GLsizei depth, Framebuffer *source)

+{

+	UNIMPLEMENTED();

+}

+

+void Texture3D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)

+{

+	if(zoffset != 0)

+	{

+		UNIMPLEMENTED(); // FIXME: add support for copying into a layer other than layer 0

+	}

+

+	if(!image[level])

+	{

+		return error(GL_INVALID_OPERATION);

+	}

+

+	if(xoffset + width > image[level]->getWidth() || yoffset + height > image[level]->getHeight())

+	{

+		return error(GL_INVALID_VALUE);

+	}

+

+	egl::Image *renderTarget = source->getRenderTarget();

+

+	if(!renderTarget)

+	{

+		ERR("Failed to retrieve the render target.");

+		return error(GL_OUT_OF_MEMORY);

+	}

+

+	sw::Rect sourceRect = { x, y, x + width, y + height };

+	sourceRect.clip(0, 0, source->getColorbuffer()->getWidth(), source->getColorbuffer()->getHeight());

+

+	copy(renderTarget, sourceRect, image[level]->getFormat(), xoffset, yoffset, image[level]);

+

+	renderTarget->release();

+}

+

+void Texture3D::setImage(egl::Image *sharedImage)

+{

+	sharedImage->addRef();

+

+	if(image[0])

+	{

+		image[0]->unbind(this);

+	}

+

+	image[0] = sharedImage;

+}

+

+// Tests for 3D texture sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 85.

+bool Texture3D::isSamplerComplete() const

+{

+	if(!image[0])

+	{

+		return false;

+	}

+

+	GLsizei width = image[0]->getWidth();

+	GLsizei height = image[0]->getHeight();

+	GLsizei depth = image[0]->getDepth();

+

+	if(width <= 0 || height <= 0 || depth <= 0)

+	{

+		return false;

+	}

+

+	if(isMipmapFiltered())

+	{

+		if(!isMipmapComplete())

+		{

+			return false;

+		}

+	}

+

+	return true;

+}

+

+// Tests for 3D texture (mipmap) completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.

+bool Texture3D::isMipmapComplete() const

+{

+	GLsizei width = image[0]->getWidth();

+	GLsizei height = image[0]->getHeight();

+	GLsizei depth = image[0]->getDepth();

+

+	int q = log2(std::max(std::max(width, height), depth));

+

+	for(int level = 1; level <= q; level++)

+	{

+		if(!image[level])

+		{

+			return false;

+		}

+

+		if(image[level]->getFormat() != image[0]->getFormat())

+		{

+			return false;

+		}

+

+		if(image[level]->getType() != image[0]->getType())

+		{

+			return false;

+		}

+

+		if(image[level]->getWidth() != std::max(1, width >> level))

+		{

+			return false;

+		}

+

+		if(image[level]->getHeight() != std::max(1, height >> level))

+		{

+			return false;

+		}

+

+		if(image[level]->getDepth() != std::max(1, depth >> level))

+		{

+			return false;

+		}

+	}

+

+	return true;

+}

+

+bool Texture3D::isCompressed(GLenum target, GLint level) const

+{

+	return IsCompressed(getFormat(target, level));

+}

+

+bool Texture3D::isDepth(GLenum target, GLint level) const

+{

+	return IsDepthTexture(getFormat(target, level));

+}

+

+void Texture3D::generateMipmaps()

+{

+	UNIMPLEMENTED();

+}

+

+egl::Image *Texture3D::getImage(unsigned int level)

+{

+	return image[level];

+}

+

+Renderbuffer *Texture3D::getRenderbuffer(GLenum target)

+{

+	if(target != GL_TEXTURE_3D_OES)

+	{

+		return error(GL_INVALID_OPERATION, (Renderbuffer *)NULL);

+	}

+

+	if(mColorbufferProxy == NULL)

+	{

+		mColorbufferProxy = new Renderbuffer(id(), new RenderbufferTexture3D(this));

+	}

+

+	return mColorbufferProxy;

+}

+

+egl::Image *Texture3D::getRenderTarget(GLenum target, unsigned int level)

+{

+	ASSERT(target == GL_TEXTURE_3D_OES);

+	ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);

+

+	if(image[level])

+	{

+		image[level]->addRef();

+	}

+

+	return image[level];

+}

+

+bool Texture3D::isShared(GLenum target, unsigned int level) const

+{

+	ASSERT(target == GL_TEXTURE_3D_OES);

+	ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);

+

+	if(mSurface)   // Bound to an EGLSurface

+	{

+		return true;

+	}

+

+	if(!image[level])

+	{

+		return false;

+	}

+

+	return image[level]->isShared();

+}

+

 TextureExternal::TextureExternal(GLuint id) : Texture2D(id)

 {

     mMinFilter = GL_LINEAR;

     mMagFilter = GL_LINEAR;

     mWrapS = GL_CLAMP_TO_EDGE;

-    mWrapT = GL_CLAMP_TO_EDGE;

+	mWrapT = GL_CLAMP_TO_EDGE;

+	mWrapR = GL_CLAMP_TO_EDGE;

 }

 

 TextureExternal::~TextureExternal()

diff --git a/src/OpenGL/libGLESv2/Texture.h b/src/OpenGL/libGLESv2/Texture.h
index 100cf01..ae0ec86 100644
--- a/src/OpenGL/libGLESv2/Texture.h
+++ b/src/OpenGL/libGLESv2/Texture.h
@@ -63,17 +63,20 @@
     bool setMinFilter(GLenum filter);

     bool setMagFilter(GLenum filter);

     bool setWrapS(GLenum wrap);

-    bool setWrapT(GLenum wrap);

+	bool setWrapT(GLenum wrap);

+	bool setWrapR(GLenum wrap);

 	bool setMaxAnisotropy(GLfloat textureMaxAnisotropy);

 

     GLenum getMinFilter() const;

     GLenum getMagFilter() const;

     GLenum getWrapS() const;

-    GLenum getWrapT() const;

+	GLenum getWrapT() const;

+	GLenum getWrapR() const;

 	GLfloat getMaxAnisotropy() const;

 

     virtual GLsizei getWidth(GLenum target, GLint level) const = 0;

-    virtual GLsizei getHeight(GLenum target, GLint level) const = 0;

+	virtual GLsizei getHeight(GLenum target, GLint level) const = 0;

+	virtual GLsizei getDepth(GLenum target, GLint level) const;

     virtual GLenum getFormat(GLenum target, GLint level) const = 0;

     virtual GLenum getType(GLenum target, GLint level) const = 0;

     virtual sw::Format getInternalFormat(GLenum target, GLint level) const = 0;

@@ -89,13 +92,13 @@
     virtual bool isShared(GLenum target, unsigned int level) const = 0;

 

     virtual void generateMipmaps() = 0;

-    virtual void copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source) = 0;

+	virtual void copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source) = 0;

 

 protected:

     void setImage(GLenum format, GLenum type, GLint unpackAlignment, const void *pixels, egl::Image *image);

-    void subImage(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels, egl::Image *image);

+    void subImage(GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels, egl::Image *image);

     void setCompressedImage(GLsizei imageSize, const void *pixels, egl::Image *image);

-    void subImageCompressed(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels, egl::Image *image);

+    void subImageCompressed(GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *pixels, egl::Image *image);

 

 	bool copy(egl::Image *source, const sw::Rect &sourceRect, GLenum destFormat, GLint xoffset, GLint yoffset, egl::Image *dest);

 

@@ -105,6 +108,7 @@
     GLenum mMagFilter;

     GLenum mWrapS;

     GLenum mWrapT;

+    GLenum mWrapR;

 	GLfloat mMaxAnisotropy;

 

 	sw::Resource *resource;

@@ -134,7 +138,7 @@
     void subImage(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels);

     void subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels);

     void copyImage(GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source);

-    void copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source);

+	void copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source);

 

 	void setImage(egl::Image *image);

 

@@ -193,7 +197,7 @@
     void subImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels);

     void subImageCompressed(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels);

     void copyImage(GLenum target, GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source);

-    virtual void copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source);

+	virtual void copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source);

 

     virtual bool isSamplerComplete() const;

     virtual bool isCompressed(GLenum target, GLint level) const;

@@ -226,6 +230,65 @@
 	unsigned int mFaceProxyRefs[6];

 };

 

+class Texture3D : public Texture

+{

+public:

+	explicit Texture3D(GLuint id);

+

+	virtual ~Texture3D();

+

+	void addProxyRef(const Renderbuffer *proxy);

+	void releaseProxy(const Renderbuffer *proxy);

+

+	virtual GLenum getTarget() const;

+

+	virtual GLsizei getWidth(GLenum target, GLint level) const;

+	virtual GLsizei getHeight(GLenum target, GLint level) const;

+	virtual GLsizei getDepth(GLenum target, GLint level) const;

+	virtual GLenum getFormat(GLenum target, GLint level) const;

+	virtual GLenum getType(GLenum target, GLint level) const;

+	virtual sw::Format getInternalFormat(GLenum target, GLint level) const;

+	virtual int getLevelCount() const;

+

+	void setImage(GLint level, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels);

+	void setCompressedImage(GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei depth, GLsizei imageSize, const void *pixels);

+	void subImage(GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels);

+	void subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *pixels);

+	void copyImage(GLint level, GLenum format, GLint x, GLint y, GLint z, GLsizei width, GLsizei height, GLsizei depth, Framebuffer *source);

+	void copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source);

+

+	void setImage(egl::Image *image);

+

+	virtual bool isSamplerComplete() const;

+	virtual bool isCompressed(GLenum target, GLint level) const;

+	virtual bool isDepth(GLenum target, GLint level) const;

+	virtual void bindTexImage(egl::Surface *surface);

+	virtual void releaseTexImage();

+

+	virtual void generateMipmaps();

+

+	virtual Renderbuffer *getRenderbuffer(GLenum target);

+	virtual egl::Image *getRenderTarget(GLenum target, unsigned int level);

+	virtual bool isShared(GLenum target, unsigned int level) const;

+

+	egl::Image *getImage(unsigned int level);

+

+protected:

+	bool isMipmapComplete() const;

+

+	egl::Image *image[IMPLEMENTATION_MAX_TEXTURE_LEVELS];

+

+	egl::Surface *mSurface;

+

+	// A specific internal reference count is kept for colorbuffer proxy references,

+	// because, as the renderbuffer acting as proxy will maintain a binding pointer

+	// back to this texture, there would be a circular reference if we used a binding

+	// pointer here. This reference count will cause the pointer to be set to NULL if

+	// the count drops to zero, but will not cause deletion of the Renderbuffer.

+	Renderbuffer *mColorbufferProxy;

+	unsigned int mProxyRefs;

+};

+

 class TextureExternal : public Texture2D

 {

 public:

diff --git a/src/OpenGL/libGLESv2/libGLESv2.cpp b/src/OpenGL/libGLESv2/libGLESv2.cpp
index 7f289dc..f354579 100644
--- a/src/OpenGL/libGLESv2/libGLESv2.cpp
+++ b/src/OpenGL/libGLESv2/libGLESv2.cpp
@@ -33,6 +33,15 @@
 #include <exception>

 #include <limits>

 

+typedef std::pair<GLenum, GLenum> InternalFormatTypePair;

+typedef std::map<InternalFormatTypePair, GLenum> FormatMap;

+

+// A helper function to insert data into the format map with fewer characters.

+static void InsertFormatMapping(FormatMap& map, GLenum internalformat, GLenum format, GLenum type)

+{

+	map[InternalFormatTypePair(internalformat, type)] = format;

+}

+

 static bool validImageSize(GLint level, GLsizei width, GLsizei height)

 {

 	if(level < 0 || level >= es2::IMPLEMENTATION_MAX_TEXTURE_LEVELS || width < 0 || height < 0)

@@ -63,14 +72,14 @@
 	if(compressed)

 	{

 		if((width % 4 != 0 && width != texture->getWidth(target, 0)) ||

-		   (height % 4 != 0 && height != texture->getHeight(target, 0)))

+			(height % 4 != 0 && height != texture->getHeight(target, 0)))

 		{

 			return error(GL_INVALID_OPERATION, false);

 		}

 	}

 

 	if(xoffset + width > texture->getWidth(target, level) ||

-	   yoffset + height > texture->getHeight(target, level))

+		yoffset + height > texture->getHeight(target, level))

 	{

 		return error(GL_INVALID_VALUE, false);

 	}

@@ -78,6 +87,200 @@
 	return true;

 }

 

+static bool validateSubImageParams(bool compressed, GLsizei width, GLsizei height, GLsizei depth, GLint xoffset, GLint yoffset, GLint zoffset, GLenum target, GLint level, GLenum format, es2::Texture *texture)

+{

+	if(!texture)

+	{

+		return error(GL_INVALID_OPERATION, false);

+	}

+

+	if(compressed != texture->isCompressed(target, level))

+	{

+		return error(GL_INVALID_OPERATION, false);

+	}

+

+	if(format != GL_NONE && format != texture->getFormat(target, level))

+	{

+		return error(GL_INVALID_OPERATION, false);

+	}

+

+	if(compressed)

+	{

+		if((width % 4 != 0 && width != texture->getWidth(target, 0)) ||

+		   (height % 4 != 0 && height != texture->getHeight(target, 0)) ||

+		   (depth % 4 != 0 && depth != texture->getDepth(target, 0)))

+		{

+			return error(GL_INVALID_OPERATION, false);

+		}

+	}

+

+	if(xoffset + width > texture->getWidth(target, level) ||

+	   yoffset + height > texture->getHeight(target, level) ||

+	   zoffset + depth > texture->getDepth(target, level))

+	{

+		return error(GL_INVALID_VALUE, false);

+	}

+

+	return true;

+}

+

+static bool validateColorBufferFormat(GLenum textureFormat, GLenum colorbufferFormat)

+{

+	// [OpenGL ES 2.0.24] table 3.9

+	switch(textureFormat)

+	{

+	case GL_ALPHA:

+		if(colorbufferFormat != GL_ALPHA &&

+			colorbufferFormat != GL_RGBA &&

+			colorbufferFormat != GL_RGBA4 &&

+			colorbufferFormat != GL_RGB5_A1 &&

+			colorbufferFormat != GL_RGBA8_OES)

+		{

+			return error(GL_INVALID_OPERATION, false);

+		}

+		break;

+	case GL_LUMINANCE:

+	case GL_RGB:

+		if(colorbufferFormat != GL_RGB &&

+			colorbufferFormat != GL_RGB565 &&

+			colorbufferFormat != GL_RGB8_OES &&

+			colorbufferFormat != GL_RGBA &&

+			colorbufferFormat != GL_RGBA4 &&

+			colorbufferFormat != GL_RGB5_A1 &&

+			colorbufferFormat != GL_RGBA8_OES)

+		{

+			return error(GL_INVALID_OPERATION, false);

+		}

+		break;

+	case GL_LUMINANCE_ALPHA:

+	case GL_RGBA:

+		if(colorbufferFormat != GL_RGBA &&

+			colorbufferFormat != GL_RGBA4 &&

+			colorbufferFormat != GL_RGB5_A1 &&

+			colorbufferFormat != GL_RGBA8_OES)

+		{

+			return error(GL_INVALID_OPERATION, false);

+		}

+		break;

+	case GL_ETC1_RGB8_OES:

+		return error(GL_INVALID_OPERATION, false);

+	case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:

+	case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:

+	case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:

+	case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:

+		if(S3TC_SUPPORT)

+		{

+			return error(GL_INVALID_OPERATION, false);

+		}

+		else

+		{

+			return error(GL_INVALID_ENUM, false);

+		}

+	case GL_DEPTH_COMPONENT:

+	case GL_DEPTH_STENCIL_OES:

+		return error(GL_INVALID_OPERATION, false);

+	default:

+		return error(GL_INVALID_ENUM, false);

+	}

+	return true;

+}

+

+static FormatMap BuildFormatMap3D()

+{

+	FormatMap map;

+

+	//                       Internal format | Format | Type

+	InsertFormatMapping(map, GL_RGB, GL_RGB, GL_UNSIGNED_BYTE);

+	InsertFormatMapping(map, GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5);

+	InsertFormatMapping(map, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE);

+	InsertFormatMapping(map, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4);

+	InsertFormatMapping(map, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1);

+	InsertFormatMapping(map, GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE);

+	InsertFormatMapping(map, GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE);

+	InsertFormatMapping(map, GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE);

+	InsertFormatMapping(map, GL_R8_EXT, GL_RED_EXT, GL_UNSIGNED_BYTE);

+	InsertFormatMapping(map, GL_R16F_EXT, GL_RED_EXT, GL_HALF_FLOAT_OES);

+	InsertFormatMapping(map, GL_R16F_EXT, GL_RED_EXT, GL_FLOAT);

+	InsertFormatMapping(map, GL_R32F_EXT, GL_RED_EXT, GL_FLOAT);

+	InsertFormatMapping(map, GL_RG8_EXT, GL_RG_EXT, GL_UNSIGNED_BYTE);

+	InsertFormatMapping(map, GL_R16F_EXT, GL_RED_EXT, GL_HALF_FLOAT_OES);

+	InsertFormatMapping(map, GL_R16F_EXT, GL_RED_EXT, GL_FLOAT);

+	InsertFormatMapping(map, GL_RG32F_EXT, GL_RG_EXT, GL_FLOAT);

+	InsertFormatMapping(map, GL_RGB8_OES, GL_RGB, GL_UNSIGNED_BYTE);

+	InsertFormatMapping(map, GL_SRGB8_NV, GL_RGB, GL_UNSIGNED_BYTE);

+	InsertFormatMapping(map, GL_RGB565, GL_RGB, GL_UNSIGNED_BYTE);

+	InsertFormatMapping(map, GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5);

+	InsertFormatMapping(map, GL_RGB16F_EXT, GL_HALF_FLOAT_OES, GL_FLOAT);

+	InsertFormatMapping(map, GL_RGB32F_EXT, GL_RGB, GL_FLOAT);

+	InsertFormatMapping(map, GL_RGBA8_OES, GL_RGBA, GL_UNSIGNED_BYTE);

+	InsertFormatMapping(map, GL_SRGB8_ALPHA8_EXT, GL_RGBA, GL_UNSIGNED_BYTE);

+	InsertFormatMapping(map, GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_BYTE);

+	InsertFormatMapping(map, GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1);

+	InsertFormatMapping(map, GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV_EXT);

+	InsertFormatMapping(map, GL_RGBA4, GL_RGBA, GL_UNSIGNED_BYTE);

+	InsertFormatMapping(map, GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4);

+	InsertFormatMapping(map, GL_RGB10_A2_EXT, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV_EXT);

+	InsertFormatMapping(map, GL_RGBA16F_EXT, GL_RGBA, GL_HALF_FLOAT_OES);

+	InsertFormatMapping(map, GL_RGBA16F_EXT, GL_RGBA, GL_FLOAT);

+	InsertFormatMapping(map, GL_RGBA32F_EXT, GL_RGBA, GL_FLOAT);

+

+	return map;

+}

+

+static bool ValidateType3D(GLenum type)

+{

+	switch(type)

+	{

+	case GL_UNSIGNED_BYTE:

+	case GL_BYTE:

+	case GL_UNSIGNED_SHORT:

+	case GL_SHORT:

+	case GL_UNSIGNED_INT:

+	case GL_INT:

+	case GL_HALF_FLOAT_OES:

+	case GL_FLOAT:

+	case GL_UNSIGNED_SHORT_5_6_5:

+	case GL_UNSIGNED_SHORT_4_4_4_4:

+	case GL_UNSIGNED_SHORT_5_5_5_1:

+	case GL_UNSIGNED_INT_2_10_10_10_REV_EXT:

+		return true;

+	default:

+		break;

+	}

+	return false;

+}

+

+static bool ValidateFormat3D(GLenum format)

+{

+	switch(format)

+	{

+	case GL_RED_EXT:

+	case GL_RG_EXT:

+	case GL_RGB:

+	case GL_RGBA:

+	case GL_DEPTH_COMPONENT:

+	case GL_DEPTH_STENCIL_OES:

+	case GL_LUMINANCE_ALPHA:

+	case GL_LUMINANCE:

+	case GL_ALPHA:

+		return true;

+	default:

+		break;

+	}

+	return false;

+}

+

+static bool ValidateInternalFormat3D(GLenum internalformat, GLenum format, GLenum type)

+{

+	static const FormatMap formatMap = BuildFormatMap3D();

+	FormatMap::const_iterator iter = formatMap.find(InternalFormatTypePair(internalformat, type));

+	if(iter != formatMap.end())

+	{

+		return iter->second == format;

+	}

+	return false;

+}

+

 extern "C"

 {

 

@@ -301,6 +504,9 @@
 		case GL_TEXTURE_EXTERNAL_OES:

 			context->bindTextureExternal(texture);

 			return;

+		case GL_TEXTURE_3D_OES:

+			context->bindTexture3D(texture);

+			return;

 		default:

 			return error(GL_INVALID_ENUM);

 		}

@@ -730,7 +936,7 @@
 

 	if(context)

 	{

-		if(level > es2::IMPLEMENTATION_MAX_TEXTURE_LEVELS)

+		if(level >= es2::IMPLEMENTATION_MAX_TEXTURE_LEVELS)

 		{

 			return error(GL_INVALID_VALUE);

 		}

@@ -850,7 +1056,7 @@
 

 	if(context)

 	{

-		if(level > es2::IMPLEMENTATION_MAX_TEXTURE_LEVELS)

+		if(level >= es2::IMPLEMENTATION_MAX_TEXTURE_LEVELS)

 		{

 			return error(GL_INVALID_VALUE);

 		}

@@ -956,58 +1162,9 @@
 		es2::Renderbuffer *source = framebuffer->getColorbuffer();

 		GLenum colorbufferFormat = source->getFormat();

 

-		// [OpenGL ES 2.0.24] table 3.9

-		switch(internalformat)

+		if(!validateColorBufferFormat(internalformat, colorbufferFormat))

 		{

-		case GL_ALPHA:

-			if(colorbufferFormat != GL_ALPHA &&

-			   colorbufferFormat != GL_RGBA &&

-			   colorbufferFormat != GL_RGBA4 &&

-			   colorbufferFormat != GL_RGB5_A1 &&

-			   colorbufferFormat != GL_RGBA8_OES)

-			{

-				return error(GL_INVALID_OPERATION);

-			}

-			break;

-		case GL_LUMINANCE:

-		case GL_RGB:

-			if(colorbufferFormat != GL_RGB &&

-			   colorbufferFormat != GL_RGB565 &&

-			   colorbufferFormat != GL_RGB8_OES &&

-			   colorbufferFormat != GL_RGBA &&

-			   colorbufferFormat != GL_RGBA4 &&

-			   colorbufferFormat != GL_RGB5_A1 &&

-			   colorbufferFormat != GL_RGBA8_OES)

-			{

-				return error(GL_INVALID_OPERATION);

-			}

-			break;

-		case GL_LUMINANCE_ALPHA:

-		case GL_RGBA:

-			if(colorbufferFormat != GL_RGBA &&

-			   colorbufferFormat != GL_RGBA4 &&

-			   colorbufferFormat != GL_RGB5_A1 &&

-			   colorbufferFormat != GL_RGBA8_OES)

-			{

-				return error(GL_INVALID_OPERATION);

-			}

-			break;

-		case GL_ETC1_RGB8_OES:

-			return error(GL_INVALID_OPERATION);

-		case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:

-		case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:

-		case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:

-		case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:

-			if(S3TC_SUPPORT)

-			{

-				return error(GL_INVALID_OPERATION);

-			}

-			else

-			{

-				return error(GL_INVALID_ENUM);

-			}

-		default:

-			return error(GL_INVALID_ENUM);

+			return;

 		}

 

 		if(target == GL_TEXTURE_2D)

@@ -1066,7 +1223,7 @@
 

 	if(context)

 	{

-		if(level > es2::IMPLEMENTATION_MAX_TEXTURE_LEVELS)

+		if(level >= es2::IMPLEMENTATION_MAX_TEXTURE_LEVELS)

 		{

 			return error(GL_INVALID_VALUE);

 		}

@@ -1104,64 +1261,12 @@
 

 		GLenum textureFormat = texture->getFormat(target, level);

 

-		// [OpenGL ES 2.0.24] table 3.9

-		switch(textureFormat)

+		if(!validateColorBufferFormat(textureFormat, colorbufferFormat))

 		{

-		case GL_ALPHA:

-			if(colorbufferFormat != GL_ALPHA &&

-			   colorbufferFormat != GL_RGBA &&

-			   colorbufferFormat != GL_RGBA4 &&

-			   colorbufferFormat != GL_RGB5_A1 &&

-			   colorbufferFormat != GL_RGBA8_OES)

-			{

-				return error(GL_INVALID_OPERATION);

-			}

-			break;

-		case GL_LUMINANCE:

-		case GL_RGB:

-			if(colorbufferFormat != GL_RGB &&

-			   colorbufferFormat != GL_RGB565 &&

-			   colorbufferFormat != GL_RGB8_OES &&

-			   colorbufferFormat != GL_RGBA &&

-			   colorbufferFormat != GL_RGBA4 &&

-			   colorbufferFormat != GL_RGB5_A1 &&

-			   colorbufferFormat != GL_RGBA8_OES)

-			{

-				return error(GL_INVALID_OPERATION);

-			}

-			break;

-		case GL_LUMINANCE_ALPHA:

-		case GL_RGBA:

-			if(colorbufferFormat != GL_RGBA &&

-			   colorbufferFormat != GL_RGBA4 &&

-			   colorbufferFormat != GL_RGB5_A1 &&

-			   colorbufferFormat != GL_RGBA8_OES)

-			{

-				return error(GL_INVALID_OPERATION);

-			}

-			break;

-		case GL_ETC1_RGB8_OES:

-			return error(GL_INVALID_OPERATION);

-		case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:

-		case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:

-		case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:

-		case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:

-			if(S3TC_SUPPORT)

-			{

-				return error(GL_INVALID_OPERATION);

-			}

-			else

-			{

-				return error(GL_INVALID_ENUM);

-			}

-		case GL_DEPTH_COMPONENT:

-		case GL_DEPTH_STENCIL_OES:

-			return error(GL_INVALID_OPERATION);

-		default:

-			return error(GL_INVALID_ENUM);

+			return;

 		}

 

-		texture->copySubImage(target, level, xoffset, yoffset, x, y, width, height, framebuffer);

+		texture->copySubImage(target, level, xoffset, yoffset, 0, x, y, width, height, framebuffer);

 	}

 }

 

@@ -2861,6 +2966,7 @@
 			"GL_OES_texture_half_float "

 			"GL_OES_texture_half_float_linear "

 			"GL_OES_texture_npot "

+			"GL_OES_texture_3D "

 			"GL_EXT_blend_minmax "

 			"GL_EXT_occlusion_query_boolean "

 			"GL_EXT_read_format_bgra "

@@ -2920,6 +3026,9 @@
 		case GL_TEXTURE_WRAP_T:

 			*params = (GLfloat)texture->getWrapT();

 			break;

+		case GL_TEXTURE_WRAP_R_OES:

+			*params = (GLfloat)texture->getWrapR();

+			break;

 		case GL_TEXTURE_MAX_ANISOTROPY_EXT:

 			*params = texture->getMaxAnisotropy();

 			break;

@@ -2971,6 +3080,9 @@
 		case GL_TEXTURE_WRAP_T:

 			*params = texture->getWrapT();

 			break;

+		case GL_TEXTURE_WRAP_R_OES:

+			*params = texture->getWrapR();

+			break;

 		case GL_TEXTURE_MAX_ANISOTROPY_EXT:

 			*params = (GLint)texture->getMaxAnisotropy();

 			break;

@@ -4144,6 +4256,9 @@
 		case GL_TEXTURE_2D:

 			texture = context->getTexture2D();

 			break;

+		case GL_TEXTURE_3D_OES:

+			texture = context->getTexture3D();

+			break;

 		case GL_TEXTURE_CUBE_MAP:

 			texture = context->getTextureCubeMap();

 			break;

@@ -4168,6 +4283,12 @@
 				return error(GL_INVALID_ENUM);

 			}

 			break;

+		case GL_TEXTURE_WRAP_R_OES:

+			if(!texture->setWrapR((GLenum)param))

+			{

+				return error(GL_INVALID_ENUM);

+			}

+			break;

 		case GL_TEXTURE_MIN_FILTER:

 			if(!texture->setMinFilter((GLenum)param))

 			{

@@ -4212,6 +4333,9 @@
 		case GL_TEXTURE_2D:

 			texture = context->getTexture2D();

 			break;

+		case GL_TEXTURE_3D_OES:

+			texture = context->getTexture3D();

+			break;

 		case GL_TEXTURE_CUBE_MAP:

 			texture = context->getTextureCubeMap();

 			break;

@@ -4236,6 +4360,12 @@
 				return error(GL_INVALID_ENUM);

 			}

 			break;

+		case GL_TEXTURE_WRAP_R_OES:

+			if(!texture->setWrapR((GLenum)param))

+			{

+				return error(GL_INVALID_ENUM);

+			}

+			break;

 		case GL_TEXTURE_MIN_FILTER:

 			if(!texture->setMinFilter((GLenum)param))

 			{

@@ -4302,7 +4432,7 @@
 

 	if(context)

 	{

-		if(level > es2::IMPLEMENTATION_MAX_TEXTURE_LEVELS)

+		if(level >= es2::IMPLEMENTATION_MAX_TEXTURE_LEVELS)

 		{

 			return error(GL_INVALID_VALUE);

 		}

@@ -5047,9 +5177,373 @@
 	      "GLenum format = 0x%X, GLenum type = 0x%x, const GLvoid* pixels = 0x%0.8p)",

 	      target, level, internalformat, width, height, depth, border, format, type, pixels);

 

-	UNIMPLEMENTED();   // FIXME

+	switch(target)

+	{

+	case GL_TEXTURE_3D_OES:

+		switch(format)

+		{

+		case GL_DEPTH_COMPONENT:

+		case GL_DEPTH_STENCIL_OES:

+			return error(GL_INVALID_OPERATION);

+		default:

+			break;

+		}

+		break;

+	default:

+		return error(GL_INVALID_ENUM);

+	}

+

+	if(!ValidateType3D(type) || !ValidateFormat3D(format))

+	{

+		return error(GL_INVALID_ENUM);

+	}

+

+	if((level < 0) || (level >= es2::IMPLEMENTATION_MAX_TEXTURE_LEVELS))

+	{

+		return error(GL_INVALID_VALUE);

+	}

+

+	const GLsizei maxSize3D = es2::IMPLEMENTATION_MAX_TEXTURE_SIZE >> level;

+	if((width < 0) || (height < 0) || (depth < 0) || (width > maxSize3D) || (height > maxSize3D) || (depth > maxSize3D))

+	{

+		return error(GL_INVALID_VALUE);

+	}

+

+	if(border != 0)

+	{

+		return error(GL_INVALID_VALUE);

+	}

+

+	if(!ValidateInternalFormat3D(internalformat, format, type))

+	{

+		return error(GL_INVALID_OPERATION);

+	}

+

+	es2::Context *context = es2::getContext();

+

+	if(context)

+	{

+		es2::Texture3D *texture = context->getTexture3D();

+

+		if(!texture)

+		{

+			return error(GL_INVALID_OPERATION);

+		}

+

+		texture->setImage(level, width, height, depth, internalformat, type, context->getUnpackAlignment(), pixels);

+	}

 }

 

+void GL_APIENTRY glTexSubImage3DOES(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels)
+{
+	TRACE("(GLenum target = 0x%X, GLint level = %d, GLint xoffset = %d, GLint yoffset = %d, "

+		"GLint zoffset = %d, GLsizei width = %d, GLsizei height = %d, GLsizei depth = %d, "

+		"GLenum format = 0x%X, GLenum type = 0x%x, const GLvoid* pixels = 0x%0.8p)",

+		target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels);
+
+	switch(target)

+	{

+	case GL_TEXTURE_3D_OES:

+		break;

+	default:

+		return error(GL_INVALID_ENUM);

+	}

+

+	if(!ValidateType3D(type) || !ValidateFormat3D(format))

+	{

+		return error(GL_INVALID_ENUM);

+	}

+

+	if((level < 0) || (level >= es2::IMPLEMENTATION_MAX_TEXTURE_LEVELS))

+	{

+		return error(GL_INVALID_VALUE);

+	}

+

+	if((width < 0) || (height < 0) || (depth < 0))

+	{

+		return error(GL_INVALID_VALUE);

+	}

+

+	es2::Context *context = es2::getContext();

+

+	if(context)

+	{

+		es2::Texture3D *texture = context->getTexture3D();

+

+		if(validateSubImageParams(false, width, height, depth, xoffset, yoffset, zoffset, target, level, format, texture))

+		{

+			texture->subImage(level, xoffset, yoffset, zoffset, width, height, depth, format, type, context->getUnpackAlignment(), pixels);

+		}

+	}
+}
+
+void GL_APIENTRY glCopyTexSubImage3DOES(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height)
+{
+	TRACE("(GLenum target = 0x%X, GLint level = %d, GLint xoffset = %d, GLint yoffset = %d, "

+		"GLint zoffset = %d, GLint x = %d, GLint y = %d, GLsizei width = %d, GLsizei height = %d)",

+		target, level, xoffset, yoffset, zoffset, x, y, width, height);
+
+	switch(target)

+	{

+	case GL_TEXTURE_3D_OES:

+		break;

+	default:

+		return error(GL_INVALID_ENUM);

+	}
+
+	if((level < 0) || (level >= es2::IMPLEMENTATION_MAX_TEXTURE_LEVELS))

+	{

+		return error(GL_INVALID_VALUE);

+	}
+
+	es2::Context *context = es2::getContext();

+

+	if(context)

+	{

+		es2::Framebuffer *framebuffer = context->getReadFramebuffer();

+

+		if(framebuffer->completeness() != GL_FRAMEBUFFER_COMPLETE)

+		{

+			return error(GL_INVALID_FRAMEBUFFER_OPERATION);

+		}

+

+		if(context->getReadFramebufferHandle() != 0 && framebuffer->getColorbuffer()->getSamples() > 1)

+		{

+			return error(GL_INVALID_OPERATION);

+		}

+

+		es2::Renderbuffer *source = framebuffer->getColorbuffer();

+		GLenum colorbufferFormat = source->getFormat();

+		es2::Texture3D *texture = context->getTexture3D();

+

+		if(!validateSubImageParams(false, width, height, 1, xoffset, yoffset, zoffset, target, level, GL_NONE, texture))

+		{

+			return;

+		}

+

+		GLenum textureFormat = texture->getFormat(target, level);

+

+		if(!validateColorBufferFormat(textureFormat, colorbufferFormat))

+		{

+			return;

+		}

+		

+		texture->copySubImage(target, level, xoffset, yoffset, zoffset, x, y, width, height, framebuffer);

+	}
+}
+
+void GL_APIENTRY glCompressedTexImage3DOES(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data)
+{
+	TRACE("(GLenum target = 0x%X, GLint level = %d, GLenum internalformat = 0x%X, GLsizei width = %d, "

+		"GLsizei height = %d, GLsizei depth = %d, GLint border = %d, GLsizei imageSize = %d, const GLvoid* data = 0x%0.8p)",

+		target, level, internalformat, width, height, depth, border, imageSize, data);

+

+	switch(target)

+	{

+	case GL_TEXTURE_3D_OES:

+		break;

+	default:

+		return error(GL_INVALID_ENUM);

+	}
+

+	if((level < 0) || (level >= es2::IMPLEMENTATION_MAX_TEXTURE_LEVELS))

+	{

+		return error(GL_INVALID_VALUE);

+	}

+

+	const GLsizei maxSize3D = es2::IMPLEMENTATION_MAX_TEXTURE_SIZE >> level;

+	if((width < 0) || (height < 0) || (depth < 0) || (width > maxSize3D) || (height > maxSize3D) || (depth > maxSize3D) ||(border != 0) || (imageSize < 0))

+	{

+		return error(GL_INVALID_VALUE);

+	}

+

+	switch(internalformat)

+	{

+	case GL_ETC1_RGB8_OES:

+		break;

+	case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:

+	case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:

+	case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:

+	case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:

+		if(!S3TC_SUPPORT)

+		{

+			return error(GL_INVALID_ENUM);

+		}

+		break;

+	case GL_DEPTH_COMPONENT:

+	case GL_DEPTH_COMPONENT16:

+	case GL_DEPTH_COMPONENT32_OES:

+	case GL_DEPTH_STENCIL_OES:

+	case GL_DEPTH24_STENCIL8_OES:

+		return error(GL_INVALID_OPERATION);

+	default:

+		return error(GL_INVALID_ENUM);

+	}

+

+	if(imageSize != es2::ComputeCompressedSize(width, height, internalformat) * depth)

+	{

+		return error(GL_INVALID_VALUE);

+	}

+

+	es2::Context *context = es2::getContext();

+

+	if(context)

+	{

+		es2::Texture3D *texture = context->getTexture3D();

+

+		if(!texture)

+		{

+			return error(GL_INVALID_OPERATION);

+		}

+

+		texture->setCompressedImage(level, internalformat, width, height, depth, imageSize, data);

+	}
+}
+
+void GL_APIENTRY glCompressedTexSubImage3DOES(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data)
+{
+	TRACE("(GLenum target = 0x%X, GLint level = %d, GLint xoffset = %d, GLint yoffset = %d, "

+		"GLint zoffset = %d, GLsizei width = %d, GLsizei height = %d, GLsizei depth = %d, "

+		"GLenum format = 0x%X, GLsizei imageSize = %d, const void *data = 0x%0.8p)",

+		target, level, xoffset, yoffset, zoffset, width, height, depth, format, imageSize, data);
+
+	switch(target)

+	{

+	case GL_TEXTURE_3D_OES:

+		break;

+	default:

+		return error(GL_INVALID_ENUM);

+	}
+

+	if(xoffset < 0 || yoffset < 0 || zoffset < 0 || !validImageSize(level, width, height) || depth < 0 || imageSize < 0)

+	{

+		return error(GL_INVALID_VALUE);

+	}

+

+	switch(format)

+	{

+	case GL_ETC1_RGB8_OES:

+		break;

+	case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:

+	case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:

+	case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:

+	case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:

+		if(!S3TC_SUPPORT)

+		{

+			return error(GL_INVALID_ENUM);

+		}

+		break;

+	default:

+		return error(GL_INVALID_ENUM);

+	}

+

+	if(width == 0 || height == 0 || depth == 0 || data == NULL)

+	{

+		return;

+	}
+
+	es2::Context *context = es2::getContext();

+

+	if(context)

+	{
+		es2::Texture3D *texture = context->getTexture3D();

+

+		if(!texture)

+		{

+			return error(GL_INVALID_OPERATION);

+		}

+

+		texture->subImageCompressed(level, xoffset, yoffset, zoffset, width, height, depth, format, imageSize, data);

+	}

+}
+
+void GL_APIENTRY glFramebufferTexture3DOES(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset)
+{
+	TRACE("(GLenum target = 0x%X, GLenum attachment = 0x%X, GLenum textarget = 0x%X, "

+		"GLuint texture = %d, GLint level = %d, GLint zoffset = %d)", target, attachment, textarget, texture, level, zoffset);

+

+	if(target != GL_FRAMEBUFFER && target != GL_DRAW_FRAMEBUFFER_ANGLE && target != GL_READ_FRAMEBUFFER_ANGLE)

+	{

+		return error(GL_INVALID_ENUM);

+	}

+

+	switch(attachment)

+	{

+	case GL_COLOR_ATTACHMENT0:

+	case GL_DEPTH_ATTACHMENT:

+	case GL_STENCIL_ATTACHMENT:

+		break;

+	default:

+		return error(GL_INVALID_ENUM);

+	}

+

+	es2::Context *context = es2::getContext();

+

+	if(context)

+	{

+		if(texture == 0)

+		{

+			textarget = GL_NONE;

+		}

+		else

+		{

+			es2::Texture *tex = context->getTexture(texture);

+

+			if(tex == NULL)

+			{

+				return error(GL_INVALID_OPERATION);

+			}

+

+			if(tex->isCompressed(textarget, level))

+			{

+				return error(GL_INVALID_OPERATION);

+			}

+

+			switch(textarget)

+			{

+			case GL_TEXTURE_3D_OES:

+				if(tex->getTarget() != GL_TEXTURE_3D_OES)

+				{

+					return error(GL_INVALID_OPERATION);

+				}

+				break;

+			default:

+				return error(GL_INVALID_ENUM);

+			}

+

+			if(level != 0)

+			{

+				return error(GL_INVALID_VALUE);

+			}

+		}

+

+		es2::Framebuffer *framebuffer = NULL;

+		GLuint framebufferHandle = 0;

+		if(target == GL_READ_FRAMEBUFFER_ANGLE)

+		{

+			framebuffer = context->getReadFramebuffer();

+			framebufferHandle = context->getReadFramebufferHandle();

+		}

+		else

+		{

+			framebuffer = context->getDrawFramebuffer();

+			framebufferHandle = context->getDrawFramebufferHandle();

+		}

+

+		if(framebufferHandle == 0 || !framebuffer)

+		{

+			return error(GL_INVALID_OPERATION);

+		}

+

+		switch(attachment)

+		{

+		case GL_COLOR_ATTACHMENT0:  framebuffer->setColorbuffer(textarget, texture);   break;

+		case GL_DEPTH_ATTACHMENT:   framebuffer->setDepthbuffer(textarget, texture);   break;

+		case GL_STENCIL_ATTACHMENT: framebuffer->setStencilbuffer(textarget, texture); break;

+		}

+	}
+}
+

 void GL_APIENTRY glEGLImageTargetTexture2DOES(GLenum target, GLeglImageOES image)

 {

 	if(egl::getClientVersion() == 1)

diff --git a/src/OpenGL/libGLESv2/utilities.cpp b/src/OpenGL/libGLESv2/utilities.cpp
index 8ebcb16..acfa903 100644
--- a/src/OpenGL/libGLESv2/utilities.cpp
+++ b/src/OpenGL/libGLESv2/utilities.cpp
@@ -32,6 +32,7 @@
 		case GL_SAMPLER_2D:

 		case GL_SAMPLER_CUBE:

         case GL_SAMPLER_EXTERNAL_OES:

+		case GL_SAMPLER_3D_OES:

 			return 1;

 		case GL_BOOL_VEC2:

 		case GL_FLOAT_VEC2:

@@ -77,7 +78,8 @@
 		case GL_INT:

 		case GL_SAMPLER_2D:

 		case GL_SAMPLER_CUBE:

-        case GL_SAMPLER_EXTERNAL_OES:

+		case GL_SAMPLER_EXTERNAL_OES:

+		case GL_SAMPLER_3D_OES:

 		case GL_INT_VEC2:

 		case GL_INT_VEC3:

 		case GL_INT_VEC4:

@@ -122,6 +124,7 @@
 		case GL_SAMPLER_2D:

 		case GL_SAMPLER_CUBE:

         case GL_SAMPLER_EXTERNAL_OES:

+		case GL_SAMPLER_3D_OES:

 			return 1;

 		case GL_FLOAT_MAT2:

 			return 2;