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 | |
11 | namespace 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* = (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* [] = |
214 | { |
215 | "#define OPENGL\n" , |
216 | versionDefine |
217 | }; |
218 | |
219 | UINT32 = sizeof(EXTRA_LINES) / sizeof(EXTRA_LINES[0]); |
220 | UINT32 = 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* = (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 | }} |