cocos2d-x 源码分析




std::string FileUtils::fullPathForFilename(const std::string &filename)
{   //如果文件名为string的size()==0
    if (filename.empty())
    {
        return "";
    }
    //如果是绝对路径
    if (isAbsolutePath(filename))
    {
        return filename;
    }

    // Already Cached ?  如果已经缓存了,则直接从缓存里面取
    auto cacheIter = _fullPathCache.find(filename);
    if( cacheIter != _fullPathCache.end() )
    {
        return cacheIter->second;
    }
    
    // Get the new file name. 获得一份新文件名
    const std::string newFilename( getNewFilename(filename) );
    
	std::string fullpath;
    //cbegin()返回确定范围中第一个元素地址的const 迭代器。cend()指向刚超出范围末尾的位置的 const 随机访问迭代器。
    for (auto searchIt = _searchPathArray.cbegin(); searchIt != _searchPathArray.cend(); ++searchIt)
    {
        for (auto resolutionIt = _searchResolutionsOrderArray.cbegin(); resolutionIt != _searchResolutionsOrderArray.cend(); ++resolutionIt)
        {
            fullpath = this->getPathForFilename(newFilename, *resolutionIt, *searchIt);
            
            if (fullpath.length() > 0)
            {
                // Using the filename passed in as key.
                _fullPathCache.insert(std::make_pair(filename, fullpath));
                return fullpath;
            }
        }
    }
    
    CCLOG("cocos2d: fullPathForFilename: No file found at %s. Possible missing file.", filename.c_str());

    // XXX: Should it return nullptr ? or an empty string ?
    // The file wasn't found, return the file name passed in.
    return filename;
}



/**
   获取文件的路径
   @param 文件名
   @param 文件夹
   @param 文件路径
*/
std::string FileUtils::getPathForFilename(const std::string& filename, const std::string& resolutionDirectory, const std::string& searchPath)
{
    std::string file = filename;
    std::string file_path = "";
    size_t pos = filename.find_last_of("/");   //找到文件名匹配“/”最后一个字符的索引,没有返回npos
    if (pos != std::string::npos)
    {
        file_path = filename.substr(0, pos+1);  //截取字符串获得文件路径
        file = filename.substr(pos+1);          //截取字符串获得文件名
    }
    
    // searchPath + file_path + resourceDirectory   搜索路径 + 文件路径 + 资源文件夹
    std::string path = searchPath;
    path += file_path;
    path += resolutionDirectory;
    //获取目录的完整路径和文件名。调用相应平台的获取路径的方法。
    path = getFullPathForDirectoryAndFilename(path, file);
    
    //CCLOG("getPathForFilename, fullPath = %s", path.c_str());
    return path;
}




static Data getData(const std::string& filename, bool forString)
{
    if (filename.empty())
    {
        return Data::Null;
    }
    
    Data ret;
    unsigned char* buffer = nullptr;
    ssize_t size = 0;
    const char* mode = nullptr;  //文件读取模式
    if (forString)
        mode = "rt";
    else
        mode = "rb";
    
    do
    {
        // Read the file from hardware   从硬件读取文件
        std::string fullPath = FileUtils::getInstance()->fullPathForFilename(filename);
        FILE *fp = fopen(fullPath.c_str(), mode);
        CC_BREAK_IF(!fp);
        fseek(fp,0,SEEK_END); //移动文件指针到指定的位置。fp是文件的指针,0 是起源的字节,SEEK_END 文件尾,从文件头读到文件尾
        size = ftell(fp);  //获取当前文件指针的位置。
        fseek(fp,0,SEEK_SET);  //从文件头读到文件开始处
        
        if (forString)
        {
            buffer = (unsigned char*)malloc(sizeof(unsigned char) * (size + 1));  //分配内存
            buffer[size] = '\0';   //某位加'\0',可能表示结束吧
        }
        else
        {
            buffer = (unsigned char*)malloc(sizeof(unsigned char) * size);
        }
        
        size = fread(buffer, sizeof(unsigned char), size, fp);  //读取文件
        fclose(fp);  //关闭文件
    } while (0);
    
    if (nullptr == buffer || 0 == size)
    {
        std::string msg = "Get data from file(";
        msg.append(filename).append(") failed!");
        CCLOG("%s", msg.c_str());
    }
    else
    {
        ret.fastSet(buffer, size);  //设置Data的数据 _bytes = bytes;  _size = size;
    }
    
    return ret;
}



// 返回data的字节
std::string FileUtils::getStringFromFile(const std::string& filename)
{
    Data data = getData(filename, true);
    if (data.isNull())
    	return "";
    
    std::string ret((const char*)data.getBytes());
    return ret;
}



