OGLShaderProgram.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. // Copyright (c) 2008-2022 the Urho3D project
  2. // License: MIT
  3. #include "../../Precompiled.h"
  4. #include "../../Graphics/Graphics.h"
  5. #include "../../GraphicsAPI/ConstantBuffer.h"
  6. #include "../../GraphicsAPI/GraphicsImpl.h"
  7. #include "../../GraphicsAPI/ShaderVariation.h"
  8. #include "../../IO/Log.h"
  9. #include "OGLShaderProgram.h"
  10. #include "../../DebugNew.h"
  11. namespace Urho3D
  12. {
  13. static const char* shaderParameterGroups[] = {
  14. "frame",
  15. "camera",
  16. "zone",
  17. "light",
  18. "material",
  19. "object",
  20. "custom"
  21. };
  22. static unsigned NumberPostfix(const String& str)
  23. {
  24. for (unsigned i = 0; i < str.Length(); ++i)
  25. {
  26. if (IsDigit(str[i]))
  27. return ToU32(str.CString() + i);
  28. }
  29. return M_MAX_UNSIGNED;
  30. }
  31. i32 ShaderProgram_OGL::globalFrameNumber = 0;
  32. const void* ShaderProgram_OGL::globalParameterSources[MAX_SHADER_PARAMETER_GROUPS];
  33. ShaderProgram_OGL::ShaderProgram_OGL(Graphics* graphics, ShaderVariation* vertexShader, ShaderVariation* pixelShader) :
  34. GPUObject(graphics),
  35. vertexShader_(vertexShader),
  36. pixelShader_(pixelShader)
  37. {
  38. for (auto& parameterSource : parameterSources_)
  39. parameterSource = (const void*)M_MAX_UNSIGNED;
  40. }
  41. ShaderProgram_OGL::~ShaderProgram_OGL()
  42. {
  43. Release();
  44. }
  45. void ShaderProgram_OGL::OnDeviceLost()
  46. {
  47. if (object_.name_ && !graphics_->IsDeviceLost())
  48. glDeleteProgram(object_.name_);
  49. GPUObject::OnDeviceLost();
  50. if (graphics_ && graphics_->GetShaderProgram_OGL() == this)
  51. graphics_->SetShaders(nullptr, nullptr);
  52. linkerOutput_.Clear();
  53. }
  54. void ShaderProgram_OGL::Release()
  55. {
  56. if (object_.name_)
  57. {
  58. if (!graphics_)
  59. return;
  60. if (!graphics_->IsDeviceLost())
  61. {
  62. if (graphics_->GetShaderProgram_OGL() == this)
  63. graphics_->SetShaders(nullptr, nullptr);
  64. glDeleteProgram(object_.name_);
  65. }
  66. object_.name_ = 0;
  67. linkerOutput_.Clear();
  68. shaderParameters_.Clear();
  69. vertexAttributes_.Clear();
  70. usedVertexAttributes_ = 0;
  71. for (bool& useTextureUnit : useTextureUnits_)
  72. useTextureUnit = false;
  73. for (unsigned i = 0; i < MAX_SHADER_PARAMETER_GROUPS; ++i)
  74. constantBuffers_[i].Reset();
  75. }
  76. }
  77. bool ShaderProgram_OGL::Link()
  78. {
  79. Release();
  80. if (!vertexShader_ || !pixelShader_ || !vertexShader_->GetGPUObjectName() || !pixelShader_->GetGPUObjectName())
  81. return false;
  82. object_.name_ = glCreateProgram();
  83. if (!object_.name_)
  84. {
  85. linkerOutput_ = "Could not create shader program";
  86. return false;
  87. }
  88. glAttachShader(object_.name_, vertexShader_->GetGPUObjectName());
  89. glAttachShader(object_.name_, pixelShader_->GetGPUObjectName());
  90. glLinkProgram(object_.name_);
  91. int linked, length;
  92. glGetProgramiv(object_.name_, GL_LINK_STATUS, &linked);
  93. if (!linked)
  94. {
  95. glGetProgramiv(object_.name_, GL_INFO_LOG_LENGTH, &length);
  96. linkerOutput_.Resize((unsigned)length);
  97. int outLength;
  98. glGetProgramInfoLog(object_.name_, length, &outLength, &linkerOutput_[0]);
  99. glDeleteProgram(object_.name_);
  100. object_.name_ = 0;
  101. }
  102. else
  103. linkerOutput_.Clear();
  104. if (!object_.name_)
  105. return false;
  106. const int MAX_NAME_LENGTH = 256;
  107. char nameBuffer[MAX_NAME_LENGTH];
  108. int attributeCount, uniformCount, elementCount, nameLength;
  109. GLenum type;
  110. glUseProgram(object_.name_);
  111. // Check for vertex attributes
  112. glGetProgramiv(object_.name_, GL_ACTIVE_ATTRIBUTES, &attributeCount);
  113. for (int i = 0; i < attributeCount; ++i)
  114. {
  115. glGetActiveAttrib(object_.name_, i, (GLsizei)MAX_NAME_LENGTH, &nameLength, &elementCount, &type, nameBuffer);
  116. String name = String(nameBuffer, nameLength);
  117. VertexElementSemantic semantic = MAX_VERTEX_ELEMENT_SEMANTICS;
  118. i8 semanticIndex = 0;
  119. // Go in reverse order so that "binormal" is detected before "normal"
  120. for (i32 j = MAX_VERTEX_ELEMENT_SEMANTICS - 1; j >= 0; --j)
  121. {
  122. if (name.Contains(ShaderVariation::elementSemanticNames_OGL[j], false))
  123. {
  124. semantic = (VertexElementSemantic)j;
  125. unsigned index = NumberPostfix(name);
  126. if (index != M_MAX_UNSIGNED)
  127. semanticIndex = (i8)index;
  128. break;
  129. }
  130. }
  131. if (semantic == MAX_VERTEX_ELEMENT_SEMANTICS)
  132. {
  133. URHO3D_LOGWARNING("Found vertex attribute " + name + " with no known semantic in shader program " +
  134. vertexShader_->GetFullName() + " " + pixelShader_->GetFullName());
  135. continue;
  136. }
  137. int location = glGetAttribLocation(object_.name_, name.CString());
  138. vertexAttributes_[{(i8)semantic, semanticIndex}] = location;
  139. usedVertexAttributes_ |= (1u << location);
  140. }
  141. // Check for constant buffers
  142. #ifndef URHO3D_GLES2
  143. HashMap<unsigned, unsigned> blockToBinding;
  144. if (Graphics::GetGL3Support())
  145. {
  146. int numUniformBlocks = 0;
  147. glGetProgramiv(object_.name_, GL_ACTIVE_UNIFORM_BLOCKS, &numUniformBlocks);
  148. for (int i = 0; i < numUniformBlocks; ++i)
  149. {
  150. glGetActiveUniformBlockName(object_.name_, (GLuint)i, MAX_NAME_LENGTH, &nameLength, nameBuffer);
  151. String name(nameBuffer, (unsigned)nameLength);
  152. unsigned blockIndex = glGetUniformBlockIndex(object_.name_, name.CString());
  153. unsigned group = M_MAX_UNSIGNED;
  154. // Try to recognize the use of the buffer from its name
  155. for (unsigned j = 0; j < MAX_SHADER_PARAMETER_GROUPS; ++j)
  156. {
  157. if (name.Contains(shaderParameterGroups[j], false))
  158. {
  159. group = j;
  160. break;
  161. }
  162. }
  163. // If name is not recognized, search for a digit in the name and use that as the group index
  164. if (group == M_MAX_UNSIGNED)
  165. group = NumberPostfix(name);
  166. if (group >= MAX_SHADER_PARAMETER_GROUPS)
  167. {
  168. URHO3D_LOGWARNING("Skipping unrecognized uniform block " + name + " in shader program " + vertexShader_->GetFullName() +
  169. " " + pixelShader_->GetFullName());
  170. continue;
  171. }
  172. // Find total constant buffer data size
  173. int dataSize;
  174. glGetActiveUniformBlockiv(object_.name_, blockIndex, GL_UNIFORM_BLOCK_DATA_SIZE, &dataSize);
  175. if (!dataSize)
  176. continue;
  177. unsigned bindingIndex = group;
  178. // Vertex shader constant buffer bindings occupy slots starting from zero to maximum supported, pixel shader bindings
  179. // from that point onward
  180. ShaderType shaderType = VS;
  181. if (name.Contains("PS", false))
  182. {
  183. bindingIndex += MAX_SHADER_PARAMETER_GROUPS;
  184. shaderType = PS;
  185. }
  186. glUniformBlockBinding(object_.name_, blockIndex, bindingIndex);
  187. blockToBinding[blockIndex] = bindingIndex;
  188. constantBuffers_[bindingIndex] = graphics_->GetOrCreateConstantBuffer(shaderType, bindingIndex, (unsigned)dataSize);
  189. }
  190. }
  191. #endif
  192. // Check for shader parameters and texture units
  193. glGetProgramiv(object_.name_, GL_ACTIVE_UNIFORMS, &uniformCount);
  194. for (int i = 0; i < uniformCount; ++i)
  195. {
  196. glGetActiveUniform(object_.name_, (GLuint)i, MAX_NAME_LENGTH, nullptr, &elementCount, &type, nameBuffer);
  197. int location = glGetUniformLocation(object_.name_, nameBuffer);
  198. // Check for array index included in the name and strip it
  199. String name(nameBuffer);
  200. i32 index = name.Find('[');
  201. if (index != String::NPOS)
  202. {
  203. // If not the first index, skip
  204. if (name.Find("[0]", index) == String::NPOS)
  205. continue;
  206. name = name.Substring(0, index);
  207. }
  208. if (name[0] == 'c')
  209. {
  210. // Store constant uniform
  211. String paramName = name.Substring(1);
  212. ShaderParameter parameter{paramName, type, location};
  213. bool store = location >= 0;
  214. #ifndef URHO3D_GLES2
  215. // If running OpenGL 3, the uniform may be inside a constant buffer
  216. if (parameter.location_ < 0 && Graphics::GetGL3Support())
  217. {
  218. int blockIndex, blockOffset;
  219. glGetActiveUniformsiv(object_.name_, 1, (const GLuint*)&i, GL_UNIFORM_BLOCK_INDEX, &blockIndex);
  220. glGetActiveUniformsiv(object_.name_, 1, (const GLuint*)&i, GL_UNIFORM_OFFSET, &blockOffset);
  221. if (blockIndex >= 0)
  222. {
  223. parameter.offset_ = blockOffset;
  224. parameter.bufferPtr_ = constantBuffers_[blockToBinding[blockIndex]];
  225. store = true;
  226. }
  227. }
  228. #endif
  229. if (store)
  230. shaderParameters_[StringHash(paramName)] = parameter;
  231. }
  232. else if (location >= 0 && name[0] == 's')
  233. {
  234. // Set the samplers here so that they do not have to be set later
  235. unsigned unit = graphics_->GetTextureUnit(name.Substring(1));
  236. if (unit >= MAX_TEXTURE_UNITS)
  237. unit = NumberPostfix(name);
  238. if (unit < MAX_TEXTURE_UNITS)
  239. {
  240. useTextureUnits_[unit] = true;
  241. glUniform1iv(location, 1, reinterpret_cast<int*>(&unit));
  242. }
  243. }
  244. }
  245. // Rehash the parameter & vertex attributes maps to ensure minimal load factor
  246. vertexAttributes_.Rehash(NextPowerOfTwo(vertexAttributes_.Size()));
  247. shaderParameters_.Rehash(NextPowerOfTwo(shaderParameters_.Size()));
  248. return true;
  249. }
  250. ShaderVariation* ShaderProgram_OGL::GetVertexShader() const
  251. {
  252. return vertexShader_;
  253. }
  254. ShaderVariation* ShaderProgram_OGL::GetPixelShader() const
  255. {
  256. return pixelShader_;
  257. }
  258. bool ShaderProgram_OGL::HasParameter(StringHash param) const
  259. {
  260. return shaderParameters_.Find(param) != shaderParameters_.End();
  261. }
  262. const ShaderParameter* ShaderProgram_OGL::GetParameter(StringHash param) const
  263. {
  264. HashMap<StringHash, ShaderParameter>::ConstIterator i = shaderParameters_.Find(param);
  265. if (i != shaderParameters_.End())
  266. return &i->second_;
  267. else
  268. return nullptr;
  269. }
  270. bool ShaderProgram_OGL::NeedParameterUpdate(ShaderParameterGroup group, const void* source)
  271. {
  272. // If global framenumber has changed, invalidate all per-program parameter sources now
  273. if (globalFrameNumber != frameNumber_)
  274. {
  275. for (auto& parameterSource : parameterSources_)
  276. parameterSource = (const void*)M_MAX_UNSIGNED;
  277. frameNumber_ = globalFrameNumber;
  278. }
  279. // The shader program may use a mixture of constant buffers and individual uniforms even in the same group
  280. #ifndef URHO3D_GLES2
  281. bool useBuffer = constantBuffers_[group].Get() || constantBuffers_[group + MAX_SHADER_PARAMETER_GROUPS].Get();
  282. bool useIndividual = !constantBuffers_[group].Get() || !constantBuffers_[group + MAX_SHADER_PARAMETER_GROUPS].Get();
  283. bool needUpdate = false;
  284. if (useBuffer && globalParameterSources[group] != source)
  285. {
  286. globalParameterSources[group] = source;
  287. needUpdate = true;
  288. }
  289. if (useIndividual && parameterSources_[group] != source)
  290. {
  291. parameterSources_[group] = source;
  292. needUpdate = true;
  293. }
  294. return needUpdate;
  295. #else
  296. if (parameterSources_[group] != source)
  297. {
  298. parameterSources_[group] = source;
  299. return true;
  300. }
  301. else
  302. return false;
  303. #endif
  304. }
  305. void ShaderProgram_OGL::ClearParameterSource(ShaderParameterGroup group)
  306. {
  307. // The shader program may use a mixture of constant buffers and individual uniforms even in the same group
  308. #ifndef URHO3D_GLES2
  309. bool useBuffer = constantBuffers_[group].Get() || constantBuffers_[group + MAX_SHADER_PARAMETER_GROUPS].Get();
  310. bool useIndividual = !constantBuffers_[group].Get() || !constantBuffers_[group + MAX_SHADER_PARAMETER_GROUPS].Get();
  311. if (useBuffer)
  312. globalParameterSources[group] = (const void*)M_MAX_UNSIGNED;
  313. if (useIndividual)
  314. parameterSources_[group] = (const void*)M_MAX_UNSIGNED;
  315. #else
  316. parameterSources_[group] = (const void*)M_MAX_UNSIGNED;
  317. #endif
  318. }
  319. void ShaderProgram_OGL::ClearParameterSources()
  320. {
  321. ++globalFrameNumber;
  322. if (!globalFrameNumber)
  323. ++globalFrameNumber;
  324. #ifndef URHO3D_GLES2
  325. for (auto& globalParameterSource : globalParameterSources)
  326. globalParameterSource = (const void*)M_MAX_UNSIGNED;
  327. #endif
  328. }
  329. void ShaderProgram_OGL::ClearGlobalParameterSource(ShaderParameterGroup group)
  330. {
  331. globalParameterSources[group] = (const void*)M_MAX_UNSIGNED;
  332. }
  333. }