/xbmc/guilib/Shader.cpp

http://github.com/xbmc/xbmc · C++ · 384 lines · 301 code · 54 blank · 29 comment · 33 complexity · ff66669e275c5f36005302624aaa2a92 MD5 · raw file

  1. /*
  2. * Copyright (C) 2005-2018 Team Kodi
  3. * This file is part of Kodi - https://kodi.tv
  4. *
  5. * SPDX-License-Identifier: GPL-2.0-or-later
  6. * See LICENSES/README.md for more information.
  7. */
  8. #include "Shader.h"
  9. #include "ServiceBroker.h"
  10. #include "filesystem/File.h"
  11. #include "rendering/RenderSystem.h"
  12. #include "utils/GLUtils.h"
  13. #include "utils/StringUtils.h"
  14. #include "utils/log.h"
  15. #ifdef HAS_GLES
  16. #define GLchar char
  17. #endif
  18. #define LOG_SIZE 1024
  19. using namespace Shaders;
  20. using namespace XFILE;
  21. //////////////////////////////////////////////////////////////////////
  22. // CShader
  23. //////////////////////////////////////////////////////////////////////
  24. bool CShader::LoadSource(const std::string& filename, const std::string& prefix)
  25. {
  26. if(filename.empty())
  27. return true;
  28. CFileStream file;
  29. std::string path = "special://xbmc/system/shaders/";
  30. path += CServiceBroker::GetRenderSystem()->GetShaderPath(filename);
  31. path += filename;
  32. if(!file.Open(path))
  33. {
  34. CLog::Log(LOGERROR, "CYUVShaderGLSL::CYUVShaderGLSL - failed to open file %s", filename.c_str());
  35. return false;
  36. }
  37. getline(file, m_source, '\0');
  38. size_t pos = 0;
  39. size_t versionPos = m_source.find("#version");
  40. if (versionPos != std::string::npos)
  41. {
  42. versionPos = m_source.find("\n", versionPos);
  43. if (versionPos != std::string::npos)
  44. pos = versionPos + 1;
  45. }
  46. m_source.insert(pos, prefix);
  47. m_filenames = filename;
  48. return true;
  49. }
  50. bool CShader::AppendSource(const std::string& filename)
  51. {
  52. if(filename.empty())
  53. return true;
  54. CFileStream file;
  55. std::string temp;
  56. std::string path = "special://xbmc/system/shaders/";
  57. path += CServiceBroker::GetRenderSystem()->GetShaderPath(filename);
  58. path += filename;
  59. if(!file.Open(path))
  60. {
  61. CLog::Log(LOGERROR, "CShader::AppendSource - failed to open file %s", filename.c_str());
  62. return false;
  63. }
  64. getline(file, temp, '\0');
  65. m_source.append(temp);
  66. m_filenames.append(" " + filename);
  67. return true;
  68. }
  69. bool CShader::InsertSource(const std::string& filename, const std::string& loc)
  70. {
  71. if(filename.empty())
  72. return true;
  73. CFileStream file;
  74. std::string temp;
  75. std::string path = "special://xbmc/system/shaders/";
  76. path += CServiceBroker::GetRenderSystem()->GetShaderPath(filename);
  77. path += filename;
  78. if(!file.Open(path))
  79. {
  80. CLog::Log(LOGERROR, "CShader::InsertSource - failed to open file %s", filename.c_str());
  81. return false;
  82. }
  83. getline(file, temp, '\0');
  84. size_t locPos = m_source.find(loc);
  85. if (locPos == std::string::npos)
  86. {
  87. CLog::Log(LOGERROR, "CShader::InsertSource - could not find location %s", loc.c_str());
  88. return false;
  89. }
  90. m_source.insert(locPos, temp);
  91. m_filenames.append(" " + filename);
  92. return true;
  93. }
  94. std::string CShader::GetSourceWithLineNumbers() const
  95. {
  96. int i{1};
  97. auto lines = StringUtils::Split(m_source, "\n");
  98. for (auto& line : lines)
  99. {
  100. line.insert(0, StringUtils::Format("%3d: ", i));
  101. i++;
  102. }
  103. auto output = StringUtils::Join(lines, "\n");
  104. return output;
  105. }
  106. //////////////////////////////////////////////////////////////////////
  107. // CGLSLVertexShader
  108. //////////////////////////////////////////////////////////////////////
  109. bool CGLSLVertexShader::Compile()
  110. {
  111. GLint params[4];
  112. Free();
  113. m_vertexShader = glCreateShader(GL_VERTEX_SHADER);
  114. const char *ptr = m_source.c_str();
  115. glShaderSource(m_vertexShader, 1, &ptr, 0);
  116. glCompileShader(m_vertexShader);
  117. glGetShaderiv(m_vertexShader, GL_COMPILE_STATUS, params);
  118. VerifyGLState();
  119. if (params[0] != GL_TRUE)
  120. {
  121. GLchar log[LOG_SIZE];
  122. CLog::Log(LOGERROR, "GL: Error compiling vertex shader");
  123. glGetShaderInfoLog(m_vertexShader, LOG_SIZE, NULL, log);
  124. CLog::Log(LOGERROR, "%s", log);
  125. m_lastLog = log;
  126. m_compiled = false;
  127. }
  128. else
  129. {
  130. GLchar log[LOG_SIZE];
  131. GLsizei length;
  132. glGetShaderInfoLog(m_vertexShader, LOG_SIZE, &length, log);
  133. if (length > 0)
  134. {
  135. CLog::Log(LOGDEBUG, "GL: Vertex Shader compilation log:");
  136. CLog::Log(LOGDEBUG, "%s", log);
  137. }
  138. m_lastLog = log;
  139. m_compiled = true;
  140. }
  141. return m_compiled;
  142. }
  143. void CGLSLVertexShader::Free()
  144. {
  145. if (m_vertexShader)
  146. glDeleteShader(m_vertexShader);
  147. m_vertexShader = 0;
  148. }
  149. //////////////////////////////////////////////////////////////////////
  150. // CGLSLPixelShader
  151. //////////////////////////////////////////////////////////////////////
  152. bool CGLSLPixelShader::Compile()
  153. {
  154. GLint params[4];
  155. Free();
  156. // Pixel shaders are not mandatory.
  157. if (m_source.length()==0)
  158. {
  159. CLog::Log(LOGINFO, "GL: No pixel shader, fixed pipeline in use");
  160. return true;
  161. }
  162. m_pixelShader = glCreateShader(GL_FRAGMENT_SHADER);
  163. const char *ptr = m_source.c_str();
  164. glShaderSource(m_pixelShader, 1, &ptr, 0);
  165. glCompileShader(m_pixelShader);
  166. glGetShaderiv(m_pixelShader, GL_COMPILE_STATUS, params);
  167. if (params[0] != GL_TRUE)
  168. {
  169. GLchar log[LOG_SIZE];
  170. CLog::Log(LOGERROR, "GL: Error compiling pixel shader");
  171. glGetShaderInfoLog(m_pixelShader, LOG_SIZE, NULL, log);
  172. CLog::Log(LOGERROR, "%s", log);
  173. m_lastLog = log;
  174. m_compiled = false;
  175. }
  176. else
  177. {
  178. GLchar log[LOG_SIZE];
  179. GLsizei length;
  180. glGetShaderInfoLog(m_pixelShader, LOG_SIZE, &length, log);
  181. if (length > 0)
  182. {
  183. CLog::Log(LOGDEBUG, "GL: Pixel Shader compilation log:");
  184. CLog::Log(LOGDEBUG, "%s", log);
  185. }
  186. m_lastLog = log;
  187. m_compiled = true;
  188. }
  189. return m_compiled;
  190. }
  191. void CGLSLPixelShader::Free()
  192. {
  193. if (m_pixelShader)
  194. glDeleteShader(m_pixelShader);
  195. m_pixelShader = 0;
  196. }
  197. //////////////////////////////////////////////////////////////////////
  198. // CGLSLShaderProgram
  199. //////////////////////////////////////////////////////////////////////
  200. CGLSLShaderProgram::CGLSLShaderProgram()
  201. {
  202. m_pFP = new CGLSLPixelShader();
  203. m_pVP = new CGLSLVertexShader();
  204. }
  205. CGLSLShaderProgram::CGLSLShaderProgram(const std::string& vert,
  206. const std::string& frag)
  207. {
  208. m_pFP = new CGLSLPixelShader();
  209. m_pFP->LoadSource(frag);
  210. m_pVP = new CGLSLVertexShader();
  211. m_pVP->LoadSource(vert);
  212. }
  213. CGLSLShaderProgram::~CGLSLShaderProgram()
  214. {
  215. Free();
  216. }
  217. void CGLSLShaderProgram::Free()
  218. {
  219. m_pVP->Free();
  220. VerifyGLState();
  221. m_pFP->Free();
  222. VerifyGLState();
  223. if (m_shaderProgram)
  224. {
  225. glDeleteProgram(m_shaderProgram);
  226. }
  227. m_shaderProgram = 0;
  228. m_ok = false;
  229. m_lastProgram = 0;
  230. }
  231. bool CGLSLShaderProgram::CompileAndLink()
  232. {
  233. GLint params[4];
  234. // free resources
  235. Free();
  236. // compiled vertex shader
  237. if (!m_pVP->Compile())
  238. {
  239. CLog::Log(LOGERROR, "GL: Error compiling vertex shader: {}", m_pVP->GetName());
  240. CLog::Log(LOGDEBUG, "GL: vertex shader source:\n{}", m_pVP->GetSourceWithLineNumbers());
  241. return false;
  242. }
  243. // compile pixel shader
  244. if (!m_pFP->Compile())
  245. {
  246. m_pVP->Free();
  247. CLog::Log(LOGERROR, "GL: Error compiling fragment shader: {}", m_pFP->GetName());
  248. CLog::Log(LOGDEBUG, "GL: fragment shader source:\n{}", m_pFP->GetSourceWithLineNumbers());
  249. return false;
  250. }
  251. // create program object
  252. if (!(m_shaderProgram = glCreateProgram()))
  253. {
  254. CLog::Log(LOGERROR, "GL: Error creating shader program handle");
  255. goto error;
  256. }
  257. // attach the vertex shader
  258. glAttachShader(m_shaderProgram, m_pVP->Handle());
  259. VerifyGLState();
  260. // if we have a pixel shader, attach it. If not, fixed pipeline
  261. // will be used.
  262. if (m_pFP->Handle())
  263. {
  264. glAttachShader(m_shaderProgram, m_pFP->Handle());
  265. VerifyGLState();
  266. }
  267. // link the program
  268. glLinkProgram(m_shaderProgram);
  269. glGetProgramiv(m_shaderProgram, GL_LINK_STATUS, params);
  270. if (params[0]!=GL_TRUE)
  271. {
  272. GLchar log[LOG_SIZE];
  273. CLog::Log(LOGERROR, "GL: Error linking shader");
  274. glGetProgramInfoLog(m_shaderProgram, LOG_SIZE, NULL, log);
  275. CLog::Log(LOGERROR, "%s", log);
  276. goto error;
  277. }
  278. VerifyGLState();
  279. m_validated = false;
  280. m_ok = true;
  281. OnCompiledAndLinked();
  282. VerifyGLState();
  283. return true;
  284. error:
  285. m_ok = false;
  286. Free();
  287. return false;
  288. }
  289. bool CGLSLShaderProgram::Enable()
  290. {
  291. if (OK())
  292. {
  293. glUseProgram(m_shaderProgram);
  294. if (OnEnabled())
  295. {
  296. if (!m_validated)
  297. {
  298. // validate the program
  299. GLint params[4];
  300. glValidateProgram(m_shaderProgram);
  301. glGetProgramiv(m_shaderProgram, GL_VALIDATE_STATUS, params);
  302. if (params[0]!=GL_TRUE)
  303. {
  304. GLchar log[LOG_SIZE];
  305. CLog::Log(LOGERROR, "GL: Error validating shader");
  306. glGetProgramInfoLog(m_shaderProgram, LOG_SIZE, NULL, log);
  307. CLog::Log(LOGERROR, "%s", log);
  308. }
  309. m_validated = true;
  310. }
  311. VerifyGLState();
  312. return true;
  313. }
  314. else
  315. {
  316. glUseProgram(0);
  317. return false;
  318. }
  319. return true;
  320. }
  321. return false;
  322. }
  323. void CGLSLShaderProgram::Disable()
  324. {
  325. if (OK())
  326. {
  327. glUseProgram(0);
  328. OnDisabled();
  329. }
  330. }