bool GLProgram::initWithByteArrays(const GLchar* vShaderByteArray, const GLchar* fShaderByteArray)
{

#if (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT) || (CC_TARGET_PLATFORM == CC_PLATFORM_WP8)
    GLboolean hasCompiler = false;
    glGetBooleanv(GL_SHADER_COMPILER, &hasCompiler);
    _hasShaderCompiler = (hasCompiler == GL_TRUE);

    if(!_hasShaderCompiler)
    {
        return initWithPrecompiledProgramByteArray(vShaderByteArray,fShaderByteArray);
    }
#endif
    //创建一个空程序对象并返回一个它可以引用的非0值。
    _program = glCreateProgram();
   //openGL 的错误输出
    CHECK_GL_ERROR_DEBUG();

    _vertShader = _fragShader = 0;

    if (vShaderByteArray)
    {
        if (!compileShader(&_vertShader, GL_VERTEX_SHADER, vShaderByteArray))
        {
            CCLOG("cocos2d: ERROR: Failed to compile vertex shader");
            return false;
       }
    }

    // Create and compile fragment shader
    if (fShaderByteArray)
    {
        if (!compileShader(&_fragShader, GL_FRAGMENT_SHADER, fShaderByteArray))
        {
            CCLOG("cocos2d: ERROR: Failed to compile fragment shader");
            return false;
        }
    }

    if (_vertShader)
    {
//将一个着色器对象绑定到一个程度对象
        glAttachShader(_program, _vertShader);
    }
    CHECK_GL_ERROR_DEBUG();

    if (_fragShader)
    {
        glAttachShader(_program, _fragShader);
    }
    _hashForUniforms = nullptr;
    
    CHECK_GL_ERROR_DEBUG();

#if (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT) || (CC_TARGET_PLATFORM == CC_PLATFORM_WP8)
    _shaderId = CCPrecompiledShaders::getInstance()->addShaders(vShaderByteArray, fShaderByteArray);
#endif

    return true;
}


注意 : 

 CHECK_GL_ERROR_DEBUG(); 是OpenGL得debug错误输出,有用



// 编译着色器
bool GLProgram::compileShader(GLuint * shader, GLenum type, const GLchar* source)
{
    GLint status;
 
    if (!source)
    {
        return false;
    }
   //修改在特定平台下的type值,并添加上一些着色器的代码
    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"
        "uniform sampler2D CC_Texture0;\n"
        "uniform sampler2D CC_Texture1;\n"
        "uniform sampler2D CC_Texture2;\n"
        "uniform sampler2D CC_Texture3;\n"
        "//CC INCLUDES END\n\n",
        source,
    };
    //创建一个着色器对象
    *shader = glCreateShader(type);
// 替换一个着色器对象中的源代码。
第一个参数:指定源代码被替换你的着色器对象的句柄,
第二个参数:指定string和length数组中得元素数量,
第三个参数:指定一个包含将要被载入着色器源代码的字符串的指针数组
第四个参数:指定字符串长度数组。
//着色器对象中原来存储的任何源代码将完全被替换。
//如果最后一个参数为nullptr,那么每个字符串将被假定以空结束符结束。否则,它将指向一个为每一个string的相应元素包含一个字符串长度的数组
    glShaderSource(*shader, sizeof(sources)/sizeof(*sources), sources, nullptr);
//编译一个着色器对象,编译已经被存储到有shader指定的着色器对象的源代码字符串。 P519 opengl超级宝典
    glCompileShader(*shader);

// 返回一个来自着色器对象的参数
// GL_COMPILE_STATUS  在最后一次着色器上编译操作成功的情况下返回GL_TURE,否则返回GL_FALSE。
 P602 超级宝典
    glGetShaderiv(*shader, GL_COMPILE_STATUS, &status);

    if (! status)
    {
        GLsizei length;
// 获取被请去用来存储返回源代码字符串的缓冲区的大小
        glGetShaderiv(*shader, GL_SHADER_SOURCE_LENGTH, &length);
//分配内存块
        GLchar* src = (GLchar *)malloc(sizeof(GLchar) * length);
//返回来自一个着色器对象的源代码字符串。将 返回 源代码字符串 中 length 个字符,返回到src中。
//@param 指定要查询的着色器对象,
 @param 指定用来存储返回的源代码字符串的字符缓冲器的大小,
@param 返回在source中返回的字符串的长度(不包括空终止符),
@param 指定一个用来返回源代码字符串的字符数组。
        glGetShaderSource(*shader, length, nullptr, src);
        CCLOG("cocos2d: ERROR: Failed to compile shader:\n%s", src);
        
        if (type == GL_VERTEX_SHADER)
        {
            CCLOG("cocos2d: %s", getVertexShaderLog().c_str());
        }
        else
        {
            CCLOG("cocos2d: %s", getFragmentShaderLog().c_str());
        }
        //解除分配或释放内存块。
        free(src);
//中止当前进程并返回错误代码。
        abort();
    }
    return (status == GL_TRUE);
}



