Transform Feedback and Sampler API Implemented

Implemented API functions related to creating,
binding, deleting or accessing the parameters
of Transform Feedback and Sampler objects.

Change-Id: I267605733758332439addd71c24f5f438d22ba46
Reviewed-on: https://swiftshader-review.googlesource.com/2814
Tested-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 120035c..2ef8661 100644
--- a/src/OpenGL/libGLESv2/Context.cpp
+++ b/src/OpenGL/libGLESv2/Context.cpp
@@ -1343,6 +1343,11 @@
     return mState.elementArrayBuffer;

 }

 

+TransformFeedback *Context::getTransformFeedback()

+{

+	return getTransformFeedback(mState.transformFeedback);

+}

+

 Program *Context::getCurrentProgram()

 {

     return mResourceManager->getProgram(mState.currentProgram);

diff --git a/src/OpenGL/libGLESv2/libGLESv3.cpp b/src/OpenGL/libGLESv2/libGLESv3.cpp
index 08b9149..0a4b2b6 100644
--- a/src/OpenGL/libGLESv2/libGLESv3.cpp
+++ b/src/OpenGL/libGLESv2/libGLESv3.cpp
@@ -15,6 +15,7 @@
 #include "Framebuffer.h"

 #include "Program.h"

 #include "Query.h"

+#include "Sampler.h"

 #include "Texture.h"

 #include "common/debug.h"

 

@@ -1320,15 +1321,15 @@
 		error(GL_INVALID_VALUE);

 	}

 

-	if((access & ~(GL_MAP_READ_BIT |

-				   GL_MAP_WRITE_BIT |

-				   GL_MAP_INVALIDATE_RANGE_BIT |

-				   GL_MAP_INVALIDATE_BUFFER_BIT |

-				   GL_MAP_FLUSH_EXPLICIT_BIT |

-				   GL_MAP_UNSYNCHRONIZED_BIT)) != 0)

-	{

-		error(GL_INVALID_VALUE);

-	}

+		if((access & ~(GL_MAP_READ_BIT |

+		               GL_MAP_WRITE_BIT |

+		               GL_MAP_INVALIDATE_RANGE_BIT |

+		               GL_MAP_INVALIDATE_BUFFER_BIT |

+		               GL_MAP_FLUSH_EXPLICIT_BIT |

+		               GL_MAP_UNSYNCHRONIZED_BIT)) != 0)

+		{

+			error(GL_INVALID_VALUE);

+		}

 

 	UNIMPLEMENTED();

 	return nullptr;

@@ -1356,7 +1357,17 @@
 {

 	TRACE("(GLuint array = %d)", array);

 

-	UNIMPLEMENTED();

+	if(array == 0)

+	{

+		return;

+	}

+

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

+

+	if(context && !context->bindVertexArray(array))

+	{

+		return error(GL_INVALID_OPERATION);

+	}

 }

 

 void GL_APIENTRY glDeleteVertexArrays(GLsizei n, const GLuint *arrays)

@@ -1368,7 +1379,15 @@
 		return error(GL_INVALID_VALUE);

 	}

 

-	UNIMPLEMENTED();

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

+

+	if(context)

+	{

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

+		{

+			context->deleteVertexArray(arrays[i]);

+		}

+	}

 }

 

 void GL_APIENTRY glGenVertexArrays(GLsizei n, GLuint *arrays)

@@ -1380,14 +1399,38 @@
 		return error(GL_INVALID_VALUE);

 	}

 

-	UNIMPLEMENTED();

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

+

+	if(context)

+	{

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

+		{

+			arrays[i] = context->createVertexArray();

+		}

+	}

 }

 

 GLboolean GL_APIENTRY glIsVertexArray(GLuint array)

 {

 	TRACE("(GLuint array = %d)", array);

 

-	UNIMPLEMENTED();

+	if(array == 0)

+	{

+		return GL_FALSE;

+	}

+

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

+

+	if(context)

+	{

+		es2::VertexArray *arrayObject = context->getVertexArray(array);

+

+		if(arrayObject)

+		{

+			return GL_TRUE;

+		}

+	}

+

 	return GL_FALSE;

 }

 

