好记性不如铅笔头

C && C++, cocos2dx, 编程

cocos2dx学习笔记:CCGLProgram

CCGLProgram是用来编译shader的基础工具类,这里记录下里面的代码注释。

CONTENTS

参考

http://blog.csdn.net/while0/article/details/9666829 】

http://blog.csdn.net/carl_zhong/article/details/12218629 】

ccConfig.h

先记录下 ccConfig.h 中的中对于宏 CC_ENABLE_GL_STATE_CACHE 的介绍。

/** @def CC_ENABLE_GL_STATE_CACHE
 If enabled, cocos2d will maintain an OpenGL state cache internally to avoid unnecessary switches.
 In order to use them, you have to use the following functions, instead of the the GL ones:
    - ccGLUseProgram() instead of glUseProgram()
    - ccGLDeleteProgram() instead of glDeleteProgram()
    - ccGLBlendFunc() instead of glBlendFunc()

 If this functionality is disabled, then ccGLUseProgram(), ccGLDeleteProgram(), ccGLBlendFunc() will call the GL ones, without using the cache.

 It is recommended to enable whenever possible to improve speed.
 If you are migrating your code from GL ES 1.1, then keep it disabled. Once all your code works as expected, turn it on.
 
 Default value: Enabled by default
 
 @since v2.0.0
 */
#ifndef CC_ENABLE_GL_STATE_CACHE
#define CC_ENABLE_GL_STATE_CACHE 1
#endif

ccGLStateCache.cpp部分代码:

后续的代码走读可能会用到这里的一两个函数,索性全贴出来算了。

void ccGLDeleteProgram( GLuint program )
{
#if CC_ENABLE_GL_STATE_CACHE
    if(program == s_uCurrentShaderProgram)
    {
        s_uCurrentShaderProgram = -1;
    }
#endif // CC_ENABLE_GL_STATE_CACHE

    glDeleteProgram( program );
}

void ccGLUseProgram( GLuint program )
{
#if CC_ENABLE_GL_STATE_CACHE
    if( program != s_uCurrentShaderProgram ) {
        s_uCurrentShaderProgram = program;
        glUseProgram(program);
    }
#else
    glUseProgram(program);
#endif // CC_ENABLE_GL_STATE_CACHE
}

static void SetBlending(GLenum sfactor, GLenum dfactor)
{
	if (sfactor == GL_ONE && dfactor == GL_ZERO)
    {
		glDisable(GL_BLEND);
	}
    else
    {
		glEnable(GL_BLEND);
		glBlendFunc(sfactor, dfactor);
	}
}

void ccGLBlendFunc(GLenum sfactor, GLenum dfactor)
{
#if CC_ENABLE_GL_STATE_CACHE
    if (sfactor != s_eBlendingSource || dfactor != s_eBlendingDest)
    {
        s_eBlendingSource = sfactor;
        s_eBlendingDest = dfactor;
        SetBlending(sfactor, dfactor);
    }
#else
    SetBlending( sfactor, dfactor );
#endif // CC_ENABLE_GL_STATE_CACHE
}

void ccGLBlendResetToCache(void)
{
	glBlendEquation(GL_FUNC_ADD);
#if CC_ENABLE_GL_STATE_CACHE
	SetBlending(s_eBlendingSource, s_eBlendingDest);
#else
	SetBlending(CC_BLEND_SRC, CC_BLEND_DST);
#endif // CC_ENABLE_GL_STATE_CACHE
}

void ccGLBindTexture2D(GLuint textureId)
{
    ccGLBindTexture2DN(0, textureId);
}

void ccGLBindTexture2DN(GLuint textureUnit, GLuint textureId)
{
#if CC_ENABLE_GL_STATE_CACHE
    CCAssert(textureUnit < kCCMaxActiveTexture, "textureUnit is too big");
    if (s_uCurrentBoundTexture[textureUnit] != textureId)
    {
        s_uCurrentBoundTexture[textureUnit] = textureId;
        glActiveTexture(GL_TEXTURE0 + textureUnit);
        glBindTexture(GL_TEXTURE_2D, textureId);
    }
#else
    glActiveTexture(GL_TEXTURE0 + textureUnit);
    glBindTexture(GL_TEXTURE_2D, textureId);
#endif
}


void ccGLDeleteTexture(GLuint textureId)
{
    ccGLDeleteTextureN(0, textureId);
}