bool GLProgram::initWithFilenames(const std::string &vShaderFilename, const std::string &fShaderFilename)
{
    auto fileUtils = FileUtils::getInstance();
    std::string vertexSource = fileUtils->getStringFromFile(FileUtils::getInstance()->fullPathForFilename(vShaderFilename));
    std::string fragmentSource = fileUtils->getStringFromFile(FileUtils::getInstance()->fullPathForFilename(fShaderFilename));

    //创建程序,编译着色器对象,将两者连起来
    return initWithByteArrays(vertexSource.c_str(), fragmentSource.c_str());
}


// 连接由program 指定的承诺过度对象。如果GL_VERTEX_SHADER类型的任何着色器对象绑定到program,那么他们将被用来创建一个可执行文件,这个可执行文件将在课编程顶点处理器中运行。其他着色器类似。

//解析顶点属性和Uniform

bool GLProgram::link()
{
    CCASSERT(_program != 0, "Cannot link invalid program");

#if (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT) || (CC_TARGET_PLATFORM == CC_PLATFORM_WP8)
    if(!_hasShaderCompiler)
    {
        // precompiled shader program is already linked

        //bindPredefinedVertexAttribs();
        parseVertexAttribs();
        parseUniforms();
        return true;
    }
#endif

    GLint status = GL_TRUE;

//将通用顶点属性索引与指定的属性变量关联起来,用来将program指定的程序对象中得一个用户定义的属性变量和一个通用顶点属性索引进行绑定,
bindPredefinedVertexAttribs(); //连接由program 指定的承诺过度对象。如果GL_VERTEX_SHADER类型的任何着色器对象绑定到program,那么他们将被用来创建一个可执行文件,这个可执行文件将在课编程顶点处理器中运行。其他着色器类似。


//将一个着色器对象绑定到一个程度对象 glAttachShader(_program, _vertShader);
    glLinkProgram(_program);

    //解析顶点属性和Uniform
    parseVertexAttribs();  
    parseUniforms();

    if (_vertShader)
    {  //删除一个着色器对象
        glDeleteShader(_vertShader);
    }
    
    if (_fragShader)
    {
        glDeleteShader(_fragShader);
    }
    
    _vertShader = _fragShader = 0;
    
#if DEBUG || (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT) || (CC_TARGET_PLATFORM == CC_PLATFORM_WP8)
    glGetProgramiv(_program, GL_LINK_STATUS, &status);
    
    if (status == GL_FALSE)
    {
        CCLOG("cocos2d: ERROR: Failed to link program: %i", _program);
        GL::deleteProgram(_program);
        _program = 0;
    }
#endif

#if (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT) || (CC_TARGET_PLATFORM == CC_PLATFORM_WP8)
    if (status == GL_TRUE)
    {
        CCPrecompiledShaders::getInstance()->addProgram(_program, _shaderId);
    }
#endif

    return (status == GL_TRUE);
}



void GLProgram::bindPredefinedVertexAttribs()
{
    static const struct {
        const char *attributeName;
        int location;
    } attribute_locations[] =
    {
//const char* GLProgram::ATTRIBUTE_NAME_COLOR = "a_color";
const * GLProgram::ATTRIBUTE_NAME_POSITION = "a_position";
const char* GLProgram::ATTRIBUTE_NAME_TEX_COORD = "a_texCoord";
const char* GLProgram::ATTRIBUTE_NAME_NORMAL = "a_normal";
             {GLProgram::ATTRIBUTE_NAME_POSITION, GLProgram::VERTEX_ATTRIB_POSITION},
        {GLProgram::ATTRIBUTE_NAME_COLOR, GLProgram::VERTEX_ATTRIB_COLOR},
        {GLProgram::ATTRIBUTE_NAME_TEX_COORD, GLProgram::VERTEX_ATTRIB_TEX_COORD},
        {GLProgram::ATTRIBUTE_NAME_NORMAL, GLProgram::VERTEX_ATTRIB_NORMAL},
    };
// 包含的大小
    const int size = sizeof(attribute_locations) / sizeof(attribute_locations[0]);

    for(int i=0; i
	//将通用顶点属性索引与指定的属性变量关联起来,用来将program指定的程序对象中得一个用户定义的属性变量和一个通用顶点属性索引进行绑定,
        glBindAttribLocation(_program, attribute_locations[i].location, attribute_locations[i].attributeName);
    }
}



