BsGLSLParamParser.h 17 KB

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