You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1161 lines
34 KiB
C++
1161 lines
34 KiB
C++
/*=========================================================================
|
|
|
|
Program: Visualization Toolkit
|
|
Module: vtkShaderProgram2.cxx
|
|
|
|
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
|
|
All rights reserved.
|
|
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
|
|
|
|
This software is distributed WITHOUT ANY WARRANTY; without even
|
|
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
PURPOSE. See the above copyright notice for more information.
|
|
|
|
=========================================================================*/
|
|
#include "vtkShaderProgram2.h"
|
|
|
|
#include "vtkObjectFactory.h"
|
|
#include "vtkOpenGLExtensionManager.h"
|
|
#include "vtkOpenGLRenderWindow.h"
|
|
#include "vtkShader2Collection.h"
|
|
#include "vtkShader2.h"
|
|
#include "vtkUniformVariables.h"
|
|
|
|
#include "vtkgl.h"
|
|
#include "vtkOpenGLError.h"
|
|
|
|
#include <vector>
|
|
#include <sstream>
|
|
#include <cassert>
|
|
#include "vtkStdString.h"
|
|
|
|
static GLenum vtkGeometryTypeInVTKToGL[5] = {
|
|
GL_POINTS, // VTK_GEOMETRY_SHADER_IN_TYPE_POINTS=0
|
|
GL_LINES, // VTK_GEOMETRY_SHADER_IN_TYPE_LINES=1,
|
|
vtkgl::LINES_ADJACENCY_ARB, // VTK_GEOMETRY_SHADER_IN_TYPE_LINES_ADJACENCY=2
|
|
GL_TRIANGLES, // VTK_GEOMETRY_SHADER_IN_TYPE_TRIANGLES=3
|
|
vtkgl::TRIANGLES_ADJACENCY_ARB // VTK_GEOMETRY_SHADER_IN_TYPE_TRIANGLES_ADJACENCY=4
|
|
};
|
|
|
|
static const char *vtkGeometryTypeInAsStringArray[5] = {
|
|
"points",
|
|
"lines",
|
|
"lines with adjacency",
|
|
"triangles",
|
|
"triangles with adjacency"
|
|
};
|
|
|
|
static GLenum vtkGeometryTypeOutVTKToGL[3] = {
|
|
GL_POINTS, // VTK_GEOMETRY_SHADER_OUT_TYPE_POINTS=0
|
|
GL_LINE_STRIP, // VTK_GEOMETRY_SHADER_OUT_TYPE_LINE_STRIP=1,
|
|
GL_TRIANGLE_STRIP, // VTK_GEOMETRY_SHADER_OUT_TYPE_TRIANGLE_STRIP=2
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkStandardNewMacro(vtkShaderProgram2);
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkCxxSetObjectMacro(vtkShaderProgram2, UniformVariables, vtkUniformVariables);
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkShaderProgram2::vtkShaderProgram2()
|
|
{
|
|
this->Context = NULL;
|
|
this->ExtensionsLoaded = false;
|
|
|
|
this->Id = 0;
|
|
this->SavedId = 0;
|
|
this->Shaders = vtkShader2Collection::New(); // an empty list.
|
|
|
|
this->LastBuildStatus = VTK_SHADER_PROGRAM2_COMPILE_FAILED;
|
|
|
|
this->GeometryTypeIn = VTK_GEOMETRY_SHADER_IN_TYPE_POINTS;
|
|
this->GeometryTypeOut = VTK_GEOMETRY_SHADER_OUT_TYPE_POINTS;
|
|
this->GeometryVerticesOut = 1;
|
|
|
|
// 8 as an initial capacity is nice because the allocation is aligned on
|
|
// 32-bit or 64-bit architecture.
|
|
|
|
this->LastLinkLogCapacity = 8;
|
|
this->LastLinkLog = new char[this->LastLinkLogCapacity];
|
|
this->LastLinkLog[0] = '\0'; // empty string
|
|
|
|
this->LastValidateLogCapacity = 8;
|
|
this->LastValidateLog = new char[this->LastValidateLogCapacity];
|
|
this->LastValidateLog[0] = '\0'; // empty string
|
|
|
|
this->UniformVariables = vtkUniformVariables::New(); // empty list
|
|
this->PrintErrors = true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void vtkShaderProgram2::ReleaseGraphicsResources()
|
|
{
|
|
// because we don't hold a reference to the render
|
|
// context we don't have any control on when it is
|
|
// destroyed. In fact it may be destroyed before
|
|
// we are(eg smart pointers), in which case we should
|
|
// do nothing.
|
|
this->Shaders->ReleaseGraphicsResources();
|
|
if (this->Context && (this->Id != 0))
|
|
{
|
|
vtkgl::DeleteProgram(this->Id);
|
|
vtkOpenGLCheckErrorMacro("failed at glDeleteProgram");
|
|
this->Id = 0;
|
|
}
|
|
this->LastBuildStatus = VTK_SHADER_PROGRAM2_COMPILE_FAILED;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkShaderProgram2::~vtkShaderProgram2()
|
|
{
|
|
this->ReleaseGraphicsResources();
|
|
|
|
delete[] this->LastLinkLog;
|
|
delete[] this->LastValidateLog;
|
|
|
|
if (this->UniformVariables)
|
|
{
|
|
this->UniformVariables->Delete();
|
|
}
|
|
|
|
if (this->Shaders)
|
|
{
|
|
this->Shaders->Delete();
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool vtkShaderProgram2::IsSupported(vtkRenderWindow *renWin)
|
|
{
|
|
vtkOpenGLRenderWindow *context
|
|
= dynamic_cast<vtkOpenGLRenderWindow*>(renWin);
|
|
|
|
if (!context) return false;
|
|
|
|
vtkOpenGLExtensionManager *e = context->GetExtensionManager();
|
|
|
|
bool multiTexture = e->ExtensionSupported("GL_VERSION_1_3") ||
|
|
e->ExtensionSupported("GL_ARB_multitexture");
|
|
|
|
bool glsl = e->ExtensionSupported("GL_VERSION_2_0") ||
|
|
(e->ExtensionSupported("GL_ARB_shading_language_100") &&
|
|
e->ExtensionSupported("GL_ARB_shader_objects") &&
|
|
e->ExtensionSupported("GL_ARB_vertex_shader") &&
|
|
e->ExtensionSupported("GL_ARB_fragment_shader"));
|
|
|
|
return multiTexture && glsl;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool vtkShaderProgram2::LoadRequiredExtensions(vtkRenderWindow *renWin)
|
|
{
|
|
vtkOpenGLRenderWindow *context
|
|
= dynamic_cast<vtkOpenGLRenderWindow*>(renWin);
|
|
|
|
this->ExtensionsLoaded = false;
|
|
|
|
if (!context) return false;
|
|
|
|
vtkOpenGLExtensionManager *e = context->GetExtensionManager();
|
|
|
|
bool gl13 = e->ExtensionSupported("GL_VERSION_1_3") == 1;
|
|
bool gl20 = e->ExtensionSupported("GL_VERSION_2_0") == 1;
|
|
|
|
bool multiTexture = gl13 || e->ExtensionSupported("GL_ARB_multitexture");
|
|
bool glsl = gl20 || (e->ExtensionSupported("GL_ARB_shading_language_100") &&
|
|
e->ExtensionSupported("GL_ARB_shader_objects") &&
|
|
e->ExtensionSupported("GL_ARB_vertex_shader") &&
|
|
e->ExtensionSupported("GL_ARB_fragment_shader"));
|
|
|
|
this->ExtensionsLoaded = (multiTexture && glsl);
|
|
|
|
if (this->ExtensionsLoaded)
|
|
{
|
|
if (gl13)
|
|
{
|
|
e->LoadExtension("GL_VERSION_1_3");
|
|
}
|
|
else
|
|
{
|
|
e->LoadCorePromotedExtension("GL_ARB_multitexture");
|
|
}
|
|
if (gl20)
|
|
{
|
|
e->LoadExtension("GL_VERSION_2_0");
|
|
}
|
|
else
|
|
{
|
|
e->LoadCorePromotedExtension("GL_ARB_shading_language_100");
|
|
e->LoadCorePromotedExtension("GL_ARB_shader_objects");
|
|
e->LoadCorePromotedExtension("GL_ARB_vertex_shader");
|
|
e->LoadCorePromotedExtension("GL_ARB_fragment_shader");
|
|
}
|
|
}
|
|
|
|
return this->ExtensionsLoaded;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
vtkRenderWindow *vtkShaderProgram2::GetContext()
|
|
{
|
|
return this->Context;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
void vtkShaderProgram2::SetContext(vtkRenderWindow *renWin)
|
|
{
|
|
// avoid pointless reassignment
|
|
if (this->Context == renWin)
|
|
{
|
|
return;
|
|
}
|
|
// free ressources
|
|
this->ReleaseGraphicsResources();
|
|
this->Context = NULL;
|
|
this->Modified();
|
|
// all done if assigned null
|
|
if (!renWin)
|
|
{
|
|
return;
|
|
}
|
|
// check for support
|
|
vtkOpenGLRenderWindow *context
|
|
= dynamic_cast<vtkOpenGLRenderWindow*>(renWin);
|
|
|
|
if ( !context
|
|
|| !this->LoadRequiredExtensions(renWin) )
|
|
{
|
|
vtkErrorMacro("The context does not support the required extensions.");
|
|
return;
|
|
}
|
|
// intialize
|
|
this->Context = renWin;
|
|
this->Context->MakeCurrent();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Description:
|
|
// Tells if at least one of the shaders is a vertex shader.
|
|
// If yes, it means the vertex processing of the fixed-pipeline is bypassed.
|
|
// If no, it means the vertex processing of the fixed-pipeline is used.
|
|
bool vtkShaderProgram2::HasVertexShaders()
|
|
{
|
|
return (this->Shaders) ? this->Shaders->HasVertexShaders() : false;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Description:
|
|
// Tells if at least one of the shaders is a tessellation control shader.
|
|
bool vtkShaderProgram2::HasTessellationControlShaders()
|
|
{
|
|
return (this->Shaders) ? this->Shaders->HasTessellationControlShaders() : false;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Description:
|
|
// Tells if at least one of the shaders is a tessellation evaluation shader.
|
|
bool vtkShaderProgram2::HasTessellationEvaluationShaders()
|
|
{
|
|
return (this->Shaders) ? this->Shaders->HasTessellationEvaluationShaders() : false;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Description:
|
|
// Tells if at least one of the shaders is a geometry shader.
|
|
bool vtkShaderProgram2::HasGeometryShaders()
|
|
{
|
|
return (this->Shaders) ? this->Shaders->HasGeometryShaders() : false;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Description:
|
|
// Tells if at least one of the shaders is a fragment shader.
|
|
// If yes, it means the fragment processing of the fixed-pipeline is
|
|
// bypassed.
|
|
// If no, it means the fragment processing of the fixed-pipeline is used.
|
|
bool vtkShaderProgram2::HasFragmentShaders()
|
|
{
|
|
return (this->Shaders) ? this->Shaders->HasFragmentShaders() : false;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Description:
|
|
// Tell if the program is the one currently used by OpenGL.
|
|
bool vtkShaderProgram2::IsUsed()
|
|
{
|
|
assert("pre: context_is_set" && this->Context);
|
|
assert("pre: current_context_matches" && this->Context->IsCurrent());
|
|
|
|
GLint value;
|
|
glGetIntegerv(vtkgl::CURRENT_PROGRAM,&value);
|
|
return static_cast<GLuint>(value) == static_cast<GLuint>(this->Id);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Description:
|
|
// Tells if a display list is under construction with GL_COMPILE mode.
|
|
// Return false if there is no display list under construction of if the
|
|
// mode is GL_COMPILE_AND_EXECUTE.
|
|
// Used internally and provided as a public method for whoever find it
|
|
// useful.
|
|
bool vtkShaderProgram2::DisplayListUnderCreationInCompileMode()
|
|
{
|
|
assert("pre: context_is_set" && this->Context);
|
|
assert("pre: current_context_matches" && this->Context->IsCurrent());
|
|
|
|
bool result = false;
|
|
GLint value;
|
|
glGetIntegerv(GL_LIST_INDEX, &value);
|
|
if (value != 0) // some display list is under construction.
|
|
{
|
|
glGetIntegerv(GL_LIST_MODE, &value);
|
|
if (value == GL_COMPILE)
|
|
{
|
|
result = true;
|
|
}
|
|
else
|
|
{
|
|
if (value != GL_COMPILE_AND_EXECUTE)
|
|
{
|
|
std::ostringstream ost;
|
|
ost << "Unexpected display list creation mode:" << hex << value << dec;
|
|
vtkErrorMacro(<< ost.str().c_str());
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
void vtkShaderProgram2::Use()
|
|
{
|
|
assert("pre: context_is_set" && this->Context);
|
|
assert("pre: current_context_matches" && this->Context->IsCurrent());
|
|
this->Build();
|
|
|
|
// We need to know if this call happens in a display list or not because
|
|
// glGetIntegerv(vtkgl::CURRENT_PROGRAM,&value) is executed immediately
|
|
// while vtkgl::UseProgram(id) is just compiled and its execution is
|
|
// postpone in GL_COMPILE mode.
|
|
|
|
if (this->LastBuildStatus == VTK_SHADER_PROGRAM2_LINK_SUCCEEDED)
|
|
{
|
|
GLuint progId = static_cast<GLuint>(this->Id);
|
|
if (this->DisplayListUnderCreationInCompileMode())
|
|
{
|
|
// don't look at current program, don't save it, don't restore it
|
|
// later.
|
|
vtkgl::UseProgram(progId);
|
|
vtkOpenGLCheckErrorMacro("failed at glUseProgram");
|
|
}
|
|
else
|
|
{
|
|
GLint value;
|
|
glGetIntegerv(vtkgl::CURRENT_PROGRAM, &value);
|
|
if (static_cast<GLuint>(value) != progId)
|
|
{
|
|
this->SavedId = static_cast<unsigned int>(value);
|
|
#ifndef NDEBUG
|
|
if (this->SavedId != 0)
|
|
{
|
|
vtkWarningMacro(<<"another program was used (id=" << this->SavedId
|
|
<<"), our id is" << progId << ".");
|
|
}
|
|
#endif
|
|
vtkgl::UseProgram(progId);
|
|
vtkOpenGLCheckErrorMacro("failed at glUseProgram");
|
|
}
|
|
assert("check: in_use" && this->IsUsed());
|
|
}
|
|
this->SendUniforms();
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Description:
|
|
// Restore the previous shader program (or fixed-pipeline).
|
|
void vtkShaderProgram2::Restore()
|
|
{
|
|
assert("pre: context_is_set" && this->Context);
|
|
assert("pre: current_context_matches" && this->Context->IsCurrent());
|
|
|
|
if (this->DisplayListUnderCreationInCompileMode())
|
|
{
|
|
vtkgl::UseProgram(0);
|
|
this->SavedId = 0;
|
|
}
|
|
else
|
|
{
|
|
GLint value;
|
|
glGetIntegerv(vtkgl::CURRENT_PROGRAM, &value);
|
|
if (static_cast<GLuint>(value) != static_cast<GLuint>(this->Id))
|
|
{
|
|
vtkErrorMacro("Restore failed because the porgram is not in use");
|
|
}
|
|
vtkgl::UseProgram(static_cast<GLuint>(this->SavedId));
|
|
vtkOpenGLCheckErrorMacro("failed at glUseProgram");
|
|
this->SavedId = 0;
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Description:
|
|
// Force the current shader program to be the fixed-pipeline.
|
|
// Warning: this call will be compiled if called inside a display list
|
|
// creation.
|
|
void vtkShaderProgram2::RestoreFixedPipeline()
|
|
{
|
|
assert("pre: context_is_set" && this->Context);
|
|
assert("pre: current_context_matches" && this->Context->IsCurrent());
|
|
|
|
vtkgl::UseProgram(0);
|
|
this->SavedId = 0;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
void vtkShaderProgram2::Build()
|
|
{
|
|
assert("pre: context_is_set" && this->Context);
|
|
assert("pre: current_context_matches" && this->Context->IsCurrent());
|
|
|
|
if (this->Id == 0 || this->LastLinkTime<this->MTime ||
|
|
(this->Shaders && this->LastLinkTime<this->Shaders->GetMTime()))
|
|
{
|
|
this->LastBuildStatus=VTK_SHADER_PROGRAM2_COMPILE_FAILED;
|
|
GLuint progId = static_cast<GLuint>(this->Id);
|
|
if (progId == 0)
|
|
{
|
|
progId = vtkgl::CreateProgram();
|
|
vtkOpenGLCheckErrorMacro("failed at glCreateProgram");
|
|
if (progId == 0)
|
|
{
|
|
vtkErrorMacro("failed to create program");
|
|
return;
|
|
}
|
|
|
|
this->Id = static_cast<unsigned int>(progId);
|
|
}
|
|
// Detach all previous shaders (some may have disappeared
|
|
// from this->Shaders)
|
|
GLint numberOfAttachedShaders;
|
|
vtkgl::GetProgramiv(progId, vtkgl::ATTACHED_SHADERS, &numberOfAttachedShaders);
|
|
if (numberOfAttachedShaders > 0)
|
|
{
|
|
GLuint *attachedShaders = new GLuint[numberOfAttachedShaders];
|
|
vtkgl::GetAttachedShaders(progId,numberOfAttachedShaders,0, attachedShaders);
|
|
int i = 0;
|
|
while(i < numberOfAttachedShaders)
|
|
{
|
|
vtkgl::DetachShader(progId, attachedShaders[i]);
|
|
vtkOpenGLCheckErrorMacro("failed at glDettachShader");
|
|
|
|
++i;
|
|
}
|
|
delete[] attachedShaders;
|
|
}
|
|
|
|
// We compile all the shaders, even if one fails so that
|
|
// we can get info logs for all shaders.
|
|
bool compileDone = true;
|
|
this->Shaders->InitTraversal();
|
|
vtkShader2 *s = this->Shaders->GetNextShader();
|
|
while (s)
|
|
{
|
|
// Propagate the context to the shader.
|
|
s->SetContext(this->Context);
|
|
s->Compile();
|
|
if (s->GetLastCompileStatus())
|
|
{
|
|
vtkgl::AttachShader(progId, static_cast<GLuint>(s->GetId()));
|
|
vtkOpenGLCheckErrorMacro("failed at glAttachShader");
|
|
}
|
|
else
|
|
{
|
|
compileDone = false;
|
|
if (this->PrintErrors)
|
|
{
|
|
vtkErrorMacro(
|
|
<<" a shader failed to compile. Its log is:\n"
|
|
<< s->GetLastCompileLog() << "\n. Its source code is:\n"
|
|
<< s->GetSourceCode());
|
|
}
|
|
}
|
|
s = this->Shaders->GetNextShader();
|
|
}
|
|
|
|
if (compileDone)
|
|
{
|
|
this->LastBuildStatus = VTK_SHADER_PROGRAM2_LINK_FAILED;
|
|
|
|
// It is required to pass geometry shader parameters before linking.
|
|
if (this->HasGeometryShaders())
|
|
{
|
|
vtkgl::ProgramParameteriARB(progId, vtkgl::GEOMETRY_INPUT_TYPE_ARB,
|
|
static_cast<GLint>(vtkGeometryTypeInVTKToGL[this->GeometryTypeIn]));
|
|
vtkgl::ProgramParameteriARB(progId, vtkgl::GEOMETRY_OUTPUT_TYPE_ARB,
|
|
static_cast<GLint>(vtkGeometryTypeOutVTKToGL[this->GeometryTypeOut]));
|
|
vtkgl::ProgramParameteriARB(progId, vtkgl::GEOMETRY_VERTICES_OUT_ARB,
|
|
this->GeometryVerticesOut);
|
|
}
|
|
|
|
vtkgl::LinkProgram(progId);
|
|
vtkOpenGLCheckErrorMacro("failed at glLinkProgram");
|
|
|
|
GLint value;
|
|
vtkgl::GetProgramiv(progId, vtkgl::LINK_STATUS ,&value);
|
|
if (value == GL_TRUE)
|
|
{
|
|
this->LastBuildStatus = VTK_SHADER_PROGRAM2_LINK_SUCCEEDED;
|
|
}
|
|
|
|
vtkgl::GetProgramiv(progId, vtkgl::INFO_LOG_LENGTH, &value);
|
|
if (static_cast<size_t>(value)>this->LastLinkLogCapacity)
|
|
{
|
|
delete[] this->LastLinkLog;
|
|
this->LastLinkLogCapacity = static_cast<size_t>(value);
|
|
this->LastLinkLog = new char[this->LastLinkLogCapacity];
|
|
}
|
|
vtkgl::GetProgramInfoLog(progId, value, 0, this->LastLinkLog);
|
|
|
|
if (this->LastBuildStatus == VTK_SHADER_PROGRAM2_LINK_SUCCEEDED)
|
|
{
|
|
this->LastLinkTime.Modified();
|
|
}
|
|
else
|
|
{
|
|
if (this->PrintErrors)
|
|
{
|
|
vtkErrorMacro(<<" the shader program failed to link. Its log is:\n"
|
|
<< this->GetLastLinkLog() << "the shaders are: ");
|
|
size_t i = 0;
|
|
size_t c = static_cast<size_t>(this->Shaders->GetNumberOfItems());
|
|
this->Shaders->InitTraversal();
|
|
s = this->Shaders->GetNextShader();
|
|
while (s)
|
|
{
|
|
vtkErrorMacro(<<"shader #" << i << "/" << c <<
|
|
" (" <<s->GetTypeAsString()
|
|
<<") log is:\n" << s->GetLastCompileLog()
|
|
<< "\n. Its source code is:\n" << s->GetSourceCode());
|
|
s = this->Shaders->GetNextShader();
|
|
++i;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
void vtkShaderProgram2::SendUniforms()
|
|
{
|
|
assert("pre: context_is_set" && this->Context);
|
|
assert("pre: current_context_matches" && this->Context->IsCurrent());
|
|
assert("pre: built" &&
|
|
this->GetLastBuildStatus() == VTK_SHADER_PROGRAM2_LINK_SUCCEEDED);
|
|
|
|
bool needUpdate = this->LastSendUniformsTime<this->LastLinkTime;
|
|
if (!needUpdate)
|
|
{
|
|
needUpdate=this->UniformVariables!=0 &&
|
|
this->LastSendUniformsTime<this->UniformVariables->GetMTime();
|
|
}
|
|
|
|
this->Shaders->InitTraversal();
|
|
vtkShader2 *s = this->Shaders->GetNextShader();
|
|
vtkUniformVariables *list;
|
|
while (!needUpdate && s)
|
|
{
|
|
list = s->GetUniformVariables();
|
|
needUpdate = (list!=0 && this->LastSendUniformsTime<list->GetMTime());
|
|
s = this->Shaders->GetNextShader();
|
|
}
|
|
|
|
if (needUpdate)
|
|
{
|
|
bool inListCreation = this->DisplayListUnderCreationInCompileMode();
|
|
bool isUsed = false;
|
|
if (!inListCreation)
|
|
{
|
|
isUsed = this->IsUsed();
|
|
if (!isUsed)
|
|
{
|
|
this->Use();
|
|
}
|
|
}
|
|
|
|
// NOTE -- a given uniform variable state is held and is set from
|
|
// two different places, in the program and in the shader. If not
|
|
// used carefully this is both inefficient and error prone!! However
|
|
// it's done like that to simplify support for arbitrary collections of
|
|
// shaders.
|
|
|
|
this->Shaders->InitTraversal();
|
|
s = this->Shaders->GetNextShader();
|
|
while (s)
|
|
{
|
|
list = s->GetUniformVariables();
|
|
list->Start();
|
|
while(!list->IsAtEnd())
|
|
{
|
|
const char *name = list->GetCurrentName();
|
|
GLint uniformId = this->GetUniformLocationInternal(name);
|
|
vtkOpenGLCheckErrorMacro("failed at glGetUniformLocation");
|
|
if (uniformId != -1)
|
|
{
|
|
// -1 means is not an active uniform
|
|
// it does not mean it is an error.
|
|
list->SendCurrentUniform(uniformId);
|
|
}
|
|
list->Next();
|
|
}
|
|
s = this->Shaders->GetNextShader();
|
|
}
|
|
|
|
// override some of the values of the uniform variables set at the shader
|
|
// level with the uniform values set at the program level.
|
|
list = this->GetUniformVariables();
|
|
list->Start();
|
|
while (!list->IsAtEnd())
|
|
{
|
|
const char *name = list->GetCurrentName();
|
|
GLint uniformId = this->GetUniformLocationInternal(name);
|
|
if (uniformId != -1)
|
|
{
|
|
// -1 means is not an active uniform
|
|
// it does not mean it is an error.
|
|
list->SendCurrentUniform(uniformId);
|
|
}
|
|
list->Next();
|
|
}
|
|
|
|
if (!inListCreation && !isUsed)
|
|
{
|
|
this->Restore();
|
|
}
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
int vtkShaderProgram2::GetUniformLocationInternal(const char *name)
|
|
{
|
|
assert(this->Id!=0);
|
|
assert(name!=NULL);
|
|
int loc = (int)vtkgl::GetUniformLocation((GLuint)this->Id, name);
|
|
vtkOpenGLCheckErrorMacro("failed at glGetUniformLocation");
|
|
return loc;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
int vtkShaderProgram2::GetUniformLocation(const char *name)
|
|
{
|
|
assert(this->Id!=0);
|
|
assert(name!=NULL);
|
|
int loc = (int)vtkgl::GetUniformLocation((GLuint)this->Id, name);
|
|
vtkOpenGLCheckErrorMacro("failed at glGetUniformLocation");
|
|
#ifndef NDEBUG
|
|
if (loc==-1)
|
|
{ vtkErrorMacro("no variable named " << name); }
|
|
#endif
|
|
return loc;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
#define vtkSetUniformAPIMacro(ctype, num, letter) \
|
|
void vtkShaderProgram2::SetUniform##num##letter(int loc, ctype *val) \
|
|
{ \
|
|
assert(loc!=-1); \
|
|
vtkgl::Uniform##num##letter##v(loc, 1, val); \
|
|
vtkOpenGLCheckErrorMacro("failed at glUniform" #num #letter "v"); \
|
|
}
|
|
vtkSetUniformAPIMacro(float, 1, f);
|
|
vtkSetUniformAPIMacro(float, 2, f);
|
|
vtkSetUniformAPIMacro(float, 3, f);
|
|
vtkSetUniformAPIMacro(float, 4, f);
|
|
vtkSetUniformAPIMacro(int, 1, i);
|
|
vtkSetUniformAPIMacro(int, 2, i);
|
|
vtkSetUniformAPIMacro(int, 3, i);
|
|
vtkSetUniformAPIMacro(int, 4, i);
|
|
|
|
// ----------------------------------------------------------------------------
|
|
void vtkShaderProgram2::UseProgram()
|
|
{
|
|
assert(this->Context);
|
|
vtkgl::UseProgram((GLuint)this->Id);
|
|
vtkOpenGLCheckErrorMacro("failed at glUseProgram");
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
void vtkShaderProgram2::UnuseProgram()
|
|
{
|
|
assert(this->Context);
|
|
vtkgl::UseProgram((GLuint)0U);
|
|
vtkOpenGLCheckErrorMacro("failed at glUseProgram");
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Description:
|
|
// Introspection. Return the list of active uniform variables of the program.
|
|
void vtkShaderProgram2::PrintActiveUniformVariables(
|
|
ostream &os,
|
|
vtkIndent indent)
|
|
{
|
|
assert("pre: context_is_set" && this->Context);
|
|
assert("pre: current_context_matches" && this->Context->IsCurrent());
|
|
assert("pre: built" &&
|
|
this->GetLastBuildStatus() == VTK_SHADER_PROGRAM2_LINK_SUCCEEDED);
|
|
|
|
GLint params;
|
|
GLuint progId = static_cast<GLuint>(this->Id);
|
|
|
|
// info about the list of active uniform variables
|
|
vtkgl::GetProgramiv(progId, vtkgl::ACTIVE_UNIFORMS, ¶ms);
|
|
os<< indent << "There are " << params << " active uniform variables."<<endl;
|
|
GLuint i = 0;
|
|
GLuint c = static_cast<GLuint>(params);
|
|
vtkgl::GetProgramiv(progId, vtkgl::ACTIVE_UNIFORM_MAX_LENGTH, ¶ms);
|
|
|
|
GLint buffSize = params;
|
|
char *name = new char[buffSize + 1];
|
|
GLint size;
|
|
GLenum type;
|
|
bool isInt;
|
|
int elementSize;
|
|
bool isSampler;
|
|
GLenum textureBinding;
|
|
while (i < c)
|
|
{
|
|
vtkgl::GetActiveUniform(progId, i, buffSize, 0, &size, &type, name);
|
|
os << indent << i <<" ";
|
|
os << indent;
|
|
isInt = true;
|
|
isSampler = false;
|
|
textureBinding = GL_TEXTURE_BINDING_1D;
|
|
elementSize = 1;
|
|
switch(type)
|
|
{
|
|
case GL_FLOAT:
|
|
os << "float";
|
|
isInt = false;
|
|
break;
|
|
case vtkgl::FLOAT_VEC2:
|
|
os << "vec2";
|
|
isInt = false;
|
|
elementSize = 2;
|
|
break;
|
|
case vtkgl::FLOAT_VEC3:
|
|
os << "vec3";
|
|
isInt = false;
|
|
elementSize = 3;
|
|
break;
|
|
case vtkgl::FLOAT_VEC4:
|
|
os << "vec4";
|
|
isInt = false;
|
|
elementSize = 4;
|
|
break;
|
|
case GL_INT:
|
|
os << "int";
|
|
break;
|
|
case vtkgl::INT_VEC2:
|
|
os << "ivec2";
|
|
elementSize = 2;
|
|
break;
|
|
case vtkgl::INT_VEC3:
|
|
os << "ivec3";
|
|
elementSize = 3;
|
|
break;
|
|
case vtkgl::INT_VEC4:
|
|
os << "ivec4";
|
|
elementSize = 4;
|
|
break;
|
|
case vtkgl::BOOL:
|
|
os << "bool";
|
|
break;
|
|
case vtkgl::BOOL_VEC2:
|
|
os << "bvec2";
|
|
elementSize = 2;
|
|
break;
|
|
case vtkgl::BOOL_VEC3:
|
|
os << "bvec3";
|
|
elementSize = 3;
|
|
break;
|
|
case vtkgl::BOOL_VEC4:
|
|
os << "bvec4";
|
|
elementSize = 4;
|
|
break;
|
|
case vtkgl::FLOAT_MAT2:
|
|
os << "mat2";
|
|
isInt = false;
|
|
elementSize = 4;
|
|
break;
|
|
case vtkgl::FLOAT_MAT3:
|
|
os << "mat3";
|
|
isInt = false;
|
|
elementSize = 9;
|
|
break;
|
|
case vtkgl::FLOAT_MAT4:
|
|
os << "mat4";
|
|
isInt = false;
|
|
elementSize = 16;
|
|
break;
|
|
case vtkgl::FLOAT_MAT2x3:
|
|
os << "mat2x3";
|
|
isInt = false;
|
|
elementSize = 6;
|
|
break;
|
|
case vtkgl::FLOAT_MAT2x4:
|
|
os << "mat2x4";
|
|
isInt = false;
|
|
elementSize = 8;
|
|
break;
|
|
case vtkgl::FLOAT_MAT3x2:
|
|
os << "mat3x2";
|
|
isInt = false;
|
|
elementSize = 6;
|
|
break;
|
|
case vtkgl::FLOAT_MAT3x4:
|
|
os << "mat3x4";
|
|
isInt = false;
|
|
elementSize = 12;
|
|
break;
|
|
case vtkgl::FLOAT_MAT4x2:
|
|
os << "mat4x2";
|
|
isInt = false;
|
|
elementSize = 8;
|
|
break;
|
|
case vtkgl::FLOAT_MAT4x3:
|
|
os << "mat4x3";
|
|
isInt = false;
|
|
elementSize = 12;
|
|
break;
|
|
case vtkgl::SAMPLER_1D:
|
|
os << "sampler1D";
|
|
isSampler = true;
|
|
textureBinding = GL_TEXTURE_BINDING_1D;
|
|
break;
|
|
case vtkgl::SAMPLER_2D:
|
|
os << "sampler2D";
|
|
isSampler = true;
|
|
textureBinding = GL_TEXTURE_BINDING_2D;
|
|
break;
|
|
case vtkgl::SAMPLER_3D:
|
|
os << "sampler3D";
|
|
isSampler = true;
|
|
textureBinding = vtkgl::TEXTURE_BINDING_3D;
|
|
break;
|
|
case vtkgl::SAMPLER_CUBE:
|
|
os<<"samplerCube";
|
|
isSampler = true;
|
|
textureBinding = vtkgl::TEXTURE_BINDING_CUBE_MAP;
|
|
break;
|
|
case vtkgl::SAMPLER_1D_SHADOW:
|
|
os << "sampler1DShadow";
|
|
isSampler = true;
|
|
textureBinding = GL_TEXTURE_BINDING_1D;
|
|
break;
|
|
case vtkgl::SAMPLER_2D_SHADOW:
|
|
os << "sampler2DShadow";
|
|
isSampler = true;
|
|
textureBinding = GL_TEXTURE_BINDING_2D;
|
|
break;
|
|
case vtkgl::SAMPLER_2D_RECT_ARB:
|
|
os << "sampler2DRect";
|
|
isSampler = true;
|
|
textureBinding = vtkgl::TEXTURE_BINDING_RECTANGLE_ARB;
|
|
break;
|
|
case vtkgl::SAMPLER_2D_RECT_SHADOW_ARB:
|
|
os << "sampler2DRectShadow";
|
|
isSampler = true;
|
|
textureBinding = vtkgl::TEXTURE_BINDING_RECTANGLE_ARB;
|
|
break;
|
|
}
|
|
os << " " << name;
|
|
if (size>1)
|
|
{
|
|
os << "[" << size << "]";
|
|
}
|
|
os << "=";
|
|
if (size > 1)
|
|
{
|
|
os << "{";
|
|
}
|
|
GLint *ivalues = 0;
|
|
GLfloat *fvalues = 0;
|
|
|
|
if (isInt)
|
|
{
|
|
ivalues = new GLint[elementSize];
|
|
}
|
|
else
|
|
{
|
|
fvalues = new GLfloat[elementSize];
|
|
}
|
|
|
|
int element = 0;
|
|
while (element<size)
|
|
{
|
|
std::ostringstream ost2;
|
|
ost2 << name;
|
|
if (size > 1)
|
|
{
|
|
ost2<<"["<<element<<"]";
|
|
}
|
|
vtkStdString arrayElementName;
|
|
arrayElementName = ost2.str();
|
|
GLint loc = vtkgl::GetUniformLocation(progId,arrayElementName);
|
|
if (elementSize > 1)
|
|
{
|
|
os << "{";
|
|
}
|
|
if (loc == -1)
|
|
{
|
|
// this a built-in variable like:
|
|
// gl_DepthRange.near, gl_DepthRange.far, gl_DepthRange.diff,
|
|
// gl_ProjectionMatrixInverse, ...
|
|
// we cannot get the value through vtkgl::GetUniform*()
|
|
os << "<built-in value>";
|
|
}
|
|
else
|
|
{
|
|
if (isInt)
|
|
{
|
|
vtkgl::GetUniformiv(progId, loc, ivalues);
|
|
int j = 0;
|
|
while (j < elementSize)
|
|
{
|
|
os << ivalues[j];
|
|
if (j < (elementSize - 1))
|
|
{
|
|
os << " ";
|
|
}
|
|
++j;
|
|
}
|
|
if (isSampler)
|
|
{
|
|
os << " (Texture Unit)->";
|
|
GLint savedActiveTextureUnit;
|
|
glGetIntegerv(vtkgl::ACTIVE_TEXTURE,&savedActiveTextureUnit);
|
|
savedActiveTextureUnit = static_cast<GLint>(
|
|
static_cast<GLenum>(savedActiveTextureUnit) - vtkgl::TEXTURE0);
|
|
if (savedActiveTextureUnit != ivalues[0])
|
|
{
|
|
vtkgl::ActiveTexture(vtkgl::TEXTURE0 +
|
|
static_cast<GLenum>(ivalues[0]));
|
|
}
|
|
GLint textureObject;
|
|
glGetIntegerv(textureBinding, &textureObject);
|
|
if (savedActiveTextureUnit != ivalues[0])
|
|
{
|
|
vtkgl::ActiveTexture(
|
|
vtkgl::TEXTURE0 + static_cast<GLenum>(savedActiveTextureUnit));
|
|
}
|
|
os << textureObject << " (Texture Object)";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
vtkgl::GetUniformfv(progId, loc, fvalues);
|
|
int j = 0;
|
|
while(j < elementSize)
|
|
{
|
|
os << fvalues[j];
|
|
if (j < (elementSize - 1))
|
|
{
|
|
os << " ";
|
|
}
|
|
++j;
|
|
}
|
|
}
|
|
}
|
|
if (elementSize > 1)
|
|
{
|
|
os << "}";
|
|
}
|
|
if (element < (size - 1))
|
|
{
|
|
os << " ";
|
|
}
|
|
++element;
|
|
}
|
|
delete[] ivalues;
|
|
delete[] fvalues;
|
|
|
|
if (size > 1)
|
|
{
|
|
os << "}";
|
|
}
|
|
os << endl;
|
|
++i;
|
|
}
|
|
delete[] name;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Description:
|
|
// Call PrintActiveUniformVariables on cout. Useful for calling inside gdb.
|
|
void vtkShaderProgram2::PrintActiveUniformVariablesOnCout()
|
|
{
|
|
assert("pre: context_is_set" && this->Context);
|
|
assert("pre: current_context_matches" && this->Context->IsCurrent());
|
|
assert("pre: built" &&
|
|
this->GetLastBuildStatus() == VTK_SHADER_PROGRAM2_LINK_SUCCEEDED);
|
|
vtkIndent i;
|
|
this->PrintActiveUniformVariables(cout, i);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Description:
|
|
// Tell if the shader program is valid with the current OpenGL state.
|
|
bool vtkShaderProgram2::IsValid()
|
|
{
|
|
assert("pre: context_is_set" && this->Context);
|
|
assert("pre: current_context_matches" && this->Context->IsCurrent());
|
|
assert("pre: built" &&
|
|
this->GetLastBuildStatus() == VTK_SHADER_PROGRAM2_LINK_SUCCEEDED);
|
|
|
|
// this line change the program log.
|
|
GLuint progId = static_cast<GLuint>(this->Id);
|
|
vtkgl::ValidateProgram(progId);
|
|
|
|
GLint value;
|
|
vtkgl::GetProgramiv(progId, vtkgl::VALIDATE_STATUS, &value);
|
|
|
|
bool result = (value == GL_TRUE);
|
|
|
|
vtkgl::GetProgramiv(progId, vtkgl::INFO_LOG_LENGTH, &value);
|
|
if (static_cast<size_t>(value)>this->LastValidateLogCapacity)
|
|
{
|
|
delete[] this->LastValidateLog;
|
|
this->LastValidateLogCapacity = static_cast<size_t>(value);
|
|
this->LastValidateLog = new char[this->LastValidateLogCapacity];
|
|
}
|
|
vtkgl::GetProgramInfoLog(progId, value, 0, this->LastValidateLog);
|
|
|
|
return result;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Description:
|
|
// Tells if the last build: failed during compilation of one of the
|
|
// shader, fails during link of the program or succeeded to link the
|
|
// program.
|
|
// Initial value is VTK_SHADER_PROGRAM2_COMPILE_FAILED.
|
|
// \post valid_value: result== VTK_SHADER_PROGRAM2_COMPILE_FAILED ||
|
|
// result==VTK_SHADER_PROGRAM2_LINK_FAILED ||
|
|
// result==VTK_SHADER_PROGRAM2_LINK_SUCCEEDED
|
|
int vtkShaderProgram2::GetLastBuildStatus()
|
|
{
|
|
return this->LastBuildStatus;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Description:
|
|
// Return the log of the last link as a string.
|
|
// Initial value is the empty string ""='\0'.
|
|
// \post result_exists: result!=0
|
|
const char *vtkShaderProgram2::GetLastLinkLog()
|
|
{
|
|
assert("post: result_exists" && this->LastLinkLog);
|
|
return this->LastLinkLog;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Description:
|
|
// Return the log of the last call to IsValid as a string.
|
|
// Initial value is the empty string ""='\0'.
|
|
// \post result_exists: result!=0
|
|
const char *vtkShaderProgram2::GetLastValidateLog()
|
|
{
|
|
assert("post: result_exists" && this->LastValidateLog);
|
|
return this->LastValidateLog;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkShaderProgram2::GetAttributeLocation(const char *name)
|
|
{
|
|
assert("pre: context_is_set" && this->Context);
|
|
assert("pre: current_context_matches" && this->Context->IsCurrent());
|
|
assert("pre: name_exists" && name);
|
|
assert("pre: built" &&
|
|
this->LastBuildStatus == VTK_SHADER_PROGRAM2_LINK_SUCCEEDED);
|
|
int loc = vtkgl::GetAttribLocation(this->Id, name);
|
|
vtkOpenGLCheckErrorMacro("glGetAttribLocation");
|
|
return loc;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkShaderProgram2::PrintSelf(ostream& os, vtkIndent indent)
|
|
{
|
|
this->Superclass::PrintSelf(os, indent);
|
|
|
|
os << indent << "Context: ";
|
|
if (this->Context)
|
|
{
|
|
os << static_cast<void *>(this->Context) <<endl;
|
|
}
|
|
else
|
|
{
|
|
os << "none" << endl;
|
|
}
|
|
|
|
os << indent << "PrintErrors: ";
|
|
if (this->PrintErrors)
|
|
{
|
|
os << "true" << endl;
|
|
}
|
|
else
|
|
{
|
|
os << "false" << endl;
|
|
}
|
|
|
|
os << indent << "LastBuildStatus: ";
|
|
switch(this->LastBuildStatus)
|
|
{
|
|
case VTK_SHADER_PROGRAM2_COMPILE_FAILED:
|
|
os << "Compile failed";
|
|
break;
|
|
case VTK_SHADER_PROGRAM2_LINK_FAILED:
|
|
os << "Link failed";
|
|
break;
|
|
case VTK_SHADER_PROGRAM2_LINK_SUCCEEDED:
|
|
os << "Link succeeded";
|
|
break;
|
|
default:
|
|
os << "ERROR unknown value!";
|
|
break;
|
|
}
|
|
os << endl;
|
|
|
|
os << indent << "OpenGL Id: " << this->Id << endl;
|
|
|
|
os << indent << "GeometryTypeIn: " <<
|
|
vtkGeometryTypeInAsStringArray[this->GeometryTypeIn] << endl;
|
|
|
|
os << indent << "GeometryTypeOut: " <<
|
|
vtkGeometryTypeInAsStringArray[this->GeometryTypeOut] << endl;
|
|
|
|
os << indent << "GeometryVerticesOut: " << this->GeometryVerticesOut << endl;
|
|
|
|
os << indent << "UniformVariables: ";
|
|
if (this->UniformVariables)
|
|
{
|
|
this->UniformVariables->PrintSelf(os, indent);
|
|
}
|
|
else
|
|
{
|
|
os << "none" << endl;
|
|
}
|
|
|
|
os << indent << "Shaders: ";
|
|
if (this->Shaders)
|
|
{
|
|
this->Shaders->PrintSelf(os, indent);
|
|
}
|
|
else
|
|
{
|
|
os << "none" << endl;
|
|
}
|
|
}
|