//解析顶点属性,把顶点信息存储到数组中_vertexAttribs
void GLProgram::parseVertexAttribs()
{
//移除所有元素。
    _vertexAttribs.clear();

	// Query and store vertex attribute meta-data from the program.
	GLint activeAttributes;
	GLint length;
// 返回program的活动属性变量的数量。P596
	glGetProgramiv(_program, GL_ACTIVE_ATTRIBUTES, &activeAttributes);
	if(activeAttributes > 0)
	{
        VertexAttrib attribute;

// 返回program最长的活动属性名的长度,包括终止符(也就是说,被请求存储最长的活动属性名的字符缓冲器的大小)。如果不存在活动的属性,则返回0.比如 name = "a_position"
		glGetProgramiv(_program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &length);
		if(length > 0)
		{
//在调用 alloca的函数返回的时候, 它分配的内存会自动释放。也就是说, 用 alloca 分配的内存在栈上 这是百度百科上摘录的
			GLchar* attribName = (GLchar*) alloca(length + 1);

			for(int i = 0; i < activeAttributes; ++i)
			{
				// Query attribute info. 返回关于指定程序对象的活动属性变量的信息。
				@param 指定要被查询的程度对象。
                               @param 指定将要被查询的属性变量的索引 
                               @prame 指定允许OpenGL写入由name指示的字符缓冲区中得字符的最大数量。
                              @param 指定除NULL遗爱的值被传递的话,则返回实际上有OpenGL写入由name指示的字符串的字符数(不包括空终止符)。
                                @param 返回属性变量的大小
                              @param 返回属性变量的数据类型
                                @param 返回一个包括属性变量名的空终止字符串
				glGetActiveAttrib(_program, i, length, NULL, &attribute.size, &attribute.type, attribName);
				attribName[length] = '\0';
                attribute.name = std::string(attribName);

				// Query the pre-assigned attribute location  返回一个属性变量的位置
				attribute.index = glGetAttribLocation(_program, attribName);
                //存储到属性数组中去,以属性名位key
                _vertexAttribs[attribute.name] = attribute;
			}
		}
	}
}


void GLProgram::parseUniforms()
{
    _userUniforms.clear();

	// Query and store uniforms from the program.
	GLint activeUniforms;
	glGetProgramiv(_program, GL_ACTIVE_UNIFORMS, &activeUniforms);
	if(activeUniforms > 0)
	{
        GLint length;
		glGetProgramiv(_program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &length);
		if(length > 0)
		{
            Uniform uniform;

			GLchar* uniformName = (GLchar*)alloca(length + 1);

			for(int i = 0; i < activeUniforms; ++i)
			{
				// Query uniform info.
				glGetActiveUniform(_program, i, length, NULL, &uniform.size, &uniform.type, uniformName);
				uniformName[length] = '\0';

                // Only add uniforms that are not built-in.
                // The ones that start with 'CC_' are built-ins
//使用当前区域设置或指定的区域设置,比较两个字符串字符。
                if(strncmp("CC_", uniformName, 3) != 0) {

                    // remove possible array '[]' from uniform name
                    if(uniform.size > 1 && length > 3)
                    {
                        char* c = strrchr(uniformName, '[');
                        if(c)
                        {
                            *c = '\0';
                        }
                    }
                    uniform.name = std::string(uniformName);
                    uniform.location = glGetUniformLocation(_program, uniformName);
                    
                    _userUniforms[uniform.name] = uniform;
                }
			}
		}
	}
}