@@ -1464,12 +1507,50 @@
 		return error(GL_INVALID_ENUM);

 	}

 

-	UNIMPLEMENTED();

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

+

+	if(context)

+	{

+		es2::TransformFeedback *transformFeedbackObject = context->getTransformFeedback();

+

+		if(transformFeedbackObject)

+		{

+			if(transformFeedbackObject->isActive())

+			{

+				return error(GL_INVALID_OPERATION);

+			}

+			transformFeedbackObject->begin(primitiveMode);

+		}

+		else

+		{

+			return error(GL_INVALID_OPERATION);

+		}

+	}

 }

 

 void GL_APIENTRY glEndTransformFeedback(void)

 {

-	UNIMPLEMENTED();

+	TRACE("()");

+

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

+

+	if(context)

+	{

+		es2::TransformFeedback *transformFeedbackObject = context->getTransformFeedback();

+

+		if(transformFeedbackObject)

+		{

+			if(!transformFeedbackObject->isActive())

+			{

+				return error(GL_INVALID_OPERATION);

+			}

+			transformFeedbackObject->end();

+		}

+		else

+		{

+			return error(GL_INVALID_OPERATION);

+		}

+	}

 }

 

 void GL_APIENTRY glBindBufferRange(GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size)

@@ -1477,16 +1558,47 @@
 	TRACE("(GLenum target = 0x%X, GLuint index = %d, GLuint buffer = %d, GLintptr offset = %d, GLsizeiptr size = %d)",

 	      target, index, buffer, offset, size);

 

-	switch(target)

+	if(buffer != 0 && size <= 0)

 	{

-	case GL_TRANSFORM_FEEDBACK_BUFFER:

-	case GL_UNIFORM_BUFFER:

-		break;

-	default:

-		return error(GL_INVALID_ENUM);

+		return error(GL_INVALID_VALUE);

 	}

 

-	UNIMPLEMENTED();

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

+

+	if(context)

+	{

+		switch(target)

+		{

+		case GL_TRANSFORM_FEEDBACK_BUFFER:

+			if(index >= es2::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS)

+			{

+				return error(GL_INVALID_VALUE);

+			}

+			if(size & 0x3 || offset & 0x3) // size and offset must be multiples of 4

+			{

+				return error(GL_INVALID_VALUE);

+			}

+			else

+			{

+				es2::TransformFeedback* transformFeedback = context->getTransformFeedback();

+				transformFeedback->setBuffer(index, context->getBuffer(buffer), offset, size);

+			}

+			break;

+		case GL_UNIFORM_BUFFER:

+			if(index >= es2::IMPLEMENTATION_MAX_UNIFORM_BUFFER_BINDINGS)

+			{

+				return error(GL_INVALID_VALUE);

+			}

+			if(offset % es2::IMPLEMENTATION_UNIFORM_BUFFER_OFFSET_ALIGNMENT != 0)

+			{

+				return error(GL_INVALID_VALUE);

+			}

+			UNIMPLEMENTED();

+			break;

+		default:

+			return error(GL_INVALID_ENUM);

+		}

+	}

 }

 

 void GL_APIENTRY glBindBufferBase(GLenum target, GLuint index, GLuint buffer)

@@ -1494,16 +1606,34 @@
 	TRACE("(GLenum target = 0x%X, GLuint index = %d, GLuint buffer = %d)",

 	      target, index, buffer);

 

-	switch(target)

-	{

-	case GL_TRANSFORM_FEEDBACK_BUFFER:

-	case GL_UNIFORM_BUFFER:

-		break;

-	default:

-		return error(GL_INVALID_ENUM);

-	}

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

 

-	UNIMPLEMENTED();

+	if(context)