void ccGLDeleteTextureN(GLuint textureUnit, GLuint textureId)
{
#if CC_ENABLE_GL_STATE_CACHE
	if (s_uCurrentBoundTexture[textureUnit] == textureId)
    {
		s_uCurrentBoundTexture[textureUnit] = -1;
    }
#endif // CC_ENABLE_GL_STATE_CACHE
    
	glDeleteTextures(1, &textureId);
}

void ccGLBindVAO(GLuint vaoId)
{
#if CC_TEXTURE_ATLAS_USE_VAO  
    
#if CC_ENABLE_GL_STATE_CACHE
	if (s_uVAO != vaoId)
	{
		s_uVAO = vaoId;
		glBindVertexArray(vaoId);
	}
#else
	glBindVertexArray(vaoId);
#endif // CC_ENABLE_GL_STATE_CACHE
    
#endif
}

void ccGLEnable(ccGLServerState flags)
{
#if CC_ENABLE_GL_STATE_CACHE

//    int enabled = 0;
//
//    /* GL_BLEND */
//    if( (enabled = (flags & CC_GL_BLEND)) != (s_eGLServerState & CC_GL_BLEND) ) {
//        if( enabled ) {
//            glEnable( GL_BLEND );
//            s_eGLServerState |= CC_GL_BLEND;
//        } else {
//            glDisable( GL_BLEND );
//            s_eGLServerState &=  ~CC_GL_BLEND;
//        }
//    }

#else
//    if( flags & CC_GL_BLEND )
//        glEnable( GL_BLEND );
//    else
//        glDisable( GL_BLEND );
#endif
}

//#pragma mark - GL Vertex Attrib functions

void ccGLEnableVertexAttribs( unsigned int flags )
{
    ccGLBindVAO(0);
    
    /* Position */
    bool enablePosition = flags & kCCVertexAttribFlag_Position;

    if( enablePosition != s_bVertexAttribPosition ) {
        if( enablePosition )
            glEnableVertexAttribArray( kCCVertexAttrib_Position );
        else
            glDisableVertexAttribArray( kCCVertexAttrib_Position );

        s_bVertexAttribPosition = enablePosition;
    }

    /* Color */
    bool enableColor = (flags & kCCVertexAttribFlag_Color) != 0 ? true : false;

    if( enableColor != s_bVertexAttribColor ) {
        if( enableColor )
            glEnableVertexAttribArray( kCCVertexAttrib_Color );
        else
            glDisableVertexAttribArray( kCCVertexAttrib_Color );

        s_bVertexAttribColor = enableColor;
    }

    /* Tex Coords */
    bool enableTexCoords = (flags & kCCVertexAttribFlag_TexCoords) != 0 ? true : false;

    if( enableTexCoords != s_bVertexAttribTexCoords ) {
        if( enableTexCoords )
            glEnableVertexAttribArray( kCCVertexAttrib_TexCoords );
        else
            glDisableVertexAttribArray( kCCVertexAttrib_TexCoords );

        s_bVertexAttribTexCoords = enableTexCoords;
    }
}

CCGLProgram.cpp部分代码注释:

NS_CC_BEGIN

//==>>hash表的element,主要用来存放常量的值
typedef struct _hashUniformEntry
{
    GLvoid*         value;       // value
    unsigned int    location;    // Key
    UT_hash_handle  hh;          // hash entry
} tHashUniformEntry;

CCGLProgram::CCGLProgram()
: m_uProgram(0)
, m_uVertShader(0)
, m_uFragShader(0)
, m_pHashForUniforms(NULL)
, m_bUsesTime(false)
, m_hasShaderCompiler(true)
{
    memset(m_uUniforms, 0, sizeof(m_uUniforms));
}

CCGLProgram::~CCGLProgram()
{
    CCLOGINFO("cocos2d: %s %d deallocing 0x%X", __FUNCTION__, __LINE__, this);

    // there is no need to delete the shaders. They should have been already deleted.
    CCAssert(m_uVertShader == 0, "Vertex Shaders should have been already deleted");
    CCAssert(m_uFragShader == 0, "Fragment Shaders should have been already deleted");

    if (m_uProgram) 
    {
        ccGLDeleteProgram(m_uProgram);    //==>>删除program
    }

    tHashUniformEntry *current_element, *tmp;

    // Purge uniform hash
    HASH_ITER(hh, m_pHashForUniforms, current_element, tmp)   //==>>删除hash列表
    {
        HASH_DEL(m_pHashForUniforms, current_element);
        free(current_element->value);
        free(current_element);
    }
}