void GLProgram::updateUniforms()
{
     // 返回一个整数值,代表一个程序对象中的指定统一位置变量的位置。name必须是一个不包含任何空白的空终止字符串。
    _builtInUniforms[UNIFORM_P_MATRIX] = glGetUniformLocation(_program, UNIFORM_NAME_P_MATRIX);
    _builtInUniforms[UNIFORM_MV_MATRIX] = glGetUniformLocation(_program, UNIFORM_NAME_MV_MATRIX);
    _builtInUniforms[UNIFORM_MVP_MATRIX] = glGetUniformLocation(_program, UNIFORM_NAME_MVP_MATRIX);
    
    _builtInUniforms[UNIFORM_TIME] = glGetUniformLocation(_program, UNIFORM_NAME_TIME);
    _builtInUniforms[UNIFORM_SIN_TIME] = glGetUniformLocation(_program, UNIFORM_NAME_SIN_TIME);
    _builtInUniforms[UNIFORM_COS_TIME] = glGetUniformLocation(_program, UNIFORM_NAME_COS_TIME);

    _builtInUniforms[UNIFORM_RANDOM01] = glGetUniformLocation(_program, UNIFORM_NAME_RANDOM01);

    _builtInUniforms[UNIFORM_SAMPLER0] = glGetUniformLocation(_program, UNIFORM_NAME_SAMPLER0);
    _builtInUniforms[UNIFORM_SAMPLER1] = glGetUniformLocation(_program, UNIFORM_NAME_SAMPLER1);
    _builtInUniforms[UNIFORM_SAMPLER2] = glGetUniformLocation(_program, UNIFORM_NAME_SAMPLER2);
    _builtInUniforms[UNIFORM_SAMPLER3] = glGetUniformLocation(_program, UNIFORM_NAME_SAMPLER3);

// 标记 哪一个Uniform 被使用了。
    _flags.usesP = _builtInUniforms[UNIFORM_P_MATRIX] != -1;
    _flags.usesMV = _builtInUniforms[UNIFORM_MV_MATRIX] != -1;
    _flags.usesMVP = _builtInUniforms[UNIFORM_MVP_MATRIX] != -1;
    _flags.usesTime = (
                       _builtInUniforms[UNIFORM_TIME] != -1 ||
                       _builtInUniforms[UNIFORM_SIN_TIME] != -1 ||
                       _builtInUniforms[UNIFORM_COS_TIME] != -1
                       );
    _flags.usesRandom = _builtInUniforms[UNIFORM_RANDOM01] != -1;
//将由program指定的程序对象作为当前渲染状态的一部分来进行安装。
    this->use();
    
    // Since sample most probably won't change, set it to 0,1,2,3 now.
    if(_builtInUniforms[UNIFORM_SAMPLER0] != -1)
       setUniformLocationWith1i(_builtInUniforms[UNIFORM_SAMPLER0], 0);
    if(_builtInUniforms[UNIFORM_SAMPLER1] != -1)
        setUniformLocationWith1i(_builtInUniforms[UNIFORM_SAMPLER1], 1);
    if(_builtInUniforms[UNIFORM_SAMPLER2] != -1)
        setUniformLocationWith1i(_builtInUniforms[UNIFORM_SAMPLER2], 2);
    if(_builtInUniforms[UNIFORM_SAMPLER3] != -1)
        setUniformLocationWith1i(_builtInUniforms[UNIFORM_SAMPLER3], 3);
}



void GLProgram::use()
{
    GL::useProgram(_program);
}



void useProgram( GLuint program )
{
#if CC_ENABLE_GL_STATE_CACHE
    if( program != s_currentShaderProgram ) {
        s_currentShaderProgram = program;
//将由program指定的程序对象作为当前渲染状态的一部分来进行安装。通过使用glAttchShader成功的将着色器对象绑定到程序对象,成功使用glCompileShader来编译这些着色器对象,成功使用
//glLinkProgram来连接这些着色器对象,能够在这个程序对象中会创建一个或多个可执行文件。P688 OpenGL超级宝典
        glUseProgram(program);
    }
#else
    glUseProgram(program);
#endif // CC_ENABLE_GL_STATE_CACHE
}


// Uniform hash 实体

void GLProgram::setUniformLocationWith1i(GLint location, GLint i1)
{
    //存放到UThash里面的,如果更新了返回ture.
    bool updated = updateUniformLocation(location, &i1, sizeof(i1)*1);
    //
    if( updated )
    {
       //为当前的对象指定一个统一值变量的值
        glUniform1i( (GLint)location, i1);
    }
}



typedef struct _hashUniformEntry
{
    GLvoid*         value;       // value
    unsigned int    location;    // Key
    UT_hash_handle  hh;          // hash entry
} tHashUniformEntry;





// Uniform cache