+	{

+		switch(target)

+		{

+		case GL_TRANSFORM_FEEDBACK_BUFFER:

+			if(index >= es2::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS)

+			{

+				return error(GL_INVALID_VALUE);

+			}

+			else

+			{

+				es2::TransformFeedback* transformFeedback = context->getTransformFeedback();

+				transformFeedback->setBuffer(index, context->getBuffer(buffer));

+			}

+			break;

+		case GL_UNIFORM_BUFFER:

+			if(index >= es2::IMPLEMENTATION_MAX_UNIFORM_BUFFER_BINDINGS)

+			{

+				return error(GL_INVALID_VALUE);

+			}

+			UNIMPLEMENTED();

+			break;

+		default:

+			return error(GL_INVALID_ENUM);

+		}

+	}

 }

 

 void GL_APIENTRY glTransformFeedbackVaryings(GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode)

@@ -1511,6 +1641,31 @@
 	TRACE("(GLuint program = %d, GLsizei count = %d, const GLchar *const*varyings = 0x%0.8p, GLenum bufferMode = 0x%X)",

 	      program, count, varyings, bufferMode);

 

+	switch(bufferMode)

+	{

+	case GL_SEPARATE_ATTRIBS:

+		if(count > es2::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS)

+		{

+			return error(GL_INVALID_VALUE);

+		}

+	case GL_INTERLEAVED_ATTRIBS:

+		break;

+	default:

+		return error(GL_INVALID_ENUM);

+	}

+

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

+

+	if(context)

+	{

+		es2::Program *programObject = context->getProgram(program);

+

+		if(!programObject)

+		{

+			return error(GL_INVALID_VALUE);

+		}

+	}

+

 	UNIMPLEMENTED();

 }

 

@@ -2259,6 +2414,17 @@
 		return error(GL_INVALID_VALUE);

 	}

 

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

+

+	if(context)

+	{

+		es2::TransformFeedback* transformFeedback = context->getTransformFeedback();

+		if(transformFeedback && transformFeedback->isActive() && (mode != transformFeedback->primitiveMode()))

+		{

+			return error(GL_INVALID_OPERATION);

+		}

+	}

+

 	UNIMPLEMENTED();

 }

 

@@ -2296,6 +2462,17 @@
 		return error(GL_INVALID_VALUE);

 	}

 

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

+

+	if(context)

+	{

+		es2::TransformFeedback* transformFeedback = context->getTransformFeedback();

+		if(transformFeedback && transformFeedback->isActive() && !transformFeedback->isPaused())

+		{

+			return error(GL_INVALID_OPERATION);

+		}

+	}

+

 	UNIMPLEMENTED();

 }

 

@@ -2400,7 +2577,15 @@
 		return error(GL_INVALID_VALUE);

 	}

 

-	UNIMPLEMENTED();

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

+

+	if(context)

+	{

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

+		{

+			samplers[i] = context->createSampler();

+		}

+	}

 }

 

 void GL_APIENTRY glDeleteSamplers(GLsizei count, const GLuint *samplers)

@@ -2412,14 +2597,38 @@
 		return error(GL_INVALID_VALUE);

 	}

 

-	UNIMPLEMENTED();

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

+

+	if(context)

+	{

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

+		{

+			context->deleteSampler(samplers[i]);

+		}

+	}

 }

 

 GLboolean GL_APIENTRY glIsSampler(GLuint sampler)

 {

 	TRACE("(GLuint sampler = %d)", sampler);

 

-	UNIMPLEMENTED();

+	if(sampler == 0)

+	{

+		return GL_FALSE;

+	}

+

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

+

+	if(context)

+	{

+		es2::Sampler *samplerObject = context->getSampler(sampler);

+

+		if(samplerObject)

+		{

+			return GL_TRUE;

+		}

+	}

+

 	return GL_FALSE;

 }

 

