void GLSLESProgramPipeline::compileAndLink()
	{
#if GL_EXT_separate_shader_objects
        GLint linkStatus = 0;
        
        glGenProgramPipelinesEXT(1, &mGLProgramPipelineHandle);
        GL_CHECK_ERROR
        glBindProgramPipelineEXT(mGLProgramPipelineHandle);
        GL_CHECK_ERROR

		// Compile and attach Vertex Program
        if(mVertexProgram && !mVertexProgram->isLinked())
        {
            if (!mVertexProgram->getGLSLProgram()->compile(true))
            {
                mTriedToLinkAndFailed = true;
                return;
            }
            GLuint programHandle = mVertexProgram->getGLSLProgram()->getGLProgramHandle();
            glProgramParameteriEXT(programHandle, GL_PROGRAM_SEPARABLE_EXT, GL_TRUE);
            GL_CHECK_ERROR
            mVertexProgram->getGLSLProgram()->attachToProgramObject(programHandle);
            glLinkProgram(programHandle);
            GL_CHECK_ERROR
            glGetProgramiv(programHandle, GL_LINK_STATUS, &linkStatus);
            GL_CHECK_ERROR
            
            if(linkStatus)
            {
                mVertexProgram->setLinked(linkStatus);
                mLinked |= VERTEX_PROGRAM_LINKED;
            }
            
            GL_CHECK_ERROR
            // TODO: Just fail out here if the program doesn't link?
            mTriedToLinkAndFailed = !linkStatus;
            
            logObjectInfo( getCombinedName() + String("GLSL vertex program result : "), programHandle );
            
            setSkeletalAnimationIncluded(mVertexProgram->isSkeletalAnimationIncluded());
        }
        
		// Compile and attach Fragment Program
        if(mFragmentProgram && !mFragmentProgram->isLinked())
        {
            if (!mFragmentProgram->getGLSLProgram()->compile(true))
            {
                mTriedToLinkAndFailed = true;
                return;
            }

            GLuint programHandle = mFragmentProgram->getGLSLProgram()->getGLProgramHandle();
            glProgramParameteriEXT(programHandle, GL_PROGRAM_SEPARABLE_EXT, GL_TRUE);
            GL_CHECK_ERROR
            mFragmentProgram->getGLSLProgram()->attachToProgramObject(programHandle);
            glLinkProgram(programHandle);
            GL_CHECK_ERROR
            glGetProgramiv(programHandle, GL_LINK_STATUS, &linkStatus);
            GL_CHECK_ERROR
            
            if(linkStatus)
            {
                mFragmentProgram->setLinked(linkStatus);
                mLinked |= FRAGMENT_PROGRAM_LINKED;
            }
            
            mTriedToLinkAndFailed = !linkStatus;
            
            logObjectInfo( getCombinedName() + String("GLSL fragment program result : "), programHandle );
        }
        
		if(mLinked)
		{
			if ( GpuProgramManager::getSingleton().getSaveMicrocodesToCache() )
			{
				// Add to the microcode to the cache
				String name;
				name = getCombinedName();

				// Get buffer size
				GLint binaryLength = 0;

#if GL_OES_get_program_binary
				glGetProgramiv(mGLHandle, GL_PROGRAM_BINARY_LENGTH_OES, &binaryLength);
                GL_CHECK_ERROR;
#endif

                // Create microcode
                GpuProgramManager::Microcode newMicrocode = 
                    GpuProgramManager::getSingleton().createMicrocode((unsigned long)binaryLength + sizeof(GLenum));

#if GL_OES_get_program_binary
				// Get binary
				glGetProgramBinaryOES(mGLHandle, binaryLength, NULL, (GLenum *)newMicrocode->getPtr(), newMicrocode->getPtr() + sizeof(GLenum));
                GL_CHECK_ERROR;
#endif

        		// Add to the microcode to the cache
				GpuProgramManager::getSingleton().addMicrocodeToCache(name, newMicrocode);
			}
            if(mVertexProgram && mVertexProgram->isLinked())
            {
                glUseProgramStagesEXT(mGLProgramPipelineHandle, GL_VERTEX_SHADER_BIT_EXT, mVertexProgram->getGLSLProgram()->getGLProgramHandle());
                GL_CHECK_ERROR
            }
            if(mFragmentProgram && mFragmentProgram->isLinked())
            {
                glUseProgramStagesEXT(mGLProgramPipelineHandle, GL_FRAGMENT_SHADER_BIT_EXT, mFragmentProgram->getGLSLProgram()->getGLProgramHandle());
                GL_CHECK_ERROR
            }
JNIEXPORT void JNICALL Java_org_lwjgl_opengles_EXTSeparateShaderObjects_glUseProgramStagesEXT(JNIEnv *__env, jclass clazz, jint pipeline, jint stages, jint program) {
    glUseProgramStagesEXTPROC glUseProgramStagesEXT = (glUseProgramStagesEXTPROC)tlsGetFunction(512);
    UNUSED_PARAM(clazz)
    glUseProgramStagesEXT(pipeline, stages, program);
}
void GLSLESProgramPipeline::compileAndLink()
{
#if GL_EXT_separate_shader_objects && OGRE_PLATFORM != OGRE_PLATFORM_NACL
    GLint linkStatus = 0;

    OGRE_CHECK_GL_ERROR(glGenProgramPipelinesEXT(1, &mGLProgramPipelineHandle));
    OGRE_CHECK_GL_ERROR(glBindProgramPipelineEXT(mGLProgramPipelineHandle));

    // Compile and attach Vertex Program
    if(mVertexProgram && !mVertexProgram->isLinked())
    {
        try
        {
            mVertexProgram->getGLSLProgram()->compile(true);
        }
        catch (Exception& e)
        {
            LogManager::getSingleton().stream() << e.getDescription();
            mTriedToLinkAndFailed = true;
            return;
        }
        GLuint programHandle = mVertexProgram->getGLSLProgram()->getGLProgramHandle();
        OGRE_CHECK_GL_ERROR(glProgramParameteriEXT(programHandle, GL_PROGRAM_SEPARABLE_EXT, GL_TRUE));
        mVertexProgram->getGLSLProgram()->attachToProgramObject(programHandle);
        OGRE_CHECK_GL_ERROR(glLinkProgram(programHandle));
        OGRE_CHECK_GL_ERROR(glGetProgramiv(programHandle, GL_LINK_STATUS, &linkStatus));

        if(linkStatus)
        {
            mVertexProgram->setLinked(linkStatus);
            mLinked |= VERTEX_PROGRAM_LINKED;
        }

        mTriedToLinkAndFailed = !linkStatus;

        logObjectInfo( getCombinedName() + String("GLSL vertex program result : "), programHandle );

        setSkeletalAnimationIncluded(mVertexProgram->isSkeletalAnimationIncluded());
    }

    // Compile and attach Fragment Program
    if(mFragmentProgram && !mFragmentProgram->isLinked())
    {
        try
        {
            mFragmentProgram->getGLSLProgram()->compile(true);
        }
        catch (Exception& e)
        {
            LogManager::getSingleton().stream() << e.getDescription();
            mTriedToLinkAndFailed = true;
            return;
        }

        GLuint programHandle = mFragmentProgram->getGLSLProgram()->getGLProgramHandle();
        OGRE_CHECK_GL_ERROR(glProgramParameteriEXT(programHandle, GL_PROGRAM_SEPARABLE_EXT, GL_TRUE));
        mFragmentProgram->getGLSLProgram()->attachToProgramObject(programHandle);
        OGRE_CHECK_GL_ERROR(glLinkProgram(programHandle));
        OGRE_CHECK_GL_ERROR(glGetProgramiv(programHandle, GL_LINK_STATUS, &linkStatus));

        if(linkStatus)
        {
            mFragmentProgram->setLinked(linkStatus);
            mLinked |= FRAGMENT_PROGRAM_LINKED;
        }

        mTriedToLinkAndFailed = !linkStatus;

        logObjectInfo( getCombinedName() + String("GLSL fragment program result : "), programHandle );
    }

    if(mLinked)
    {
        if(mVertexProgram && mVertexProgram->isLinked())
        {
            OGRE_CHECK_GL_ERROR(glUseProgramStagesEXT(mGLProgramPipelineHandle, GL_VERTEX_SHADER_BIT_EXT, mVertexProgram->getGLSLProgram()->getGLProgramHandle()));
        }
        if(mFragmentProgram && mFragmentProgram->isLinked())
        {
            OGRE_CHECK_GL_ERROR(glUseProgramStagesEXT(mGLProgramPipelineHandle, GL_FRAGMENT_SHADER_BIT_EXT, mFragmentProgram->getGLSLProgram()->getGLProgramHandle()));
        }

        // Validate pipeline
        logObjectInfo( getCombinedName() + String("GLSL program pipeline result : "), mGLProgramPipelineHandle );
#if GL_EXT_debug_label && OGRE_PLATFORM != OGRE_PLATFORM_NACL
        if(mVertexProgram && mFragmentProgram)
            OGRE_IF_IOS_VERSION_IS_GREATER_THAN(5.0)
            glLabelObjectEXT(GL_PROGRAM_PIPELINE_OBJECT_EXT, mGLProgramPipelineHandle, 0,
                             (mVertexProgram->getName() + "/" + mFragmentProgram->getName()).c_str());
#endif
    }
#endif
}