//参数location 是类似 glGetUniformLocation(_program, UNIFORM_NAME_SAMPLER0) 返回的值
// 主要是把数据存储到 _hashForUniforms(他是c的hash UIhash)
bool GLProgram::updateUniformLocation(GLint location, const GLvoid* data, unsigned int bytes)
{
    if (location < 0)
    {
        return false;
    }

    bool updated = true;
    //Uniform hash 实体
    tHashUniformEntry *element = nullptr;
    //使用c的hash UThash :通过索引查询哈希表中的相应词汇。通过location 查询后放到element
    HASH_FIND_INT(_hashForUniforms, &location, element);

    //如果不存在的话
    if (! element)
    {
//分配内存
        element = (tHashUniformEntry*)malloc( sizeof(*element) );

// key 赋值hash结构的key,结构参考Uniform hash 实体 tHashUniformEntry,参考上面的代码
        element->location = location;

// value   设置value,它使一个指针,GLvoid*  value;
        element->value = malloc( bytes );
//上面分配好内存之后,然后吧data的值赋过来,我的理解
        memcpy(element->value, data, bytes );
//UThash 按照索引添加一个实体
        HASH_ADD_INT(_hashForUniforms, location, element);
    }
    //如果已经存在的话,则直接拷贝数据
    else
    {
//比较内存区域buf1和buf2的前count个字节,如果一样则不更新。
        if (memcmp(element->value, data, bytes) == 0)
        {
            updated = false;
        }
        else
        {
            memcpy(element->value, data, bytes);
        }
    }

    return updated;
}

 
  

//主要是调用GLProgramState::getOrCreateWithGLProgram(glProgram); 里面的方法,在它里面保存了一些属性和uniform数据等
void Node::setGLProgram(GLProgram *glProgram)
{
//_glProgramState 为null   或者  _glProgramState不为null,但是不等于参数传进来的glProgram 
    if (_glProgramState == nullptr || (_glProgramState && _glProgramState->getGLProgram() != glProgram))
    {
        CC_SAFE_RELEASE(_glProgramState);
        _glProgramState = GLProgramState::getOrCreateWithGLProgram(glProgram);
        _glProgramState->retain();
    }
}



GLProgram* getGLProgram() const { return _glprogram; }


#define CC_SAFE_RELEASE(p)          do { if(p) { (p)->release(); } } while(0)   // 引用计数减1,删除对象delete this,

void Ref::release()
{
    CCASSERT(_referenceCount > 0, "reference count should greater than 0");
//引用计数减1
    --_referenceCount;
//如果引用计数 == 0 的话
    if (_referenceCount == 0)
    {
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
        auto poolManager = PoolManager::getInstance();
        if (!poolManager->getCurrentPool()->isClearing() && poolManager->isObjectInPools(this))
        {
            // Trigger an assert if the reference count is 0 but the Ref is still in autorelease pool.
            // This happens when 'autorelease/release' were not used in pairs with 'new/retain'.
            //
            // Wrong usage (1):
            //
            // auto obj = Node::create();   // Ref = 1, but it's an autorelease Ref which means it was in the autorelease pool.
            // obj->autorelease();   // Wrong: If you wish to invoke autorelease several times, you should retain `obj` first.
            //
            // Wrong usage (2):
            //
            // auto obj = Node::create();
            // obj->release();   // Wrong: obj is an autorelease Ref, it will be released when clearing current pool.
            //
            // Correct usage (1):
            //
            // auto obj = Node::create();
            //                     |-   new Node();     // `new` is the pair of the `autorelease` of next line
            //                     |-   autorelease();  // The pair of `new Node`.
            //
            // obj->retain();
            // obj->autorelease();  // This `autorelease` is the pair of `retain` of previous line.
            //
            // Correct usage (2):
            //
            // auto obj = Node::create();
            // obj->retain();
            // obj->release();   // This `release` is the pair of `retain` of previous line.
            CCASSERT(false, "The reference shouldn't be 0 because it is still in autorelease pool.");
        }
#endif

#if CC_USE_MEM_LEAK_DETECTION
        untrackRef(this);
#endif
        delete this;
    }
}


GLProgramState* GLProgramState::getOrCreateWithGLProgram(GLProgram *glprogram)
{
    GLProgramState* ret = GLProgramStateCache::getInstance()->getGLProgramState(glprogram);
    return ret;
}


GLProgramState* GLProgramStateCache::getGLProgramState(GLProgram* glprogram)
{
//从_glProgramStates Map 数据结构里面查找
    const auto& itr = _glProgramStates.find(glprogram);
//如果找到就返回itr->second 可能是值吧,我的理解,待证实。
    if (itr != _glProgramStates.end())
    {
        return itr->second;
    }
    //在内存不足时,new (std::nothrow)并不抛出异常,而是将指针置NULL。
    //参见 #include “new”;
    auto ret = new (std::nothrow) GLProgramState;
//如果ret 不为null,并且调用_glProgramStates 的init方法成功,	_glProgramStates Map 插入 一条数据,Map插入方式是
    if(ret && ret->init(glprogram)) {
        _glProgramStates.insert(glprogram, ret);
        //初始化为1,new时加了一个,然后现在,减去一个
        ret->release();
        return ret;
    }
    
    CC_SAFE_RELEASE(ret);
    return ret;
}