@@ -2427,7 +2636,27 @@
 {

 	TRACE("(GLuint unit = %d, GLuint sampler = %d)", unit, sampler);

 

-	UNIMPLEMENTED();

+	if(unit >= es2::MAX_COMBINED_TEXTURE_IMAGE_UNITS)

+	{

+		return error(GL_INVALID_VALUE);

+	}

+

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

+

+	if(context)

+	{

+		if(sampler != 0)

+		{

+			es2::Sampler *samplerObject = context->getSampler(sampler);

+

+			if(!samplerObject)

+			{

+				return error(GL_INVALID_OPERATION);

+			}

+		}

+

+		context->bindSampler(unit, sampler);

+	}

 }

 

 void GL_APIENTRY glSamplerParameteri(GLuint sampler, GLenum pname, GLint param)

@@ -2443,7 +2672,119 @@
 	TRACE("(GLuint sampler = %d, GLenum pname = 0x%X, const GLint *param = 0x%0.8p)",

 	      sampler, pname, param);

 

-	UNIMPLEMENTED();

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

+

+	if(context)

+	{

+		es2::Sampler *samplerObject = (sampler != 0) ? context->getSampler(sampler) : nullptr;

+

+		if(!samplerObject)

+		{

+			return error(GL_INVALID_VALUE);

+		}

+

+		switch(pname)

+		{

+		case GL_TEXTURE_WRAP_S:

+			switch(*param)

+			{

+			case GL_CLAMP_TO_EDGE:

+			case GL_MIRRORED_REPEAT:

+			case GL_REPEAT:

+				samplerObject->mWrapModeS = *param;

+				break;

+			default:

+				return error(GL_INVALID_ENUM);

+			}

+			break;

+		case GL_TEXTURE_WRAP_T:

+			switch(*param)

+			{

+			case GL_CLAMP_TO_EDGE:

+			case GL_MIRRORED_REPEAT:

+			case GL_REPEAT:

+				samplerObject->mWrapModeT = *param;

+				break;

+			default:

+				return error(GL_INVALID_ENUM);

+			}

+			break;

+		case GL_TEXTURE_WRAP_R:

+			switch(*param)

+			{

+			case GL_CLAMP_TO_EDGE:

+			case GL_MIRRORED_REPEAT:

+			case GL_REPEAT:

+				samplerObject->mWrapModeR = *param;

+				break;

+			default:

+				return error(GL_INVALID_ENUM);

+			}

+			break;

+		case GL_TEXTURE_MIN_FILTER:

+			switch(*param)

+			{

+			case GL_NEAREST:

+			case GL_LINEAR:

+			case GL_NEAREST_MIPMAP_NEAREST:

+			case GL_LINEAR_MIPMAP_NEAREST:

+			case GL_NEAREST_MIPMAP_LINEAR:

+			case GL_LINEAR_MIPMAP_LINEAR:

+				samplerObject->mMinFilter = *param;

+				break;

+			default:

+				return error(GL_INVALID_ENUM);

+			}

+			break;

+		case GL_TEXTURE_MAG_FILTER:

+			switch(*param)

+			{

+			case GL_NEAREST:

+			case GL_LINEAR:

+				samplerObject->mMagFilter = *param;

+				break;

+			default:

+				return error(GL_INVALID_ENUM);

+			}

+			break;

+		case GL_TEXTURE_MIN_LOD:

+			samplerObject->mMinLod = (GLfloat)*param;

+			break;

+		case GL_TEXTURE_MAX_LOD:

+			samplerObject->mMaxLod = (GLfloat)*param;

+			break;

+		case GL_TEXTURE_COMPARE_MODE:

+			switch(*param)

+			{

+			case GL_COMPARE_REF_TO_TEXTURE:

+			case GL_NONE:

+				samplerObject->mCompareMode = *param;

+				break;

+			default:

+				return error(GL_INVALID_ENUM);

+			}

+			break;

+		case GL_TEXTURE_COMPARE_FUNC:

+			switch(*param)

+			{

+			case GL_LEQUAL:

+			case GL_GEQUAL:

+			case GL_LESS:

+			case GL_GREATER:

+			case GL_EQUAL:

+			case GL_NOTEQUAL:

+			case GL_ALWAYS:

+			case GL_NEVER:

+				samplerObject->mCompareFunc = *param;

+				break;

+			default:

+				return error(GL_INVALID_ENUM);

+			}

+			break;

+		default:

+			return error(GL_INVALID_ENUM);

+		}

+	}

 }

 

 void GL_APIENTRY glSamplerParameterf(GLuint sampler, GLenum pname, GLfloat param)

