1/******************************************************************************
2
3 @File         OGLES2/PVRTShader.cpp
4
5 @Title        OGLES2/PVRTShader
6
7 @Version
8
9 @Copyright    Copyright (c) Imagination Technologies Limited.
10
11 @Platform     ANSI compatible
12
13 @Description  Shader handling for OpenGL ES 2.0
14
15******************************************************************************/
16
17#include "PVRTString.h"
18#include "PVRTShader.h"
19#include "PVRTResourceFile.h"
20#include "PVRTGlobal.h"
21#include <ctype.h>
22#include <string.h>
23
24/*!***************************************************************************
25 @Function		PVRTShaderLoadSourceFromMemory
26 @Input			pszShaderCode		shader source code
27 @Input			Type				type of shader (GL_VERTEX_SHADER or GL_FRAGMENT_SHADER)
28 @Output		pObject				the resulting shader object
29 @Output		pReturnError		the error message if it failed
30 @Input			aszDefineArray		Array of defines to be pre-appended to shader string
31 @Input			uiDefArraySize		Size of the define array
32 @Return		PVR_SUCCESS on success and PVR_FAIL on failure (also fills the str string)
33 @Description	Loads a shader source code into memory and compiles it.
34				It also pre-appends the array of defines that have been passed in
35				to the source code before compilation.
36*****************************************************************************/
37EPVRTError PVRTShaderLoadSourceFromMemory(	const char* pszShaderCode,
38											const GLenum Type,
39											GLuint* const pObject,
40											CPVRTString* const pReturnError,
41											const char* const* aszDefineArray, GLuint uiDefArraySize)
42{
43	// Append define's here if there are any
44	CPVRTString pszShaderString;
45
46	if(uiDefArraySize > 0)
47	{
48		while(isspace(*pszShaderCode))
49			++pszShaderCode;
50
51		if(*pszShaderCode == '#')
52		{
53			const char* tmp = pszShaderCode + 1;
54
55			while(isspace(*tmp))
56				++tmp;
57
58			if(strncmp(tmp, "version", 7) == 0)
59			{
60				const char* c = strchr(pszShaderCode, '\n');
61
62				if(c)
63				{
64					size_t length = c - pszShaderCode + 1;
65					pszShaderString = CPVRTString(pszShaderCode, length);
66					pszShaderCode += length;
67				}
68				else
69				{
70					pszShaderString = CPVRTString(pszShaderCode) + "\n";
71					pszShaderCode = '\0';
72				}
73			}
74		}
75
76		for(GLuint i = 0 ; i < uiDefArraySize; ++i)
77		{
78			pszShaderString += "#define ";
79			pszShaderString += aszDefineArray[i];
80			pszShaderString += "\n";
81		}
82	}
83
84	// Append the shader code to the string
85	pszShaderString += pszShaderCode;
86
87	/* Create and compile the shader object */
88    *pObject = glCreateShader(Type);
89	const char* pszString(pszShaderString.c_str());
90	glShaderSource(*pObject, 1, &pszString, NULL);
91    glCompileShader(*pObject);
92
93	/* Test if compilation succeeded */
94	GLint ShaderCompiled;
95    glGetShaderiv(*pObject, GL_COMPILE_STATUS, &ShaderCompiled);
96	if (!ShaderCompiled)
97	{
98		int i32InfoLogLength, i32CharsWritten;
99		glGetShaderiv(*pObject, GL_INFO_LOG_LENGTH, &i32InfoLogLength);
100		char* pszInfoLog = new char[i32InfoLogLength];
101        glGetShaderInfoLog(*pObject, i32InfoLogLength, &i32CharsWritten, pszInfoLog);
102		*pReturnError = CPVRTString("Failed to compile shader: ") + pszInfoLog + "\n";
103		delete [] pszInfoLog;
104		glDeleteShader(*pObject);
105		return PVR_FAIL;
106	}
107
108	return PVR_SUCCESS;
109}
110
111/*!***************************************************************************
112 @Function		PVRTShaderLoadBinaryFromMemory
113 @Input			ShaderData		shader compiled binary data
114 @Input			Size			size of shader binary data in bytes
115 @Input			Type			type of shader (GL_VERTEX_SHADER or GL_FRAGMENT_SHADER)
116 @Input			Format			shader binary format
117 @Output		pObject			the resulting shader object
118 @Output		pReturnError	the error message if it failed
119 @Return		PVR_SUCCESS on success and PVR_FAIL on failure (also fills the str string)
120 @Description	Takes a shader binary from memory and passes it to the GL.
121*****************************************************************************/
122EPVRTError PVRTShaderLoadBinaryFromMemory(	const void* const ShaderData,
123											const size_t Size,
124											const GLenum Type,
125											const GLenum Format,
126											GLuint* const pObject,
127											CPVRTString* const pReturnError)
128{
129	/* Create and compile the shader object */
130    *pObject = glCreateShader(Type);
131
132    // Get the list of supported binary formats
133    // and if (more then 0) find given Format among them
134    GLint numFormats = 0;
135    GLint *listFormats;
136    int i;
137    glGetIntegerv(GL_NUM_SHADER_BINARY_FORMATS,&numFormats);
138    if(numFormats != 0) {
139        listFormats = new GLint[numFormats];
140        for(i=0;i<numFormats;++i)
141            listFormats[i] = 0;
142        glGetIntegerv(GL_SHADER_BINARY_FORMATS,listFormats);
143        for(i=0;i<numFormats;++i) {
144            if(listFormats[i] == (int) Format) {
145                glShaderBinary(1, pObject, Format, ShaderData, (GLint)Size);
146                if (glGetError() != GL_NO_ERROR)
147                {
148                    *pReturnError = CPVRTString("Failed to load binary shader\n");
149                    glDeleteShader(*pObject);
150                    return PVR_FAIL;
151                }
152                return PVR_SUCCESS;
153            }
154        }
155        delete [] listFormats;
156    }
157    *pReturnError = CPVRTString("Failed to load binary shader\n");
158    glDeleteShader(*pObject);
159    return PVR_FAIL;
160}
161
162/*!***************************************************************************
163 @Function		PVRTShaderLoadFromFile
164 @Input			pszBinFile			binary shader filename
165 @Input			pszSrcFile			source shader filename
166 @Input			Type				type of shader (GL_VERTEX_SHADER or GL_FRAGMENT_SHADER)
167 @Input			Format				shader binary format, or 0 for source shader
168 @Output		pObject				the resulting shader object
169 @Output		pReturnError		the error message if it failed
170 @Input			pContext			Context
171 @Input			aszDefineArray		Array of defines to be pre-appended to shader string
172 @Input			uiDefArraySize		Size of the define array
173 @Return		PVR_SUCCESS on success and PVR_FAIL on failure (also fills pReturnError)
174 @Description	Loads a shader file into memory and passes it to the GL.
175				It also passes defines that need to be pre-appended to the shader before compilation.
176*****************************************************************************/
177EPVRTError PVRTShaderLoadFromFile(	const char* const pszBinFile,
178									const char* const pszSrcFile,
179									const GLenum Type,
180									const GLenum Format,
181									GLuint* const pObject,
182									CPVRTString* const pReturnError,
183									const SPVRTContext* const pContext,
184									const char* const* aszDefineArray, GLuint uiDefArraySize)
185{
186	PVRT_UNREFERENCED_PARAMETER(pContext);
187
188	*pReturnError = "";
189
190	/*
191		Prepending defines relies on altering the source file that is loaded.
192		For this reason, the function calls the source loader instead of the binary loader if defines have
193		been passed in.
194	*/
195	if(Format && pszBinFile && uiDefArraySize == 0)
196	{
197		CPVRTResourceFile ShaderFile(pszBinFile);
198		if (ShaderFile.IsOpen())
199		{
200			if(PVRTShaderLoadBinaryFromMemory(ShaderFile.DataPtr(), ShaderFile.Size(), Type, Format, pObject, pReturnError) == PVR_SUCCESS)
201				return PVR_SUCCESS;
202		}
203
204		*pReturnError += CPVRTString("Failed to open shader ") + pszBinFile + "\n";
205	}
206
207	CPVRTResourceFile ShaderFile(pszSrcFile);
208	if (!ShaderFile.IsOpen())
209	{
210		*pReturnError += CPVRTString("Failed to open shader ") + pszSrcFile + "\n";
211		return PVR_FAIL;
212	}
213
214	CPVRTString ShaderFileString;
215	const char* pShaderData = (const char*) ShaderFile.DataPtr();
216
217	// Is our shader resource file data null terminated?
218	if(pShaderData[ShaderFile.Size()-1] != '\0')
219	{
220		// If not create a temporary null-terminated string
221		ShaderFileString.assign(pShaderData, ShaderFile.Size());
222		pShaderData = ShaderFileString.c_str();
223	}
224
225	return PVRTShaderLoadSourceFromMemory(pShaderData, Type, pObject, pReturnError, aszDefineArray, uiDefArraySize);
226}
227
228/*!***************************************************************************
229 @Function		PVRTCreateProgram
230 @Output		pProgramObject			the created program object
231 @Input			VertexShader			the vertex shader to link
232 @Input			FragmentShader			the fragment shader to link
233 @Input			pszAttribs				an array of attribute names
234 @Input			i32NumAttribs			the number of attributes to bind
235 @Output		pReturnError			the error message if it failed
236 @Returns		PVR_SUCCESS on success, PVR_FAIL if failure
237 @Description	Links a shader program.
238*****************************************************************************/
239EPVRTError PVRTCreateProgram(	GLuint* const pProgramObject,
240								const GLuint VertexShader,
241								const GLuint FragmentShader,
242								const char** const pszAttribs,
243								const int i32NumAttribs,
244								CPVRTString* const pReturnError)
245{
246	*pProgramObject = glCreateProgram();
247
248    glAttachShader(*pProgramObject, FragmentShader);
249    glAttachShader(*pProgramObject, VertexShader);
250
251	for (int i = 0; i < i32NumAttribs; ++i)
252	{
253		glBindAttribLocation(*pProgramObject, i, pszAttribs[i]);
254	}
255
256	// Link the program object
257    glLinkProgram(*pProgramObject);
258    GLint Linked;
259    glGetProgramiv(*pProgramObject, GL_LINK_STATUS, &Linked);
260	if (!Linked)
261	{
262		int i32InfoLogLength, i32CharsWritten;
263		glGetProgramiv(*pProgramObject, GL_INFO_LOG_LENGTH, &i32InfoLogLength);
264		char* pszInfoLog = new char[i32InfoLogLength];
265		glGetProgramInfoLog(*pProgramObject, i32InfoLogLength, &i32CharsWritten, pszInfoLog);
266		*pReturnError = CPVRTString("Failed to link: ") + pszInfoLog + "\n";
267		delete [] pszInfoLog;
268		return PVR_FAIL;
269	}
270
271	glUseProgram(*pProgramObject);
272
273	return PVR_SUCCESS;
274}
275
276/*****************************************************************************
277 End of file (PVRTShader.cpp)
278*****************************************************************************/
279
280