//GLProgramState 的初始化
bool GLProgramState::init(GLProgram* glprogram)
{
    CCASSERT(glprogram, "invalid shader");
    
    _glprogram = glprogram;
     //引用计数加1
    _glprogram->retain();

    for(auto &attrib : _glprogram->_vertexAttribs) {
//用VertexAttrib 构造 VertexAttribValue 对象
        VertexAttribValue value(&attrib.second);
//保存在_attributes  unordered_map 数据结构里面
        _attributes[attrib.first] = value;
    }
//uniform 值 同上
    for(auto &uniform : _glprogram->_userUniforms) {
        UniformValue value(&uniform.second, _glprogram);
        _uniforms[uniform.first] = value;
    }

    return true;
}


void GLProgram::setUniformsForBuiltins()
{
    Director* director = Director::getInstance();
    CCASSERT(nullptr != director, "Director is null when seting matrix stack");
    
    Mat4 matrixMV;
//获取相关类型的矩阵信息,
    matrixMV = director->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);

    setUniformsForBuiltins(matrixMV);
}


Mat4 Director::getMatrix(MATRIX_STACK_TYPE type)
{
    Mat4 result;
//如果类型是模型视图矩阵,则返回模型堆栈顶部的模型视图矩阵
    if(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW == type)
    {
        result = _modelViewMatrixStack.top();
    }
//如果类型是投影矩阵,则返回模型堆栈顶部的投影矩阵
else if(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION == type) { result = _projectionMatrixStack.top(); }
 
  
//如果类型是纹理矩阵,则返回模型堆栈顶部的纹理矩阵
else if(MATRIX_STACK_TYPE::MATRIX_STACK_TEXTURE == type) { result = _textureMatrixStack.top(); }
 
  
//否则 则返回模型堆栈顶部的模型视图矩阵
else { CCASSERT(false, "unknow matrix stack type, will return modelview matrix instead"); result = _modelViewMatrixStack.top(); }// float diffResult(0);// for (int index = 0; index <16; ++index)// {// diffResult += abs(result2.mat[index] - result.mat[index]);// }// if(diffResult > 1e-30)// {// CCASSERT(false, "Error in director matrix stack");// } return result;}
 
  

void GLProgram::setUniformsForBuiltins(const Mat4 &matrixMV)
{
//获得投影矩阵
    Mat4 matrixP = Director::getInstance()->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);

//_flags的信息在updatUniforms 里面设置

if(_flags.usesP)

//_builtInUniforms[UNIFORM_P_MATRIX]  获得存储的location ,它存储的是 glGetUniformLocation 返回的值

//matrixP.m 获得投影矩阵的4X4矩阵的所有列

// 数据存储到 _hashForUniforms(他是c的hash UIhash),然后OpenGL 调用 glUniformMatrix4fv  修改一个统一变量或统一变量数组的值

setUniformLocationWithMatrix4fv(_builtInUniforms[UNIFORM_P_MATRIX], matrixP.m, 1); if(_flags.usesMV) setUniformLocationWithMatrix4fv(_builtInUniforms[UNIFORM_MV_MATRIX], matrixMV.m, 1); if(_flags.usesMVP) { Mat4 matrixMVP = matrixP * matrixMV; setUniformLocationWithMatrix4fv(_builtInUniforms[UNIFORM_MVP_MATRIX], matrixMVP.m, 1); }// if(_flags.usesTime) { Director *director = Director::getInstance(); // 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.
 
  
//不提供全球最精确的时间值。Cocos2D不存储高精度时间值,因此不得不做。每帧着色器使用时间可能会非常昂贵。
// 帧数 * 帧频 计算出时间
        float time = director->getTotalFrames() * director->getAnimationInterval();
        
        setUniformLocationWith4f(_builtInUniforms[GLProgram::UNIFORM_TIME], time/10.0, time, time*2, time*4);
//sinf 是用弧度作单位计算的,一弧度=180度/3.1415 
        setUniformLocationWith4f(_builtInUniforms[GLProgram::UNIFORM_SIN_TIME], time/8.0, time/4.0, time/2.0, sinf(time));
        setUniformLocationWith4f(_builtInUniforms[GLProgram::UNIFORM_COS_TIME], time/8.0, time/4.0, time/2.0, cosf(time));
    }
    
    if(_flags.usesRandom)
        setUniformLocationWith4f(_builtInUniforms[GLProgram::UNIFORM_RANDOM01], CCRANDOM_0_1(), CCRANDOM_0_1(), CCRANDOM_0_1(), CCRANDOM_0_1());
}




 /** 
How many frames were called since the director started 
自从director开始以来调用了多少帧
 */
    inline unsigned int getTotalFrames() { return _totalFrames; }
    


 /**
获得FPS 值,帧频值
 Get the FPS value
 */
    inline double getAnimationInterval() { return _animationInterval; }




