1//************************************ bs::framework - Copyright 2018 Marko Pintera **************************************//
2//*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********//
3#include "GLSL/BsGLSLGpuProgram.h"
4#include "RenderAPI/BsRenderAPI.h"
5#include "Error/BsException.h"
6#include "GLSL/BsGLSLParamParser.h"
7#include "Managers/BsHardwareBufferManager.h"
8#include "Profiling/BsRenderStats.h"
9#include "RenderAPI/BsGpuParams.h"
10
11namespace bs { namespace ct
12{
13 UINT32 GLSLGpuProgram::sVertexShaderCount = 0;
14 UINT32 GLSLGpuProgram::sFragmentShaderCount = 0;
15 UINT32 GLSLGpuProgram::sGeometryShaderCount = 0;
16 UINT32 GLSLGpuProgram::sDomainShaderCount = 0;
17 UINT32 GLSLGpuProgram::sHullShaderCount = 0;
18 UINT32 GLSLGpuProgram::sComputeShaderCount = 0;
19
20 bool checkForGLSLError(const GLuint programObj, String& outErrorMsg)
21 {
22 StringStream stream;
23
24 GLint linkCompileSuccess = 0;
25 glGetProgramiv(programObj, GL_LINK_STATUS, &linkCompileSuccess);
26 BS_CHECK_GL_ERROR();
27
28 if (!linkCompileSuccess && programObj > 0)
29 {
30 GLint infologLength = 0;
31 glGetProgramiv(programObj, GL_INFO_LOG_LENGTH, &infologLength);
32 BS_CHECK_GL_ERROR();
33
34 if (infologLength > 0)
35 {
36 GLint charsWritten = 0;
37
38 GLchar* infoLog = (GLchar*)bs_alloc(sizeof(GLchar)* infologLength);
39
40 glGetProgramInfoLog(programObj, infologLength, &charsWritten, infoLog);
41 BS_CHECK_GL_ERROR();
42
43 stream << "Compile and linker info log: \n";
44 stream << String(infoLog);
45
46 bs_free(infoLog);
47 }
48 }
49
50 outErrorMsg = stream.str();
51 return !linkCompileSuccess;
52 }
53
54 GLSLGpuProgram::GLSLGpuProgram(const GPU_PROGRAM_DESC& desc, GpuDeviceFlags deviceMask)
55 :GpuProgram(desc, deviceMask)
56 { }
57
58 GLSLGpuProgram::~GLSLGpuProgram()
59 {
60 if (mIsCompiled && mGLHandle != 0)
61 {
62 glDeleteProgram(mGLHandle);
63 BS_CHECK_GL_ERROR();
64
65 mGLHandle = 0;
66 }
67
68 BS_INC_RENDER_STAT_CAT(ResDestroyed, RenderStatObject_GpuProgram);
69 }
70
71 void GLSLGpuProgram::initialize()
72 {
73
74#if BS_OPENGL_4_5
75 static const char* VERSION_CHARS = "450";
76#elif BS_OPENGL_4_4
77 static const char* VERSION_CHARS = "440";
78#elif BS_OPENGL_4_3
79 static const char* VERSION_CHARS = "430";
80#elif BS_OPENGL_4_2
81 static const char* VERSION_CHARS = "420";
82#else
83 static const char* VERSION_CHARS = "410";
84#endif
85
86 if (!isSupported())
87 {
88 mIsCompiled = false;
89 mCompileMessages = "Specified GPU program type is not supported by the current render system.";
90
91 GpuProgram::initialize();
92 return;
93 }
94
95 GLenum shaderType = 0x0000;
96 switch (mType)
97 {
98 case GPT_VERTEX_PROGRAM:
99 shaderType = GL_VERTEX_SHADER;
100 mProgramID = ++sVertexShaderCount;
101 break;
102 case GPT_FRAGMENT_PROGRAM:
103 shaderType = GL_FRAGMENT_SHADER;
104 mProgramID = ++sFragmentShaderCount;
105 break;
106#if BS_OPENGL_4_1 || BS_OPENGLES_3_2
107 case GPT_GEOMETRY_PROGRAM:
108 shaderType = GL_GEOMETRY_SHADER;
109 mProgramID = ++sGeometryShaderCount;
110 break;
111 case GPT_HULL_PROGRAM:
112 shaderType = GL_TESS_CONTROL_SHADER;
113 mProgramID = ++sDomainShaderCount;
114 break;
115 case GPT_DOMAIN_PROGRAM:
116 shaderType = GL_TESS_EVALUATION_SHADER;
117 mProgramID = ++sHullShaderCount;
118 break;
119#endif
120#if BS_OPENGL_4_3 || BS_OPENGLES_3_1
121 case GPT_COMPUTE_PROGRAM:
122 shaderType = GL_COMPUTE_SHADER;
123 mProgramID = ++sComputeShaderCount;
124 break;
125#endif
126 default:
127 break;
128 }
129
130 // Add preprocessor extras and main source
131 const String& source = mSource;
132 if (!source.empty())
133 {
134 Vector<GLchar*> lines;
135
136 const char* versionStr = "#version ";
137 UINT32 versionStrLen = (UINT32)strlen(versionStr);
138
139 UINT32 lineLength = 0;
140 INT32 versionLineNum = -1;
141 for (UINT32 i = 0; i < source.size(); i++)
142 {
143 if (source[i] == '\n' || source[i] == '\r')
144 {
145 assert(sizeof(source[i]) == sizeof(GLchar));
146
147 GLchar* lineData = (GLchar*)bs_stack_alloc(sizeof(GLchar) * (lineLength + 2));
148 memcpy(lineData, &source[i - lineLength], sizeof(GLchar) * lineLength);
149
150 lineData[lineLength] = '\n';
151 lineData[lineLength + 1] = '\0';
152
153 if(versionLineNum == -1 && lineLength >= versionStrLen)
154 {
155 bool isEqual = true;
156 for (UINT32 j = 0; j < versionStrLen; ++j)
157 {
158 if(lineData[j] != versionStr[j])
159 {
160 isEqual = false;
161 break;
162 }
163 }
164
165 if (isEqual)
166 versionLineNum = (INT32)lines.size();
167 }
168
169 lines.push_back(lineData);
170 lineLength = 0;
171 }
172 else
173 {
174 lineLength++;
175 }
176 }
177
178 if (lineLength > 0)
179 {
180 UINT32 end = (UINT32)source.size() - 1;
181 assert(sizeof(source[end]) == sizeof(GLchar));
182
183 GLchar* lineData = (GLchar*)bs_stack_alloc(sizeof(GLchar) * (lineLength + 1));
184 memcpy(lineData, &source[source.size() - lineLength], sizeof(GLchar) * lineLength);
185 lineData[lineLength] = '\0';
186
187 lines.push_back(lineData);
188 lineLength = 0;
189 }
190
191 int numInsertedLines = 0;
192 if(versionLineNum == -1)
193 {
194 char versionLine[50];
195 strcpy(versionLine, "#version ");
196 strcat(versionLine, VERSION_CHARS);
197 strcat(versionLine, "\n");
198
199 UINT32 length = (UINT32)strlen(versionLine) + 1;
200
201 GLchar* extraLineData = (GLchar*)bs_stack_alloc(length);
202 memcpy(extraLineData, versionLine, length);
203
204 lines.insert(lines.begin(), extraLineData);
205 numInsertedLines++;
206 }
207
208 char versionDefine[50];
209 strcpy(versionDefine, "#define OPENGL");
210 strcat(versionDefine, VERSION_CHARS);
211 strcat(versionDefine, "\n");
212
213 const char* EXTRA_LINES[] =
214 {
215 "#define OPENGL\n",
216 versionDefine
217 };
218
219 UINT32 numExtraLines = sizeof(EXTRA_LINES) / sizeof(EXTRA_LINES[0]);
220 UINT32 extraLineOffset = versionLineNum != -1 ? versionLineNum + 1 : 0;
221 for (UINT32 i = 0; i < numExtraLines; i++)
222 {
223 UINT32 length = (UINT32)strlen(EXTRA_LINES[i]) + 1;
224
225 GLchar* extraLineData = (GLchar*)bs_stack_alloc(length);
226 memcpy(extraLineData, EXTRA_LINES[i], length);
227
228 lines.insert(lines.begin() + extraLineOffset + numInsertedLines, extraLineData);
229 numInsertedLines++;
230 }
231
232 StringStream codeStream;
233 for(auto& entry : lines)
234 codeStream << entry;
235
236 for (INT32 i = numInsertedLines - 1; i >= 0; i--)
237 bs_stack_free(lines[extraLineOffset + i]);
238
239 if (numInsertedLines > 0)
240 lines.erase(lines.begin() + extraLineOffset, lines.begin() + extraLineOffset + numInsertedLines);
241
242 for (auto iter = lines.rbegin(); iter != lines.rend(); ++iter)
243 bs_stack_free(*iter);
244
245 String code = codeStream.str();
246 const char* codeRaw = code.c_str();
247 mGLHandle = glCreateShaderProgramv(shaderType, 1, (const GLchar**)&codeRaw);
248 BS_CHECK_GL_ERROR();
249
250 mCompileMessages = "";
251 mIsCompiled = !checkForGLSLError(mGLHandle, mCompileMessages);
252 }
253
254 if (mIsCompiled)
255 {
256 GLSLParamParser paramParser;
257 paramParser.buildUniformDescriptions(mGLHandle, mType, *mParametersDesc);
258
259 if (mType == GPT_VERTEX_PROGRAM)
260 {
261 Vector<VertexElement> elementList = paramParser.buildVertexDeclaration(mGLHandle);
262 mInputDeclaration = HardwareBufferManager::instance().createVertexDeclaration(elementList);
263 }
264 }
265
266 BS_INC_RENDER_STAT_CAT(ResCreated, RenderStatObject_GpuProgram);
267 GpuProgram::initialize();
268 }
269
270 bool GLSLGpuProgram::isSupported() const
271 {
272 RenderAPI* rapi = RenderAPI::instancePtr();
273 const RenderAPICapabilities& caps = rapi->getCapabilities(0);
274
275 switch (mType)
276 {
277 case GPT_GEOMETRY_PROGRAM:
278#if BS_OPENGL_4_1 || BS_OPENGLES_3_2
279 return caps.hasCapability(RSC_GEOMETRY_PROGRAM);
280#else
281 return false;
282#endif
283 case GPT_HULL_PROGRAM:
284 case GPT_DOMAIN_PROGRAM:
285#if BS_OPENGL_4_1 || BS_OPENGLES_3_2
286 return caps.hasCapability(RSC_TESSELLATION_PROGRAM);
287#else
288 return false;
289#endif
290 case GPT_COMPUTE_PROGRAM:
291#if BS_OPENGL_4_3 || BS_OPENGLES_3_1
292 return caps.hasCapability(RSC_COMPUTE_PROGRAM);
293#else
294 return false;
295#endif
296 default:
297 return true;
298 }
299 }
300}}