BsGLSLParamParser.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  1. #include "BsGLSLParamParser.h"
  2. namespace BansheeEngine
  3. {
  4. INT32 GLSLAttribute::matchesName(const String& name)
  5. {
  6. if (name.length() >= mName.length())
  7. {
  8. if (name.substr(0, mName.length()) == mName)
  9. {
  10. String indexStr = name.substr(mName.length(), name.length());
  11. return parseUnsignedInt(indexStr, 0);
  12. }
  13. }
  14. return -1;
  15. }
  16. List<VertexElement> GLSLParamParser::buildVertexDeclaration(GLuint glProgram)
  17. {
  18. GLint numAttributes = 0;
  19. glGetProgramiv(glProgram, GL_ACTIVE_ATTRIBUTES, &numAttributes);
  20. GLint maxNameSize = 0;
  21. glGetProgramiv(glProgram, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &maxNameSize);
  22. GLchar* attributeName = (GLchar*)bs_alloc<ScratchAlloc>(sizeof(GLchar)* maxNameSize);
  23. List<VertexElement> elementList;
  24. for (GLint i = 0; i < numAttributes; i++)
  25. {
  26. GLint attribSize = 0;
  27. GLenum attribType = 0;
  28. glGetActiveAttrib(glProgram, i, maxNameSize, nullptr, &attribSize, &attribType, attributeName);
  29. VertexElementSemantic semantic = VES_POSITION;
  30. UINT16 index = 0;
  31. if (attribNameToElementSemantic(attributeName, semantic, index))
  32. {
  33. VertexElementType type = glTypeToAttributeType(attribType);
  34. elementList.push_back(VertexElement(0, i, type, semantic, index));
  35. }
  36. else
  37. {
  38. LOGWRN("Cannot determine vertex input attribute type for attribute: " + String(attributeName));
  39. }
  40. }
  41. bs_free<ScratchAlloc>(attributeName);
  42. return elementList;
  43. }
  44. VertexElementType GLSLParamParser::glTypeToAttributeType(GLenum glType)
  45. {
  46. switch (glType)
  47. {
  48. case GL_FLOAT:
  49. return VET_FLOAT1;
  50. case GL_FLOAT_VEC2:
  51. return VET_FLOAT2;
  52. case GL_FLOAT_VEC3:
  53. return VET_FLOAT3;
  54. case GL_FLOAT_VEC4:
  55. return VET_FLOAT4;
  56. default:
  57. BS_EXCEPT(NotImplementedException, "OpenGL render system currently only supports float parameters.");
  58. }
  59. }
  60. bool GLSLParamParser::attribNameToElementSemantic(const String& name, VertexElementSemantic& semantic, UINT16& index)
  61. {
  62. static GLSLAttribute attributes[] =
  63. {
  64. GLSLAttribute("bs_position", VES_POSITION),
  65. GLSLAttribute("bs_normal", VES_NORMAL),
  66. GLSLAttribute("bs_tangent", VES_TANGENT),
  67. GLSLAttribute("bs_bitangent", VES_BITANGENT),
  68. GLSLAttribute("bs_texcoord", VES_TEXCOORD),
  69. GLSLAttribute("bs_color", VES_COLOR),
  70. GLSLAttribute("bs_blendweights", VES_BLEND_WEIGHTS),
  71. GLSLAttribute("bs_blendindices", VES_BLEND_INDICES)
  72. };
  73. static const UINT32 numAttribs = sizeof(attributes) / sizeof(attributes[0]);
  74. for (UINT32 i = 0; i < numAttribs; i++)
  75. {
  76. INT32 attribIndex = attributes[i].matchesName(name);
  77. if (attribIndex != -1)
  78. {
  79. index = attribIndex;
  80. semantic = attributes[i].getSemantic();
  81. return true;
  82. }
  83. }
  84. return false;
  85. }
  86. void GLSLParamParser::buildUniformDescriptions(GLuint glProgram, GpuParamDesc& returnParamDesc)
  87. {
  88. // scan through the active uniforms and add them to the reference list
  89. GLint maxBufferSize = 0;
  90. glGetProgramiv(glProgram, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxBufferSize);
  91. GLint maxBlockNameBufferSize = 0;
  92. glGetProgramiv(glProgram, GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, &maxBlockNameBufferSize);
  93. if (maxBlockNameBufferSize > maxBufferSize)
  94. maxBufferSize = maxBlockNameBufferSize;
  95. GLchar* uniformName = (GLchar*)bs_alloc<ScratchAlloc>(sizeof(GLchar)* maxBufferSize);
  96. GpuParamBlockDesc newGlobalBlockDesc;
  97. newGlobalBlockDesc.slot = 0;
  98. newGlobalBlockDesc.name = "BS_INTERNAL_Globals";
  99. newGlobalBlockDesc.blockSize = 0;
  100. newGlobalBlockDesc.isShareable = false;
  101. UINT32 textureSlot = 0;
  102. returnParamDesc.paramBlocks[newGlobalBlockDesc.name] = newGlobalBlockDesc;
  103. GpuParamBlockDesc& globalBlockDesc = returnParamDesc.paramBlocks[newGlobalBlockDesc.name];
  104. GLint uniformBlockCount = 0;
  105. glGetProgramiv(glProgram, GL_ACTIVE_UNIFORM_BLOCKS, &uniformBlockCount);
  106. Map<UINT32, String> blockSlotToName;
  107. Set<String> blockNames;
  108. for (GLuint index = 0; index < (GLuint)uniformBlockCount; index++)
  109. {
  110. GLsizei unusedSize = 0;
  111. glGetActiveUniformBlockName(glProgram, index, maxBufferSize, &unusedSize, uniformName);
  112. GpuParamBlockDesc newBlockDesc;
  113. newBlockDesc.slot = index + 1;
  114. newBlockDesc.name = uniformName;
  115. newBlockDesc.blockSize = 0;
  116. newBlockDesc.isShareable = true;
  117. returnParamDesc.paramBlocks[newBlockDesc.name] = newBlockDesc;
  118. blockSlotToName.insert(std::make_pair(newBlockDesc.slot, newBlockDesc.name));
  119. blockNames.insert(newBlockDesc.name);
  120. }
  121. Map<String, UINT32> foundFirstArrayIndex;
  122. Map<String, GpuParamDataDesc> foundStructs;
  123. // get the number of active uniforms
  124. GLint uniformCount = 0;
  125. glGetProgramiv(glProgram, GL_ACTIVE_UNIFORMS, &uniformCount);
  126. // Loop over each of the active uniforms, and add them to the reference container
  127. // only do this for user defined uniforms, ignore built in gl state uniforms
  128. for (GLuint index = 0; index < (GLuint)uniformCount; index++)
  129. {
  130. GLsizei arraySize = 0;
  131. glGetActiveUniformName(glProgram, index, maxBufferSize, &arraySize, uniformName);
  132. String paramName = String(uniformName);
  133. // Naming rules and packing rules used here are described in
  134. // OpenGL Core Specification 2.11.4
  135. // Check if parameter is a part of a struct
  136. Vector<String> nameElements = StringUtil::tokenise(paramName, ".");
  137. bool inStruct = false;
  138. String structName;
  139. if (nameElements.size() > 1)
  140. {
  141. auto uniformBlockFind = blockNames.find(nameElements[0]);
  142. // Check if the name is not a struct, and instead a Uniform block namespace
  143. if (uniformBlockFind != blockNames.end())
  144. {
  145. // Possibly it's a struct inside a named uniform block
  146. if (nameElements.size() > 2)
  147. {
  148. inStruct = true;
  149. structName = nameElements[1];
  150. }
  151. }
  152. else
  153. {
  154. inStruct = true;
  155. structName = nameElements[0];
  156. }
  157. }
  158. String cleanParamName = paramName; // Param name without array indexes
  159. // Check if the parameter is in an array
  160. UINT32 arrayIdx = 0;
  161. bool isInArray = false;
  162. if (inStruct)
  163. {
  164. // If the uniform name has a "[" in it then its an array element uniform.
  165. String::size_type arrayStart = structName.find("[");
  166. String::size_type arrayEnd = structName.find("]");
  167. if (arrayStart != String::npos)
  168. {
  169. String strArrIdx = structName.substr(arrayStart + 1, arrayEnd - (arrayStart + 1));
  170. arrayIdx = parseUnsignedInt(strArrIdx, 0);
  171. isInArray = true;
  172. structName = structName.substr(0, arrayStart);
  173. }
  174. }
  175. else
  176. {
  177. // If the uniform name has a "[" in it then its an array element uniform.
  178. String::size_type arrayStart = cleanParamName.find("[");
  179. String::size_type arrayEnd = cleanParamName.find("]");
  180. if (arrayStart != String::npos)
  181. {
  182. String strArrIdx = cleanParamName.substr(arrayStart + 1, arrayEnd - (arrayStart + 1));
  183. arrayIdx = parseUnsignedInt(strArrIdx, 0);
  184. isInArray = true;
  185. cleanParamName = cleanParamName.substr(0, arrayStart);
  186. }
  187. }
  188. if (inStruct)
  189. {
  190. // OpenGL makes struct management really difficult, which is why I have given up on implementing this so far
  191. // Some of the issues I encountered:
  192. // - Elements will be optimized out if they are not used. This makes it hard to determine proper structure size.
  193. // - If struct is within a Uniform buffer block, then it is possible because the element won't be optimized out of the buffer
  194. // - If the struct is within a global buffer, it is impossible to determine actual size, since the element will be optimized out of the buffer too
  195. // - Same issue happens with arrays, as OpenGL will optimize out array elements. With global buffers this makes it impossible to determine
  196. // actual array size (e.g. suppose OpenGL optimized out few last elements)
  197. // - Normal arrays work fine as OpenGL has utilities for reporting their actual size, but those do not work with structs
  198. BS_EXCEPT(NotImplementedException, "Structs are not supported.")
  199. }
  200. // GLSL will optimize out unused array indexes, so there's no guarantee that 0 is the first,
  201. // so we store the first one here
  202. int firstArrayIndex = 0;
  203. if (isInArray)
  204. {
  205. String& nameToSearch = cleanParamName;
  206. if (inStruct)
  207. nameToSearch = structName;
  208. auto arrayIndexFind = foundFirstArrayIndex.find(nameToSearch);
  209. if (arrayIndexFind == foundFirstArrayIndex.end())
  210. {
  211. foundFirstArrayIndex[nameToSearch] = arrayIdx;
  212. }
  213. firstArrayIndex = foundFirstArrayIndex[nameToSearch];
  214. }
  215. GLint uniformType;
  216. glGetActiveUniformsiv(glProgram, 1, &index, GL_UNIFORM_TYPE, &uniformType);
  217. bool isSampler = false;
  218. bool isImage = false;
  219. switch (uniformType)
  220. {
  221. case GL_SAMPLER_1D:
  222. case GL_SAMPLER_2D:
  223. case GL_SAMPLER_3D:
  224. case GL_SAMPLER_2D_MULTISAMPLE:
  225. case GL_SAMPLER_CUBE:
  226. isSampler = true;
  227. break;
  228. case GL_IMAGE_1D:
  229. case GL_IMAGE_2D:
  230. case GL_IMAGE_3D:
  231. case GL_IMAGE_CUBE:
  232. case GL_IMAGE_2D_MULTISAMPLE:
  233. isImage = true;
  234. break;
  235. }
  236. if (isSampler)
  237. {
  238. GpuParamObjectDesc samplerParam;
  239. samplerParam.name = paramName;
  240. samplerParam.slot = glGetUniformLocation(glProgram, uniformName);
  241. GpuParamObjectDesc textureParam;
  242. textureParam.name = paramName;
  243. textureParam.slot = samplerParam.slot;
  244. switch (uniformType)
  245. {
  246. case GL_SAMPLER_1D:
  247. samplerParam.type = GPOT_SAMPLER1D;
  248. textureParam.type = GPOT_TEXTURE1D;
  249. break;
  250. case GL_SAMPLER_2D:
  251. samplerParam.type = GPOT_SAMPLER2D;
  252. textureParam.type = GPOT_TEXTURE2D;
  253. break;
  254. case GL_SAMPLER_3D:
  255. samplerParam.type = GPOT_SAMPLER3D;
  256. textureParam.type = GPOT_TEXTURE3D;
  257. break;
  258. case GL_SAMPLER_CUBE:
  259. samplerParam.type = GPOT_SAMPLERCUBE;
  260. textureParam.type = GPOT_TEXTURECUBE;
  261. break;
  262. case GL_SAMPLER_2D_MULTISAMPLE:
  263. samplerParam.type = GPOT_SAMPLER2DMS;
  264. textureParam.type = GPOT_TEXTURE2DMS;
  265. break;
  266. }
  267. returnParamDesc.samplers.insert(std::make_pair(paramName, samplerParam));
  268. returnParamDesc.textures.insert(std::make_pair(paramName, textureParam));
  269. }
  270. else if (isImage)
  271. {
  272. GpuParamObjectDesc textureParam;
  273. textureParam.name = paramName;
  274. textureParam.slot = glGetUniformLocation(glProgram, uniformName);
  275. switch (uniformType)
  276. {
  277. case GL_IMAGE_1D:
  278. textureParam.type = GPOT_TEXTURE1D;
  279. break;
  280. case GL_IMAGE_2D:
  281. textureParam.type = GPOT_TEXTURE2D;
  282. break;
  283. case GL_IMAGE_3D:
  284. textureParam.type = GPOT_TEXTURE3D;
  285. break;
  286. }
  287. returnParamDesc.textures.insert(std::make_pair(paramName, textureParam));
  288. }
  289. else
  290. {
  291. // If array index is larger than 0 and uniform is not a part of a struct,
  292. // it means we already processed it (struct arrays are processed differently)
  293. if (!inStruct && arrayIdx != 0)
  294. continue;
  295. GLint blockIndex;
  296. glGetActiveUniformsiv(glProgram, 1, &index, GL_UNIFORM_BLOCK_INDEX, &blockIndex);
  297. GpuParamDataDesc gpuParam;
  298. if (isInArray)
  299. gpuParam.name = cleanParamName;
  300. else
  301. gpuParam.name = paramName;
  302. determineParamInfo(gpuParam, paramName, glProgram, index);
  303. if (blockIndex != -1)
  304. {
  305. GLint blockOffset;
  306. glGetActiveUniformsiv(glProgram, 1, &index, GL_UNIFORM_OFFSET, &blockOffset);
  307. blockOffset = blockOffset / 4;
  308. gpuParam.gpuMemOffset = blockOffset;
  309. gpuParam.paramBlockSlot = blockIndex + 1; // 0 is reserved for globals
  310. String& blockName = blockSlotToName[gpuParam.paramBlockSlot];
  311. GpuParamBlockDesc& curBlockDesc = returnParamDesc.paramBlocks[blockName];
  312. gpuParam.cpuMemOffset = blockOffset;
  313. curBlockDesc.blockSize = std::max(curBlockDesc.blockSize, gpuParam.cpuMemOffset + gpuParam.arrayElementStride * gpuParam.arraySize);
  314. }
  315. else
  316. {
  317. gpuParam.gpuMemOffset = glGetUniformLocation(glProgram, uniformName);
  318. gpuParam.paramBlockSlot = 0;
  319. gpuParam.cpuMemOffset = globalBlockDesc.blockSize;
  320. globalBlockDesc.blockSize = std::max(globalBlockDesc.blockSize, gpuParam.cpuMemOffset + gpuParam.arrayElementStride * gpuParam.arraySize);
  321. }
  322. // If parameter is not a part of a struct we're done
  323. if (!inStruct)
  324. {
  325. returnParamDesc.params.insert(std::make_pair(gpuParam.name, gpuParam));
  326. continue;
  327. }
  328. // If the parameter is part of a struct, then we need to update the struct definition
  329. auto findExistingStruct = foundStructs.find(structName);
  330. // Create new definition if one doesn't exist
  331. if (findExistingStruct == foundStructs.end())
  332. {
  333. foundStructs[structName] = GpuParamDataDesc();
  334. GpuParamDataDesc& structDesc = foundStructs[structName];
  335. structDesc.type = GPDT_STRUCT;
  336. structDesc.name = structName;
  337. structDesc.arraySize = 1;
  338. structDesc.elementSize = 0;
  339. structDesc.arrayElementStride = 0;
  340. structDesc.gpuMemOffset = gpuParam.gpuMemOffset;
  341. structDesc.cpuMemOffset = gpuParam.cpuMemOffset;
  342. structDesc.paramBlockSlot = gpuParam.paramBlockSlot;
  343. }
  344. // Update struct with size of the new parameter
  345. GpuParamDataDesc& structDesc = foundStructs[structName];
  346. assert(gpuParam.cpuMemOffset >= structDesc.cpuMemOffset);
  347. if (arrayIdx == firstArrayIndex) // Determine element size only using the first array element
  348. {
  349. structDesc.elementSize = std::max(structDesc.elementSize, (gpuParam.cpuMemOffset - structDesc.cpuMemOffset) + gpuParam.arrayElementStride * gpuParam.arraySize);
  350. structDesc.arrayElementStride = structDesc.elementSize;
  351. }
  352. // New array element reached, determine arrayElementStride
  353. if (arrayIdx != firstArrayIndex)
  354. {
  355. UINT32 numElements = arrayIdx - firstArrayIndex;
  356. structDesc.arrayElementStride = (gpuParam.cpuMemOffset - structDesc.cpuMemOffset) / numElements;
  357. }
  358. structDesc.arraySize = std::max(structDesc.arraySize, arrayIdx + 1);
  359. }
  360. }
  361. for (auto iter = foundStructs.begin(); iter != foundStructs.end(); ++iter)
  362. returnParamDesc.params.insert(std::make_pair(iter->first, iter->second));
  363. // Param blocks alway needs to be a multiple of 4, so make it so
  364. for (auto iter = returnParamDesc.paramBlocks.begin(); iter != returnParamDesc.paramBlocks.end(); ++iter)
  365. {
  366. GpuParamBlockDesc& blockDesc = iter->second;
  367. if (blockDesc.blockSize % 4 != 0)
  368. blockDesc.blockSize += (4 - (blockDesc.blockSize % 4));
  369. }
  370. #if BS_DEBUG_MODE
  371. // Check if manually calculated and OpenGL buffer sizes match
  372. for (auto iter = returnParamDesc.paramBlocks.begin(); iter != returnParamDesc.paramBlocks.end(); ++iter)
  373. {
  374. if (iter->second.slot == 0)
  375. continue;
  376. GLint blockSize = 0;
  377. glGetActiveUniformBlockiv(glProgram, iter->second.slot - 1, GL_UNIFORM_BLOCK_DATA_SIZE, &blockSize);
  378. assert(blockSize % 4 == 0);
  379. blockSize = blockSize / 4;
  380. if (iter->second.blockSize != blockSize)
  381. BS_EXCEPT(InternalErrorException, "OpenGL specified and manual uniform block buffer sizes don't match!");
  382. }
  383. #endif
  384. bs_free<ScratchAlloc>(uniformName);
  385. }
  386. void GLSLParamParser::determineParamInfo(GpuParamDataDesc& desc, const String& paramName, GLuint programHandle, GLuint uniformIndex)
  387. {
  388. GLint arraySize;
  389. glGetActiveUniformsiv(programHandle, 1, &uniformIndex, GL_UNIFORM_SIZE, &arraySize);
  390. desc.arraySize = arraySize;
  391. GLint uniformType;
  392. glGetActiveUniformsiv(programHandle, 1, &uniformIndex, GL_UNIFORM_TYPE, &uniformType);
  393. switch (uniformType)
  394. {
  395. case GL_BOOL:
  396. desc.type = GPDT_BOOL;
  397. desc.elementSize = 1;
  398. break;
  399. case GL_FLOAT:
  400. desc.type = GPDT_FLOAT1;
  401. desc.elementSize = 1;
  402. break;
  403. case GL_FLOAT_VEC2:
  404. desc.type = GPDT_FLOAT2;
  405. desc.elementSize = 2;
  406. break;
  407. case GL_FLOAT_VEC3:
  408. desc.type = GPDT_FLOAT3;
  409. desc.elementSize = 3;
  410. break;
  411. case GL_FLOAT_VEC4:
  412. desc.type = GPDT_FLOAT4;
  413. desc.elementSize = 4;
  414. break;
  415. case GL_INT:
  416. desc.type = GPDT_INT1;
  417. desc.elementSize = 1;
  418. break;
  419. case GL_INT_VEC2:
  420. desc.type = GPDT_INT2;
  421. desc.elementSize = 2;
  422. break;
  423. case GL_INT_VEC3:
  424. desc.type = GPDT_INT3;
  425. desc.elementSize = 3;
  426. break;
  427. case GL_INT_VEC4:
  428. desc.type = GPDT_INT4;
  429. desc.elementSize = 4;
  430. break;
  431. case GL_FLOAT_MAT2:
  432. desc.type = GPDT_MATRIX_2X2;
  433. desc.elementSize = 4;
  434. break;
  435. case GL_FLOAT_MAT3:
  436. desc.type = GPDT_MATRIX_3X3;
  437. desc.elementSize = 9;
  438. break;
  439. case GL_FLOAT_MAT4:
  440. desc.type = GPDT_MATRIX_4X4;
  441. desc.elementSize = 16;
  442. break;
  443. case GL_FLOAT_MAT2x3:
  444. desc.type = GPDT_MATRIX_2X3;
  445. desc.elementSize = 6;
  446. break;
  447. case GL_FLOAT_MAT3x2:
  448. desc.type = GPDT_MATRIX_3X2;
  449. desc.elementSize = 6;
  450. break;
  451. case GL_FLOAT_MAT2x4:
  452. desc.type = GPDT_MATRIX_2X4;
  453. desc.elementSize = 8;
  454. break;
  455. case GL_FLOAT_MAT4x2:
  456. desc.type = GPDT_MATRIX_4X2;
  457. desc.elementSize = 8;
  458. break;
  459. case GL_FLOAT_MAT3x4:
  460. desc.type = GPDT_MATRIX_3X4;
  461. desc.elementSize = 12;
  462. break;
  463. case GL_FLOAT_MAT4x3:
  464. desc.type = GPDT_MATRIX_4X3;
  465. desc.elementSize = 12;
  466. break;
  467. default:
  468. BS_EXCEPT(InternalErrorException, "Invalid shader parameter type: " + toString(uniformType) + " for parameter " + paramName);
  469. }
  470. if (arraySize > 1)
  471. {
  472. GLint arrayStride;
  473. glGetActiveUniformsiv(programHandle, 1, &uniformIndex, GL_UNIFORM_ARRAY_STRIDE, &arrayStride);
  474. if (arrayStride > 0)
  475. {
  476. assert(arrayStride % 4 == 0);
  477. desc.arrayElementStride = arrayStride / 4;
  478. }
  479. else
  480. desc.arrayElementStride = desc.elementSize;
  481. }
  482. else
  483. desc.arrayElementStride = desc.elementSize;
  484. }
  485. }