xbmc
Shader.h
1 /*
2  * Copyright (C) 2005-2019 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 
9 #pragma once
10 
11 #include "GL.h"
12 
13 #ifdef __cplusplus
14 
15 #include <stdio.h>
16 #include <string>
17 #include <vector>
18 
19 #include <kodi/AddonBase.h>
20 #include <kodi/Filesystem.h>
21 
22 #define LOG_SIZE 1024
23 #define GLchar char
24 
25 namespace kodi
26 {
27 namespace gui
28 {
29 namespace gl
30 {
31 
32 //========================================================================
34 class ATTR_DLL_LOCAL CShader
35 {
36 public:
37  CShader() = default;
38  virtual ~CShader() = default;
39  virtual bool Compile(const std::string& extraBegin = "", const std::string& extraEnd = "") = 0;
40  virtual void Free() = 0;
41  virtual GLuint Handle() = 0;
42 
43  bool LoadSource(const std::string& file)
44  {
45  char buffer[16384];
46 
47  kodi::vfs::CFile source;
48  if (!source.OpenFile(file))
49  {
50  kodi::Log(ADDON_LOG_ERROR, "CShader::%s: Failed to open file '%s'", __FUNCTION__,
51  file.c_str());
52  return false;
53  }
54  size_t len = source.Read(buffer, sizeof(buffer));
55  m_source.assign(buffer);
56  m_source[len] = 0;
57  source.Close();
58  return true;
59  }
60 
61  bool OK() const { return m_compiled; }
62 
63 protected:
64  std::string m_source;
65  std::string m_lastLog;
66  bool m_compiled = false;
67 };
68 //------------------------------------------------------------------------
69 
70 //========================================================================
72 class ATTR_DLL_LOCAL CVertexShader : public CShader
73 {
74 public:
75  CVertexShader() = default;
76  ~CVertexShader() override { Free(); }
77 
78  void Free() override
79  {
80  if (m_vertexShader)
81  glDeleteShader(m_vertexShader);
82  m_vertexShader = 0;
83  }
84 
85  bool Compile(const std::string& extraBegin = "", const std::string& extraEnd = "") override
86  {
87  GLint params[4];
88 
89  Free();
90 
91  m_vertexShader = glCreateShader(GL_VERTEX_SHADER);
92 
93  GLsizei count = 0;
94  const char* sources[3];
95  if (!extraBegin.empty())
96  sources[count++] = extraBegin.c_str();
97  if (!m_source.empty())
98  sources[count++] = m_source.c_str();
99  if (!extraEnd.empty())
100  sources[count++] = extraEnd.c_str();
101 
102  glShaderSource(m_vertexShader, count, sources, nullptr);
103  glCompileShader(m_vertexShader);
104  glGetShaderiv(m_vertexShader, GL_COMPILE_STATUS, params);
105  if (params[0] != GL_TRUE)
106  {
107  GLchar log[LOG_SIZE];
108  glGetShaderInfoLog(m_vertexShader, LOG_SIZE, nullptr, log);
109  kodi::Log(ADDON_LOG_ERROR, "CVertexShader::%s: %s", __FUNCTION__, log);
110  fprintf(stderr, "CVertexShader::%s: %s\n", __FUNCTION__, log);
111  m_lastLog = log;
112  m_compiled = false;
113  }
114  else
115  {
116  GLchar log[LOG_SIZE];
117  glGetShaderInfoLog(m_vertexShader, LOG_SIZE, nullptr, log);
118  m_lastLog = log;
119  m_compiled = true;
120  }
121  return m_compiled;
122  }
123 
124  GLuint Handle() override { return m_vertexShader; }
125 
126 protected:
127  GLuint m_vertexShader = 0;
128 };
129 //------------------------------------------------------------------------
130 
131 //========================================================================
133 class ATTR_DLL_LOCAL CPixelShader : public CShader
134 {
135 public:
136  CPixelShader() = default;
137  ~CPixelShader() { Free(); }
138  void Free() override
139  {
140  if (m_pixelShader)
141  glDeleteShader(m_pixelShader);
142  m_pixelShader = 0;
143  }
144 
145  bool Compile(const std::string& extraBegin = "", const std::string& extraEnd = "") override
146  {
147  GLint params[4];
148 
149  Free();
150 
151  m_pixelShader = glCreateShader(GL_FRAGMENT_SHADER);
152 
153  GLsizei count = 0;
154  const char* sources[3];
155  if (!extraBegin.empty())
156  sources[count++] = extraBegin.c_str();
157  if (!m_source.empty())
158  sources[count++] = m_source.c_str();
159  if (!extraEnd.empty())
160  sources[count++] = extraEnd.c_str();
161 
162  glShaderSource(m_pixelShader, count, sources, 0);
163  glCompileShader(m_pixelShader);
164  glGetShaderiv(m_pixelShader, GL_COMPILE_STATUS, params);
165  if (params[0] != GL_TRUE)
166  {
167  GLchar log[LOG_SIZE];
168  glGetShaderInfoLog(m_pixelShader, LOG_SIZE, nullptr, log);
169  kodi::Log(ADDON_LOG_ERROR, "CPixelShader::%s: %s", __FUNCTION__, log);
170  fprintf(stderr, "CPixelShader::%s: %s\n", __FUNCTION__, log);
171  m_lastLog = log;
172  m_compiled = false;
173  }
174  else
175  {
176  GLchar log[LOG_SIZE];
177  glGetShaderInfoLog(m_pixelShader, LOG_SIZE, nullptr, log);
178  m_lastLog = log;
179  m_compiled = true;
180  }
181  return m_compiled;
182  }
183 
184  GLuint Handle() override { return m_pixelShader; }
185 
186 protected:
187  GLuint m_pixelShader = 0;
188 };
189 //------------------------------------------------------------------------
190 
191 //============================================================================
276 class ATTR_DLL_LOCAL CShaderProgram
277 {
278 public:
279  //==========================================================================
285  CShaderProgram() = default;
286  //--------------------------------------------------------------------------
287 
288  //==========================================================================
295  CShaderProgram(const std::string& vert, const std::string& frag) { LoadShaderFiles(vert, frag); }
296  //--------------------------------------------------------------------------
297 
298  //==========================================================================
302  virtual ~CShaderProgram() { ShaderFree(); }
303  //--------------------------------------------------------------------------
304 
305  //==========================================================================
316  bool LoadShaderFiles(const std::string& vert, const std::string& frag)
317  {
318  if (!kodi::vfs::FileExists(vert) || !m_pVP.LoadSource(vert))
319  {
320  kodi::Log(ADDON_LOG_ERROR, "%s: Failed to load '%s'", __func__, vert.c_str());
321  return false;
322  }
323 
324  if (!kodi::vfs::FileExists(frag) || !m_pFP.LoadSource(frag))
325  {
326  kodi::Log(ADDON_LOG_ERROR, "%s: Failed to load '%s'", __func__, frag.c_str());
327  return false;
328  }
329 
330  return true;
331  }
332  //--------------------------------------------------------------------------
333 
334  //==========================================================================
361  bool CompileAndLink(const std::string& vertexExtraBegin = "",
362  const std::string& vertexExtraEnd = "",
363  const std::string& fragmentExtraBegin = "",
364  const std::string& fragmentExtraEnd = "")
365  {
366  GLint params[4];
367 
368  // free resources
369  ShaderFree();
370  m_ok = false;
371 
372  // compiled vertex shader
373  if (!m_pVP.Compile(vertexExtraBegin, vertexExtraEnd))
374  {
375  kodi::Log(ADDON_LOG_ERROR, "GL: Error compiling vertex shader");
376  return false;
377  }
378 
379  // compile pixel shader
380  if (!m_pFP.Compile(fragmentExtraBegin, fragmentExtraEnd))
381  {
382  m_pVP.Free();
383  kodi::Log(ADDON_LOG_ERROR, "GL: Error compiling fragment shader");
384  return false;
385  }
386 
387  // create program object
388  m_shaderProgram = glCreateProgram();
389  if (!m_shaderProgram)
390  {
391  kodi::Log(ADDON_LOG_ERROR, "CShaderProgram::%s: Failed to create GL program", __FUNCTION__);
392  ShaderFree();
393  return false;
394  }
395 
396  // attach the vertex shader
397  glAttachShader(m_shaderProgram, m_pVP.Handle());
398  glAttachShader(m_shaderProgram, m_pFP.Handle());
399 
400  // link the program
401  glLinkProgram(m_shaderProgram);
402  glGetProgramiv(m_shaderProgram, GL_LINK_STATUS, params);
403  if (params[0] != GL_TRUE)
404  {
405  GLchar log[LOG_SIZE];
406  glGetProgramInfoLog(m_shaderProgram, LOG_SIZE, nullptr, log);
407  kodi::Log(ADDON_LOG_ERROR, "CShaderProgram::%s: %s", __FUNCTION__, log);
408  fprintf(stderr, "CShaderProgram::%s: %s@n", __FUNCTION__, log);
409  ShaderFree();
410  return false;
411  }
412 
413  m_validated = false;
414  m_ok = true;
415  OnCompiledAndLinked();
416  return true;
417  }
418  //--------------------------------------------------------------------------
419 
420  //==========================================================================
430  bool EnableShader()
431  {
432  if (ShaderOK())
433  {
434  glUseProgram(m_shaderProgram);
435  if (OnEnabled())
436  {
437  if (!m_validated)
438  {
439  // validate the program
440  GLint params[4];
441  glValidateProgram(m_shaderProgram);
442  glGetProgramiv(m_shaderProgram, GL_VALIDATE_STATUS, params);
443  if (params[0] != GL_TRUE)
444  {
445  GLchar log[LOG_SIZE];
446  glGetProgramInfoLog(m_shaderProgram, LOG_SIZE, nullptr, log);
447  kodi::Log(ADDON_LOG_ERROR, "CShaderProgram::%s: %s", __FUNCTION__, log);
448  fprintf(stderr, "CShaderProgram::%s: %s\n", __FUNCTION__, log);
449  }
450  m_validated = true;
451  }
452  return true;
453  }
454  else
455  {
456  glUseProgram(0);
457  return false;
458  }
459  return true;
460  }
461  return false;
462  }
463  //--------------------------------------------------------------------------
464 
465  //==========================================================================
469  void DisableShader()
470  {
471  if (ShaderOK())
472  {
473  glUseProgram(0);
474  OnDisabled();
475  }
476  }
477  //--------------------------------------------------------------------------
478 
479  //==========================================================================
487  ATTR_FORCEINLINE bool ShaderOK() const { return m_ok; }
488  //--------------------------------------------------------------------------
489 
490  //==========================================================================
496  ATTR_FORCEINLINE CVertexShader& VertexShader() { return m_pVP; }
497  //--------------------------------------------------------------------------
498 
499  //==========================================================================
505  ATTR_FORCEINLINE CPixelShader& PixelShader() { return m_pFP; }
506  //--------------------------------------------------------------------------
507 
508  //==========================================================================
514  ATTR_FORCEINLINE GLuint ProgramHandle() { return m_shaderProgram; }
515  //--------------------------------------------------------------------------
516 
517  //==========================================================================
524  //==========================================================================
529  virtual void OnCompiledAndLinked() {}
530  //--------------------------------------------------------------------------
531 
532  //==========================================================================
539  virtual bool OnEnabled() { return true; }
540  //--------------------------------------------------------------------------
541 
542  //==========================================================================
546  virtual void OnDisabled() {}
547  //--------------------------------------------------------------------------
549 
550 private:
551  void ShaderFree()
552  {
553  if (m_shaderProgram)
554  glDeleteProgram(m_shaderProgram);
555  m_shaderProgram = 0;
556  m_ok = false;
557  }
558 
559  CVertexShader m_pVP;
560  CPixelShader m_pFP;
561  GLuint m_shaderProgram = 0;
562  bool m_ok = false;
563  bool m_validated = false;
564 };
565 //------------------------------------------------------------------------
566 
567 } /* namespace gl */
568 } /* namespace gui */
569 } /* namespace kodi */
570 
571 #endif /* __cplusplus */
3 : To report error messages in the log file.
Definition: addon_base.h:193