//==>>根据两个shader的内容创建一个program。包括编译,绑定shader到program等。
bool CCGLProgram::initWithVertexShaderByteArray(const GLchar* vShaderByteArray, const GLchar* fShaderByteArray)
{
	。。。
		//==>>先创建program
    m_uProgram = glCreateProgram();
    CHECK_GL_ERROR_DEBUG();

    m_uVertShader = m_uFragShader = 0;
		//==>>创建定点着色器,并且编译
    if (vShaderByteArray)
    {
        if (!compileShader(&m_uVertShader, GL_VERTEX_SHADER, vShaderByteArray))
        {
            CCLOG("cocos2d: ERROR: Failed to compile vertex shader");
 						return false;
       }
    }
		//==>>创建片段着色器,并且编译
    // Create and compile fragment shader
    if (fShaderByteArray)
    {
        if (!compileShader(&m_uFragShader, GL_FRAGMENT_SHADER, fShaderByteArray))
        {
            CCLOG("cocos2d: ERROR: Failed to compile fragment shader");
						return false;
        }
    }
		
		//==>>将定点着色器绑定到program上
    if (m_uVertShader)
    {
        glAttachShader(m_uProgram, m_uVertShader);
    }
    CHECK_GL_ERROR_DEBUG();//==>>错误检查宏

		//==>>将片段着色器绑定到program上
    if (m_uFragShader)
    {
        glAttachShader(m_uProgram, m_uFragShader);
    }
    m_pHashForUniforms = NULL;
    
    CHECK_GL_ERROR_DEBUG();


    return true;
}
//==>>读两个文件,然后将文件内容作为参数传入initWithVertexShaderByteArray
bool CCGLProgram::initWithVertexShaderFilename(const char* vShaderFilename, const char* fShaderFilename)
{
    const GLchar * vertexSource = (GLchar*) CCString::createWithContentsOfFile(CCFileUtils::sharedFileUtils()->fullPathForFilename(vShaderFilename).c_str())->getCString();
    const GLchar * fragmentSource = (GLchar*) CCString::createWithContentsOfFile(CCFileUtils::sharedFileUtils()->fullPathForFilename(fShaderFilename).c_str())->getCString();

    return initWithVertexShaderByteArray(vertexSource, fragmentSource);
}
//==>>编译并且创建shader,第一个参数是shader的句柄,第二个是类型(定点或者片段),第三个是shader的内容
bool CCGLProgram::compileShader(GLuint * shader, GLenum type, const GLchar* source)
{
    GLint status;
 
    if (!source)
    {
        return false;
    }
//==>>在shader内容的前面附上再定义一些常量(uniform)
    const GLchar *sources[] = {
#if (CC_TARGET_PLATFORM != CC_PLATFORM_WIN32 && CC_TARGET_PLATFORM != CC_PLATFORM_LINUX && CC_TARGET_PLATFORM != CC_PLATFORM_MAC)
        (type == GL_VERTEX_SHADER ? "precision highp float;\n" : "precision mediump float;\n"),
#endif
        "uniform mat4 CC_PMatrix;\n"
        "uniform mat4 CC_MVMatrix;\n"
        "uniform mat4 CC_MVPMatrix;\n"
        "uniform vec4 CC_Time;\n"
        "uniform vec4 CC_SinTime;\n"
        "uniform vec4 CC_CosTime;\n"
        "uniform vec4 CC_Random01;\n"
        "//CC INCLUDES END\n\n",
        source,
    };

//==>>创建shader,将shader的内容传入, 编译shader
    *shader = glCreateShader(type);
    glShaderSource(*shader, sizeof(sources)/sizeof(*sources), sources, NULL);
    glCompileShader(*shader);
    
//==>>获取shader的编译结果
    glGetShaderiv(*shader, GL_COMPILE_STATUS, &status);

//==>>获取shader的编译结果
    if (! status)
    {
    	//==>>如果编译失败,那么就将shader的内容打印出来。
        GLsizei length;
        glGetShaderiv(*shader, GL_SHADER_SOURCE_LENGTH, &length);
        GLchar* src = (GLchar *)malloc(sizeof(GLchar) * length);
				
        glGetShaderSource(*shader, length, NULL, src);
        CCLOG("cocos2d: ERROR: Failed to compile shader:\n%s", src);

        if (type == GL_VERTEX_SHADER)
        {
            CCLOG("cocos2d: %s", vertexShaderLog());
        }
        else
        {
            CCLOG("cocos2d: %s", fragmentShaderLog());
		}

        free(src);

#if (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT)
        return false;
#else
		abort();
#endif
    }//==>>返回是否成功。
    return (status == GL_TRUE);
}
//==>>将变量绑定到指定的index上,
void CCGLProgram::addAttribute(const char* attributeName, GLuint index)
{
    glBindAttribLocation(m_uProgram, index, attributeName);
}

