BsGLSLParamParser.cpp 17 KB

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