bool GLProgram::initWithByteArrays(const GLchar* vShaderByteArray, const GLchar* fShaderByteArray, const std::string& compileTimeDefines) { _program = glCreateProgram(); CHECK_GL_ERROR_DEBUG(); // convert defines here. If we do it in "compileShader" we will do it it twice. // a cache for the defines could be useful, but seems like overkill at this point std::string replacedDefines = ""; replaceDefines(compileTimeDefines, replacedDefines); _vertShader = _fragShader = 0; if (vShaderByteArray) { if (!compileShader(&_vertShader, GL_VERTEX_SHADER, vShaderByteArray, replacedDefines)) { CCLOG("cocos2d: ERROR: Failed to compile vertex shader"); return false; } } // Create and compile fragment shader if (fShaderByteArray) { if (!compileShader(&_fragShader, GL_FRAGMENT_SHADER, fShaderByteArray, replacedDefines)) { 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.clear(); CHECK_GL_ERROR_DEBUG(); return true; }
Effect* Effect::createFromSource(const char* vshPath, const char* vshSource, const char* fshPath, const char* fshSource, const char* defines) { GP_ASSERT(vshSource); GP_ASSERT(fshSource); const unsigned int SHADER_SOURCE_LENGTH = 3; const GLchar* shaderSource[SHADER_SOURCE_LENGTH]; char* infoLog = NULL; GLuint vertexShader; GLuint fragmentShader; GLuint program; GLint length; GLint success; // Replace all comma separated definitions with #define prefix and \n suffix std::string definesStr = ""; replaceDefines(defines, definesStr); shaderSource[0] = definesStr.c_str(); shaderSource[1] = "\n"; std::string vshSourceStr = ""; if (vshPath) { // Replace the #include "xxxxx.xxx" with the sources that come from file paths replaceIncludes(vshPath, vshSource, vshSourceStr); if (vshSource && strlen(vshSource) != 0) vshSourceStr += "\n"; } shaderSource[2] = vshPath ? vshSourceStr.c_str() : vshSource; GL_ASSERT( vertexShader = glCreateShader(GL_VERTEX_SHADER) ); GL_ASSERT( glShaderSource(vertexShader, SHADER_SOURCE_LENGTH, shaderSource, NULL) ); GL_ASSERT( glCompileShader(vertexShader) ); GL_ASSERT( glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success) ); if (success != GL_TRUE) { GL_ASSERT( glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &length) ); if (length == 0) { length = 4096; } if (length > 0) { infoLog = new char[length]; GL_ASSERT( glGetShaderInfoLog(vertexShader, length, NULL, infoLog) ); infoLog[length-1] = '\0'; } // Write out the expanded shader file. if (vshPath) writeShaderToErrorFile(vshPath, shaderSource[2]); GP_ERROR("Compile failed for vertex shader '%s' with error '%s'.", vshPath == NULL ? vshSource : vshPath, infoLog == NULL ? "" : infoLog); SAFE_DELETE_ARRAY(infoLog); // Clean up. GL_ASSERT( glDeleteShader(vertexShader) ); return NULL; } // Compile the fragment shader. std::string fshSourceStr; if (fshPath) { // Replace the #include "xxxxx.xxx" with the sources that come from file paths replaceIncludes(fshPath, fshSource, fshSourceStr); if (fshSource && strlen(fshSource) != 0) fshSourceStr += "\n"; } shaderSource[2] = fshPath ? fshSourceStr.c_str() : fshSource; GL_ASSERT( fragmentShader = glCreateShader(GL_FRAGMENT_SHADER) ); GL_ASSERT( glShaderSource(fragmentShader, SHADER_SOURCE_LENGTH, shaderSource, NULL) ); GL_ASSERT( glCompileShader(fragmentShader) ); GL_ASSERT( glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success) ); if (success != GL_TRUE) { GL_ASSERT( glGetShaderiv(fragmentShader, GL_INFO_LOG_LENGTH, &length) ); if (length == 0) { length = 4096; } if (length > 0) { infoLog = new char[length]; GL_ASSERT( glGetShaderInfoLog(fragmentShader, length, NULL, infoLog) ); infoLog[length-1] = '\0'; } // Write out the expanded shader file. if (fshPath) writeShaderToErrorFile(fshPath, shaderSource[2]); GP_ERROR("Compile failed for fragment shader (%s): %s", fshPath == NULL ? fshSource : fshPath, infoLog == NULL ? "" : infoLog); SAFE_DELETE_ARRAY(infoLog); // Clean up. GL_ASSERT( glDeleteShader(vertexShader) ); GL_ASSERT( glDeleteShader(fragmentShader) ); return NULL; } // Link program. GL_ASSERT( program = glCreateProgram() ); GL_ASSERT( glAttachShader(program, vertexShader) ); GL_ASSERT( glAttachShader(program, fragmentShader) ); GL_ASSERT( glLinkProgram(program) ); GL_ASSERT( glGetProgramiv(program, GL_LINK_STATUS, &success) ); // Delete shaders after linking. GL_ASSERT( glDeleteShader(vertexShader) ); GL_ASSERT( glDeleteShader(fragmentShader) ); // Check link status. if (success != GL_TRUE) { GL_ASSERT( glGetProgramiv(program, GL_INFO_LOG_LENGTH, &length) ); if (length == 0) { length = 4096; } if (length > 0) { infoLog = new char[length]; GL_ASSERT( glGetProgramInfoLog(program, length, NULL, infoLog) ); infoLog[length-1] = '\0'; } GP_ERROR("Linking program failed (%s,%s): %s", vshPath == NULL ? "NULL" : vshPath, fshPath == NULL ? "NULL" : fshPath, infoLog == NULL ? "" : infoLog); SAFE_DELETE_ARRAY(infoLog); // Clean up. GL_ASSERT( glDeleteProgram(program) ); return NULL; } // Create and return the new Effect. Effect* effect = new Effect(); effect->_program = program; // Query and store vertex attribute meta-data from the program. // NOTE: Rather than using glBindAttribLocation to explicitly specify our own // preferred attribute locations, we're going to query the locations that were // automatically bound by the GPU. While it can sometimes be convenient to use // glBindAttribLocation, some vendors actually reserve certain attribute indices // and therefore using this function can create compatibility issues between // different hardware vendors. GLint activeAttributes; GL_ASSERT( glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &activeAttributes) ); if (activeAttributes > 0) { GL_ASSERT( glGetProgramiv(program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &length) ); if (length > 0) { GLchar* attribName = new GLchar[length + 1]; GLint attribSize; GLenum attribType; GLint attribLocation; for (int i = 0; i < activeAttributes; ++i) { // Query attribute info. GL_ASSERT( glGetActiveAttrib(program, i, length, NULL, &attribSize, &attribType, attribName) ); attribName[length] = '\0'; // Query the pre-assigned attribute location. GL_ASSERT( attribLocation = glGetAttribLocation(program, attribName) ); // Assign the vertex attribute mapping for the effect. effect->_vertexAttributes[attribName] = attribLocation; } SAFE_DELETE_ARRAY(attribName); } } // Query and store uniforms from the program. GLint activeUniforms; GL_ASSERT( glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &activeUniforms) ); if (activeUniforms > 0) { GL_ASSERT( glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &length) ); if (length > 0) { GLchar* uniformName = new GLchar[length + 1]; GLint uniformSize; GLenum uniformType; GLint uniformLocation; unsigned int samplerIndex = 0; for (int i = 0; i < activeUniforms; ++i) { // Query uniform info. GL_ASSERT( glGetActiveUniform(program, i, length, NULL, &uniformSize, &uniformType, uniformName) ); uniformName[length] = '\0'; // null terminate if (length > 3) { // If this is an array uniform, strip array indexers off it since GL does not // seem to be consistent across different drivers/implementations in how it returns // array uniforms. On some systems it will return "u_matrixArray", while on others // it will return "u_matrixArray[0]". char* c = strrchr(uniformName, '['); if (c) { *c = '\0'; } } // Query the pre-assigned uniform location. GL_ASSERT( uniformLocation = glGetUniformLocation(program, uniformName) ); Uniform* uniform = new Uniform(); uniform->_effect = effect; uniform->_name = uniformName; uniform->_location = uniformLocation; uniform->_type = uniformType; if (uniformType == GL_SAMPLER_2D) { uniform->_index = samplerIndex; samplerIndex += uniformSize; } else { uniform->_index = 0; } effect->_uniforms[uniformName] = uniform; } SAFE_DELETE_ARRAY(uniformName); } } return effect; }