//==>>更新常量地址
void CCGLProgram::updateUniforms()
{
//==>>glGetUniformLocation用来获取program中指定的常量的句柄,这里一共获取8个常量的句柄,存放入m_uUniforms数组中
    m_uUniforms[kCCUniformPMatrix] = glGetUniformLocation(m_uProgram, kCCUniformPMatrix_s);
	m_uUniforms[kCCUniformMVMatrix] = glGetUniformLocation(m_uProgram, kCCUniformMVMatrix_s);
	m_uUniforms[kCCUniformMVPMatrix] = glGetUniformLocation(m_uProgram, kCCUniformMVPMatrix_s);
	
	m_uUniforms[kCCUniformTime] = glGetUniformLocation(m_uProgram, kCCUniformTime_s);
	m_uUniforms[kCCUniformSinTime] = glGetUniformLocation(m_uProgram, kCCUniformSinTime_s);
	m_uUniforms[kCCUniformCosTime] = glGetUniformLocation(m_uProgram, kCCUniformCosTime_s);
	
	m_bUsesTime = (
                 m_uUniforms[kCCUniformTime] != -1 ||
                 m_uUniforms[kCCUniformSinTime] != -1 ||
                 m_uUniforms[kCCUniformCosTime] != -1
                 );
    
	m_uUniforms[kCCUniformRandom01] = glGetUniformLocation(m_uProgram, kCCUniformRandom01_s);

    m_uUniforms[kCCUniformSampler] = glGetUniformLocation(m_uProgram, kCCUniformSampler_s);

//==>>激活当前的program
    this->use();

//==>>给常量赋值   
    // Since sample most probably won't change, set it to 0 now.
    this->setUniformLocationWith1i(m_uUniforms[kCCUniformSampler], 0);
}

//==>>连接Program
bool CCGLProgram::link()
{
    CCAssert(m_uProgram != 0, "Cannot link invalid program");
 
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT) || (CC_TARGET_PLATFORM == CC_PLATFORM_WP8)
    if(!m_hasShaderCompiler)
    {
        // precompiled shader program is already linked
        return true;
    }
#endif

    GLint status = GL_TRUE;
    //==>>连接Program
    glLinkProgram(m_uProgram);

		//==>>连接Program后直接将两个shader删除,个人感觉怪怪的,难道不判断成功失败吗,或者失败了也无所谓了
    if (m_uVertShader)
    {
        glDeleteShader(m_uVertShader);
    }
    
    if (m_uFragShader)
    {
        glDeleteShader(m_uFragShader);
    }
    
    m_uVertShader = m_uFragShader = 0;
	
#if DEBUG || (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT) || (CC_TARGET_PLATFORM == CC_PLATFORM_WP8)
    glGetProgramiv(m_uProgram, GL_LINK_STATUS, &status);
	
    if (status == GL_FALSE)
    {
        CCLOG("cocos2d: ERROR: Failed to link program: %i", m_uProgram);
        ccGLDeleteProgram(m_uProgram);
        m_uProgram = 0;
    }
#endif

#if (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT)
    if (status == GL_TRUE)
    {
        CCPrecompiledShaders::sharedPrecompiledShaders()->addProgram(m_uProgram, m_shaderId);
    }
#endif


    return (status == GL_TRUE);
}

//==>>激活当前的program
void CCGLProgram::use()
{
    ccGLUseProgram(m_uProgram);
}
。。。。。
。。。。。

