| 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 | }} |