1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4 
5   Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
6   All rights reserved.
7   See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
8 
9      This software is distributed WITHOUT ANY WARRANTY; without even
10      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
11      PURPOSE.  See the above copyright notice for more information.
12 
13 =========================================================================*/
14 #include "vtkShaderProgram.h"
15 #include "vtkObjectFactory.h"
16 
17 #include "vtk_glew.h"
18 #include "vtkShader.h"
19 #include "vtkMatrix3x3.h"
20 #include "vtkMatrix4x4.h"
21 #include "vtkOpenGLRenderWindow.h"
22 #include "vtkOpenGLShaderCache.h"
23 #include "vtkTypeTraits.h"
24 
25 # include <sstream>
26 
27 namespace {
28 
convertTypeToGL(int type)29 inline GLenum convertTypeToGL(int type)
30 {
31   switch (type)
32     {
33     case VTK_CHAR:
34       return GL_BYTE;
35     case VTK_UNSIGNED_CHAR:
36       return GL_UNSIGNED_BYTE;
37     case VTK_SHORT:
38       return GL_SHORT;
39     case VTK_UNSIGNED_SHORT:
40       return GL_UNSIGNED_SHORT;
41     case VTK_INT:
42       return GL_INT;
43     case VTK_UNSIGNED_INT:
44       return GL_UNSIGNED_INT;
45     case VTK_FLOAT:
46       return GL_FLOAT;
47     case VTK_DOUBLE:
48 #ifdef GL_DOUBLE
49       return GL_DOUBLE;
50 #else
51       vtkGenericWarningMacro(<< "Attempt to use GL_DOUBLE when not supported");
52       return 0;
53 #endif
54     default:
55       return 0;
56     }
57 }
58 
59 } // end anon namespace
60 
vtkStandardNewMacro(vtkShaderProgram)61 vtkStandardNewMacro(vtkShaderProgram)
62 
63 vtkShaderProgram::vtkShaderProgram() : Handle(0), VertexShaderHandle(0),
64   FragmentShaderHandle(0), Linked(false), Bound(false)
65 {
66     this->VertexShader = vtkShader::New();
67     this->VertexShader->SetType(vtkShader::Vertex);
68     this->FragmentShader = vtkShader::New();
69     this->FragmentShader->SetType(vtkShader::Fragment);
70     this->GeometryShader = vtkShader::New();
71     this->GeometryShader->SetType(vtkShader::Geometry);
72 
73     this->Compiled = false;
74 }
75 
~vtkShaderProgram()76 vtkShaderProgram::~vtkShaderProgram()
77 {
78   if (this->VertexShader)
79     {
80     this->VertexShader->Delete();
81     this->VertexShader = NULL;
82     }
83   if (this->FragmentShader)
84     {
85     this->FragmentShader->Delete();
86     this->FragmentShader = NULL;
87     }
88   if (this->GeometryShader)
89     {
90     this->GeometryShader->Delete();
91     this->GeometryShader = NULL;
92     }
93 }
94 
SetAttributeArray(const char * name,const T & array,int tupleSize,NormalizeOption normalize)95 template <class T> bool vtkShaderProgram::SetAttributeArray(const char *name,
96                                                 const T &array, int tupleSize,
97                                                 NormalizeOption normalize)
98 {
99   if (array.empty())
100     {
101     this->Error = "Refusing to upload empty array for attribute " + std::string(name) + ".";
102     return false;
103     }
104   int type = vtkTypeTraits<typename T::value_type>::VTKTypeID();
105   return this->SetAttributeArrayInternal(name, &array[0], type, tupleSize,
106                                          normalize);
107 }
108 
AttachShader(const vtkShader * shader)109 bool vtkShaderProgram::AttachShader(const vtkShader *shader)
110 {
111   if (shader->GetHandle() == 0)
112     {
113     this->Error = "Shader object was not initialized, cannot attach it.";
114     return false;
115     }
116   if (shader->GetType() == vtkShader::Unknown)
117     {
118     this->Error = "Shader object is of type Unknown and cannot be used.";
119     return false;
120     }
121 
122   if (this->Handle == 0)
123     {
124     GLuint handle_ = glCreateProgram();
125     if (handle_ == 0)
126       {
127       this->Error = "Could not create shader program.";
128       return false;
129       }
130     this->Handle = static_cast<int>(handle_);
131     this->Linked = false;
132     }
133 
134   if (shader->GetType() == vtkShader::Vertex)
135     {
136     if (VertexShaderHandle != 0)
137       {
138       glDetachShader(static_cast<GLuint>(Handle),
139                      static_cast<GLuint>(VertexShaderHandle));
140       }
141     this->VertexShaderHandle = shader->GetHandle();
142     }
143   else if (shader->GetType() == vtkShader::Fragment)
144     {
145     if (FragmentShaderHandle != 0)
146       {
147       glDetachShader(static_cast<GLuint>(Handle),
148                      static_cast<GLuint>(FragmentShaderHandle));
149       }
150     this->FragmentShaderHandle = shader->GetHandle();
151     }
152   else
153     {
154     this->Error = "Unknown shader type encountered - this should not happen.";
155     return false;
156   }
157 
158   glAttachShader(static_cast<GLuint>(this->Handle),
159                  static_cast<GLuint>(shader->GetHandle()));
160   this->Linked = false;
161   return true;
162 }
163 
DetachShader(const vtkShader * shader)164 bool vtkShaderProgram::DetachShader(const vtkShader *shader)
165 {
166   if (shader->GetHandle() == 0)
167     {
168     this->Error = "Shader object was not initialized, cannot attach it.";
169     return false;
170     }
171   if (shader->GetType() == vtkShader::Unknown)
172     {
173     this->Error = "Shader object is of type Unknown and cannot be used.";
174     return false;
175     }
176   if (this->Handle == 0)
177     {
178     this->Error = "This shader prorgram has not been initialized yet.";
179     }
180 
181   switch (shader->GetType())
182     {
183     case vtkShader::Vertex:
184       if (this->VertexShaderHandle != shader->GetHandle())
185         {
186         Error = "The supplied shader was not attached to this program.";
187         return false;
188         }
189       else
190         {
191         glDetachShader(static_cast<GLuint>(this->Handle),
192                        static_cast<GLuint>(shader->GetHandle()));
193         this->VertexShaderHandle = 0;
194         this->Linked = false;
195         return true;
196         }
197     case vtkShader::Fragment:
198       if (this->FragmentShaderHandle != shader->GetHandle())
199         {
200         this->Error = "The supplied shader was not attached to this program.";
201         return false;
202         }
203       else
204         {
205         glDetachShader(static_cast<GLuint>(this->Handle),
206                        static_cast<GLuint>(shader->GetHandle()));
207         this->FragmentShaderHandle = 0;
208         this->Linked = false;
209         return true;
210         }
211     default:
212       return false;
213     }
214 }
215 
Link()216 bool vtkShaderProgram::Link()
217 {
218   if (this->Linked)
219     {
220     return true;
221     }
222 
223   if (this->Handle == 0)
224     {
225     this->Error = "Program has not been initialized, and/or does not have shaders.";
226     return false;
227     }
228 
229   GLint isCompiled;
230   glLinkProgram(static_cast<GLuint>(this->Handle));
231   glGetProgramiv(static_cast<GLuint>(this->Handle), GL_LINK_STATUS, &isCompiled);
232   if (isCompiled == 0)
233     {
234     GLint length(0);
235     glGetProgramiv(static_cast<GLuint>(this->Handle), GL_INFO_LOG_LENGTH, &length);
236     if (length > 1)
237       {
238       char *logMessage = new char[length];
239       glGetProgramInfoLog(static_cast<GLuint>(this->Handle), length, NULL, logMessage);
240       this->Error = logMessage;
241       delete[] logMessage;
242       }
243     return false;
244     }
245   this->Linked = true;
246   this->Attributes.clear();
247   return true;
248 }
249 
Bind()250 bool vtkShaderProgram::Bind()
251 {
252   if (!this->Linked && !this->Link())
253     {
254     return false;
255     }
256 
257   glUseProgram(static_cast<GLuint>(this->Handle));
258   this->Bound = true;
259   return true;
260 }
261 
262 // return 0 if there is an issue
CompileShader()263 int vtkShaderProgram::CompileShader()
264 {
265   if (!this->GetVertexShader()->Compile())
266     {
267     int lineNum = 1;
268     std::istringstream stream(this->GetVertexShader()->GetSource());
269     std::stringstream sstm;
270     std::string aline;
271     while (std::getline(stream, aline))
272       {
273       sstm << lineNum << ": " << aline << "\n";
274       lineNum++;
275       }
276     vtkErrorMacro(<< sstm.str());
277     vtkErrorMacro(<< this->GetVertexShader()->GetError());
278     return 0;
279     }
280   if (!this->GetFragmentShader()->Compile())
281     {
282     int lineNum = 1;
283     std::istringstream stream(this->GetFragmentShader()->GetSource());
284     std::stringstream sstm;
285     std::string aline;
286     while (std::getline(stream, aline))
287       {
288       sstm << lineNum << ": " << aline << "\n";
289       lineNum++;
290       }
291     vtkErrorMacro(<< sstm.str());
292     vtkErrorMacro(<< this->GetFragmentShader()->GetError());
293     return 0;
294     }
295   if (!this->AttachShader(this->GetVertexShader()))
296     {
297     vtkErrorMacro(<< this->GetError());
298     return 0;
299     }
300   if (!this->AttachShader(this->GetFragmentShader()))
301     {
302     vtkErrorMacro(<< this->GetError());
303     return 0;
304     }
305   if (!this->Link())
306     {
307     vtkErrorMacro(<< "Links failed: " << this->GetError());
308     return 0;
309     }
310 
311   this->Compiled = true;
312   return 1;
313 }
314 
315 
316 
Release()317 void vtkShaderProgram::Release()
318 {
319   glUseProgram(0);
320   this->Bound = false;
321 }
322 
ReleaseGraphicsResources(vtkWindow * win)323 void vtkShaderProgram::ReleaseGraphicsResources(vtkWindow *win)
324 {
325   this->Release();
326 
327   if (this->Compiled)
328     {
329     this->DetachShader(this->VertexShader);
330     this->DetachShader(this->FragmentShader);
331     this->VertexShader->Cleanup();
332     this->FragmentShader->Cleanup();
333     this->Compiled = false;
334     }
335 
336   vtkOpenGLRenderWindow *renWin = vtkOpenGLRenderWindow::SafeDownCast(win);
337   if (renWin->GetShaderCache()->GetLastShaderBound() == this)
338     {
339     renWin->GetShaderCache()->ClearLastShaderBound();
340     }
341 
342   if (this->Handle != 0)
343     {
344     glDeleteProgram(this->Handle);
345     this->Handle = 0;
346     this->Linked = false;
347     }
348 
349 }
350 
EnableAttributeArray(const char * name)351 bool vtkShaderProgram::EnableAttributeArray(const char *name)
352 {
353   GLint location = static_cast<GLint>(this->FindAttributeArray(name));
354   if (location == -1)
355     {
356     this->Error = "Could not enable attribute " + std::string(name) + ". No such attribute.";
357     return false;
358     }
359   glEnableVertexAttribArray(location);
360   return true;
361 }
362 
DisableAttributeArray(const char * name)363 bool vtkShaderProgram::DisableAttributeArray(const char *name)
364 {
365   GLint location = static_cast<GLint>(this->FindAttributeArray(name));
366   if (location == -1)
367     {
368     this->Error = "Could not disable attribute " + std::string(name) + ". No such attribute.";
369     return false;
370     }
371   glDisableVertexAttribArray(location);
372   return true;
373 }
374 
375 #define BUFFER_OFFSET(i) ((char *)NULL + (i))
376 
UseAttributeArray(const char * name,int offset,size_t stride,int elementType,int elementTupleSize,NormalizeOption normalize)377 bool vtkShaderProgram::UseAttributeArray(const char *name, int offset,
378                                       size_t stride, int elementType,
379                                       int elementTupleSize,
380                                       NormalizeOption normalize)
381 {
382   GLint location = static_cast<GLint>(this->FindAttributeArray(name));
383   if (location == -1)
384     {
385     this->Error = "Could not use attribute " + std::string(name) + ". No such attribute.";
386     return false;
387     }
388   glVertexAttribPointer(location, elementTupleSize, convertTypeToGL(elementType),
389                         normalize == Normalize ? GL_TRUE : GL_FALSE,
390                         static_cast<GLsizei>(stride), BUFFER_OFFSET(offset));
391   return true;
392 }
393 
SetUniformi(const char * name,int i)394 bool vtkShaderProgram::SetUniformi(const char *name, int i)
395 {
396   GLint location = static_cast<GLint>(this->FindUniform(name));
397   if (location == -1)
398     {
399     this->Error = "Could not set uniform " + std::string(name) + ". No such uniform.";
400     return false;
401     }
402   glUniform1i(location, static_cast<GLint>(i));
403   return true;
404 }
405 
SetUniformf(const char * name,float f)406 bool vtkShaderProgram::SetUniformf(const char *name, float f)
407 {
408   GLint location = static_cast<GLint>(this->FindUniform(name));
409   if (location == -1)
410     {
411     this->Error = "Could not set uniform " + std::string(name) + ". No such uniform.";
412     return false;
413     }
414   glUniform1f(location, static_cast<GLfloat>(f));
415   return true;
416 }
417 
SetUniformMatrix(const char * name,vtkMatrix4x4 * matrix)418 bool vtkShaderProgram::SetUniformMatrix(const char *name,
419                                     vtkMatrix4x4 *matrix)
420 {
421   GLint location = static_cast<GLint>(this->FindUniform(name));
422   if (location == -1)
423     {
424     this->Error = "Could not set uniform " + std::string(name) + ". No such uniform.";
425     return false;
426     }
427   float data[16];
428   for (int i = 0; i < 16; ++i)
429     {
430     data[i] = matrix->Element[i / 4][i % 4];
431     }
432   glUniformMatrix4fv(location, 1, GL_FALSE, data);
433   return true;
434 }
435 
SetUniformMatrix3x3(const char * name,float * matrix)436 bool vtkShaderProgram::SetUniformMatrix3x3(const char *name,
437                                            float *matrix)
438 {
439   GLint location = static_cast<GLint>(this->FindUniform(name));
440   if (location == -1)
441     {
442     this->Error = "Could not set uniform " + std::string(name) + ". No such uniform.";
443     return false;
444     }
445   glUniformMatrix3fv(location, 1, GL_FALSE, matrix);
446   return true;
447 }
448 
SetUniformMatrix4x4(const char * name,float * matrix)449 bool vtkShaderProgram::SetUniformMatrix4x4(const char *name,
450                                            float *matrix)
451 {
452   GLint location = static_cast<GLint>(this->FindUniform(name));
453   if (location == -1)
454     {
455     this->Error = "Could not set uniform " + std::string(name) + ". No such uniform.";
456     return false;
457     }
458   glUniformMatrix4fv(location, 1, GL_FALSE, matrix);
459   return true;
460 }
461 
SetUniformMatrix(const char * name,vtkMatrix3x3 * matrix)462 bool vtkShaderProgram::SetUniformMatrix(const char *name,
463                                     vtkMatrix3x3 *matrix)
464 {
465   GLint location = static_cast<GLint>(this->FindUniform(name));
466   if (location == -1)
467     {
468     this->Error = "Could not set uniform " + std::string(name) + ". No such uniform.";
469     return false;
470     }
471   float data[9];
472   for (int i = 0; i < 9; ++i)
473     {
474     data[i] = matrix->GetElement(i / 3, i % 3);
475     }
476   glUniformMatrix3fv(location, 1, GL_FALSE, data);
477   return true;
478 }
479 
SetUniform1fv(const char * name,const int count,const float * v)480 bool vtkShaderProgram::SetUniform1fv(const char *name, const int count,
481                                     const float *v)
482 {
483   GLint location = static_cast<GLint>(this->FindUniform(name));
484   if (location == -1)
485     {
486     this->Error = "Could not set uniform " + std::string(name) + ". No such uniform.";
487     return false;
488     }
489   glUniform1fv(location, count, static_cast<const GLfloat *>(v));
490   return true;
491 }
492 
SetUniform1iv(const char * name,const int count,const int * v)493 bool vtkShaderProgram::SetUniform1iv(const char *name, const int count,
494                                     const int *v)
495 {
496   GLint location = static_cast<GLint>(this->FindUniform(name));
497   if (location == -1)
498     {
499     this->Error = "Could not set uniform " + std::string(name) + ". No such uniform.";
500     return false;
501     }
502   glUniform1iv(location, count, static_cast<const GLint *>(v));
503   return true;
504 }
505 
SetUniform3fv(const char * name,const int count,const float (* v)[3])506 bool vtkShaderProgram::SetUniform3fv(const char *name, const int count,
507                                     const float (*v)[3])
508 {
509   GLint location = static_cast<GLint>(this->FindUniform(name));
510   if (location == -1)
511     {
512     Error = "Could not set uniform " + std::string(name) + ". No such uniform.";
513     return false;
514     }
515   glUniform3fv(location, count, (const GLfloat *)v);
516   return true;
517 }
518 
SetUniform4fv(const char * name,const int count,const float (* v)[4])519 bool vtkShaderProgram::SetUniform4fv(const char *name, const int count,
520                                     const float (*v)[4])
521 {
522   GLint location = static_cast<GLint>(this->FindUniform(name));
523   if (location == -1)
524     {
525     Error = "Could not set uniform " + std::string(name) + ". No such uniform.";
526     return false;
527     }
528   glUniform4fv(location, count, (const GLfloat *)v);
529   return true;
530 }
531 
SetUniform2f(const char * name,const float v[2])532 bool vtkShaderProgram::SetUniform2f(const char *name, const float v[2])
533 {
534   GLint location = static_cast<GLint>(this->FindUniform(name));
535   if (location == -1)
536     {
537     this->Error = "Could not set uniform " + std::string(name) + ". No such uniform.";
538     return false;
539     }
540   glUniform2fv(location, 1, v);
541   return true;
542 }
543 
SetUniform2fv(const char * name,const int count,const float (* f)[2])544 bool vtkShaderProgram::SetUniform2fv(const char *name, const int count,
545                                     const float (*f)[2])
546 {
547   GLint location = static_cast<GLint>(this->FindUniform(name));
548   if (location == -1)
549     {
550     Error = "Could not set uniform " + std::string(name) + ". No such uniform.";
551     return false;
552     }
553   glUniform2fv(location, count, (const GLfloat *)f);
554   return true;
555 }
556 
SetUniform3f(const char * name,const float v[3])557 bool vtkShaderProgram::SetUniform3f(const char *name, const float v[3])
558 {
559   GLint location = static_cast<GLint>(this->FindUniform(name));
560   if (location == -1)
561     {
562     this->Error = "Could not set uniform " + std::string(name) + ". No such uniform.";
563     return false;
564     }
565   glUniform3fv(location, 1, v);
566   return true;
567 }
568 
SetUniform4f(const char * name,const float v[4])569 bool vtkShaderProgram::SetUniform4f(const char *name, const float v[4])
570 {
571   GLint location = static_cast<GLint>(this->FindUniform(name));
572   if (location == -1)
573     {
574     this->Error = "Could not set uniform " + std::string(name) + ". No such uniform.";
575     return false;
576     }
577   glUniform4fv(location, 1, v);
578   return true;
579 }
580 
SetUniform2i(const char * name,const int v[2])581 bool vtkShaderProgram::SetUniform2i(const char *name, const int v[2])
582 {
583   GLint location = static_cast<GLint>(this->FindUniform(name));
584   if (location == -1)
585     {
586     this->Error = "Could not set uniform " + std::string(name) + ". No such uniform.";
587     return false;
588     }
589   glUniform2iv(location, 1, v);
590   return true;
591 }
592 
SetUniform3uc(const char * name,const unsigned char v[3])593 bool vtkShaderProgram::SetUniform3uc(const char *name,
594                                     const unsigned char v[3])
595 {
596   GLint location = static_cast<GLint>(this->FindUniform(name));
597   if (location == -1)
598     {
599     this->Error = "Could not set uniform " + std::string(name) + ". No such uniform.";
600     return false;
601     }
602   float colorf[3] = {v[0] / 255.0f, v[1] / 255.0f, v[2] / 255.0f};
603   glUniform3fv(location, 1, colorf);
604   return true;
605 }
606 
SetUniform4uc(const char * name,const unsigned char v[4])607 bool vtkShaderProgram::SetUniform4uc(const char *name,
608                                     const unsigned char v[4])
609 {
610   GLint location = static_cast<GLint>(this->FindUniform(name));
611   if (location == -1)
612     {
613     this->Error = "Could not set uniform " + std::string(name) + ". No such uniform.";
614     return false;
615     }
616   float colorf[4] = {v[0] / 255.0f, v[1] / 255.0f, v[2] / 255.0f, v[3] / 255.0f};
617   glUniform4fv(location, 1, colorf);
618   return true;
619 }
620 
SetAttributeArrayInternal(const char * name,void * buffer,int type,int tupleSize,vtkShaderProgram::NormalizeOption normalize)621 bool vtkShaderProgram::SetAttributeArrayInternal(
622     const char *name, void *buffer, int type, int tupleSize,
623     vtkShaderProgram::NormalizeOption normalize)
624 {
625   if (type == -1)
626     {
627     this->Error = "Unrecognized data type for attribute " + std::string(name) + ".";
628     return false;
629     }
630   GLint location = static_cast<GLint>(this->FindAttributeArray(name));
631   if (location == -1)
632     {
633     this->Error = "Could not set attribute " + std::string(name) + ". No such attribute.";
634     return false;
635     }
636   const GLvoid *data = static_cast<const GLvoid *>(buffer);
637   glVertexAttribPointer(location, tupleSize, convertTypeToGL(type),
638                         normalize == Normalize ? GL_TRUE : GL_FALSE, 0, data);
639   return true;
640 }
641 
FindAttributeArray(const char * name)642 inline int vtkShaderProgram::FindAttributeArray(const char *name)
643 {
644   if (name == NULL || !this->Linked)
645     {
646     return -1;
647     }
648   GLint location =
649       static_cast<int>(glGetAttribLocation(static_cast<GLuint>(Handle),
650                                            (const GLchar *)name));
651   if (location == -1)
652     {
653     this->Error = "Specified attribute not found in current shader program: ";
654     this->Error += name;
655     }
656 
657   return location;
658 }
659 
FindUniform(const char * name)660 inline int vtkShaderProgram::FindUniform(const char *name)
661 {
662   if (name == NULL || !this->Linked)
663     {
664     return -1;
665     }
666   GLint location =
667       static_cast<int>(glGetUniformLocation(static_cast<GLuint>(Handle),
668                                             (const GLchar *)name));
669   if (location == -1)
670     {
671     this->Error = "Uniform " + std::string(name) + " not found in current shader program.";
672     }
673 
674   return location;
675 }
676 
677 // ----------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)678 void vtkShaderProgram::PrintSelf(ostream& os, vtkIndent indent)
679 {
680   this->Superclass::PrintSelf(os,indent);
681 }
682