@@ -2459,7 +2800,119 @@
 	TRACE("(GLuint sampler = %d, GLenum pname = 0x%X, const GLfloat *param = 0x%0.8p)",

 	      sampler, pname, param);

 

-	UNIMPLEMENTED();

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

+

+	if(context)

+	{

+		es2::Sampler *samplerObject = (sampler != 0) ? context->getSampler(sampler) : nullptr;

+

+		if(!samplerObject)

+		{

+			return error(GL_INVALID_VALUE);

+		}

+

+		switch(pname)

+		{

+		case GL_TEXTURE_WRAP_S:

+			switch((GLenum)*param)

+			{

+			case GL_CLAMP_TO_EDGE:

+			case GL_MIRRORED_REPEAT:

+			case GL_REPEAT:

+				samplerObject->mWrapModeS = (GLenum)*param;

+				break;

+			default:

+				return error(GL_INVALID_ENUM);

+			}

+			break;

+		case GL_TEXTURE_WRAP_T:

+			switch((GLenum)*param)

+			{

+			case GL_CLAMP_TO_EDGE:

+			case GL_MIRRORED_REPEAT:

+			case GL_REPEAT:

+				samplerObject->mWrapModeT = (GLenum)*param;

+				break;

+			default:

+				return error(GL_INVALID_ENUM);

+			}

+			break;

+		case GL_TEXTURE_WRAP_R:

+			switch((GLenum)*param)

+			{

+			case GL_CLAMP_TO_EDGE:

+			case GL_MIRRORED_REPEAT:

+			case GL_REPEAT:

+				samplerObject->mWrapModeR = (GLenum)*param;

+				break;

+			default:

+				return error(GL_INVALID_ENUM);

+			}

+			break;

+		case GL_TEXTURE_MIN_FILTER:

+			switch((GLenum)*param)

+			{

+			case GL_NEAREST:

+			case GL_LINEAR:

+			case GL_NEAREST_MIPMAP_NEAREST:

+			case GL_LINEAR_MIPMAP_NEAREST:

+			case GL_NEAREST_MIPMAP_LINEAR:

+			case GL_LINEAR_MIPMAP_LINEAR:

+				samplerObject->mMinFilter = (GLenum)*param;

+				break;

+			default:

+				return error(GL_INVALID_ENUM);

+			}

+			break;

+		case GL_TEXTURE_MAG_FILTER:

+			switch((GLenum)*param)

+			{

+			case GL_NEAREST:

+			case GL_LINEAR:

+				samplerObject->mMagFilter = (GLenum)*param;

+				break;

+			default:

+				return error(GL_INVALID_ENUM);

+			}

+			break;

+		case GL_TEXTURE_MIN_LOD:

+			samplerObject->mMinLod = *param;

+			break;

+		case GL_TEXTURE_MAX_LOD:

+			samplerObject->mMaxLod = *param;

+			break;

+		case GL_TEXTURE_COMPARE_MODE:

+			switch((GLenum)*param)

+			{

+			case GL_COMPARE_REF_TO_TEXTURE:

+			case GL_NONE:

+				samplerObject->mCompareMode = (GLenum)*param;

+				break;

+			default:

+				return error(GL_INVALID_ENUM);

+			}

+			break;

+		case GL_TEXTURE_COMPARE_FUNC:

+			switch((GLenum)*param)

+			{

+			case GL_LEQUAL:

+			case GL_GEQUAL:

+			case GL_LESS:

+			case GL_GREATER:

+			case GL_EQUAL:

+			case GL_NOTEQUAL:

+			case GL_ALWAYS:

+			case GL_NEVER:

+				samplerObject->mCompareFunc = (GLenum)*param;

+				break;

+			default:

+				return error(GL_INVALID_ENUM);

+			}

+			break;

+		default:

+			return error(GL_INVALID_ENUM);

+		}

+	}

 }

 

 void GL_APIENTRY glGetSamplerParameteriv(GLuint sampler, GLenum pname, GLint *params)

