PageRenderTime 61ms CodeModel.GetById 15ms app.highlight 43ms RepoModel.GetById 1ms app.codeStats 0ms

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