// @param glGetUniformLocation 返回的值 
// @param 4X4矩阵的所有列
// @param 矩阵的数量
// 数据存储到 _hashForUniforms(他是c的hash UIhash),然后OpenGL 调用 glUniformMatrix4fv  修改一个统一变量或统一变量数组的值
void GLProgram::setUniformLocationWithMatrix4fv(GLint location, const GLfloat* matrixArray, unsigned int numberOfMatrices)
{
// 主要是把数据存储到 _hashForUniforms(他是c的hash UIhash)
bool updated = updateUniformLocation(location, matrixArray, sizeof(float)*16*numberOfMatrices); if( updated ) {
 
  
//@param 指定将要被修改的统一值变量的位置
//@param 指定将要被修改的矩阵的数量
//@param 指定在矩阵的值被载入变量时是否要对矩阵进行变换
//@param 指定一个指向将用于更新指定统一变量的count值数组的指针。就是矩阵的所有列数据的数组 ,比如float m[16];
//glUniform修改一个统一变量或统一变量数组的值,将要被修改的统一变量的位置有location指定,这是一个由glGetUniformLocation返回的值。
//glUniform在通过glUseProgram而成为状态的一部分的程序对象上进行操作
        glUniformMatrix4fv( (GLint)location, (GLsizei)numberOfMatrices, GL_FALSE, matrixArray);
    }
}

// Uniform cache

//参数location 是类似 glGetUniformLocation(_program, UNIFORM_NAME_SAMPLER0) 返回的值
// 主要是把数据存储到 _hashForUniforms(他是c的hash UIhash)
bool GLProgram::updateUniformLocation(GLint location, const GLvoid* data, unsigned int bytes)
{
    if (location < 0)
    {
        return false;
    }

    bool updated = true;
    //Uniform hash 实体
    tHashUniformEntry *element = nullptr;
    //使用c的hash UThash :通过索引查询哈希表中的相应词汇。通过location 查询后放到element
    HASH_FIND_INT(_hashForUniforms, &location, element);

    //如果不存在的话
    if (! element)
    {
//分配内存
        element = (tHashUniformEntry*)malloc( sizeof(*element) );

// key 赋值hash结构的key,结构参考Uniform hash 实体 tHashUniformEntry,参考上面的代码
        element->location = location;

// value   设置value,它使一个指针,GLvoid*  value;
        element->value = malloc( bytes );
//上面分配好内存之后,然后吧data的值赋过来,我的理解
        memcpy(element->value, data, bytes );
//UThash 按照索引添加一个实体
        HASH_ADD_INT(_hashForUniforms, location, element);
    }
    //如果已经存在的话,则直接拷贝数据
    else
    {
//比较内存区域buf1和buf2的前count个字节,如果一样则不更新。
        if (memcmp(element->value, data, bytes) == 0)
        {
            updated = false;
        }
        else
        {
            memcpy(element->value, data, bytes);
        }
    }

    return updated;
}


 
  

void GLProgram::setUniformLocationWith4f(GLint location, GLfloat f1, GLfloat f2, GLfloat f3, GLfloat f4)
{
    GLfloat floats[4] = {f1,f2,f3,f4};
// 主要是把数据存储到 _hashForUniforms(他是c的hash UIhash)
bool updated = updateUniformLocation(location, floats, sizeof(floats)); if( updated ) {
 
  
//为当前程序对象指定一个统一变量的值。
// f1, f2, f3,f4 指定特定统一变量将使用的新值
glUniform4f( (GLint)location, f1, f2, f3,f4); }}
 
  


#define CC_INCREMENT_GL_DRAWN_BATCHES_AND_VERTICES(__drawcalls__, __vertices__) \
    do {                                                                \
        auto __renderer__ = Director::getInstance()->getRenderer();     \
        __renderer__->addDrawnBatches(__drawcalls__);                   \
        __renderer__->addDrawnVertices(__vertices__);                   \
    } while(0)

/** Returns the Renderer
     @since v3.0
     */
// Renderer类负责渲染。  
// 自渲染器会自动批处理 只要有可能倾向于使用“QuadCommand”对象 。  
// QuadCommand命令用于呈现一个或多个Quads
Renderer* getRenderer() const { return _renderer; }
 
  





























































































































































































你可能感兴趣的:(cocos2d-x)