@@ -2467,7 +2920,50 @@
 	TRACE("(GLuint sampler = %d, GLenum pname = 0x%X, GLint *params = 0x%0.8p)",

 	      sampler, pname, params);

 

-	UNIMPLEMENTED();

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

+

+	if(context)

+	{

+		es2::Sampler *samplerObject = (sampler != 0) ? context->getSampler(sampler) : nullptr;

+

+		if(!samplerObject)

+		{

+			return error(GL_INVALID_VALUE);

+		}

+

+		switch(pname)

+		{

+		case GL_TEXTURE_WRAP_S:

+			*params = samplerObject->mWrapModeS;

+			break;

+		case GL_TEXTURE_WRAP_T:

+			*params = samplerObject->mWrapModeT;

+			break;

+		case GL_TEXTURE_WRAP_R:

+			*params = samplerObject->mWrapModeR;

+			break;

+		case GL_TEXTURE_MIN_FILTER:

+			*params = samplerObject->mMinFilter;

+			break;

+		case GL_TEXTURE_MAG_FILTER:

+			*params = samplerObject->mMagFilter;

+			break;

+		case GL_TEXTURE_MIN_LOD:

+			*params = (GLint)samplerObject->mMinLod;

+			break;

+		case GL_TEXTURE_MAX_LOD:

+			*params = (GLint)samplerObject->mMaxLod;

+			break;

+		case GL_TEXTURE_COMPARE_MODE:

+			*params = samplerObject->mCompareMode;

+			break;

+		case GL_TEXTURE_COMPARE_FUNC:

+			*params = samplerObject->mCompareFunc;

+			break;

+		default:

+			return error(GL_INVALID_ENUM);

+		}

+	}

 }

 

 void GL_APIENTRY glGetSamplerParameterfv(GLuint sampler, GLenum pname, GLfloat *params)

@@ -2475,7 +2971,50 @@
 	TRACE("(GLuint sampler = %d, GLenum pname = 0x%X, GLfloat *params = 0x%0.8p)",

 	      sampler, pname, params);

 

-	UNIMPLEMENTED();

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

+

+	if(context)

+	{

+		es2::Sampler *samplerObject = (sampler != 0) ? context->getSampler(sampler) : nullptr;

+

+		if(!samplerObject)

+		{

+			return error(GL_INVALID_VALUE);

+		}

+

+		switch(pname)

+		{

+		case GL_TEXTURE_WRAP_S:

+			*params = (GLfloat)samplerObject->mWrapModeS;

+			break;

+		case GL_TEXTURE_WRAP_T:

+			*params = (GLfloat)samplerObject->mWrapModeT;

+			break;

+		case GL_TEXTURE_WRAP_R:

+			*params = (GLfloat)samplerObject->mWrapModeR;

+			break;

+		case GL_TEXTURE_MIN_FILTER:

+			*params = (GLfloat)samplerObject->mMinFilter;

+			break;

+		case GL_TEXTURE_MAG_FILTER:

+			*params = (GLfloat)samplerObject->mMagFilter;

+			break;

+		case GL_TEXTURE_MIN_LOD:

+			*params = samplerObject->mMinLod;

+			break;

+		case GL_TEXTURE_MAX_LOD:

+			*params = samplerObject->mMaxLod;

+			break;

+		case GL_TEXTURE_COMPARE_MODE:

+			*params = (GLfloat)samplerObject->mCompareMode;

+			break;

+		case GL_TEXTURE_COMPARE_FUNC:

+			*params = (GLfloat)samplerObject->mCompareFunc;

+			break;

+		default:

+			return error(GL_INVALID_ENUM);

+		}

+	}

 }

 

 void GL_APIENTRY glVertexAttribDivisor(GLuint index, GLuint divisor)

