| /****************************************************************************** |
| |
| @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) |
| *****************************************************************************/ |
| |