// Uniform cache
//==>>更新常量的值,第一个参数是常量的句柄值,第二个是要写入的值的地址,第三个是要写入的值所占字节长度
bool CCGLProgram::updateUniformLocation(GLint location, GLvoid* data, unsigned int bytes)
{
    if (location < 0)
    {
        return false;
    }
//==>>这里采取了更新hash表的方式,如果hash表里没有这个常量或者hash表里的值和要更新的值不一样,那么就更新它。   
    bool updated = true;
    tHashUniformEntry *element = NULL;
    HASH_FIND_INT(m_pHashForUniforms, &location, element);

    if (! element)
    {
    	//==>>如果没有这个常量,就新建一个element,放入hash表
        element = (tHashUniformEntry*)malloc( sizeof(*element) );

        // key
        element->location = location;

        // value
        element->value = malloc( bytes );
        memcpy(element->value, data, bytes );

        HASH_ADD_INT(m_pHashForUniforms, location, element);
    }
    else
    {
        if (memcmp(element->value, data, bytes) == 0)
        {
            updated = false;
        }
        else
        {
            memcpy(element->value, data, bytes);
        }
    }

    return updated;
}

//==>>用来获取program中指定的常量的句柄值
GLint CCGLProgram::getUniformLocationForName(const char* name)
{
    CCAssert(name != NULL, "Invalid uniform name" );
    CCAssert(m_uProgram != 0, "Invalid operation. Cannot get uniform location when program is not initialized");
    
    return glGetUniformLocation(m_uProgram, name);
}
//==>>更新常量的值,首先更新hash表,如果hash表返回true,表示需要更新,那么才调用GL函数更新。
void CCGLProgram::setUniformLocationWith1i(GLint location, GLint i1)
{
    bool updated =  updateUniformLocation(location, &i1, sizeof(i1)*1);
    
    if( updated )
    {
        glUniform1i( (GLint)location, i1);
    }
}
。。。。。
。。。。。
//==>>更新内置的矩阵。
void CCGLProgram::setUniformsForBuiltins()
{
    kmMat4 matrixP;
	kmMat4 matrixMV;
	kmMat4 matrixMVP;
	
	kmGLGetMatrix(KM_GL_PROJECTION, &matrixP);
	kmGLGetMatrix(KM_GL_MODELVIEW, &matrixMV);
	
	kmMat4Multiply(&matrixMVP, &matrixP, &matrixMV);
    
    setUniformLocationWithMatrix4fv(m_uUniforms[kCCUniformPMatrix], matrixP.mat, 1);
    setUniformLocationWithMatrix4fv(m_uUniforms[kCCUniformMVMatrix], matrixMV.mat, 1);
    setUniformLocationWithMatrix4fv(m_uUniforms[kCCUniformMVPMatrix], matrixMVP.mat, 1);
	
	if(m_bUsesTime)
    {
		CCDirector *director = CCDirector::sharedDirector();
		// This doesn't give the most accurate global time value.
		// Cocos2D doesn't store a high precision time value, so this will have to do.
		// Getting Mach time per frame per shader using time could be extremely expensive.
        float time = director->getTotalFrames() * director->getAnimationInterval();
		
        setUniformLocationWith4f(m_uUniforms[kCCUniformTime], time/10.0, time, time*2, time*4);
        setUniformLocationWith4f(m_uUniforms[kCCUniformSinTime], time/8.0, time/4.0, time/2.0, sinf(time));
        setUniformLocationWith4f(m_uUniforms[kCCUniformCosTime], time/8.0, time/4.0, time/2.0, cosf(time));
	}
	
	if (m_uUniforms[kCCUniformRandom01] != -1)
    {
        setUniformLocationWith4f(m_uUniforms[kCCUniformRandom01], CCRANDOM_0_1(), CCRANDOM_0_1(), CCRANDOM_0_1(), CCRANDOM_0_1());
	}
}

//==>>重置该program,清除所有数据,个人感觉有问题。调用后估计有program或者shader没有被释放
void CCGLProgram::reset()
{
    m_uVertShader = m_uFragShader = 0;
    memset(m_uUniforms, 0, sizeof(m_uUniforms));
    

    // it is already deallocated by android
    //ccGLDeleteProgram(m_uProgram);
    m_uProgram = 0;

    
    tHashUniformEntry *current_element, *tmp;
    
    // Purge uniform hash
    HASH_ITER(hh, m_pHashForUniforms, current_element, tmp) 
    {
        HASH_DEL(m_pHashForUniforms, current_element);
        free(current_element->value);
        free(current_element);
    }
    m_pHashForUniforms = NULL;
}

NS_CC_END

 

发表评论

11 + 17 =

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据