@@ -2488,39 +3027,136 @@
 {

 	TRACE("(GLenum target = 0x%X, GLuint id = %d)", target, id);

 

-	UNIMPLEMENTED();

+	if(target != GL_TRANSFORM_FEEDBACK)

+	{

+		return error(GL_INVALID_ENUM);

+	}

+

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

+

+	if(context)

+	{

+		es2::TransformFeedback *transformFeedbackObject = context->getTransformFeedback();

+

+		if(transformFeedbackObject && transformFeedbackObject->isActive() && !transformFeedbackObject->isPaused())

+		{

+			return error(GL_INVALID_OPERATION);

+		}

+

+		if(!context->bindTransformFeedback(id))

+		{

+			return error(GL_INVALID_OPERATION);

+		}

+	}

 }

 

 void GL_APIENTRY glDeleteTransformFeedbacks(GLsizei n, const GLuint *ids)

 {

 	TRACE("(GLsizei n = %d, const GLuint *ids = 0x%0.8p)", n, ids);

 

-	UNIMPLEMENTED();

+	if(n < 0)

+	{

+		return error(GL_INVALID_VALUE);

+	}

+

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

+

+	if(context)

+	{

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

+		{

+			if (ids[i] != 0)

+			{

+				context->deleteTransformFeedback(ids[i]);

+			}

+		}

+	}

 }

 

 void GL_APIENTRY glGenTransformFeedbacks(GLsizei n, GLuint *ids)

 {

 	TRACE("(GLsizei n = %d, const GLuint *ids = 0x%0.8p)", n, ids);

 

-	UNIMPLEMENTED();

+	if(n < 0)

+	{

+		return error(GL_INVALID_VALUE);

+	}

+

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

+

+	if(context)

+	{

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

+		{

+			ids[i] = context->createTransformFeedback();

+		}

+	}

 }

 

 GLboolean GL_APIENTRY glIsTransformFeedback(GLuint id)

 {

 	TRACE("(GLuint id = %d)", id);

 

-	UNIMPLEMENTED();

+	if(id == 0)

+	{

+		return GL_FALSE;

+	}

+

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

+

+	if(context)

+	{

+		es2::TransformFeedback *transformFeedbackObject = context->getTransformFeedback(id);

+

+		if(transformFeedbackObject)

+		{

+			return GL_TRUE;

+		}

+	}

+

 	return GL_FALSE;

 }

 

 void GL_APIENTRY glPauseTransformFeedback(void)

 {

-	UNIMPLEMENTED();

+	TRACE("()");

+

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

+

+	if(context)

+	{

+		es2::TransformFeedback *transformFeedbackObject = context->getTransformFeedback();

+

+		if(transformFeedbackObject)

+		{

+			if(!transformFeedbackObject->isActive() || transformFeedbackObject->isPaused())

+			{

+				return error(GL_INVALID_OPERATION);

+			}

+			transformFeedbackObject->setPaused(true);

+		}

+	}

 }

 

 void GL_APIENTRY glResumeTransformFeedback(void)

 {

-	UNIMPLEMENTED();

+	TRACE("()");

+

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

+

+	if(context)

+	{

+		es2::TransformFeedback *transformFeedbackObject = context->getTransformFeedback();

+

+		if(transformFeedbackObject)

+		{

+			if(!transformFeedbackObject->isActive() || !transformFeedbackObject->isPaused())

+			{

+				return error(GL_INVALID_OPERATION);

+			}

+			transformFeedbackObject->setPaused(false);

+		}

+	}

 }

 

 void GL_APIENTRY glGetProgramBinary(GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary)