CmGLSLParamParser.h 18 KB

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