/****************************************************************************** @File OGLES2/PVRTShader.cpp @Title OGLES2/PVRTShader @Version @Copyright Copyright (c) Imagination Technologies Limited. @Platform ANSI compatible @Description Shader handling for OpenGL ES 2.0 ******************************************************************************/ #include "PVRTString.h" #include "PVRTShader.h" #include "PVRTResourceFile.h" #include "PVRTGlobal.h" #include <ctype.h> #include <string.h> /*!*************************************************************************** @Function PVRTShaderLoadSourceFromMemory @Input pszShaderCode shader source code @Input Type type of shader (GL_VERTEX_SHADER or GL_FRAGMENT_SHADER) @Output pObject the resulting shader object @Output pReturnError the error message if it failed @Input aszDefineArray Array of defines to be pre-appended to shader string @Input uiDefArraySize Size of the define array @Return PVR_SUCCESS on success and PVR_FAIL on failure (also fills the str string) @Description Loads a shader source code into memory and compiles it. It also pre-appends the array of defines that have been passed in to the source code before compilation. *****************************************************************************/ EPVRTError PVRTShaderLoadSourceFromMemory( const char* pszShaderCode, const GLenum Type, GLuint* const pObject, CPVRTString* const pReturnError, const char* const* aszDefineArray, GLuint uiDefArraySize) { // Append define's here if there are any CPVRTString pszShaderString; if(uiDefArraySize > 0) { while(isspace(*pszShaderCode)) ++pszShaderCode; if(*pszShaderCode == '#') { const char* tmp = pszShaderCode + 1; while(isspace(*tmp)) ++tmp; if(strncmp(tmp, "version", 7) == 0) { const char* c = strchr(pszShaderCode, '\n'); if(c) { size_t length = c - pszShaderCode + 1; pszShaderString = CPVRTString(pszShaderCode, length); pszShaderCode += length; } else { pszShaderString = CPVRTString(pszShaderCode) + "\n"; pszShaderCode = '\0'; } } } for(GLuint i = 0 ; i < uiDefArraySize; ++i) { pszShaderString += "#define "; pszShaderString += aszDefineArray[i]; pszShaderString += "\n"; } } // Append the shader code to the string pszShaderString += pszShaderCode; /* Create and compile the shader object */ *pObject = glCreateShader(Type); const char* pszString(pszShaderString.c_str()); glShaderSource(*pObject, 1, &pszString, NULL); glCompileShader(*pObject); /* Test if compilation succeeded */ GLint ShaderCompiled; glGetShaderiv(*pObject, GL_COMPILE_STATUS, &ShaderCompiled); if (!ShaderCompiled) { int i32InfoLogLength, i32CharsWritten; glGetShaderiv(*pObject, GL_INFO_LOG_LENGTH, &i32InfoLogLength); char* pszInfoLog = new char[i32InfoLogLength]; glGetShaderInfoLog(*pObject, i32InfoLogLength, &i32CharsWritten, pszInfoLog); *pReturnError = CPVRTString("Failed to compile shader: ") + pszInfoLog + "\n"; delete [] pszInfoLog; glDeleteShader(*pObject); return PVR_FAIL; } return PVR_SUCCESS; } /*!*************************************************************************** @Function PVRTShaderLoadBinaryFromMemory @Input ShaderData shader compiled binary data @Input Size size of shader binary data in bytes @Input Type type of shader (GL_VERTEX_SHADER or GL_FRAGMENT_SHADER) @Input Format shader binary format @Output pObject the resulting shader object @Output pReturnError the error message if it failed @Return PVR_SUCCESS on success and PVR_FAIL on failure (also fills the str string) @Description Takes a shader binary from memory and passes it to the GL. *****************************************************************************/ EPVRTError PVRTShaderLoadBinaryFromMemory( const void* const ShaderData, const size_t Size, const GLenum Type, const GLenum Format, GLuint* const pObject, CPVRTString* const pReturnError) { /* Create and compile the shader object */ *pObject = glCreateShader(Type); // Get the list of supported binary formats // and if (more then 0) find given Format among them GLint numFormats = 0; GLint *listFormats; int i; glGetIntegerv(GL_NUM_SHADER_BINARY_FORMATS,&numFormats); if(numFormats != 0) { listFormats = new GLint[numFormats]; for(i=0;i<numFormats;++i) listFormats[i] = 0; glGetIntegerv(GL_SHADER_BINARY_FORMATS,listFormats); for(i=0;i<numFormats;++i) { if(listFormats[i] == (int) Format) { glShaderBinary(1, pObject, Format, ShaderData, (GLint)Size); if (glGetError() != GL_NO_ERROR) { *pReturnError = CPVRTString("Failed to load binary shader\n"); glDeleteShader(*pObject); return PVR_FAIL; } return PVR_SUCCESS; } } delete [] listFormats; } *pReturnError = CPVRTString("Failed to load binary shader\n"); glDeleteShader(*pObject); return PVR_FAIL; } /*!*************************************************************************** @Function PVRTShaderLoadFromFile @Input pszBinFile binary shader filename @Input pszSrcFile source shader filename @Input Type type of shader (GL_VERTEX_SHADER or GL_FRAGMENT_SHADER) @Input Format shader binary format, or 0 for source shader @Output pObject the resulting shader object @Output pReturnError the error message if it failed @Input pContext Context @Input aszDefineArray Array of defines to be pre-appended to shader string @Input uiDefArraySize Size of the define array @Return PVR_SUCCESS on success and PVR_FAIL on failure (also fills pReturnError) @Description Loads a shader file into memory and passes it to the GL. It also passes defines that need to be pre-appended to the shader before compilation. *****************************************************************************/ EPVRTError PVRTShaderLoadFromFile( const char* const pszBinFile, const char* const pszSrcFile, const GLenum Type, const GLenum Format, GLuint* const pObject, CPVRTString* const pReturnError, const SPVRTContext* const pContext, const char* const* aszDefineArray, GLuint uiDefArraySize) { PVRT_UNREFERENCED_PARAMETER(pContext); *pReturnError = ""; /* Prepending defines relies on altering the source file that is loaded. For this reason, the function calls the source loader instead of the binary loader if defines have been passed in. */ if(Format && pszBinFile && uiDefArraySize == 0) { CPVRTResourceFile ShaderFile(pszBinFile); if (ShaderFile.IsOpen()) { if(PVRTShaderLoadBinaryFromMemory(ShaderFile.DataPtr(), ShaderFile.Size(), Type, Format, pObject, pReturnError) == PVR_SUCCESS) return PVR_SUCCESS; } *pReturnError += CPVRTString("Failed to open shader ") + pszBinFile + "\n"; } CPVRTResourceFile ShaderFile(pszSrcFile); if (!ShaderFile.IsOpen()) { *pReturnError += CPVRTString("Failed to open shader ") + pszSrcFile + "\n"; return PVR_FAIL; } CPVRTString ShaderFileString; const char* pShaderData = (const char*) ShaderFile.DataPtr(); // Is our shader resource file data null terminated? if(pShaderData[ShaderFile.Size()-1] != '\0') { // If not create a temporary null-terminated string ShaderFileString.assign(pShaderData, ShaderFile.Size()); pShaderData = ShaderFileString.c_str(); } return PVRTShaderLoadSourceFromMemory(pShaderData, Type, pObject, pReturnError, aszDefineArray, uiDefArraySize); } /*!*************************************************************************** @Function PVRTCreateProgram @Output pProgramObject the created program object @Input VertexShader the vertex shader to link @Input FragmentShader the fragment shader to link @Input pszAttribs an array of attribute names @Input i32NumAttribs the number of attributes to bind @Output pReturnError the error message if it failed @Returns PVR_SUCCESS on success, PVR_FAIL if failure @Description Links a shader program. *****************************************************************************/ EPVRTError PVRTCreateProgram( GLuint* const pProgramObject, const GLuint VertexShader, const GLuint FragmentShader, const char** const pszAttribs, const int i32NumAttribs, CPVRTString* const pReturnError) { *pProgramObject = glCreateProgram(); glAttachShader(*pProgramObject, FragmentShader); glAttachShader(*pProgramObject, VertexShader); for (int i = 0; i < i32NumAttribs; ++i) { glBindAttribLocation(*pProgramObject, i, pszAttribs[i]); } // Link the program object glLinkProgram(*pProgramObject); GLint Linked; glGetProgramiv(*pProgramObject, GL_LINK_STATUS, &Linked); if (!Linked) { int i32InfoLogLength, i32CharsWritten; glGetProgramiv(*pProgramObject, GL_INFO_LOG_LENGTH, &i32InfoLogLength); char* pszInfoLog = new char[i32InfoLogLength]; glGetProgramInfoLog(*pProgramObject, i32InfoLogLength, &i32CharsWritten, pszInfoLog); *pReturnError = CPVRTString("Failed to link: ") + pszInfoLog + "\n"; delete [] pszInfoLog; return PVR_FAIL; } glUseProgram(*pProgramObject); return PVR_SUCCESS; } /***************************************************************************** End of file (PVRTShader.cpp) *****************************************************************************/