| 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/BsGLSLParamParser.h" |
| 4 | #include "RenderAPI/BsGpuParams.h" |
| 5 | |
| 6 | namespace bs { namespace ct |
| 7 | { |
| 8 | INT32 GLSLAttribute::matchesName(const String& name) |
| 9 | { |
| 10 | if (name.length() >= mName.length()) |
| 11 | { |
| 12 | if (name.substr(0, mName.length()) == mName) |
| 13 | { |
| 14 | String indexStr = name.substr(mName.length(), name.length()); |
| 15 | return parseUINT32(indexStr, 0); |
| 16 | } |
| 17 | } |
| 18 | |
| 19 | return -1; |
| 20 | } |
| 21 | |
| 22 | Vector<VertexElement> GLSLParamParser::buildVertexDeclaration(GLuint glProgram) |
| 23 | { |
| 24 | GLint numAttributes = 0; |
| 25 | glGetProgramiv(glProgram, GL_ACTIVE_ATTRIBUTES, &numAttributes); |
| 26 | BS_CHECK_GL_ERROR(); |
| 27 | |
| 28 | GLint maxNameSize = 0; |
| 29 | glGetProgramiv(glProgram, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &maxNameSize); |
| 30 | BS_CHECK_GL_ERROR(); |
| 31 | |
| 32 | GLchar* attributeName = (GLchar*)bs_alloc(sizeof(GLchar)* maxNameSize); |
| 33 | |
| 34 | Vector<VertexElement> elementList; |
| 35 | for (GLint i = 0; i < numAttributes; i++) |
| 36 | { |
| 37 | GLint attribSize = 0; |
| 38 | GLenum attribType = 0; |
| 39 | glGetActiveAttrib(glProgram, i, maxNameSize, nullptr, &attribSize, &attribType, attributeName); |
| 40 | BS_CHECK_GL_ERROR(); |
| 41 | |
| 42 | VertexElementSemantic semantic = VES_POSITION; |
| 43 | UINT16 index = 0; |
| 44 | if (attribNameToElementSemantic(attributeName, semantic, index)) |
| 45 | { |
| 46 | VertexElementType type = glTypeToAttributeType(attribType); |
| 47 | UINT32 slot = glGetAttribLocation(glProgram, attributeName); |
| 48 | BS_CHECK_GL_ERROR(); |
| 49 | |
| 50 | elementList.push_back(VertexElement(0, slot, type, semantic, index)); |
| 51 | } |
| 52 | else |
| 53 | { |
| 54 | // Ignore built-in attributes |
| 55 | if(memcmp(attributeName, "gl_" , 3) != 0) |
| 56 | LOGWRN("Cannot determine vertex input attribute type for attribute: " + String(attributeName)); |
| 57 | } |
| 58 | } |
| 59 | |
| 60 | bs_free(attributeName); |
| 61 | |
| 62 | return elementList; |
| 63 | } |
| 64 | |
| 65 | UINT32 GLSLParamParser::calcInterfaceBlockElementSizeAndOffset(GpuParamDataType type, UINT32 arraySize, UINT32& offset) |
| 66 | { |
| 67 | const GpuParamDataTypeInfo& typeInfo = bs::GpuParams::PARAM_SIZES.lookup[type]; |
| 68 | UINT32 size = (typeInfo.baseTypeSize * typeInfo.numColumns * typeInfo.numRows) / 4; |
| 69 | UINT32 alignment = typeInfo.alignment / 4; |
| 70 | |
| 71 | // Fix alignment if needed |
| 72 | UINT32 alignOffset = offset % alignment; |
| 73 | if (alignOffset != 0) |
| 74 | { |
| 75 | UINT32 padding = (alignment - alignOffset); |
| 76 | offset += padding; |
| 77 | } |
| 78 | |
| 79 | if (arraySize > 1) |
| 80 | { |
| 81 | // Array elements are always padded and aligned to vec4 |
| 82 | alignOffset = size % 4; |
| 83 | if (alignOffset != 0) |
| 84 | { |
| 85 | UINT32 padding = (4 - alignOffset); |
| 86 | size += padding; |
| 87 | } |
| 88 | |
| 89 | alignOffset = offset % 4; |
| 90 | if (alignOffset != 0) |
| 91 | { |
| 92 | UINT32 padding = (4 - alignOffset); |
| 93 | offset += padding; |
| 94 | } |
| 95 | |
| 96 | return size; |
| 97 | } |
| 98 | else |
| 99 | return size; |
| 100 | } |
| 101 | |
| 102 | VertexElementType GLSLParamParser::glTypeToAttributeType(GLenum glType) |
| 103 | { |
| 104 | switch (glType) |
| 105 | { |
| 106 | case GL_FLOAT: |
| 107 | return VET_FLOAT1; |
| 108 | case GL_FLOAT_VEC2: |
| 109 | return VET_FLOAT2; |
| 110 | case GL_FLOAT_VEC3: |
| 111 | return VET_FLOAT3; |
| 112 | case GL_FLOAT_VEC4: |
| 113 | return VET_FLOAT4; |
| 114 | case GL_INT: |
| 115 | return VET_INT1; |
| 116 | case GL_INT_VEC2: |
| 117 | return VET_INT2; |
| 118 | case GL_INT_VEC3: |
| 119 | return VET_INT3; |
| 120 | case GL_INT_VEC4: |
| 121 | return VET_INT4; |
| 122 | case GL_UNSIGNED_INT: |
| 123 | return VET_UINT1; |
| 124 | case GL_UNSIGNED_INT_VEC2: |
| 125 | return VET_UINT2; |
| 126 | case GL_UNSIGNED_INT_VEC3: |
| 127 | return VET_UINT3; |
| 128 | case GL_UNSIGNED_INT_VEC4: |
| 129 | return VET_UINT4; |
| 130 | default: |
| 131 | BS_EXCEPT(NotImplementedException, "Unsupported vertex attribute type." ); |
| 132 | } |
| 133 | |
| 134 | return VET_FLOAT4; |
| 135 | } |
| 136 | |
| 137 | bool GLSLParamParser::attribNameToElementSemantic(const String& name, VertexElementSemantic& semantic, UINT16& index) |
| 138 | { |
| 139 | static GLSLAttribute attributes[] = |
| 140 | { |
| 141 | GLSLAttribute("bs_position" , VES_POSITION), |
| 142 | GLSLAttribute("bs_normal" , VES_NORMAL), |
| 143 | GLSLAttribute("bs_tangent" , VES_TANGENT), |
| 144 | GLSLAttribute("bs_bitangent" , VES_BITANGENT), |
| 145 | GLSLAttribute("bs_texcoord" , VES_TEXCOORD), |
| 146 | GLSLAttribute("bs_color" , VES_COLOR), |
| 147 | GLSLAttribute("bs_blendweights" , VES_BLEND_WEIGHTS), |
| 148 | GLSLAttribute("bs_blendindices" , VES_BLEND_INDICES), |
| 149 | GLSLAttribute("POSITION" , VES_POSITION), |
| 150 | GLSLAttribute("NORMAL" , VES_NORMAL), |
| 151 | GLSLAttribute("TANGENT" , VES_TANGENT), |
| 152 | GLSLAttribute("BITANGENT" , VES_BITANGENT), |
| 153 | GLSLAttribute("TEXCOORD" , VES_TEXCOORD), |
| 154 | GLSLAttribute("COLOR" , VES_COLOR), |
| 155 | GLSLAttribute("BLENDWEIGHT" , VES_BLEND_WEIGHTS), |
| 156 | GLSLAttribute("BLENDINDICES" , VES_BLEND_INDICES) |
| 157 | }; |
| 158 | |
| 159 | static const UINT32 numAttribs = sizeof(attributes) / sizeof(attributes[0]); |
| 160 | |
| 161 | for (UINT32 i = 0; i < numAttribs; i++) |
| 162 | { |
| 163 | INT32 attribIndex = attributes[i].matchesName(name); |
| 164 | if (attribIndex != -1) |
| 165 | { |
| 166 | index = attribIndex; |
| 167 | semantic = attributes[i].getSemantic(); |
| 168 | return true; |
| 169 | } |
| 170 | } |
| 171 | |
| 172 | return false; |
| 173 | } |
| 174 | |
| 175 | void GLSLParamParser::buildUniformDescriptions(GLuint glProgram, GpuProgramType type, GpuParamDesc& returnParamDesc) |
| 176 | { |
| 177 | // Scan through the active uniform blocks |
| 178 | GLint maxBufferSize = 0; |
| 179 | glGetProgramiv(glProgram, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxBufferSize); |
| 180 | BS_CHECK_GL_ERROR(); |
| 181 | |
| 182 | GLint maxBlockNameBufferSize = 0; |
| 183 | glGetProgramiv(glProgram, GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, &maxBlockNameBufferSize); |
| 184 | BS_CHECK_GL_ERROR(); |
| 185 | |
| 186 | GLint maxStorageBlockNameBufferSize = 0; |
| 187 | |
| 188 | #if BS_OPENGL_4_3 || BS_OPENGLES_3_1 |
| 189 | glGetProgramInterfaceiv(glProgram, GL_SHADER_STORAGE_BLOCK, GL_MAX_NAME_LENGTH, &maxStorageBlockNameBufferSize); |
| 190 | BS_CHECK_GL_ERROR(); |
| 191 | #endif |
| 192 | |
| 193 | maxBufferSize = std::max(maxBufferSize, maxBlockNameBufferSize); |
| 194 | maxBufferSize = std::max(maxBufferSize, maxStorageBlockNameBufferSize); |
| 195 | |
| 196 | GLchar* uniformName = (GLchar*)bs_alloc(sizeof(GLchar)* maxBufferSize); |
| 197 | |
| 198 | GpuParamBlockDesc newGlobalBlockDesc; |
| 199 | newGlobalBlockDesc.slot = 0; |
| 200 | newGlobalBlockDesc.set = mapParameterToSet(type, ParamType::UniformBlock); |
| 201 | newGlobalBlockDesc.name = "BS_INTERNAL_Globals" ; |
| 202 | newGlobalBlockDesc.blockSize = 0; |
| 203 | newGlobalBlockDesc.isShareable = false; |
| 204 | |
| 205 | returnParamDesc.paramBlocks[newGlobalBlockDesc.name] = newGlobalBlockDesc; |
| 206 | GpuParamBlockDesc& globalBlockDesc = returnParamDesc.paramBlocks[newGlobalBlockDesc.name]; |
| 207 | |
| 208 | // Enumerate uniform blocks |
| 209 | GLint uniformBlockCount = 0; |
| 210 | |
| 211 | #if BS_OPENGL_4_3 || BS_OPENGLES_3_1 |
| 212 | // Use program interface extension if available |
| 213 | glGetProgramInterfaceiv(glProgram, GL_UNIFORM_BLOCK, GL_ACTIVE_RESOURCES, &uniformBlockCount); |
| 214 | BS_CHECK_GL_ERROR(); |
| 215 | #else |
| 216 | // Fall back to old API if not available |
| 217 | glGetProgramiv(glProgram, GL_ACTIVE_UNIFORM_BLOCKS, &uniformBlockCount); |
| 218 | BS_CHECK_GL_ERROR(); |
| 219 | #endif |
| 220 | |
| 221 | Map<UINT32, String> blockSlotToName; |
| 222 | Set<String> blockNames; |
| 223 | for (GLuint index = 0; index < (GLuint)uniformBlockCount; index++) |
| 224 | { |
| 225 | GLsizei unusedSize = 0; |
| 226 | |
| 227 | #if BS_OPENGL_4_3 || BS_OPENGLES_3_1 |
| 228 | glGetProgramResourceName(glProgram, GL_UNIFORM_BLOCK, index, maxBufferSize, &unusedSize, uniformName); |
| 229 | BS_CHECK_GL_ERROR(); |
| 230 | #else |
| 231 | glGetActiveUniformBlockName(glProgram, index, maxBlockNameBufferSize, &unusedSize, uniformName); |
| 232 | BS_CHECK_GL_ERROR(); |
| 233 | #endif |
| 234 | |
| 235 | GpuParamBlockDesc newBlockDesc; |
| 236 | newBlockDesc.slot = index + 1; |
| 237 | newBlockDesc.set = mapParameterToSet(type, ParamType::UniformBlock); |
| 238 | newBlockDesc.name = uniformName; |
| 239 | newBlockDesc.blockSize = 0; |
| 240 | newBlockDesc.isShareable = true; |
| 241 | |
| 242 | returnParamDesc.paramBlocks[newBlockDesc.name] = newBlockDesc; |
| 243 | blockSlotToName.insert(std::make_pair(index + 1, newBlockDesc.name)); |
| 244 | blockNames.insert(newBlockDesc.name); |
| 245 | } |
| 246 | |
| 247 | #if BS_OPENGL_4_3 || BS_OPENGLES_3_1 |
| 248 | // Scan through the shared storage blocks |
| 249 | GLint storageBlockCount = 0; |
| 250 | glGetProgramInterfaceiv(glProgram, GL_SHADER_STORAGE_BLOCK, GL_ACTIVE_RESOURCES, &storageBlockCount); |
| 251 | BS_CHECK_GL_ERROR(); |
| 252 | |
| 253 | for (GLuint index = 0; index < (GLuint)storageBlockCount; index++) |
| 254 | { |
| 255 | GLsizei unusedSize = 0; |
| 256 | glGetProgramResourceName(glProgram, GL_SHADER_STORAGE_BLOCK, index, maxBufferSize, &unusedSize, uniformName); |
| 257 | BS_CHECK_GL_ERROR(); |
| 258 | |
| 259 | GpuParamObjectDesc bufferParam; |
| 260 | bufferParam.name = uniformName; |
| 261 | bufferParam.slot = index; |
| 262 | bufferParam.type = GPOT_RWSTRUCTURED_BUFFER; |
| 263 | bufferParam.set = mapParameterToSet(type, ParamType::StorageBlock); |
| 264 | |
| 265 | returnParamDesc.buffers.insert(std::make_pair(uniformName, bufferParam)); |
| 266 | } |
| 267 | #endif |
| 268 | |
| 269 | Map<String, UINT32> foundFirstArrayIndex; |
| 270 | Map<String, GpuParamDataDesc> foundStructs; |
| 271 | |
| 272 | // Get the number of active uniforms |
| 273 | GLint uniformCount = 0; |
| 274 | glGetProgramiv(glProgram, GL_ACTIVE_UNIFORMS, &uniformCount); |
| 275 | BS_CHECK_GL_ERROR(); |
| 276 | |
| 277 | // Loop over each of the active uniforms, and add them to the reference container |
| 278 | // only do this for user defined uniforms, ignore built in gl state uniforms |
| 279 | for (GLuint index = 0; index < (GLuint)uniformCount; index++) |
| 280 | { |
| 281 | GLsizei arraySize = 0; |
| 282 | glGetActiveUniformName(glProgram, index, maxBufferSize, &arraySize, uniformName); |
| 283 | BS_CHECK_GL_ERROR(); |
| 284 | |
| 285 | String paramName = String(uniformName); |
| 286 | |
| 287 | // Naming rules and packing rules used here are described in |
| 288 | // OpenGL Core Specification 2.11.4 |
| 289 | |
| 290 | // Check if parameter is a part of a struct |
| 291 | Vector<String> nameElements = StringUtil::tokenise(paramName, "." ); |
| 292 | |
| 293 | bool inStruct = false; |
| 294 | String structName; |
| 295 | if (nameElements.size() > 1) |
| 296 | { |
| 297 | auto uniformBlockFind = blockNames.find(nameElements[0]); |
| 298 | |
| 299 | // Check if the name is not a struct, and instead a Uniform block namespace |
| 300 | if (uniformBlockFind != blockNames.end()) |
| 301 | { |
| 302 | // Possibly it's a struct inside a named uniform block |
| 303 | if (nameElements.size() > 2) |
| 304 | { |
| 305 | inStruct = true; |
| 306 | structName = nameElements[1]; |
| 307 | paramName = nameElements.back(); |
| 308 | } |
| 309 | } |
| 310 | else |
| 311 | { |
| 312 | inStruct = true; |
| 313 | structName = nameElements[0]; |
| 314 | paramName = nameElements.back(); |
| 315 | } |
| 316 | } |
| 317 | |
| 318 | String cleanParamName = paramName; // Param name without array indexes |
| 319 | |
| 320 | // Check if the parameter is in an array |
| 321 | UINT32 arrayIdx = 0; |
| 322 | bool isInArray = false; |
| 323 | if (inStruct) |
| 324 | { |
| 325 | // If the uniform name has a "[" in it then its an array element uniform. |
| 326 | String::size_type arrayStart = structName.find("[" ); |
| 327 | String::size_type arrayEnd = structName.find("]" ); |
| 328 | if (arrayStart != String::npos) |
| 329 | { |
| 330 | String strArrIdx = structName.substr(arrayStart + 1, arrayEnd - (arrayStart + 1)); |
| 331 | arrayIdx = parseUINT32(strArrIdx, 0); |
| 332 | isInArray = true; |
| 333 | |
| 334 | structName = structName.substr(0, arrayStart); |
| 335 | } |
| 336 | } |
| 337 | |
| 338 | { |
| 339 | // If the uniform name has a "[" in it then its an array element uniform. |
| 340 | String::size_type arrayStart = cleanParamName.find("[" ); |
| 341 | String::size_type arrayEnd = cleanParamName.find("]" ); |
| 342 | if (arrayStart != String::npos) |
| 343 | { |
| 344 | String strArrIdx = cleanParamName.substr(arrayStart + 1, arrayEnd - (arrayStart + 1)); |
| 345 | |
| 346 | // If in struct, we don't care about individual element array indices |
| 347 | if(!inStruct) |
| 348 | { |
| 349 | arrayIdx = parseUINT32(strArrIdx, 0); |
| 350 | isInArray = true; |
| 351 | } |
| 352 | |
| 353 | cleanParamName = cleanParamName.substr(0, arrayStart); |
| 354 | } |
| 355 | } |
| 356 | |
| 357 | // GLSL will optimize out unused array indexes, so there's no guarantee that 0 is the first, |
| 358 | // so we store the first one here |
| 359 | int firstArrayIndex = 0; |
| 360 | if (isInArray) |
| 361 | { |
| 362 | String nameToSearch = cleanParamName; |
| 363 | if (inStruct) |
| 364 | nameToSearch = structName; |
| 365 | |
| 366 | auto arrayIndexFind = foundFirstArrayIndex.find(nameToSearch); |
| 367 | if (arrayIndexFind == foundFirstArrayIndex.end()) |
| 368 | { |
| 369 | foundFirstArrayIndex[nameToSearch] = arrayIdx; |
| 370 | } |
| 371 | |
| 372 | firstArrayIndex = foundFirstArrayIndex[nameToSearch]; |
| 373 | } |
| 374 | |
| 375 | |
| 376 | GLint uniformType; |
| 377 | glGetActiveUniformsiv(glProgram, 1, &index, GL_UNIFORM_TYPE, &uniformType); |
| 378 | BS_CHECK_GL_ERROR(); |
| 379 | |
| 380 | GpuParamObjectType samplerType = GPOT_UNKNOWN; |
| 381 | GpuParamObjectType textureType = GPOT_UNKNOWN; |
| 382 | |
| 383 | bool isSampler = false; |
| 384 | bool isImage = false; |
| 385 | bool isBuffer = false; |
| 386 | bool isRWBuffer = false; |
| 387 | switch (uniformType) |
| 388 | { |
| 389 | case GL_SAMPLER_1D: |
| 390 | case GL_SAMPLER_1D_SHADOW: |
| 391 | case GL_UNSIGNED_INT_SAMPLER_1D: |
| 392 | case GL_INT_SAMPLER_1D: |
| 393 | samplerType = GPOT_SAMPLER1D; |
| 394 | textureType = GPOT_TEXTURE1D; |
| 395 | isSampler = true; |
| 396 | break; |
| 397 | case GL_SAMPLER_1D_ARRAY: |
| 398 | case GL_SAMPLER_1D_ARRAY_SHADOW: |
| 399 | case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: |
| 400 | case GL_INT_SAMPLER_1D_ARRAY: |
| 401 | samplerType = GPOT_SAMPLER1D; |
| 402 | textureType = GPOT_TEXTURE1DARRAY; |
| 403 | isSampler = true; |
| 404 | break; |
| 405 | case GL_SAMPLER_2D: |
| 406 | case GL_SAMPLER_2D_SHADOW: |
| 407 | case GL_UNSIGNED_INT_SAMPLER_2D: |
| 408 | case GL_INT_SAMPLER_2D: |
| 409 | samplerType = GPOT_SAMPLER2D; |
| 410 | textureType = GPOT_TEXTURE2D; |
| 411 | isSampler = true; |
| 412 | break; |
| 413 | case GL_SAMPLER_2D_ARRAY: |
| 414 | case GL_SAMPLER_2D_ARRAY_SHADOW: |
| 415 | case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: |
| 416 | case GL_INT_SAMPLER_2D_ARRAY: |
| 417 | samplerType = GPOT_SAMPLER2D; |
| 418 | textureType = GPOT_TEXTURE2DARRAY; |
| 419 | isSampler = true; |
| 420 | break; |
| 421 | case GL_SAMPLER_2D_MULTISAMPLE: |
| 422 | case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: |
| 423 | case GL_INT_SAMPLER_2D_MULTISAMPLE: |
| 424 | samplerType = GPOT_SAMPLER2DMS; |
| 425 | textureType = GPOT_TEXTURE2DMS; |
| 426 | isSampler = true; |
| 427 | break; |
| 428 | case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: |
| 429 | case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: |
| 430 | case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: |
| 431 | samplerType = GPOT_SAMPLER2DMS; |
| 432 | textureType = GPOT_TEXTURE2DMSARRAY; |
| 433 | isSampler = true; |
| 434 | break; |
| 435 | case GL_SAMPLER_3D: |
| 436 | case GL_UNSIGNED_INT_SAMPLER_3D: |
| 437 | case GL_INT_SAMPLER_3D: |
| 438 | samplerType = GPOT_SAMPLER3D; |
| 439 | textureType = GPOT_TEXTURE3D; |
| 440 | isSampler = true; |
| 441 | break; |
| 442 | case GL_SAMPLER_CUBE: |
| 443 | case GL_SAMPLER_CUBE_SHADOW: |
| 444 | case GL_UNSIGNED_INT_SAMPLER_CUBE: |
| 445 | case GL_INT_SAMPLER_CUBE: |
| 446 | samplerType = GPOT_SAMPLERCUBE; |
| 447 | textureType = GPOT_TEXTURECUBE; |
| 448 | isSampler = true; |
| 449 | break; |
| 450 | case GL_SAMPLER_CUBE_MAP_ARRAY: |
| 451 | case GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW: |
| 452 | case GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY: |
| 453 | case GL_INT_SAMPLER_CUBE_MAP_ARRAY: |
| 454 | samplerType = GPOT_SAMPLERCUBE; |
| 455 | textureType = GPOT_TEXTURECUBEARRAY; |
| 456 | isSampler = true; |
| 457 | break; |
| 458 | case GL_SAMPLER_BUFFER: |
| 459 | case GL_UNSIGNED_INT_SAMPLER_BUFFER: |
| 460 | case GL_INT_SAMPLER_BUFFER: |
| 461 | isBuffer = true; |
| 462 | break; |
| 463 | #if BS_OPENGL_4_2 || BS_OPENGLES_3_1 |
| 464 | case GL_IMAGE_1D: |
| 465 | case GL_UNSIGNED_INT_IMAGE_1D: |
| 466 | case GL_INT_IMAGE_1D: |
| 467 | textureType = GPOT_RWTEXTURE1D; |
| 468 | isImage = true; |
| 469 | break; |
| 470 | case GL_IMAGE_1D_ARRAY: |
| 471 | case GL_UNSIGNED_INT_IMAGE_1D_ARRAY: |
| 472 | case GL_INT_IMAGE_1D_ARRAY: |
| 473 | textureType = GPOT_RWTEXTURE1DARRAY; |
| 474 | isImage = true; |
| 475 | break; |
| 476 | case GL_IMAGE_2D: |
| 477 | case GL_UNSIGNED_INT_IMAGE_2D: |
| 478 | case GL_INT_IMAGE_2D: |
| 479 | textureType = GPOT_RWTEXTURE2D; |
| 480 | isImage = true; |
| 481 | break; |
| 482 | case GL_IMAGE_2D_ARRAY: |
| 483 | case GL_UNSIGNED_INT_IMAGE_2D_ARRAY: |
| 484 | case GL_INT_IMAGE_2D_ARRAY: |
| 485 | textureType = GPOT_RWTEXTURE2DARRAY; |
| 486 | isImage = true; |
| 487 | break; |
| 488 | case GL_IMAGE_2D_MULTISAMPLE: |
| 489 | case GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE: |
| 490 | case GL_INT_IMAGE_2D_MULTISAMPLE: |
| 491 | textureType = GPOT_RWTEXTURE2DMS; |
| 492 | isImage = true; |
| 493 | break; |
| 494 | case GL_IMAGE_2D_MULTISAMPLE_ARRAY: |
| 495 | case GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY: |
| 496 | case GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY: |
| 497 | textureType = GPOT_RWTEXTURE2DMSARRAY; |
| 498 | isImage = true; |
| 499 | break; |
| 500 | case GL_IMAGE_3D: |
| 501 | case GL_UNSIGNED_INT_IMAGE_3D: |
| 502 | case GL_INT_IMAGE_3D: |
| 503 | textureType = GPOT_RWTEXTURE3D; |
| 504 | isImage = true; |
| 505 | break; |
| 506 | case GL_IMAGE_BUFFER: |
| 507 | case GL_UNSIGNED_INT_IMAGE_BUFFER: |
| 508 | case GL_INT_IMAGE_BUFFER: |
| 509 | isRWBuffer = true; |
| 510 | break; |
| 511 | #endif |
| 512 | } |
| 513 | |
| 514 | if (isSampler) |
| 515 | { |
| 516 | GpuParamObjectDesc samplerParam; |
| 517 | samplerParam.name = paramName; |
| 518 | samplerParam.type = samplerType; |
| 519 | samplerParam.slot = glGetUniformLocation(glProgram, uniformName); |
| 520 | samplerParam.set = mapParameterToSet(type, ParamType::Sampler); |
| 521 | |
| 522 | GpuParamObjectDesc textureParam; |
| 523 | textureParam.name = paramName; |
| 524 | textureParam.type = textureType; |
| 525 | textureParam.slot = samplerParam.slot; |
| 526 | textureParam.set = mapParameterToSet(type, ParamType::Texture); |
| 527 | |
| 528 | returnParamDesc.samplers.insert(std::make_pair(paramName, samplerParam)); |
| 529 | returnParamDesc.textures.insert(std::make_pair(paramName, textureParam)); |
| 530 | |
| 531 | BS_CHECK_GL_ERROR(); |
| 532 | } |
| 533 | else if (isImage) |
| 534 | { |
| 535 | GpuParamObjectDesc textureParam; |
| 536 | textureParam.name = paramName; |
| 537 | textureParam.type = textureType; |
| 538 | textureParam.slot = glGetUniformLocation(glProgram, uniformName); |
| 539 | textureParam.set = mapParameterToSet(type, ParamType::Image); |
| 540 | |
| 541 | returnParamDesc.loadStoreTextures.insert(std::make_pair(paramName, textureParam)); |
| 542 | |
| 543 | BS_CHECK_GL_ERROR(); |
| 544 | } |
| 545 | else if (isBuffer) |
| 546 | { |
| 547 | GpuParamObjectDesc bufferParam; |
| 548 | bufferParam.name = paramName; |
| 549 | bufferParam.type = GPOT_BYTE_BUFFER; |
| 550 | bufferParam.slot = glGetUniformLocation(glProgram, uniformName); |
| 551 | bufferParam.set = mapParameterToSet(type, ParamType::Texture); |
| 552 | |
| 553 | returnParamDesc.buffers.insert(std::make_pair(paramName, bufferParam)); |
| 554 | |
| 555 | BS_CHECK_GL_ERROR(); |
| 556 | } |
| 557 | else if(isRWBuffer) |
| 558 | { |
| 559 | GpuParamObjectDesc bufferParam; |
| 560 | bufferParam.name = paramName; |
| 561 | bufferParam.type = GPOT_RWBYTE_BUFFER; |
| 562 | bufferParam.slot = glGetUniformLocation(glProgram, uniformName); |
| 563 | bufferParam.set = mapParameterToSet(type, ParamType::Image); |
| 564 | |
| 565 | returnParamDesc.buffers.insert(std::make_pair(paramName, bufferParam)); |
| 566 | |
| 567 | BS_CHECK_GL_ERROR(); |
| 568 | } |
| 569 | else |
| 570 | { |
| 571 | // If array index is larger than 0 and uniform is not a part of a struct, |
| 572 | // it means we already processed it (struct arrays are processed differently) |
| 573 | if (!inStruct && arrayIdx != 0) |
| 574 | continue; |
| 575 | |
| 576 | GLint blockIndex; |
| 577 | glGetActiveUniformsiv(glProgram, 1, &index, GL_UNIFORM_BLOCK_INDEX, &blockIndex); |
| 578 | BS_CHECK_GL_ERROR(); |
| 579 | |
| 580 | GpuParamDataDesc gpuParam; |
| 581 | |
| 582 | if (isInArray) |
| 583 | gpuParam.name = cleanParamName; |
| 584 | else |
| 585 | gpuParam.name = paramName; |
| 586 | |
| 587 | determineParamInfo(gpuParam, paramName, glProgram, index); |
| 588 | |
| 589 | if (blockIndex != -1) |
| 590 | { |
| 591 | GLint blockOffset; |
| 592 | glGetActiveUniformsiv(glProgram, 1, &index, GL_UNIFORM_OFFSET, &blockOffset); |
| 593 | |
| 594 | blockOffset = blockOffset / 4; |
| 595 | |
| 596 | gpuParam.gpuMemOffset = blockOffset; |
| 597 | |
| 598 | String& blockName = blockSlotToName[blockIndex + 1]; |
| 599 | GpuParamBlockDesc& curBlockDesc = returnParamDesc.paramBlocks[blockName]; |
| 600 | |
| 601 | gpuParam.paramBlockSlot = curBlockDesc.slot; |
| 602 | gpuParam.paramBlockSet = mapParameterToSet(type, ParamType::UniformBlock); |
| 603 | gpuParam.cpuMemOffset = blockOffset; |
| 604 | curBlockDesc.blockSize = std::max(curBlockDesc.blockSize, gpuParam.cpuMemOffset + gpuParam.arrayElementStride * gpuParam.arraySize); |
| 605 | |
| 606 | BS_CHECK_GL_ERROR(); |
| 607 | } |
| 608 | else |
| 609 | { |
| 610 | gpuParam.gpuMemOffset = glGetUniformLocation(glProgram, uniformName); |
| 611 | gpuParam.paramBlockSlot = 0; |
| 612 | gpuParam.paramBlockSet = mapParameterToSet(type, ParamType::UniformBlock); |
| 613 | gpuParam.cpuMemOffset = globalBlockDesc.blockSize; |
| 614 | |
| 615 | globalBlockDesc.blockSize = std::max(globalBlockDesc.blockSize, gpuParam.cpuMemOffset + gpuParam.arrayElementStride * gpuParam.arraySize); |
| 616 | |
| 617 | BS_CHECK_GL_ERROR(); |
| 618 | } |
| 619 | |
| 620 | // If parameter is not a part of a struct we're done. Also done if parameter is part of a struct, but |
| 621 | // not part of a uniform block (in which case we treat struct members as separate parameters) |
| 622 | if (!inStruct || blockIndex == -1) |
| 623 | { |
| 624 | returnParamDesc.params.insert(std::make_pair(gpuParam.name, gpuParam)); |
| 625 | continue; |
| 626 | } |
| 627 | |
| 628 | // If the parameter is part of a struct, then we need to update the struct definition |
| 629 | auto findExistingStruct = foundStructs.find(structName); |
| 630 | |
| 631 | // Create new definition if one doesn't exist |
| 632 | if (findExistingStruct == foundStructs.end()) |
| 633 | { |
| 634 | foundStructs[structName] = GpuParamDataDesc(); |
| 635 | GpuParamDataDesc& structDesc = foundStructs[structName]; |
| 636 | structDesc.type = GPDT_STRUCT; |
| 637 | structDesc.name = structName; |
| 638 | structDesc.arraySize = 1; |
| 639 | structDesc.elementSize = 0; |
| 640 | structDesc.arrayElementStride = 0; |
| 641 | structDesc.gpuMemOffset = gpuParam.gpuMemOffset; |
| 642 | structDesc.cpuMemOffset = gpuParam.cpuMemOffset; |
| 643 | structDesc.paramBlockSlot = gpuParam.paramBlockSlot; |
| 644 | structDesc.paramBlockSet = gpuParam.paramBlockSet; |
| 645 | } |
| 646 | |
| 647 | // Update struct with size of the new parameter |
| 648 | GpuParamDataDesc& structDesc = foundStructs[structName]; |
| 649 | |
| 650 | if (arrayIdx == (UINT32)firstArrayIndex) // Determine element size only using the first array element |
| 651 | { |
| 652 | structDesc.elementSize = std::max(structDesc.elementSize, gpuParam.cpuMemOffset + |
| 653 | gpuParam.arrayElementStride * gpuParam.arraySize); |
| 654 | |
| 655 | structDesc.gpuMemOffset = std::min(structDesc.gpuMemOffset, gpuParam.gpuMemOffset); |
| 656 | structDesc.cpuMemOffset = std::min(structDesc.cpuMemOffset, gpuParam.cpuMemOffset); |
| 657 | } |
| 658 | |
| 659 | structDesc.arraySize = std::max(structDesc.arraySize, arrayIdx + 1); |
| 660 | } |
| 661 | } |
| 662 | |
| 663 | for(auto& entry : foundStructs) |
| 664 | { |
| 665 | entry.second.elementSize = entry.second.elementSize - entry.second.cpuMemOffset; |
| 666 | entry.second.arrayElementStride = Math::divideAndRoundUp(entry.second.elementSize, 4U) * 4; |
| 667 | |
| 668 | returnParamDesc.params.insert(std::make_pair(entry.first, entry.second)); |
| 669 | } |
| 670 | |
| 671 | // Param blocks always need to be a multiple of 4, so make it so |
| 672 | for (auto iter = returnParamDesc.paramBlocks.begin(); iter != returnParamDesc.paramBlocks.end(); ++iter) |
| 673 | { |
| 674 | GpuParamBlockDesc& blockDesc = iter->second; |
| 675 | |
| 676 | if (blockDesc.blockSize % 4 != 0) |
| 677 | blockDesc.blockSize += (4 - (blockDesc.blockSize % 4)); |
| 678 | } |
| 679 | |
| 680 | #if BS_DEBUG_MODE |
| 681 | // Check if manually calculated and OpenGL buffer sizes match |
| 682 | for (auto iter = returnParamDesc.paramBlocks.begin(); iter != returnParamDesc.paramBlocks.end(); ++iter) |
| 683 | { |
| 684 | if (iter->second.slot == 0) |
| 685 | continue; |
| 686 | |
| 687 | GLint blockSize = 0; |
| 688 | glGetActiveUniformBlockiv(glProgram, iter->second.slot - 1, GL_UNIFORM_BLOCK_DATA_SIZE, &blockSize); |
| 689 | BS_CHECK_GL_ERROR(); |
| 690 | |
| 691 | assert(blockSize % 4 == 0); |
| 692 | blockSize = blockSize / 4; |
| 693 | |
| 694 | if ((INT32)iter->second.blockSize != blockSize) |
| 695 | BS_EXCEPT(InternalErrorException, "OpenGL specified and manual uniform block buffer sizes don't match!" ); |
| 696 | } |
| 697 | #endif |
| 698 | |
| 699 | bs_free(uniformName); |
| 700 | } |
| 701 | |
| 702 | void GLSLParamParser::determineParamInfo(GpuParamDataDesc& desc, const String& paramName, GLuint programHandle, GLuint uniformIndex) |
| 703 | { |
| 704 | GLint arraySize; |
| 705 | glGetActiveUniformsiv(programHandle, 1, &uniformIndex, GL_UNIFORM_SIZE, &arraySize); |
| 706 | BS_CHECK_GL_ERROR(); |
| 707 | desc.arraySize = arraySize; |
| 708 | |
| 709 | GLint uniformType; |
| 710 | glGetActiveUniformsiv(programHandle, 1, &uniformIndex, GL_UNIFORM_TYPE, &uniformType); |
| 711 | BS_CHECK_GL_ERROR(); |
| 712 | |
| 713 | switch (uniformType) |
| 714 | { |
| 715 | case GL_BOOL: |
| 716 | desc.type = GPDT_BOOL; |
| 717 | desc.elementSize = 1; |
| 718 | break; |
| 719 | case GL_FLOAT: |
| 720 | desc.type = GPDT_FLOAT1; |
| 721 | desc.elementSize = 1; |
| 722 | break; |
| 723 | case GL_FLOAT_VEC2: |
| 724 | desc.type = GPDT_FLOAT2; |
| 725 | desc.elementSize = 2; |
| 726 | break; |
| 727 | case GL_FLOAT_VEC3: |
| 728 | desc.type = GPDT_FLOAT3; |
| 729 | desc.elementSize = 3; |
| 730 | break; |
| 731 | case GL_FLOAT_VEC4: |
| 732 | desc.type = GPDT_FLOAT4; |
| 733 | desc.elementSize = 4; |
| 734 | break; |
| 735 | case GL_INT: |
| 736 | case GL_UNSIGNED_INT: |
| 737 | desc.type = GPDT_INT1; |
| 738 | desc.elementSize = 1; |
| 739 | break; |
| 740 | case GL_INT_VEC2: |
| 741 | case GL_UNSIGNED_INT_VEC2: |
| 742 | desc.type = GPDT_INT2; |
| 743 | desc.elementSize = 2; |
| 744 | break; |
| 745 | case GL_INT_VEC3: |
| 746 | case GL_UNSIGNED_INT_VEC3: |
| 747 | desc.type = GPDT_INT3; |
| 748 | desc.elementSize = 3; |
| 749 | break; |
| 750 | case GL_INT_VEC4: |
| 751 | case GL_UNSIGNED_INT_VEC4: |
| 752 | desc.type = GPDT_INT4; |
| 753 | desc.elementSize = 4; |
| 754 | break; |
| 755 | case GL_FLOAT_MAT2: |
| 756 | desc.type = GPDT_MATRIX_2X2; |
| 757 | desc.elementSize = 4; |
| 758 | break; |
| 759 | case GL_FLOAT_MAT3: |
| 760 | desc.type = GPDT_MATRIX_3X3; |
| 761 | desc.elementSize = 9; |
| 762 | break; |
| 763 | case GL_FLOAT_MAT4: |
| 764 | desc.type = GPDT_MATRIX_4X4; |
| 765 | desc.elementSize = 16; |
| 766 | break; |
| 767 | case GL_FLOAT_MAT2x3: |
| 768 | desc.type = GPDT_MATRIX_2X3; |
| 769 | desc.elementSize = 6; |
| 770 | break; |
| 771 | case GL_FLOAT_MAT3x2: |
| 772 | desc.type = GPDT_MATRIX_3X2; |
| 773 | desc.elementSize = 6; |
| 774 | break; |
| 775 | case GL_FLOAT_MAT2x4: |
| 776 | desc.type = GPDT_MATRIX_2X4; |
| 777 | desc.elementSize = 8; |
| 778 | break; |
| 779 | case GL_FLOAT_MAT4x2: |
| 780 | desc.type = GPDT_MATRIX_4X2; |
| 781 | desc.elementSize = 8; |
| 782 | break; |
| 783 | case GL_FLOAT_MAT3x4: |
| 784 | desc.type = GPDT_MATRIX_3X4; |
| 785 | desc.elementSize = 12; |
| 786 | break; |
| 787 | case GL_FLOAT_MAT4x3: |
| 788 | desc.type = GPDT_MATRIX_4X3; |
| 789 | desc.elementSize = 12; |
| 790 | break; |
| 791 | default: |
| 792 | BS_EXCEPT(InternalErrorException, "Invalid shader parameter type: " + toString(uniformType) + " for parameter " + paramName); |
| 793 | } |
| 794 | |
| 795 | if (arraySize > 1) |
| 796 | { |
| 797 | GLint arrayStride; |
| 798 | glGetActiveUniformsiv(programHandle, 1, &uniformIndex, GL_UNIFORM_ARRAY_STRIDE, &arrayStride); |
| 799 | BS_CHECK_GL_ERROR(); |
| 800 | |
| 801 | if (arrayStride > 0) |
| 802 | { |
| 803 | assert(arrayStride % 4 == 0); |
| 804 | |
| 805 | desc.arrayElementStride = arrayStride / 4; |
| 806 | } |
| 807 | else |
| 808 | desc.arrayElementStride = desc.elementSize; |
| 809 | } |
| 810 | else |
| 811 | desc.arrayElementStride = desc.elementSize; |
| 812 | } |
| 813 | |
| 814 | UINT32 GLSLParamParser::mapParameterToSet(GpuProgramType progType, ParamType paramType) |
| 815 | { |
| 816 | UINT32 progTypeIdx = (UINT32)progType; |
| 817 | UINT32 paramTypeIdx = (UINT32)paramType; |
| 818 | |
| 819 | return progTypeIdx * (UINT32)ParamType::Count + paramTypeIdx; |
| 820 | } |
| 821 | }} |
| 822 | |