//----------------------------------------------------------------------- GLSLLinkProgram::GLSLLinkProgram(GLSLGpuProgram* vertexProgram, GLSLGpuProgram* geometryProgram, GLSLGpuProgram* fragmentProgram) : mVertexProgram(vertexProgram) , mGeometryProgram(geometryProgram) , mFragmentProgram(fragmentProgram) , mUniformRefsBuilt(false) , mLinked(false) { //checkForGLSLError( "GLSLLinkProgram::GLSLLinkProgram", "Error prior to Creating GLSL Program Object", 0); glGetError(); //Clean up the error. Otherwise will flood log. mGLHandle = glCreateProgramObjectARB(); checkForGLSLError( "GLSLLinkProgram::GLSLLinkProgram", "Error Creating GLSL Program Object", 0 ); // tell shaders to attach themselves to the LinkProgram // let the shaders do the attaching since they may have several children to attach if (mVertexProgram) { mVertexProgram->getGLSLProgram()->attachToProgramObject(mGLHandle); setSkeletalAnimationIncluded(mVertexProgram->isSkeletalAnimationIncluded()); } if (mGeometryProgram) { mGeometryProgram->getGLSLProgram()->attachToProgramObject(mGLHandle); //Don't set adjacency flag. We handle it internally and expose "false" } if (mFragmentProgram) { mFragmentProgram->getGLSLProgram()->attachToProgramObject(mGLHandle); } }
//----------------------------------------------------------------------- void GLSLProgram::attachToProgramObject( const GLhandleARB programObject ) { glAttachObjectARB( programObject, mGLHandle ); checkForGLSLError( "GLSLLinkProgram::GLSLLinkProgram", "Error attaching " + mName + " shader object to GLSL Program Object", programObject ); // attach child objects GLSLProgramContainerIterator childprogramcurrent = mAttachedGLSLPrograms.begin(); GLSLProgramContainerIterator childprogramend = mAttachedGLSLPrograms.end(); while (childprogramcurrent != childprogramend) { GLSLProgram* childShader = *childprogramcurrent; // bug in ATI GLSL linker : modules without main function must be recompiled each time // they are linked to a different program object // don't check for compile errors since there won't be any // *** minor inconvenience until ATI fixes there driver childShader->compile(false); childShader->attachToProgramObject( programObject ); ++childprogramcurrent; } }
//----------------------------------------------------------------------- void GLSLProgram::loadFromSource(void) { // only create a shader object if glsl is supported if (isSupported()) { checkForGLSLError( "GLSLProgram::GLSLProgram", "GL Errors before creating shader object", 0 ); // create shader object mGLHandle = glCreateShaderObjectARB( (mType == GPT_VERTEX_PROGRAM) ? GL_VERTEX_SHADER_ARB : GL_FRAGMENT_SHADER_ARB ); checkForGLSLError( "GLSLProgram::GLSLProgram", "Error creating GLSL shader Object", 0 ); } const char* SLSource = mSource.c_str(); glShaderSourceARB(mGLHandle, 1, &SLSource, NULL); // check for load errors checkForGLSLError( "GLSLProgram::loadFromSource", "Cannot load GLSL high-level shader source : " + mName, 0 ); compile(); }
//--------------------------------------------------------------------------- bool GLSLProgram::compile(const bool checkErrors) { glCompileShaderARB(mGLHandle); // check for compile errors glGetObjectParameterivARB(mGLHandle, GL_OBJECT_COMPILE_STATUS_ARB, &mCompiled); // force exception if not compiled if (checkErrors) { checkForGLSLError( "GLSLProgram::loadFromSource", "Cannot compile GLSL high-level shader : " + mName + " ", mGLHandle, !mCompiled, !mCompiled ); if (mCompiled) { logObjectInfo( mName + " : GLSL compiled ", mGLHandle ); } } return (mCompiled == 1); }
//----------------------------------------------------------------------- void GLSLLinkProgram::updateUniforms(GpuProgramParametersSharedPtr params, uint16 mask, GpuProgramType fromProgType) { // iterate through uniform reference list and update uniform values GLUniformReferenceIterator currentUniform = mGLUniformReferences.begin(); GLUniformReferenceIterator endUniform = mGLUniformReferences.end(); for (; currentUniform != endUniform; ++currentUniform) { // Only pull values from buffer it's supposed to be in (vertex or fragment) // This method will be called twice, once for vertex program params, // and once for fragment program params. if (fromProgType == currentUniform->mSourceProgType) { const GpuConstantDefinition* def = currentUniform->mConstantDef; if (def->variability & mask) { GLsizei glArraySize = (GLsizei)def->arraySize; // get the index in the parameter real list switch (def->constType) { case GCT_FLOAT1: glUniform1fvARB(currentUniform->mLocation, glArraySize, params->getFloatPointer(def->physicalIndex)); break; case GCT_FLOAT2: glUniform2fvARB(currentUniform->mLocation, glArraySize, params->getFloatPointer(def->physicalIndex)); break; case GCT_FLOAT3: glUniform3fvARB(currentUniform->mLocation, glArraySize, params->getFloatPointer(def->physicalIndex)); break; case GCT_FLOAT4: glUniform4fvARB(currentUniform->mLocation, glArraySize, params->getFloatPointer(def->physicalIndex)); break; case GCT_MATRIX_2X2: glUniformMatrix2fvARB(currentUniform->mLocation, glArraySize, GL_TRUE, params->getFloatPointer(def->physicalIndex)); break; case GCT_MATRIX_2X3: if (GLEW_VERSION_2_1) { glUniformMatrix2x3fv(currentUniform->mLocation, glArraySize, GL_TRUE, params->getFloatPointer(def->physicalIndex)); } break; case GCT_MATRIX_2X4: if (GLEW_VERSION_2_1) { glUniformMatrix2x4fv(currentUniform->mLocation, glArraySize, GL_TRUE, params->getFloatPointer(def->physicalIndex)); } break; case GCT_MATRIX_3X2: if (GLEW_VERSION_2_1) { glUniformMatrix3x2fv(currentUniform->mLocation, glArraySize, GL_TRUE, params->getFloatPointer(def->physicalIndex)); } break; case GCT_MATRIX_3X3: glUniformMatrix3fvARB(currentUniform->mLocation, glArraySize, GL_TRUE, params->getFloatPointer(def->physicalIndex)); break; case GCT_MATRIX_3X4: if (GLEW_VERSION_2_1) { glUniformMatrix3x4fv(currentUniform->mLocation, glArraySize, GL_TRUE, params->getFloatPointer(def->physicalIndex)); } break; case GCT_MATRIX_4X2: if (GLEW_VERSION_2_1) { glUniformMatrix4x2fv(currentUniform->mLocation, glArraySize, GL_TRUE, params->getFloatPointer(def->physicalIndex)); } break; case GCT_MATRIX_4X3: if (GLEW_VERSION_2_1) { glUniformMatrix4x3fv(currentUniform->mLocation, glArraySize, GL_TRUE, params->getFloatPointer(def->physicalIndex)); } break; case GCT_MATRIX_4X4: glUniformMatrix4fvARB(currentUniform->mLocation, glArraySize, GL_TRUE, params->getFloatPointer(def->physicalIndex)); break; case GCT_INT1: glUniform1ivARB(currentUniform->mLocation, glArraySize, (GLint*)params->getIntPointer(def->physicalIndex)); break; case GCT_INT2: glUniform2ivARB(currentUniform->mLocation, glArraySize, (GLint*)params->getIntPointer(def->physicalIndex)); break; case GCT_INT3: glUniform3ivARB(currentUniform->mLocation, glArraySize, (GLint*)params->getIntPointer(def->physicalIndex)); break; case GCT_INT4: glUniform4ivARB(currentUniform->mLocation, glArraySize, (GLint*)params->getIntPointer(def->physicalIndex)); break; case GCT_SAMPLER1D: case GCT_SAMPLER1DSHADOW: case GCT_SAMPLER2D: case GCT_SAMPLER2DSHADOW: case GCT_SAMPLER3D: case GCT_SAMPLERCUBE: // samplers handled like 1-element ints glUniform1ivARB(currentUniform->mLocation, 1, (GLint*)params->getIntPointer(def->physicalIndex)); break; case GCT_UNKNOWN: break; } // end switch #if OGRE_DEBUG_MODE checkForGLSLError( "GLSLLinkProgram::updateUniforms", "Error updating uniform", 0 ); #endif } // variability & mask } // fromProgType == currentUniform->mSourceProgType } // end for }
//----------------------------------------------------------------------- void GLSLLinkProgram::activate(void) { if (!mLinked) { if (mVertexProgram) { // Some drivers (e.g. OS X on nvidia) incorrectly determine the attribute binding automatically // and end up aliasing existing built-ins. So avoid! // Bind all used attribs - not all possible ones otherwise we'll get // lots of warnings in the log, and also may end up aliasing names used // as varyings by accident // Because we can't ask GL whether an attribute is used in the shader // until it is linked (chicken and egg!) we have to parse the source size_t numAttribs = sizeof(msCustomAttributes)/sizeof(CustomAttribute); const String& vpSource = mVertexProgram->getGLSLProgram()->getSource(); for (size_t i = 0; i < numAttribs; ++i) { const CustomAttribute& a = msCustomAttributes[i]; // we're looking for either: // attribute vec<n> <semantic_name> // in vec<n> <semantic_name> // The latter is recommended in GLSL 1.3 onwards // be slightly flexible about formatting String::size_type pos = vpSource.find(a.name); if (pos != String::npos) { String::size_type startpos = vpSource.find("attribute", pos < 20 ? 0 : pos-20); if (startpos == String::npos) startpos = vpSource.find("in", pos-20); if (startpos != String::npos && startpos < pos) { // final check String expr = vpSource.substr(startpos, pos + a.name.length() - startpos); StringVector vec = StringUtil::split(expr); if ((vec[0] == "in" || vec[0] == "attribute") && vec[2] == a.name) glBindAttribLocationARB(mGLHandle, a.attrib, a.name.c_str()); } } } } if (mGeometryProgram) { RenderOperation::OperationType inputOperationType = mGeometryProgram->getGLSLProgram()->getInputOperationType(); glProgramParameteriEXT(mGLHandle,GL_GEOMETRY_INPUT_TYPE_EXT, getGLGeometryInputPrimitiveType(inputOperationType, mGeometryProgram->isAdjacencyInfoRequired())); RenderOperation::OperationType outputOperationType = mGeometryProgram->getGLSLProgram()->getOutputOperationType(); switch (outputOperationType) { case RenderOperation::OT_POINT_LIST: case RenderOperation::OT_LINE_STRIP: case RenderOperation::OT_TRIANGLE_STRIP: case RenderOperation::OT_LINE_LIST: case RenderOperation::OT_TRIANGLE_LIST: case RenderOperation::OT_TRIANGLE_FAN: break; } glProgramParameteriEXT(mGLHandle,GL_GEOMETRY_OUTPUT_TYPE_EXT, getGLGeometryOutputPrimitiveType(outputOperationType)); glProgramParameteriEXT(mGLHandle,GL_GEOMETRY_VERTICES_OUT_EXT, mGeometryProgram->getGLSLProgram()->getMaxOutputVertices()); } glLinkProgramARB( mGLHandle ); glGetObjectParameterivARB( mGLHandle, GL_OBJECT_LINK_STATUS_ARB, &mLinked ); // force logging and raise exception if not linked checkForGLSLError( "GLSLLinkProgram::Activate", "Error linking GLSL Program Object : ", mGLHandle, !mLinked, !mLinked ); if(mLinked) { logObjectInfo( String("GLSL link result : "), mGLHandle ); buildGLUniformReferences(); extractAttributes(); } } if (mLinked) { checkForGLSLError( "GLSLLinkProgram::Activate", "Error prior to using GLSL Program Object : ", mGLHandle, false, false); glUseProgramObjectARB( mGLHandle ); checkForGLSLError( "GLSLLinkProgram::Activate", "Error using GLSL Program Object : ", mGLHandle, false, false); } }
void GLSLGpuProgram::initialize() { #if BS_OPENGL_4_5 static const char* VERSION_CHARS = "450"; #elif BS_OPENGL_4_4 static const char* VERSION_CHARS = "440"; #elif BS_OPENGL_4_3 static const char* VERSION_CHARS = "430"; #elif BS_OPENGL_4_2 static const char* VERSION_CHARS = "420"; #else static const char* VERSION_CHARS = "410"; #endif if (!isSupported()) { mIsCompiled = false; mCompileMessages = "Specified GPU program type is not supported by the current render system."; GpuProgram::initialize(); return; } GLenum shaderType = 0x0000; switch (mType) { case GPT_VERTEX_PROGRAM: shaderType = GL_VERTEX_SHADER; mProgramID = ++sVertexShaderCount; break; case GPT_FRAGMENT_PROGRAM: shaderType = GL_FRAGMENT_SHADER; mProgramID = ++sFragmentShaderCount; break; #if BS_OPENGL_4_1 || BS_OPENGLES_3_2 case GPT_GEOMETRY_PROGRAM: shaderType = GL_GEOMETRY_SHADER; mProgramID = ++sGeometryShaderCount; break; case GPT_HULL_PROGRAM: shaderType = GL_TESS_CONTROL_SHADER; mProgramID = ++sDomainShaderCount; break; case GPT_DOMAIN_PROGRAM: shaderType = GL_TESS_EVALUATION_SHADER; mProgramID = ++sHullShaderCount; break; #endif #if BS_OPENGL_4_3 || BS_OPENGLES_3_1 case GPT_COMPUTE_PROGRAM: shaderType = GL_COMPUTE_SHADER; mProgramID = ++sComputeShaderCount; break; #endif default: break; } // Add preprocessor extras and main source const String& source = mSource; if (!source.empty()) { Vector<GLchar*> lines; const char* versionStr = "#version "; UINT32 versionStrLen = (UINT32)strlen(versionStr); UINT32 lineLength = 0; INT32 versionLineNum = -1; for (UINT32 i = 0; i < source.size(); i++) { if (source[i] == '\n' || source[i] == '\r') { assert(sizeof(source[i]) == sizeof(GLchar)); GLchar* lineData = (GLchar*)bs_stack_alloc(sizeof(GLchar) * (lineLength + 2)); memcpy(lineData, &source[i - lineLength], sizeof(GLchar) * lineLength); lineData[lineLength] = '\n'; lineData[lineLength + 1] = '\0'; if(versionLineNum == -1 && lineLength >= versionStrLen) { bool isEqual = true; for (UINT32 j = 0; j < versionStrLen; ++j) { if(lineData[j] != versionStr[j]) { isEqual = false; break; } } if (isEqual) versionLineNum = (INT32)lines.size(); } lines.push_back(lineData); lineLength = 0; } else { lineLength++; } } if (lineLength > 0) { UINT32 end = (UINT32)source.size() - 1; assert(sizeof(source[end]) == sizeof(GLchar)); GLchar* lineData = (GLchar*)bs_stack_alloc(sizeof(GLchar) * (lineLength + 1)); memcpy(lineData, &source[source.size() - lineLength], sizeof(GLchar) * lineLength); lineData[lineLength] = '\0'; lines.push_back(lineData); lineLength = 0; } int numInsertedLines = 0; if(versionLineNum == -1) { char versionLine[50]; strcpy(versionLine, "#version "); strcat(versionLine, VERSION_CHARS); strcat(versionLine, "\n"); UINT32 length = (UINT32)strlen(versionLine) + 1; GLchar* extraLineData = (GLchar*)bs_stack_alloc(length); memcpy(extraLineData, versionLine, length); lines.insert(lines.begin(), extraLineData); numInsertedLines++; } char versionDefine[50]; strcpy(versionDefine, "#define OPENGL"); strcat(versionDefine, VERSION_CHARS); strcat(versionDefine, "\n"); const char* EXTRA_LINES[] = { "#define OPENGL\n", versionDefine }; UINT32 numExtraLines = sizeof(EXTRA_LINES) / sizeof(EXTRA_LINES[0]); UINT32 extraLineOffset = versionLineNum != -1 ? versionLineNum + 1 : 0; for (UINT32 i = 0; i < numExtraLines; i++) { UINT32 length = (UINT32)strlen(EXTRA_LINES[i]) + 1; GLchar* extraLineData = (GLchar*)bs_stack_alloc(length); memcpy(extraLineData, EXTRA_LINES[i], length); lines.insert(lines.begin() + extraLineOffset + numInsertedLines, extraLineData); numInsertedLines++; } StringStream codeStream; for(auto& entry : lines) codeStream << entry; for (INT32 i = numInsertedLines - 1; i >= 0; i--) bs_stack_free(lines[extraLineOffset + i]); if (numInsertedLines > 0) lines.erase(lines.begin() + extraLineOffset, lines.begin() + extraLineOffset + numInsertedLines); for (auto iter = lines.rbegin(); iter != lines.rend(); ++iter) bs_stack_free(*iter); String code = codeStream.str(); const char* codeRaw = code.c_str(); mGLHandle = glCreateShaderProgramv(shaderType, 1, (const GLchar**)&codeRaw); BS_CHECK_GL_ERROR(); mCompileMessages = ""; mIsCompiled = !checkForGLSLError(mGLHandle, mCompileMessages); } if (mIsCompiled) { GLSLParamParser paramParser; paramParser.buildUniformDescriptions(mGLHandle, mType, *mParametersDesc); if (mType == GPT_VERTEX_PROGRAM) { Vector<VertexElement> elementList = paramParser.buildVertexDeclaration(mGLHandle); mInputDeclaration = HardwareBufferManager::instance().createVertexDeclaration(elementList); } } BS_INC_RENDER_STAT_CAT(ResCreated, RenderStatObject_GpuProgram); GpuProgram::initialize(); }