panda3d/panda/src/glstuff/glShaderContext_src.cxx

2493 lines
81 KiB
C++

// Filename: glShaderContext_src.cxx
// Created by: jyelon (01Sep05)
// Updated by: fperazzi, PandaSE (29Apr10) (updated CLP with note that some
// parameter types only supported under Cg)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#ifndef OPENGLES_1
#include "pStatGPUTimer.h"
TypeHandle CLP(ShaderContext)::_type_handle;
#ifndef GL_GEOMETRY_SHADER_EXT
#define GL_GEOMETRY_SHADER_EXT 0x8DD9
#endif
#ifndef GL_GEOMETRY_VERTICES_OUT_EXT
#define GL_GEOMETRY_VERTICES_OUT_EXT 0x8DDA
#endif
#ifndef GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT
#define GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT 0x8DE0
#endif
////////////////////////////////////////////////////////////////////
// Function: GLShaderContext::ParseAndSetShaderUniformVars
// Access: Public
// Description: The Panda CG shader syntax defines a useful set of shorthand notations for setting nodepath
// properties as shaderinputs. For example, float4 mspos_XXX refers to nodepath XXX's position
// in model space. This function is a rough attempt to reimplement some of the shorthand
// notations for GLSL. The code is ~99% composed of excerpts dealing with matrix shaderinputs
// from Shader::compile_parameter.
//
// Given a uniform variable name queried from the compiled shader passed in via arg_id,
// 1) parse the name
// 2a) if the name refers to a Panda shorthand notation
// push the appropriate matrix into shader._mat_spec
// returns True
// 2b) If the name doesn't refer to a Panda shorthand notation
// returns False
//
// The boolean return is used to notify down-river processing whether the shader var/parm was
// actually picked up and the appropriate ShaderMatSpec pushed onto _mat_spec.
////////////////////////////////////////////////////////////////////
bool CLP(ShaderContext)::
parse_and_set_short_hand_shader_vars(Shader::ShaderArgId &arg_id, GLenum param_type, GLint param_size, Shader *objShader) {
Shader::ShaderArgInfo p;
p._id = arg_id;
p._cat = GLCAT;
string basename(arg_id._name);
// Split it at the underscores.
vector_string pieces;
tokenize(basename, pieces, "_");
if (pieces.size() == 0 || pieces[0].size() < 3) {
return false;
}
// mstrans, wstrans, vstrans, cstrans, mspos, wspos, vspos, cspos
if (strcmp(pieces[0].c_str() + 1, "strans") == 0 ||
strcmp(pieces[0].c_str() + 1, "spos") == 0) {
pieces.push_back("to");
switch (pieces[0][0]) {
case 'm':
pieces.push_back("model");
break;
case 'w':
pieces.push_back("world");
break;
case 'v':
pieces.push_back("view");
break;
case 'c':
pieces.push_back("clip");
break;
default:
return false;
}
if (strcmp(pieces[0].c_str() + 1, "strans") == 0) {
pieces[0] = "trans";
} else {
pieces[0] = "row3";
}
// mat_modelproj et al
} else if (pieces[0].size() == 3 &&
(pieces[0] == "mat" || pieces[0] == "inv" ||
pieces[0] == "tps" || pieces[0] == "itp")) {
if (!objShader->cp_errchk_parameter_words(p, 2)) {
return false;
}
string trans = pieces[0];
string matrix = pieces[1];
pieces.clear();
if (matrix == "modelview") {
tokenize("trans_model_to_apiview", pieces, "_");
} else if (matrix == "projection") {
tokenize("trans_apiview_to_apiclip", pieces, "_");
} else if (matrix == "modelproj") {
tokenize("trans_model_to_apiclip", pieces, "_");
} else {
objShader->cp_report_error(p,"unrecognized matrix name");
return false;
}
if (trans == "mat") {
pieces[0] = "trans";
} else if (trans == "inv") {
string t = pieces[1];
pieces[1] = pieces[3];
pieces[3] = t;
} else if (trans == "tps") {
pieces[0] = "tpose";
} else if (trans == "itp") {
string t = pieces[1];
pieces[1] = pieces[3];
pieces[3] = t;
pieces[0] = "tpose";
}
}
// Implement the transform-matrix generator.
if ((pieces[0] == "trans") ||
(pieces[0] == "tpose") ||
(pieces[0] == "row0") ||
(pieces[0] == "row1") ||
(pieces[0] == "row2") ||
(pieces[0] == "row3") ||
(pieces[0] == "col0") ||
(pieces[0] == "col1") ||
(pieces[0] == "col2") ||
(pieces[0] == "col3")) {
Shader::ShaderMatSpec bind;
bind._id = arg_id;
bind._func = Shader::SMF_compose;
int next = 1;
pieces.push_back("");
// Decide whether this is a matrix or vector.
if (param_type == GL_FLOAT_MAT4) {
if (pieces[0] == "trans") bind._piece = Shader::SMP_whole;
else if (pieces[0] == "tpose") bind._piece = Shader::SMP_transpose;
else {
GLCAT.error() << basename << " should be vec4, not mat3\n";
return false;
}
} else if (param_type == GL_FLOAT_MAT3) {
if (pieces[0] == "trans") bind._piece = Shader::SMP_upper3x3;
else if (pieces[0] == "tpose") bind._piece = Shader::SMP_transpose3x3;
else {
GLCAT.error() << basename << " should be vec4, not mat3\n";
return false;
}
} else if (param_type == GL_FLOAT_VEC4) {
if (pieces[0] == "trans") bind._piece = Shader::SMP_col0;
else if (pieces[0] == "tpose") bind._piece = Shader::SMP_row0;
else if (pieces[0] == "row0") bind._piece = Shader::SMP_row0;
else if (pieces[0] == "row1") bind._piece = Shader::SMP_row1;
else if (pieces[0] == "row2") bind._piece = Shader::SMP_row2;
else if (pieces[0] == "row3") bind._piece = Shader::SMP_row3;
else if (pieces[0] == "col0") bind._piece = Shader::SMP_col0;
else if (pieces[0] == "col1") bind._piece = Shader::SMP_col1;
else if (pieces[0] == "col2") bind._piece = Shader::SMP_col2;
else if (pieces[0] == "col3") bind._piece = Shader::SMP_col3;
else {
GLCAT.error() << basename << " should be mat4, not vec4\n";
return false;
}
} else if (pieces[0] == "row3") {
// We'll permit this too, simply because we can support it.
switch (param_type) {
case GL_FLOAT:
bind._piece = Shader::SMP_row3x1;
break;
case GL_FLOAT_VEC2:
bind._piece = Shader::SMP_row3x2;
break;
case GL_FLOAT_VEC3:
bind._piece = Shader::SMP_row3x3;
break;
default:
GLCAT.error() << basename << " should be vec4\n";
return false;
}
} else if (pieces[0] == "trans" || pieces[0] == "tpose") {
GLCAT.error() << basename << " should be mat4 or mat3\n";
return false;
} else {
GLCAT.error() << basename << " should be vec4\n";
return false;
}
if (!objShader->cp_parse_coord_sys(p, pieces, next, bind, true)) {
return false;
}
if (!objShader->cp_parse_delimiter(p, pieces, next)) {
return false;
}
if (!objShader->cp_parse_coord_sys(p, pieces, next, bind, false)) {
return false;
}
if (!objShader->cp_parse_eol(p, pieces, next)) {
return false;
}
// clip == apiclip in OpenGL, and the apiclip matrices are cached.
if (bind._part[0] == Shader::SMO_view_to_clip) {
bind._part[0] = Shader::SMO_view_to_apiclip;
} else if (bind._part[0] == Shader::SMO_clip_to_view) {
bind._part[0] = Shader::SMO_apiclip_to_view;
}
if (bind._part[1] == Shader::SMO_view_to_clip) {
bind._part[1] = Shader::SMO_view_to_apiclip;
} else if (bind._part[1] == Shader::SMO_clip_to_view) {
bind._part[1] = Shader::SMO_apiclip_to_view;
}
objShader->cp_optimize_mat_spec(bind);
objShader->_mat_spec.push_back(bind);
if (param_size > 1) {
// We support arrays of rows and arrays of columns, so we can
// run the GLSL shaders that cgc spits out.
if (bind._piece == Shader::SMP_row0 || bind._piece == Shader::SMP_col0) {
if (param_size > 4) {
GLCAT.warning() << basename << "[" << param_size << "] is too large, only the first four elements will be defined\n";
param_size = 4;
}
for (int i = 1; i < param_size; ++i) {
bind._id._seqno += 1;
bind._piece = (Shader::ShaderMatPiece)((int)bind._piece + 1);
objShader->_mat_spec.push_back(bind);
}
} else {
GLCAT.warning() << basename << "[" << param_size << "] should not be an array, only the first element will be defined\n";
}
}
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////
// Function: GLShaderContext::Constructor
// Access: Public
// Description: xyz
////////////////////////////////////////////////////////////////////
CLP(ShaderContext)::
CLP(ShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderContext(s) {
_glgsg = glgsg;
_glsl_program = 0;
_uses_standard_vertex_arrays = false;
_has_divisor = false;
_color_attrib_index = -1;
_transform_table_index = -1;
_slider_table_index = -1;
_validated = !gl_validate_shaders;
nassertv(s->get_language() == Shader::SL_GLSL);
// We compile and analyze the shader here, instead of in shader.cxx,
// to avoid gobj getting a dependency on GL stuff.
if (!glsl_compile_and_link()) {
release_resources();
s->_error_flag = true;
return;
}
// Create a buffer the size of the longest uniform name.
GLint name_buflen = 0;
_glgsg->_glGetProgramiv(_glsl_program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &name_buflen);
char* name_buffer = (char *)alloca(max(64, name_buflen));
#ifndef OPENGLES
// Get the used uniform blocks.
if (_glgsg->_supports_uniform_buffers) {
GLint block_count, block_maxlength;
_glgsg->_glGetProgramiv(_glsl_program, GL_ACTIVE_UNIFORM_BLOCKS, &block_count);
_glgsg->_glGetProgramiv(_glsl_program, GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, &block_maxlength);
char *block_name_cstr = (char *)alloca(max(64, block_maxlength));
for (int i = 0; i < block_count; ++i) {
block_name_cstr[0] = 0;
_glgsg->_glGetActiveUniformBlockName(_glsl_program, i, block_maxlength, NULL, block_name_cstr);
reflect_uniform_block(i, block_name_cstr, name_buffer, name_buflen);
}
}
#endif // !OPENGLES
// Bind the program, so that we can call glUniform1i for the textures.
_glgsg->_glUseProgram(_glsl_program);
// Analyze the uniforms.
GLint param_count = 0;
_glgsg->_glGetProgramiv(_glsl_program, GL_ACTIVE_UNIFORMS, &param_count);
_shader->_ptr_spec.clear();
_shader->_mat_spec.clear();
_shader->_tex_spec.clear();
for (int i = 0; i < param_count; ++i) {
reflect_uniform(i, name_buffer, name_buflen);
}
// Now we've processed the uniforms, we'll process the attribs.
_glgsg->_glGetProgramiv(_glsl_program, GL_ACTIVE_ATTRIBUTES, &param_count);
_glgsg->_glGetProgramiv(_glsl_program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &name_buflen);
name_buffer = (char *)alloca(max(64, name_buflen));
_shader->_var_spec.clear();
for (int i = 0; i < param_count; ++i) {
reflect_attribute(i, name_buffer, name_buflen);
}
_glgsg->_glUseProgram(0);
_glgsg->report_my_gl_errors();
}
////////////////////////////////////////////////////////////////////
// Function: GLShaderContext::reflect_attribute
// Access: Public
// Description: Analyzes the vertex attribute and stores the
// information it needs to remember.
////////////////////////////////////////////////////////////////////
void CLP(ShaderContext)::
reflect_attribute(int i, char *name_buffer, GLsizei name_buflen) {
GLint param_size;
GLenum param_type;
// Get the name, size, and type of this attribute.
name_buffer[0] = 0;
_glgsg->_glGetActiveAttrib(_glsl_program, i, name_buflen, NULL,
&param_size, &param_type, name_buffer);
// Get the attrib location.
GLint p = _glgsg->_glGetAttribLocation(_glsl_program, name_buffer);
if (GLCAT.is_debug()) {
GLCAT.debug()
<< "Active attribute " << name_buffer << " with size " << param_size
<< " and type 0x" << hex << param_type << dec
<< " is bound to location " << p << "\n";
}
if (p == -1 || strncmp(name_buffer, "gl_", 3) == 0) {
// A gl_ attribute such as gl_Vertex requires us to pass the standard
// vertex arrays as we would do without shader. Not all drivers return
// -1 in glGetAttribLocation for gl_ prefixed attributes, so we check the
// prefix of the input ourselves, just to be sure.
_uses_standard_vertex_arrays = true;
return;
}
Shader::ShaderArgId arg_id;
arg_id._name = name_buffer;
arg_id._seqno = p;
Shader::ShaderVarSpec bind;
bind._id = arg_id;
bind._name = NULL;
bind._append_uv = -1;
bind._elements = 1;
// Check if this is an integer input- if so, we have to bind it differently.
bind._integer = (param_type == GL_BOOL ||
param_type == GL_BOOL_VEC2 ||
param_type == GL_BOOL_VEC3 ||
param_type == GL_BOOL_VEC4 ||
param_type == GL_INT ||
param_type == GL_INT_VEC2 ||
param_type == GL_INT_VEC3 ||
param_type == GL_INT_VEC4 ||
#ifndef OPENGLES
param_type == GL_UNSIGNED_INT_VEC2 ||
param_type == GL_UNSIGNED_INT_VEC3 ||
param_type == GL_UNSIGNED_INT_VEC4 ||
#endif
param_type == GL_UNSIGNED_INT);
// Check if it has a p3d_ prefix - if so, assign special meaning.
if (strncmp(name_buffer, "p3d_", 4) == 0) {
string noprefix(name_buffer + 4);
if (noprefix == "Vertex") {
bind._name = InternalName::get_vertex();
} else if (noprefix == "Normal") {
bind._name = InternalName::get_normal();
} else if (noprefix == "Color") {
bind._name = InternalName::get_color();
// Save the index, so we can apply special handling to this attrib.
_color_attrib_index = p;
} else if (noprefix.substr(0, 7) == "Tangent") {
bind._name = InternalName::get_tangent();
if (noprefix.size() > 7) {
bind._append_uv = atoi(noprefix.substr(7).c_str());
}
} else if (noprefix.substr(0, 8) == "Binormal") {
bind._name = InternalName::get_binormal();
if (noprefix.size() > 8) {
bind._append_uv = atoi(noprefix.substr(8).c_str());
}
} else if (noprefix.substr(0, 13) == "MultiTexCoord") {
bind._name = InternalName::get_texcoord();
bind._append_uv = atoi(noprefix.substr(13).c_str());
} else {
GLCAT.error() << "Unrecognized vertex attrib '" << name_buffer << "'!\n";
return;
}
} else {
// Arbitrarily named attribute.
bind._name = InternalName::make(name_buffer);
}
// Get the number of bind points for arrays and matrices.
switch (param_type) {
case GL_FLOAT_MAT3:
#ifndef OPENGLES
case GL_DOUBLE_MAT3:
#endif
bind._elements = 3 * param_size;
break;
case GL_FLOAT_MAT4:
#ifndef OPENGLES
case GL_DOUBLE_MAT4:
#endif
bind._elements = 4 * param_size;
break;
default:
bind._elements = param_size;
break;
}
_shader->_var_spec.push_back(bind);
}
#ifndef OPENGLES
////////////////////////////////////////////////////////////////////
// Function: GLShaderContext::reflect_uniform_block
// Access: Public
// Description: Analyzes the uniform block and stores its format.
////////////////////////////////////////////////////////////////////
void CLP(ShaderContext)::
reflect_uniform_block(int i, const char *name, char *name_buffer, GLsizei name_buflen) {
//GLint offset = 0;
GLint data_size = 0;
GLint param_count = 0;
GLsizei param_size;
_glgsg->_glGetActiveUniformBlockiv(_glsl_program, i, GL_UNIFORM_BLOCK_DATA_SIZE, &data_size);
_glgsg->_glGetActiveUniformBlockiv(_glsl_program, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &param_count);
if (param_count <= 0) {
return;
}
// We use a GeomVertexArrayFormat to describe the uniform buffer layout.
//GeomVertexArrayFormat block_format;
//block_format.set_pad_to(data_size);
// Get an array containing the indices of all the uniforms in this block.
GLuint *indices = (GLuint *)alloca(param_count * sizeof(GLint));
_glgsg->_glGetActiveUniformBlockiv(_glsl_program, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, (GLint *)indices);
// Acquire information about the way the uniforms in this block are packed.
GLint *offsets = (GLint *)alloca(param_count * sizeof(GLint));
GLint *mstrides = (GLint *)alloca(param_count * sizeof(GLint));
GLint *astrides = (GLint *)alloca(param_count * sizeof(GLint));
_glgsg->_glGetActiveUniformsiv(_glsl_program, param_count, indices, GL_UNIFORM_OFFSET, offsets);
_glgsg->_glGetActiveUniformsiv(_glsl_program, param_count, indices, GL_UNIFORM_MATRIX_STRIDE, mstrides);
_glgsg->_glGetActiveUniformsiv(_glsl_program, param_count, indices, GL_UNIFORM_ARRAY_STRIDE, astrides);
for (int ui = 0; ui < param_count; ++ui) {
name_buffer[0] = 0;
GLint param_size;
GLenum param_type;
_glgsg->_glGetActiveUniform(_glsl_program, indices[ui], name_buflen, NULL, &param_size, &param_type, name_buffer);
// Strip off [0] suffix that some drivers append to arrays.
size_t size = strlen(name_buffer);
if (size > 3 && strncmp(name_buffer + (size - 3), "[0]", 3) == 0) {
name_buffer[size - 3] = 0;
}
GeomEnums::NumericType numeric_type;
GeomEnums::Contents contents = GeomEnums::C_other;
int num_components = 1;
switch (param_type) {
case GL_INT:
case GL_INT_VEC2:
case GL_INT_VEC3:
case GL_INT_VEC4:
numeric_type = GeomEnums::NT_int32;
break;
case GL_BOOL:
case GL_BOOL_VEC2:
case GL_BOOL_VEC3:
case GL_BOOL_VEC4:
case GL_UNSIGNED_INT:
case GL_UNSIGNED_INT_VEC2:
case GL_UNSIGNED_INT_VEC3:
case GL_UNSIGNED_INT_VEC4:
numeric_type = GeomEnums::NT_uint32;
break;
case GL_FLOAT:
case GL_FLOAT_VEC2:
case GL_FLOAT_VEC3:
case GL_FLOAT_VEC4:
case GL_FLOAT_MAT2:
case GL_FLOAT_MAT3:
case GL_FLOAT_MAT4:
numeric_type = GeomEnums::NT_float32;
break;
case GL_DOUBLE:
case GL_DOUBLE_VEC2:
case GL_DOUBLE_VEC3:
case GL_DOUBLE_VEC4:
case GL_DOUBLE_MAT2:
case GL_DOUBLE_MAT3:
case GL_DOUBLE_MAT4:
numeric_type = GeomEnums::NT_float64;
break;
default:
GLCAT.warning() << "Ignoring uniform '" << name_buffer
<< "' with unsupported type 0x" << hex << param_type << dec << "\n";
continue;
}
switch (param_type) {
case GL_INT_VEC2:
case GL_BOOL_VEC2:
case GL_UNSIGNED_INT_VEC2:
case GL_FLOAT_VEC2:
case GL_DOUBLE_VEC2:
num_components = 2;
break;
case GL_INT_VEC3:
case GL_BOOL_VEC3:
case GL_UNSIGNED_INT_VEC3:
case GL_FLOAT_VEC3:
case GL_DOUBLE_VEC3:
num_components = 3;
break;
case GL_INT_VEC4:
case GL_BOOL_VEC4:
case GL_UNSIGNED_INT_VEC4:
case GL_FLOAT_VEC4:
case GL_DOUBLE_VEC4:
num_components = 4;
break;
case GL_FLOAT_MAT3:
case GL_DOUBLE_MAT3:
num_components = 3;
contents = GeomEnums::C_matrix;
nassertd(param_size <= 1 || astrides[ui] == mstrides[ui] * 3) continue;
param_size *= 3;
break;
case GL_FLOAT_MAT4:
case GL_DOUBLE_MAT4:
num_components = 4;
contents = GeomEnums::C_matrix;
nassertd(param_size <= 1 || astrides[ui] == mstrides[ui] * 4) continue;
param_size *= 4;
break;
}
//GeomVertexColumn column(InternalName::make(name_buffer),
// num_components, numeric_type, contents,
// offsets[ui], 4, param_size, astrides[ui]);
//block_format.add_column(column);
}
//if (GLCAT.is_debug()) {
// GLCAT.debug() << "Active uniform block " << name << " has format:\n";
// block_format.write(GLCAT.debug(false), 2);
//}
//UniformBlock block;
//block._name = InternalName::make(name);
//block._format = GeomVertexArrayFormat::register_format(&block_format);
//block._buffer = 0;
//_uniform_blocks.push_back(block);
}
#endif // !OPENGLES
////////////////////////////////////////////////////////////////////
// Function: GLShaderContext::reflect_uniform
// Access: Public
// Description: Analyzes a single uniform variable and considers
// how it should be handled and bound.
////////////////////////////////////////////////////////////////////
void CLP(ShaderContext)::
reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) {
GLint param_size;
GLenum param_type;
// Get the name, location, type and size of this uniform.
name_buffer[0] = 0;
_glgsg->_glGetActiveUniform(_glsl_program, i, name_buflen, NULL, &param_size, &param_type, name_buffer);
GLint p = _glgsg->_glGetUniformLocation(_glsl_program, name_buffer);
if (GLCAT.is_debug()) {
GLCAT.debug()
<< "Active uniform " << name_buffer << " with size " << param_size
<< " and type 0x" << hex << param_type << dec
<< " is bound to location " << p << "\n";
}
if (p < 0) {
// Special meaning, or it's in a uniform block. Let it go.
return;
}
// Strip off [0] suffix that some drivers append to arrays.
size_t size = strlen(name_buffer);
if (size > 3 && strncmp(name_buffer + (size - 3), "[0]", 3) == 0) {
size -= 3;
name_buffer[size] = 0;
}
string param_name(name_buffer);
Shader::ShaderArgId arg_id;
arg_id._name = param_name;
arg_id._seqno = p;
// Check if it has a p3d_ prefix - if so, assign special meaning.
if (strncmp(name_buffer, "p3d_", 4) == 0) {
string noprefix(name_buffer + 4);
// Check for matrix inputs.
bool transpose = false;
bool inverse = false;
string matrix_name(noprefix);
size = matrix_name.size();
// Check for and chop off any "Transpose" or "Inverse" suffix.
if (size > 15 && matrix_name.compare(size - 9, 9, "Transpose") == 0) {
transpose = true;
matrix_name = matrix_name.substr(0, size - 9);
}
size = matrix_name.size();
if (size > 13 && matrix_name.compare(size - 7, 7, "Inverse") == 0) {
inverse = true;
matrix_name = matrix_name.substr(0, size - 7);
}
size = matrix_name.size();
// Now if the suffix that is left over is "Matrix",
// we know that it is supposed to be a matrix input.
if (size > 6 && matrix_name.compare(size - 6, 6, "Matrix") == 0) {
Shader::ShaderMatSpec bind;
bind._id = arg_id;
bind._func = Shader::SMF_compose;
if (param_type == GL_FLOAT_MAT3) {
if (transpose) {
bind._piece = Shader::SMP_upper3x3;
} else {
bind._piece = Shader::SMP_transpose3x3;
}
} else {
if (transpose) {
bind._piece = Shader::SMP_transpose;
} else {
bind._piece = Shader::SMP_whole;
}
}
bind._arg[0] = NULL;
bind._arg[1] = NULL;
bind._dep[0] = Shader::SSD_general | Shader::SSD_transform;
bind._dep[1] = Shader::SSD_general | Shader::SSD_transform;
if (matrix_name == "ModelViewProjectionMatrix") {
if (inverse) {
bind._part[0] = Shader::SMO_apiclip_to_apiview;
bind._part[1] = Shader::SMO_apiview_to_model;
} else {
bind._part[0] = Shader::SMO_model_to_apiview;
bind._part[1] = Shader::SMO_apiview_to_apiclip;
}
} else if (matrix_name == "ModelViewMatrix") {
bind._func = Shader::SMF_first;
bind._part[0] = inverse ? Shader::SMO_apiview_to_model
: Shader::SMO_model_to_apiview;
bind._part[1] = Shader::SMO_identity;
} else if (matrix_name == "ProjectionMatrix") {
bind._func = Shader::SMF_first;
bind._part[0] = inverse ? Shader::SMO_apiclip_to_apiview
: Shader::SMO_apiview_to_apiclip;
bind._part[1] = Shader::SMO_identity;
} else if (matrix_name == "NormalMatrix") {
// This is really the upper 3x3 of the ModelViewMatrixInverseTranspose.
bind._func = Shader::SMF_first;
bind._part[0] = inverse ? Shader::SMO_model_to_apiview
: Shader::SMO_apiview_to_model;
bind._part[1] = Shader::SMO_identity;
if (param_type != GL_FLOAT_MAT3) {
GLCAT.error() << "p3d_NormalMatrix input should be mat3, not mat4!\n";
}
} else if (matrix_name == "ModelMatrix") {
if (inverse) {
bind._part[0] = Shader::SMO_world_to_view;
bind._part[1] = Shader::SMO_view_to_model;
} else {
bind._part[0] = Shader::SMO_model_to_view;
bind._part[1] = Shader::SMO_view_to_world;
}
} else if (matrix_name == "ViewMatrix") {
if (inverse) {
bind._part[0] = Shader::SMO_apiview_to_view;
bind._part[1] = Shader::SMO_view_to_world;
} else {
bind._part[0] = Shader::SMO_world_to_view;
bind._part[1] = Shader::SMO_view_to_apiview;
}
} else if (matrix_name == "ViewProjectionMatrix") {
if (inverse) {
bind._part[0] = Shader::SMO_apiclip_to_view;
bind._part[1] = Shader::SMO_view_to_world;
} else {
bind._part[0] = Shader::SMO_world_to_view;
bind._part[1] = Shader::SMO_view_to_apiclip;
}
} else {
GLCAT.error() << "Unrecognized uniform matrix name '" << matrix_name << "'!\n";
return;
}
_shader->_mat_spec.push_back(bind);
return;
}
if (size > 7 && noprefix.substr(0, 7) == "Texture") {
Shader::ShaderTexSpec bind;
bind._id = arg_id;
bind._name = 0;
string tail;
bind._stage = string_to_int(noprefix.substr(7), tail);
if (!tail.empty()) {
GLCAT.error()
<< "Error parsing shader input name: unexpected '"
<< tail << "' in '" << param_name << "'\n";
return;
}
if (get_sampler_texture_type(bind._desired_type, param_type)) {
_glgsg->_glUniform1i(p, _shader->_tex_spec.size());
_shader->_tex_spec.push_back(bind);
} else {
GLCAT.error()
<< "Could not bind texture input " << param_name << "\n";
}
return;
}
if (size > 9 && noprefix.substr(0, 9) == "Material.") {
Shader::ShaderMatSpec bind;
bind._id = arg_id;
bind._func = Shader::SMF_first;
bind._part[0] = Shader::SMO_attr_material;
bind._arg[0] = NULL;
bind._dep[0] = Shader::SSD_general | Shader::SSD_material;
bind._part[1] = Shader::SMO_identity;
bind._arg[1] = NULL;
bind._dep[1] = Shader::SSD_NONE;
if (noprefix == "Material.ambient") {
if (param_type != GL_FLOAT_VEC4) {
GLCAT.error()
<< "p3d_Material.ambient should be vec4\n";
}
bind._piece = Shader::SMP_row0;
_shader->_mat_spec.push_back(bind);
return;
} else if (noprefix == "Material.diffuse") {
if (param_type != GL_FLOAT_VEC4) {
GLCAT.error()
<< "p3d_Material.diffuse should be vec4\n";
}
bind._piece = Shader::SMP_row1;
_shader->_mat_spec.push_back(bind);
return;
} else if (noprefix == "Material.emission") {
if (param_type != GL_FLOAT_VEC4) {
GLCAT.error()
<< "p3d_Material.emission should be vec4\n";
}
bind._piece = Shader::SMP_row2;
_shader->_mat_spec.push_back(bind);
return;
} else if (noprefix == "Material.specular") {
if (param_type != GL_FLOAT_VEC3) {
GLCAT.error()
<< "p3d_Material.specular should be vec3\n";
}
bind._piece = Shader::SMP_row3x3;
_shader->_mat_spec.push_back(bind);
return;
} else if (noprefix == "Material.shininess") {
if (param_type != GL_FLOAT) {
GLCAT.error()
<< "p3d_Material.shininess should be float\n";
}
bind._piece = Shader::SMP_cell15;
_shader->_mat_spec.push_back(bind);
return;
}
}
if (noprefix == "ColorScale") {
Shader::ShaderMatSpec bind;
bind._id = arg_id;
bind._func = Shader::SMF_first;
bind._part[0] = Shader::SMO_attr_colorscale;
bind._arg[0] = NULL;
bind._dep[0] = Shader::SSD_general | Shader::SSD_colorscale;
bind._part[1] = Shader::SMO_identity;
bind._arg[1] = NULL;
bind._dep[1] = Shader::SSD_NONE;
if (param_type == GL_FLOAT_VEC3) {
bind._piece = Shader::SMP_row3x3;
} else if (param_type == GL_FLOAT_VEC4) {
bind._piece = Shader::SMP_row3;
} else {
GLCAT.error()
<< "p3d_ColorScale should be vec3 or vec4\n";
return;
}
_shader->_mat_spec.push_back(bind);
return;
}
if (noprefix == "Color") {
Shader::ShaderMatSpec bind;
bind._id = arg_id;
bind._func = Shader::SMF_first;
bind._part[0] = Shader::SMO_attr_color;
bind._arg[0] = NULL;
bind._dep[0] = Shader::SSD_general | Shader::SSD_color;
bind._part[1] = Shader::SMO_identity;
bind._arg[1] = NULL;
bind._dep[1] = Shader::SSD_NONE;
if (param_type == GL_FLOAT_VEC3) {
bind._piece = Shader::SMP_row3x3;
} else if (param_type == GL_FLOAT_VEC4) {
bind._piece = Shader::SMP_row3;
} else {
GLCAT.error()
<< "p3d_Color should be vec3 or vec4\n";
return;
}
_shader->_mat_spec.push_back(bind);
return;
}
if (noprefix == "ClipPlane") {
if (param_type != GL_FLOAT_VEC4) {
GLCAT.error()
<< "p3d_ClipPlane should be vec4 or vec4[]\n";
return;
}
for (int i = 0; i < param_size; ++i) {
Shader::ShaderMatSpec bind;
bind._id = arg_id;
bind._id._seqno = p + i;
bind._piece = Shader::SMP_row3;
bind._func = Shader::SMF_first;
bind._index = i;
bind._part[0] = Shader::SMO_apiview_clipplane_i;
bind._arg[0] = NULL;
bind._dep[0] = Shader::SSD_general | Shader::SSD_clip_planes;
bind._part[1] = Shader::SMO_identity;
bind._arg[1] = NULL;
bind._dep[1] = Shader::SSD_NONE;
_shader->_mat_spec.push_back(bind);
}
return;
}
if (noprefix == "LightModel.ambient") {
Shader::ShaderMatSpec bind;
bind._id = arg_id;
bind._func = Shader::SMF_first;
bind._part[0] = Shader::SMO_light_ambient;
bind._arg[0] = NULL;
bind._dep[0] = Shader::SSD_general | Shader::SSD_light;
bind._part[1] = Shader::SMO_identity;
bind._arg[1] = NULL;
bind._dep[1] = Shader::SSD_NONE;
if (param_type == GL_FLOAT_VEC3) {
bind._piece = Shader::SMP_row3x3;
} else if (param_type == GL_FLOAT_VEC4) {
bind._piece = Shader::SMP_row3;
} else {
GLCAT.error()
<< "p3d_LightModel.ambient should be vec3 or vec4\n";
return;
}
_shader->_mat_spec.push_back(bind);
return;
}
if (noprefix == "TransformTable") {
if (param_type != GL_FLOAT_MAT4) {
GLCAT.error()
<< "p3d_TransformTable should be uniform mat4\n";
return;
}
_transform_table_index = p;
_transform_table_size = param_size;
return;
}
if (noprefix == "SliderTable") {
if (param_type != GL_FLOAT) {
GLCAT.error()
<< "p3d_SliderTable should be uniform float\n";
return;
}
_slider_table_index = p;
_slider_table_size = param_size;
return;
}
GLCAT.error() << "Unrecognized uniform name '" << param_name << "'!\n";
return;
} else if (strncmp(name_buffer, "osg_", 4) == 0) {
string noprefix(name_buffer + 4);
// These inputs are supported by OpenSceneGraph. We can support
// them as well, to increase compatibility.
// Other inputs we may support in the future:
// int osg_FrameNumber
Shader::ShaderMatSpec bind;
bind._id = arg_id;
bind._arg[0] = NULL;
bind._arg[1] = NULL;
if (noprefix == "ViewMatrix") {
bind._piece = Shader::SMP_whole;
bind._func = Shader::SMF_compose;
bind._part[0] = Shader::SMO_world_to_view;
bind._part[1] = Shader::SMO_view_to_apiview;
bind._dep[0] = Shader::SSD_general | Shader::SSD_transform;
bind._dep[1] = Shader::SSD_general | Shader::SSD_transform;
_shader->_mat_spec.push_back(bind);
return;
} else if (noprefix == "InverseViewMatrix") {
bind._piece = Shader::SMP_whole;
bind._func = Shader::SMF_compose;
bind._part[0] = Shader::SMO_apiview_to_view;
bind._part[1] = Shader::SMO_view_to_world;
bind._dep[0] = Shader::SSD_general | Shader::SSD_transform;
bind._dep[1] = Shader::SSD_general | Shader::SSD_transform;
_shader->_mat_spec.push_back(bind);
return;
} else if (noprefix == "FrameTime") {
bind._piece = Shader::SMP_row3x1;
bind._func = Shader::SMF_first;
bind._part[0] = Shader::SMO_frame_time;
bind._part[1] = Shader::SMO_identity;
bind._dep[0] = Shader::SSD_general;
bind._dep[1] = Shader::SSD_NONE;
_shader->_mat_spec.push_back(bind);
return;
} else if (noprefix == "DeltaFrameTime") {
bind._piece = Shader::SMP_row3x1;
bind._func = Shader::SMF_first;
bind._part[0] = Shader::SMO_frame_delta;
bind._part[1] = Shader::SMO_identity;
bind._dep[0] = Shader::SSD_general;
bind._dep[1] = Shader::SSD_NONE;
_shader->_mat_spec.push_back(bind);
return;
}
} else {
// Tries to parse shorthand notations like mspos_XXX and trans_model_to_clip_of_XXX
if (parse_and_set_short_hand_shader_vars(arg_id, param_type, param_size, _shader)) {
return;
}
}
if (param_size == 1) {
// A single uniform (not an array, or an array of size 1).
switch (param_type) {
#ifndef OPENGLES
case GL_INT_SAMPLER_1D:
case GL_INT_SAMPLER_2D:
case GL_INT_SAMPLER_3D:
case GL_INT_SAMPLER_2D_ARRAY:
case GL_INT_SAMPLER_CUBE:
case GL_INT_SAMPLER_BUFFER:
case GL_UNSIGNED_INT_SAMPLER_1D:
case GL_UNSIGNED_INT_SAMPLER_2D:
case GL_UNSIGNED_INT_SAMPLER_3D:
case GL_UNSIGNED_INT_SAMPLER_CUBE:
case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
case GL_UNSIGNED_INT_SAMPLER_BUFFER:
case GL_SAMPLER_1D_SHADOW:
case GL_SAMPLER_1D:
case GL_SAMPLER_CUBE_SHADOW:
case GL_SAMPLER_2D_ARRAY:
case GL_SAMPLER_2D_ARRAY_SHADOW:
case GL_SAMPLER_BUFFER:
#endif // !OPENGLES
case GL_SAMPLER_2D:
case GL_SAMPLER_2D_SHADOW:
case GL_SAMPLER_3D:
case GL_SAMPLER_CUBE: {
Shader::ShaderTexSpec bind;
bind._id = arg_id;
bind._name = InternalName::make(param_name);
bind._desired_type = Texture::TT_2d_texture;
bind._stage = 0;
if (get_sampler_texture_type(bind._desired_type, param_type)) {
_glgsg->_glUniform1i(p, _shader->_tex_spec.size());
_shader->_tex_spec.push_back(bind);
}
return;
}
case GL_FLOAT_MAT2:
#ifndef OPENGLES
case GL_FLOAT_MAT2x3:
case GL_FLOAT_MAT2x4:
case GL_FLOAT_MAT3x2:
case GL_FLOAT_MAT3x4:
case GL_FLOAT_MAT4x2:
case GL_FLOAT_MAT4x3:
#endif
GLCAT.warning() << "GLSL shader requested an unsupported matrix type\n";
return;
case GL_FLOAT_MAT3: {
Shader::ShaderMatSpec bind;
bind._id = arg_id;
bind._piece = Shader::SMP_upper3x3;
bind._func = Shader::SMF_first;
bind._part[0] = Shader::SMO_mat_constant_x;
bind._arg[0] = InternalName::make(param_name);
bind._dep[0] = Shader::SSD_general | Shader::SSD_shaderinputs;
bind._part[1] = Shader::SMO_identity;
bind._arg[1] = NULL;
bind._dep[1] = Shader::SSD_NONE;
_shader->_mat_spec.push_back(bind);
return;
}
case GL_FLOAT_MAT4: {
Shader::ShaderMatSpec bind;
bind._id = arg_id;
bind._piece = Shader::SMP_whole;
bind._func = Shader::SMF_first;
bind._part[0] = Shader::SMO_mat_constant_x;
bind._arg[0] = InternalName::make(param_name);
bind._dep[0] = Shader::SSD_general | Shader::SSD_shaderinputs;
bind._part[1] = Shader::SMO_identity;
bind._arg[1] = NULL;
bind._dep[1] = Shader::SSD_NONE;
_shader->_mat_spec.push_back(bind);
return;
}
case GL_FLOAT:
case GL_FLOAT_VEC2:
case GL_FLOAT_VEC3:
case GL_FLOAT_VEC4: {
PT(InternalName) iname = InternalName::make(param_name);
if (iname->get_parent() != InternalName::get_root()) {
// It might be something like an attribute of a shader
// input, like a light parameter. It might also just be
// a custom struct parameter. We can't know yet, sadly.
Shader::ShaderMatSpec bind;
bind._id = arg_id;
switch (param_type) {
case GL_FLOAT:
bind._piece = Shader::SMP_row3x1;
break;
case GL_FLOAT_VEC2:
bind._piece = Shader::SMP_row3x2;
break;
case GL_FLOAT_VEC3:
bind._piece = Shader::SMP_row3x3;
break;
default:
bind._piece = Shader::SMP_row3;
}
bind._func = Shader::SMF_first;
bind._part[0] = Shader::SMO_vec_constant_x_attrib;
bind._arg[0] = iname;
bind._dep[0] = Shader::SSD_general | Shader::SSD_shaderinputs;
bind._part[1] = Shader::SMO_identity;
bind._arg[1] = NULL;
bind._dep[1] = Shader::SSD_NONE;
_shader->_mat_spec.push_back(bind);
return;
} // else fall through
}
case GL_BOOL:
case GL_BOOL_VEC2:
case GL_BOOL_VEC3:
case GL_BOOL_VEC4:
case GL_INT:
case GL_INT_VEC2:
case GL_INT_VEC3:
case GL_INT_VEC4: {
Shader::ShaderPtrSpec bind;
bind._id = arg_id;
switch (param_type) {
case GL_BOOL:
case GL_INT:
case GL_FLOAT: bind._dim[1] = 1; break;
case GL_BOOL_VEC2:
case GL_INT_VEC2:
case GL_FLOAT_VEC2: bind._dim[1] = 2; break;
case GL_BOOL_VEC3:
case GL_INT_VEC3:
case GL_FLOAT_VEC3: bind._dim[1] = 3; break;
case GL_BOOL_VEC4:
case GL_INT_VEC4:
case GL_FLOAT_VEC4: bind._dim[1] = 4; break;
case GL_FLOAT_MAT3: bind._dim[1] = 9; break;
case GL_FLOAT_MAT4: bind._dim[1] = 16; break;
}
switch (param_type) {
case GL_BOOL:
case GL_BOOL_VEC2:
case GL_BOOL_VEC3:
case GL_BOOL_VEC4:
case GL_INT:
case GL_INT_VEC2:
case GL_INT_VEC3:
case GL_INT_VEC4:
bind._type = Shader::SPT_int;
break;
case GL_FLOAT:
case GL_FLOAT_VEC2:
case GL_FLOAT_VEC3:
case GL_FLOAT_VEC4:
case GL_FLOAT_MAT3:
case GL_FLOAT_MAT4:
bind._type = Shader::SPT_float;
break;
}
bind._arg = InternalName::make(param_name);
bind._dim[0] = 1;
bind._dep[0] = Shader::SSD_general | Shader::SSD_shaderinputs;
bind._dep[1] = Shader::SSD_NONE;
_shader->_ptr_spec.push_back(bind);
return;
}
#ifndef OPENGLES
case GL_IMAGE_1D_EXT:
case GL_IMAGE_2D_EXT:
case GL_IMAGE_3D_EXT:
case GL_IMAGE_CUBE_EXT:
case GL_IMAGE_2D_ARRAY_EXT:
case GL_IMAGE_BUFFER_EXT:
case GL_INT_IMAGE_1D_EXT:
case GL_INT_IMAGE_2D_EXT:
case GL_INT_IMAGE_3D_EXT:
case GL_INT_IMAGE_CUBE_EXT:
case GL_INT_IMAGE_2D_ARRAY_EXT:
case GL_INT_IMAGE_BUFFER_EXT:
case GL_UNSIGNED_INT_IMAGE_1D_EXT:
case GL_UNSIGNED_INT_IMAGE_2D_EXT:
case GL_UNSIGNED_INT_IMAGE_3D_EXT:
case GL_UNSIGNED_INT_IMAGE_CUBE_EXT:
case GL_UNSIGNED_INT_IMAGE_2D_ARRAY_EXT:
case GL_UNSIGNED_INT_IMAGE_BUFFER_EXT:
// This won't really change at runtime, so we might as well
// bind once and then forget about it.
_glgsg->_glUniform1i(p, _glsl_img_inputs.size());
{
ImageInput input;
input._name = InternalName::make(param_name);
input._writable = false;
input._gtc = NULL;
_glsl_img_inputs.push_back(input);
}
return;
#endif
default:
GLCAT.warning() << "Ignoring unrecognized GLSL parameter type!\n";
}
} else {
// A uniform array.
switch (param_type) {
case GL_FLOAT_MAT2:
#ifndef OPENGLES
case GL_FLOAT_MAT2x3:
case GL_FLOAT_MAT2x4:
case GL_FLOAT_MAT3x2:
case GL_FLOAT_MAT3x4:
case GL_FLOAT_MAT4x2:
case GL_FLOAT_MAT4x3:
#endif
GLCAT.warning() << "GLSL shader requested an unrecognized matrix array type\n";
return;
case GL_BOOL:
case GL_BOOL_VEC2:
case GL_BOOL_VEC3:
case GL_BOOL_VEC4:
case GL_INT:
case GL_INT_VEC2:
case GL_INT_VEC3:
case GL_INT_VEC4:
case GL_FLOAT:
case GL_FLOAT_VEC2:
case GL_FLOAT_VEC3:
case GL_FLOAT_VEC4:
case GL_FLOAT_MAT3:
case GL_FLOAT_MAT4: {
Shader::ShaderPtrSpec bind;
bind._id = arg_id;
switch (param_type) {
case GL_BOOL:
case GL_INT:
case GL_FLOAT: bind._dim[1] = 1; break;
case GL_BOOL_VEC2:
case GL_INT_VEC2:
case GL_FLOAT_VEC2: bind._dim[1] = 2; break;
case GL_BOOL_VEC3:
case GL_INT_VEC3:
case GL_FLOAT_VEC3: bind._dim[1] = 3; break;
case GL_BOOL_VEC4:
case GL_INT_VEC4:
case GL_FLOAT_VEC4: bind._dim[1] = 4; break;
case GL_FLOAT_MAT3: bind._dim[1] = 9; break;
case GL_FLOAT_MAT4: bind._dim[1] = 16; break;
}
switch (param_type) {
case GL_BOOL:
case GL_BOOL_VEC2:
case GL_BOOL_VEC3:
case GL_BOOL_VEC4:
case GL_INT:
case GL_INT_VEC2:
case GL_INT_VEC3:
case GL_INT_VEC4:
bind._type = Shader::SPT_int;
break;
case GL_FLOAT:
case GL_FLOAT_VEC2:
case GL_FLOAT_VEC3:
case GL_FLOAT_VEC4:
case GL_FLOAT_MAT3:
case GL_FLOAT_MAT4:
bind._type = Shader::SPT_float;
break;
}
bind._arg = InternalName::make(param_name);
bind._dim[0] = param_size;
bind._dep[0] = Shader::SSD_general | Shader::SSD_shaderinputs;
bind._dep[1] = Shader::SSD_NONE;
_shader->_ptr_spec.push_back(bind);
return;
}
default:
GLCAT.warning() << "Ignoring unrecognized GLSL parameter array type!\n";
}
}
}
////////////////////////////////////////////////////////////////////
// Function: GLShaderContext::get_sampler_texture_type
// Access: Public
// Description: Returns the texture type required for the given
// GL sampler type. Returns false if unsupported.
////////////////////////////////////////////////////////////////////
bool CLP(ShaderContext)::
get_sampler_texture_type(int &out, GLenum param_type) {
switch (param_type) {
#ifndef OPENGLES
case GL_SAMPLER_1D_SHADOW:
if (!_glgsg->_supports_shadow_filter) {
GLCAT.error()
<< "GLSL shader uses shadow sampler, which is unsupported by the driver.\n";
return false;
}
// Fall through
case GL_INT_SAMPLER_1D:
case GL_UNSIGNED_INT_SAMPLER_1D:
case GL_SAMPLER_1D:
out = Texture::TT_1d_texture;
return true;
case GL_INT_SAMPLER_2D:
case GL_UNSIGNED_INT_SAMPLER_2D:
#endif
case GL_SAMPLER_2D:
out = Texture::TT_2d_texture;
return true;
case GL_SAMPLER_2D_SHADOW:
out = Texture::TT_2d_texture;
if (!_glgsg->_supports_shadow_filter) {
GLCAT.error()
<< "GLSL shader uses shadow sampler, which is unsupported by the driver.\n";
return false;
}
return true;
#ifndef OPENGLES
case GL_INT_SAMPLER_3D:
case GL_UNSIGNED_INT_SAMPLER_3D:
#endif
case GL_SAMPLER_3D:
out = Texture::TT_3d_texture;
if (_glgsg->_supports_3d_texture) {
return true;
} else {
GLCAT.error()
<< "GLSL shader uses 3D texture, which is unsupported by the driver.\n";
return false;
}
#ifndef OPENGLES
case GL_SAMPLER_CUBE_SHADOW:
if (!_glgsg->_supports_shadow_filter) {
GLCAT.error()
<< "GLSL shader uses shadow sampler, which is unsupported by the driver.\n";
return false;
}
// Fall through
case GL_INT_SAMPLER_CUBE:
case GL_UNSIGNED_INT_SAMPLER_CUBE:
#endif
case GL_SAMPLER_CUBE:
out = Texture::TT_cube_map;
if (!_glgsg->_supports_cube_map) {
GLCAT.error()
<< "GLSL shader uses cube map, which is unsupported by the driver.\n";
return false;
}
return true;
#ifndef OPENGLES
case GL_SAMPLER_2D_ARRAY_SHADOW:
if (!_glgsg->_supports_shadow_filter) {
GLCAT.error()
<< "GLSL shader uses shadow sampler, which is unsupported by the driver.\n";
return false;
}
// Fall through
case GL_INT_SAMPLER_2D_ARRAY:
case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
case GL_SAMPLER_2D_ARRAY:
out = Texture::TT_2d_texture_array;
if (_glgsg->_supports_2d_texture_array) {
return true;
} else {
GLCAT.error()
<< "GLSL shader uses 2D texture array, which is unsupported by the driver.\n";
return false;
}
case GL_INT_SAMPLER_BUFFER:
case GL_UNSIGNED_INT_SAMPLER_BUFFER:
case GL_SAMPLER_BUFFER:
out = Texture::TT_buffer_texture;
if (_glgsg->_supports_buffer_texture) {
return true;
} else {
GLCAT.error()
<< "GLSL shader uses buffer texture, which is unsupported by the driver.\n";
return false;
}
#endif
default:
GLCAT.error()
<< "GLSL shader uses unsupported sampler type for texture input.\n";
return false;
}
}
////////////////////////////////////////////////////////////////////
// Function: GLShaderContext::Destructor
// Access: Public
// Description: xyz
////////////////////////////////////////////////////////////////////
CLP(ShaderContext)::
~CLP(ShaderContext)() {
release_resources();
}
////////////////////////////////////////////////////////////////////
// Function: GLShaderContext::release_resources
// Access: Public
// Description: Should deallocate all system resources (such as
// vertex program handles or Cg contexts).
////////////////////////////////////////////////////////////////////
void CLP(ShaderContext)::
release_resources() {
if (!_glgsg) {
return;
}
if (_glsl_program != 0) {
GLSLShaders::const_iterator it;
for (it = _glsl_shaders.begin(); it != _glsl_shaders.end(); ++it) {
_glgsg->_glDetachShader(_glsl_program, *it);
}
_glgsg->_glDeleteProgram(_glsl_program);
_glsl_program = 0;
}
GLSLShaders::const_iterator it;
for (it = _glsl_shaders.begin(); it != _glsl_shaders.end(); ++it) {
_glgsg->_glDeleteShader(*it);
}
_glsl_shaders.clear();
_glgsg->report_my_gl_errors();
}
////////////////////////////////////////////////////////////////////
// Function: GLShaderContext::bind
// Access: Public
// Description: This function is to be called to enable a new
// shader. It also initializes all of the shader's
// input parameters.
////////////////////////////////////////////////////////////////////
void CLP(ShaderContext)::
bind(bool reissue_parameters) {
// GLSL shaders need to be bound before passing parameters.
if (!_shader->get_error_flag()) {
_glgsg->_glUseProgram(_glsl_program);
}
if (reissue_parameters) {
// Pass in k-parameters and transform-parameters
issue_parameters(Shader::SSD_general);
}
if (!_validated) {
_glgsg->_glValidateProgram(_glsl_program);
glsl_report_program_errors(_glsl_program, false);
_validated = true;
}
_glgsg->report_my_gl_errors();
}
////////////////////////////////////////////////////////////////////
// Function: GLShaderContext::unbind
// Access: Public
// Description: This function disables a currently-bound shader.
////////////////////////////////////////////////////////////////////
void CLP(ShaderContext)::
unbind() {
_glgsg->_glUseProgram(0);
_glgsg->report_my_gl_errors();
}
////////////////////////////////////////////////////////////////////
// Function: GLShaderContext::issue_parameters
// Access: Public
// Description: This function gets called whenever the RenderState
// or TransformState has changed, but the Shader
// itself has not changed. It loads new values into the
// shader's parameters.
//
// If "altered" is false, that means you promise that
// the parameters for this shader context have already
// been issued once, and that since the last time the
// parameters were issued, no part of the render
// state has changed except the external and internal
// transforms.
////////////////////////////////////////////////////////////////////
void CLP(ShaderContext)::
issue_parameters(int altered) {
//PStatGPUTimer timer(_glgsg, _glgsg->_draw_set_state_shader_parameters_pcollector);
if (!valid()) {
return;
}
// Iterate through _ptr parameters
for (int i = 0; i < (int)_shader->_ptr_spec.size(); ++i) {
Shader::ShaderPtrSpec &spec = _shader->_ptr_spec[i];
if (altered & (spec._dep[0] | spec._dep[1])) {
const Shader::ShaderPtrData* ptr_data = _glgsg->fetch_ptr_parameter(spec);
if (ptr_data == NULL) { //the input is not contained in ShaderPtrData
release_resources();
return;
}
GLint p = spec._id._seqno;
switch (spec._type) {
case Shader::SPT_float:
{
float *data = NULL;
switch (ptr_data->_type) {
case Shader::SPT_int:
// Convert int data to float data.
data = (float*) alloca(sizeof(float) * spec._dim[0] * spec._dim[1]);
for (int i = 0; i < (spec._dim[0] * spec._dim[1]); ++i) {
data[i] = (float)(((int*)ptr_data->_ptr)[i]);
}
break;
case Shader::SPT_double:
// Downgrade double data to float data.
data = (float*) alloca(sizeof(float) * spec._dim[0] * spec._dim[1]);
for (int i = 0; i < (spec._dim[0] * spec._dim[1]); ++i) {
data[i] = (float)(((double*)ptr_data->_ptr)[i]);
}
break;
case Shader::SPT_float:
data = (float*)ptr_data->_ptr;
break;
default:
nassertd(false) continue;
}
switch (spec._dim[1]) {
case 1: _glgsg->_glUniform1fv(p, spec._dim[0], (float*)data); continue;
case 2: _glgsg->_glUniform2fv(p, spec._dim[0], (float*)data); continue;
case 3: _glgsg->_glUniform3fv(p, spec._dim[0], (float*)data); continue;
case 4: _glgsg->_glUniform4fv(p, spec._dim[0], (float*)data); continue;
case 9: _glgsg->_glUniformMatrix3fv(p, spec._dim[0], GL_FALSE, (float*)data); continue;
case 16: _glgsg->_glUniformMatrix4fv(p, spec._dim[0], GL_FALSE, (float*)data); continue;
}
nassertd(false) continue;
}
break;
case Shader::SPT_int:
if (ptr_data->_type != Shader::SPT_int) {
GLCAT.error()
<< "Cannot pass floating-point data to integer shader input '" << spec._id._name << "'\n";
// Deactivate it to make sure the user doesn't get flooded with this error.
spec._dep[0] = 0;
spec._dep[1] = 0;
} else {
switch (spec._dim[1]) {
case 1: _glgsg->_glUniform1iv(p, spec._dim[0], (int*)ptr_data->_ptr); continue;
case 2: _glgsg->_glUniform2iv(p, spec._dim[0], (int*)ptr_data->_ptr); continue;
case 3: _glgsg->_glUniform3iv(p, spec._dim[0], (int*)ptr_data->_ptr); continue;
case 4: _glgsg->_glUniform4iv(p, spec._dim[0], (int*)ptr_data->_ptr); continue;
}
nassertd(false) continue;
}
break;
case Shader::SPT_double:
GLCAT.error() << "Passing double-precision shader inputs to GLSL shaders is not currently supported\n";
// Deactivate it to make sure the user doesn't get flooded with this error.
spec._dep[0] = 0;
spec._dep[1] = 0;
default:
continue;
}
}
}
for (int i = 0; i < (int)_shader->_mat_spec.size(); ++i) {
Shader::ShaderMatSpec &spec = _shader->_mat_spec[i];
if (altered & (spec._dep[0] | spec._dep[1])) {
const LMatrix4 *val = _glgsg->fetch_specified_value(spec, altered);
if (!val) continue;
#ifndef STDFLOAT_DOUBLE
// In this case, the data is already single-precision.
const PN_float32 *data = val->get_data();
#else
// In this case, we have to convert it.
LMatrix4f valf = LCAST(PN_float32, *val);
const PN_float32 *data = valf.get_data();
#endif
GLint p = spec._id._seqno;
switch (spec._piece) {
case Shader::SMP_whole: _glgsg->_glUniformMatrix4fv(p, 1, GL_FALSE, data); continue;
case Shader::SMP_transpose: _glgsg->_glUniformMatrix4fv(p, 1, GL_TRUE, data); continue;
case Shader::SMP_col0: _glgsg->_glUniform4f(p, data[0], data[4], data[ 8], data[12]); continue;
case Shader::SMP_col1: _glgsg->_glUniform4f(p, data[1], data[5], data[ 9], data[13]); continue;
case Shader::SMP_col2: _glgsg->_glUniform4f(p, data[2], data[6], data[10], data[14]); continue;
case Shader::SMP_col3: _glgsg->_glUniform4f(p, data[3], data[7], data[11], data[15]); continue;
case Shader::SMP_row0: _glgsg->_glUniform4fv(p, 1, data+ 0); continue;
case Shader::SMP_row1: _glgsg->_glUniform4fv(p, 1, data+ 4); continue;
case Shader::SMP_row2: _glgsg->_glUniform4fv(p, 1, data+ 8); continue;
case Shader::SMP_row3: _glgsg->_glUniform4fv(p, 1, data+12); continue;
case Shader::SMP_row3x1: _glgsg->_glUniform1fv(p, 1, data+12); continue;
case Shader::SMP_row3x2: _glgsg->_glUniform2fv(p, 1, data+12); continue;
case Shader::SMP_row3x3: _glgsg->_glUniform3fv(p, 1, data+12); continue;
case Shader::SMP_upper3x3:
{
#ifndef STDFLOAT_DOUBLE
LMatrix3f upper3 = val->get_upper_3();
#else
LMatrix3f upper3 = valf.get_upper_3();
#endif
_glgsg->_glUniformMatrix3fv(p, 1, false, upper3.get_data());
continue;
}
case Shader::SMP_transpose3x3:
{
#ifndef STDFLOAT_DOUBLE
LMatrix3f upper3 = val->get_upper_3();
#else
LMatrix3f upper3 = valf.get_upper_3();
#endif
_glgsg->_glUniformMatrix3fv(p, 1, true, upper3.get_data());
continue;
}
case Shader::SMP_cell15:
_glgsg->_glUniform1fv(p, 1, data+15);
continue;
}
}
}
_glgsg->report_my_gl_errors();
}
////////////////////////////////////////////////////////////////////
// Function: GLShaderContext::update_transform_table
// Access: Public
// Description: Changes the active transform table, used for hardware
// skinning.
////////////////////////////////////////////////////////////////////
void CLP(ShaderContext)::
update_transform_table(const TransformTable *table) {
LMatrix4f *matrices = (LMatrix4f *)alloca(_transform_table_size * 64);
int i = 0;
if (table != NULL) {
int num_transforms = min(_transform_table_size, table->get_num_transforms());
for (; i < num_transforms; ++i) {
#ifdef STDFLOAT_DOUBLE
LMatrix4 matrix;
table->get_transform(i)->get_matrix(matrix);
matrices[i] = LCAST(float, matrix);
#else
table->get_transform(i)->get_matrix(matrices[i]);
#endif
}
}
for (; i < _transform_table_size; ++i) {
matrices[i] = LMatrix4f::ident_mat();
}
_glgsg->_glUniformMatrix4fv(_transform_table_index, _transform_table_size,
GL_FALSE, (float *)matrices);
}
////////////////////////////////////////////////////////////////////
// Function: GLShaderContext::update_slider_table
// Access: Public
// Description: Changes the active slider table, used for hardware
// skinning.
////////////////////////////////////////////////////////////////////
void CLP(ShaderContext)::
update_slider_table(const SliderTable *table) {
float *sliders = (float *)alloca(_slider_table_size * 4);
memset(sliders, 0, _slider_table_size * 4);
if (table != NULL) {
int num_sliders = min(_slider_table_size, table->get_num_sliders());
for (int i = 0; i < num_sliders; ++i) {
sliders[i] = table->get_slider(i)->get_slider();
}
}
_glgsg->_glUniform1fv(_slider_table_index, _slider_table_size, sliders);
}
////////////////////////////////////////////////////////////////////
// Function: GLShaderContext::disable_shader_vertex_arrays
// Access: Public
// Description: Disable all the vertex arrays used by this shader.
////////////////////////////////////////////////////////////////////
void CLP(ShaderContext)::
disable_shader_vertex_arrays() {
if (!valid()) {
return;
}
for (int i=0; i<(int)_shader->_var_spec.size(); i++) {
const Shader::ShaderVarSpec &bind = _shader->_var_spec[i];
GLint p = bind._id._seqno;
if (_has_divisor) {
_glgsg->_glVertexAttribDivisor(p, 0);
}
for (int i = 0; i < bind._elements; ++i) {
_glgsg->_glDisableVertexAttribArray(p + i);
}
}
_glgsg->report_my_gl_errors();
}
////////////////////////////////////////////////////////////////////
// Function: GLShaderContext::update_shader_vertex_arrays
// Access: Public
// Description: Disables all vertex arrays used by the previous
// shader, then enables all the vertex arrays needed
// by this shader. Extracts the relevant vertex array
// data from the gsg.
// The current implementation is inefficient, because
// it may unnecessarily disable arrays then immediately
// reenable them. We may optimize this someday.
////////////////////////////////////////////////////////////////////
bool CLP(ShaderContext)::
update_shader_vertex_arrays(ShaderContext *prev, bool force) {
if (prev) {
prev->disable_shader_vertex_arrays();
}
if (!valid()) {
return true;
}
#ifdef SUPPORT_IMMEDIATE_MODE
if (_glgsg->_use_sender) {
GLCAT.error() << "immediate mode shaders not implemented yet\n";
} else
#endif // SUPPORT_IMMEDIATE_MODE
{
const GeomVertexArrayDataHandle *array_reader;
Geom::NumericType numeric_type;
int start, stride, num_values;
int nvarying = _shader->_var_spec.size();
for (int i = 0; i < nvarying; ++i) {
const Shader::ShaderVarSpec &bind = _shader->_var_spec[i];
InternalName *name = bind._name;
int texslot = bind._append_uv;
if (texslot >= 0 && texslot < _glgsg->_state_texture->get_num_on_stages()) {
TextureStage *stage = _glgsg->_state_texture->get_on_stage(texslot);
InternalName *texname = stage->get_texcoord_name();
if (name == InternalName::get_texcoord()) {
name = texname;
} else if (texname != InternalName::get_texcoord()) {
name = name->append(texname->get_basename());
}
}
GLint p = bind._id._seqno;
// Don't apply vertex colors if they are disabled with a ColorAttrib.
int num_elements, element_stride, divisor;
bool normalized;
if ((p != _color_attrib_index || _glgsg->_vertex_colors_enabled) &&
_glgsg->_data_reader->get_array_info(name, array_reader,
num_values, numeric_type,
normalized, start, stride, divisor,
num_elements, element_stride)) {
const unsigned char *client_pointer;
if (!_glgsg->setup_array_data(client_pointer, array_reader, force)) {
return false;
}
client_pointer += start;
for (int i = 0; i < num_elements; ++i) {
_glgsg->_glEnableVertexAttribArray(p);
#ifndef OPENGLES
if (bind._integer) {
_glgsg->_glVertexAttribIPointer(p, num_values, _glgsg->get_numeric_type(numeric_type),
stride, client_pointer);
} else
#endif
if (numeric_type == GeomEnums::NT_packed_dabc) {
// GL_BGRA is a special accepted value available since OpenGL 3.2.
// It requires us to pass GL_TRUE for normalized.
_glgsg->_glVertexAttribPointer(p, GL_BGRA, GL_UNSIGNED_BYTE,
GL_TRUE, stride, client_pointer);
} else {
_glgsg->_glVertexAttribPointer(p, num_values,
_glgsg->get_numeric_type(numeric_type),
normalized, stride, client_pointer);
}
if (_glgsg->_supports_vertex_attrib_divisor) {
_glgsg->_glVertexAttribDivisor(p, divisor);
_has_divisor = true;
}
++p;
client_pointer += element_stride;
}
} else {
for (int i = 0; i < bind._elements; ++i) {
_glgsg->_glDisableVertexAttribArray(p + i);
}
if (p == _color_attrib_index) {
// Vertex colors are disabled or not present. Apply flat color.
#if defined(STDFLOAT_DOUBLE) && !defined(OPENGLES)
_glgsg->_glVertexAttrib4dv(p, _glgsg->_scene_graph_color.get_data());
#else
_glgsg->_glVertexAttrib4fv(p, _glgsg->_scene_graph_color.get_data());
#endif
}
}
}
}
if (_transform_table_index >= 0) {
const TransformTable *table = _glgsg->_data_reader->get_transform_table();
update_transform_table(table);
}
if (_slider_table_index >= 0) {
const SliderTable *table = _glgsg->_data_reader->get_slider_table();
update_slider_table(table);
}
_glgsg->report_my_gl_errors();
return true;
}
////////////////////////////////////////////////////////////////////
// Function: GLShaderContext::disable_shader_texture_bindings
// Access: Public
// Description: Disable all the texture bindings used by this shader.
////////////////////////////////////////////////////////////////////
void CLP(ShaderContext)::
disable_shader_texture_bindings() {
if (!valid()) {
return;
}
for (int i = 0; i < _shader->_tex_spec.size(); ++i) {
#ifndef OPENGLES
// Check if bindless was used, if so, there's nothing to unbind.
if (_glgsg->_supports_bindless_texture) {
GLint p = _shader->_tex_spec[i]._id._seqno;
if (_glsl_uniform_handles.count(p) > 0) {
continue;
}
}
if (_glgsg->_supports_multi_bind) {
// There are non-bindless textures to unbind, and we're lazy,
// so let's go and unbind everything after this point using one
// multi-bind call, and then break out of the loop.
_glgsg->_glBindTextures(i, _shader->_tex_spec.size() - i, NULL);
break;
}
#endif
_glgsg->_glActiveTexture(GL_TEXTURE0 + i);
switch (_shader->_tex_spec[i]._desired_type) {
case Texture::TT_1d_texture:
#ifndef OPENGLES
glBindTexture(GL_TEXTURE_1D, 0);
#endif
break;
case Texture::TT_2d_texture:
glBindTexture(GL_TEXTURE_2D, 0);
break;
case Texture::TT_3d_texture:
#ifndef OPENGLES_1
glBindTexture(GL_TEXTURE_3D, 0);
#endif
break;
case Texture::TT_2d_texture_array:
#ifndef OPENGLES
glBindTexture(GL_TEXTURE_2D_ARRAY_EXT, 0);
#endif
break;
case Texture::TT_cube_map:
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
break;
case Texture::TT_buffer_texture:
#ifndef OPENGLES
glBindTexture(GL_TEXTURE_BUFFER, 0);
#endif
break;
}
}
#ifndef OPENGLES
// Now unbind all the image units. Not sure if we *have* to do this.
int num_image_units = min(_glsl_img_inputs.size(), (size_t)_glgsg->_max_image_units);
if (num_image_units > 0) {
if (_glgsg->_supports_multi_bind) {
_glgsg->_glBindImageTextures(0, num_image_units, NULL);
} else {
for (int i = 0; i < num_image_units; ++i) {
_glgsg->_glBindImageTexture(i, 0, 0, GL_FALSE, 0, GL_READ_ONLY, GL_R8);
}
}
if (gl_enable_memory_barriers) {
for (int i = 0; i < num_image_units; ++i) {
ImageInput &input = _glsl_img_inputs[i];
if (input._gtc != NULL) {
input._gtc->mark_incoherent(input._writable);
input._gtc = NULL;
}
}
}
}
#endif
_glgsg->report_my_gl_errors();
}
////////////////////////////////////////////////////////////////////
// Function: GLShaderContext::update_shader_texture_bindings
// Access: Public
// Description: Disables all texture bindings used by the previous
// shader, then enables all the texture bindings needed
// by this shader. Extracts the relevant vertex array
// data from the gsg.
// The current implementation is inefficient, because
// it may unnecessarily disable textures then immediately
// reenable them. We may optimize this someday.
////////////////////////////////////////////////////////////////////
void CLP(ShaderContext)::
update_shader_texture_bindings(ShaderContext *prev) {
if (prev) {
prev->disable_shader_texture_bindings();
}
if (!valid()) {
return;
}
#ifndef OPENGLES
GLbitfield barriers = 0;
// First bind all the 'image units'; a bit of an esoteric OpenGL feature right now.
int num_image_units = min(_glsl_img_inputs.size(), (size_t)_glgsg->_max_image_units);
if (num_image_units > 0) {
for (int i = 0; i < num_image_units; ++i) {
ImageInput &input = _glsl_img_inputs[i];
const ParamTextureImage *param = NULL;
Texture *tex;
const ShaderInput *sinp = _glgsg->_target_shader->get_shader_input(input._name);
if (sinp->get_value_type() == ShaderInput::M_texture_image) {
param = (const ParamTextureImage *)sinp->get_param();
tex = param->get_texture();
} else if (sinp->get_value_type() == ShaderInput::M_texture) {
// People find it convenient to be able to pass a texture without further ado.
tex = sinp->get_texture();
} else {
GLCAT.error()
<< "Mismatching type for parameter " << *input._name << ", expected texture image binding\n";
continue;
}
GLuint gl_tex = 0;
CLP(TextureContext) *gtc;
if (tex != NULL) {
int view = _glgsg->get_current_tex_view_offset();
gtc = DCAST(CLP(TextureContext), tex->prepare_now(view, _glgsg->_prepared_objects, _glgsg));
if (gtc != (TextureContext*)NULL) {
input._gtc = gtc;
gl_tex = gtc->_index;
_glgsg->update_texture(gtc, true);
if (gtc->needs_barrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT)) {
barriers |= GL_SHADER_IMAGE_ACCESS_BARRIER_BIT;
}
}
}
input._writable = false;
if (gl_tex == 0) {
_glgsg->_glBindImageTexture(i, 0, 0, GL_FALSE, 0, GL_READ_ONLY, GL_R8);
} else {
//TODO: automatically convert to sized type instead of plain GL_RGBA
// If a base type is used, it will crash.
GLenum internal_format = gtc->_internal_format;
if (internal_format == GL_RGBA || internal_format == GL_RGB) {
GLCAT.error()
<< "Texture " << tex->get_name() << " has an unsized format. Textures bound "
<< "to a shader as an image need a sized format.\n";
// This may not actually be right, but may still prevent a crash.
internal_format = _glgsg->get_internal_image_format(tex, true);
}
GLenum access = GL_READ_WRITE;
GLint bind_level = 0;
GLint bind_layer = 0;
GLboolean layered = GL_TRUE;
if (param != NULL) {
layered = param->get_bind_layered();
bind_level = param->get_bind_level();
bind_layer = param->get_bind_layer();
bool has_read = param->has_read_access();
bool has_write = param->has_write_access();
input._writable = has_write;
if (has_read && has_write) {
access = GL_READ_WRITE;
} else if (has_read) {
access = GL_READ_ONLY;
} else if (has_write) {
access = GL_WRITE_ONLY;
} else {
access = GL_READ_ONLY;
gl_tex = 0;
}
}
_glgsg->_glBindImageTexture(i, gl_tex, bind_level, layered, bind_layer,
access, gtc->_internal_format);
}
}
}
#endif
// We get the TextureAttrib directly from the _target_rs, not the
// filtered TextureAttrib in _target_texture.
const TextureAttrib *texattrib = DCAST(TextureAttrib, _glgsg->_target_rs->get_attrib_def(TextureAttrib::get_class_slot()));
nassertv(texattrib != (TextureAttrib *)NULL);
for (int i = 0; i < (int)_shader->_tex_spec.size(); ++i) {
Shader::ShaderTexSpec &spec = _shader->_tex_spec[i];
const InternalName *id = spec._name;
Texture *tex = NULL;
int view = _glgsg->get_current_tex_view_offset();
SamplerState sampler;
if (id != NULL) {
// Named texture input.
if (!_glgsg->_target_shader->has_shader_input(id)) {
// This used to be legal for some reason, so don't trigger the assert.
GLCAT.error()
<< "Shader input " << *id << " is not present.\n";
continue;
}
tex = _glgsg->_target_shader->get_shader_input_texture(id, &sampler);
} else {
if (spec._stage >= texattrib->get_num_on_stages()) {
// Apply a white texture in order to make it easier to use a shader
// that takes a texture on a model that doesn't have a texture applied.
_glgsg->_glActiveTexture(GL_TEXTURE0 + i);
_glgsg->apply_white_texture();
continue;
}
TextureStage *stage = texattrib->get_on_stage(spec._stage);
tex = texattrib->get_on_texture(stage);
sampler = texattrib->get_on_sampler(stage);
view += stage->get_tex_view_offset();
}
if (tex == NULL) {
continue;
}
if (tex->get_texture_type() != spec._desired_type) {
if (id != NULL) {
GLCAT.error()
<< "Sampler type of GLSL shader input '" << *id << "' does not "
"match type of texture " << *tex << ".\n";
} else {
GLCAT.error()
<< "Sampler type of GLSL shader input p3d_Texture" << spec._stage
<< " does not match type of texture " << *tex << ".\n";
}
//TODO: also check whether shadow sampler textures have shadow filter enabled.
}
CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tex->prepare_now(view, _glgsg->_prepared_objects, _glgsg));
if (gtc == NULL) {
continue;
}
GLint p = spec._id._seqno;
#ifndef OPENGLES
// If it was recently written to, we will have to issue a memory barrier soon.
if (gtc->needs_barrier(GL_TEXTURE_FETCH_BARRIER_BIT)) {
barriers |= GL_TEXTURE_FETCH_BARRIER_BIT;
}
// Try bindless texturing first, if supported.
if (gl_use_bindless_texture && _glgsg->_supports_bindless_texture) {
// We demand the real texture, since we won't be able
// to change the texture properties after this point.
if (!_glgsg->update_texture(gtc, true)) {
continue;
}
GLuint64 handle = gtc->get_handle();
if (handle != 0) {
gtc->make_handle_resident();
gtc->set_active(true);
// Check if we have already specified this texture handle.
// If so, no need to call glUniformHandle again.
pmap<GLint, GLuint64>::const_iterator it;
it = _glsl_uniform_handles.find(p);
if (it != _glsl_uniform_handles.end() && it->second == handle) {
// Already specified.
continue;
} else {
_glgsg->_glUniformHandleui64(p, handle);
_glsl_uniform_handles[p] = handle;
}
continue;
}
}
#endif
// Bindless texturing wasn't supported or didn't work, so
// let's just bind the texture normally.
_glgsg->_glActiveTexture(GL_TEXTURE0 + i);
if (!_glgsg->update_texture(gtc, false)) {
continue;
}
_glgsg->apply_texture(gtc);
_glgsg->apply_sampler(i, sampler, gtc);
}
#ifndef OPENGLES
if (barriers != 0) {
// Issue a memory barrier prior to this shader's execution.
_glgsg->issue_memory_barrier(barriers);
}
#endif
_glgsg->report_my_gl_errors();
}
////////////////////////////////////////////////////////////////////
// Function: Shader::glsl_report_shader_errors
// Access: Private
// Description: This subroutine prints the infolog for a shader.
////////////////////////////////////////////////////////////////////
void CLP(ShaderContext)::
glsl_report_shader_errors(GLuint shader, Shader::ShaderType type, bool fatal) {
char *info_log;
GLint length = 0;
GLint num_chars = 0;
_glgsg->_glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length);
if (length <= 1) {
return;
}
info_log = (char *) alloca(length);
_glgsg->_glGetShaderInfoLog(shader, length, &num_chars, info_log);
if (strcmp(info_log, "Success.\n") == 0 ||
strcmp(info_log, "No errors.\n") == 0) {
return;
}
// Parse the errors so that we can substitute in actual file
// locations instead of source indices.
istringstream log(info_log);
string line;
while (getline(log, line)) {
int fileno, lineno;
int prefixlen = 0;
// First is AMD/Intel driver syntax, second is NVIDIA syntax.
if (sscanf(line.c_str(), "ERROR: %d:%d: %n", &fileno, &lineno, &prefixlen) == 2
&& prefixlen > 0) {
Filename fn = _shader->get_filename_from_index(fileno, type);
GLCAT.error(false)
<< "ERROR: " << fn << ":" << lineno << ": " << (line.c_str() + prefixlen) << "\n";
} else if (sscanf(line.c_str(), "WARNING: %d:%d: %n", &fileno, &lineno, &prefixlen) == 2
&& prefixlen > 0) {
Filename fn = _shader->get_filename_from_index(fileno, type);
GLCAT.warning(false)
<< "WARNING: " << fn << ":" << lineno << ": " << (line.c_str() + prefixlen) << "\n";
} else if (sscanf(line.c_str(), "%d(%d) : %n", &fileno, &lineno, &prefixlen) == 2
&& prefixlen > 0) {
Filename fn = _shader->get_filename_from_index(fileno, type);
GLCAT.error(false)
<< fn << "(" << lineno << ") : " << (line.c_str() + prefixlen) << "\n";
} else if (!fatal) {
GLCAT.warning(false) << line << "\n";
} else {
GLCAT.error(false) << line << "\n";
}
}
}
////////////////////////////////////////////////////////////////////
// Function: Shader::glsl_report_program_errors
// Access: Private
// Description: This subroutine prints the infolog for a program.
////////////////////////////////////////////////////////////////////
void CLP(ShaderContext)::
glsl_report_program_errors(GLuint program, bool fatal) {
char *info_log;
GLint length = 0;
GLint num_chars = 0;
_glgsg->_glGetProgramiv(program, GL_INFO_LOG_LENGTH, &length);
if (length > 1) {
info_log = (char *) alloca(length);
_glgsg->_glGetProgramInfoLog(program, length, &num_chars, info_log);
if (strcmp(info_log, "Success.\n") != 0 &&
strcmp(info_log, "No errors.\n") != 0 &&
strcmp(info_log, "Validation successful.\n") != 0) {
if (!fatal) {
GLCAT.warning()
<< "Shader " << _shader->get_filename() << " produced the "
<< "following warnings:\n" << info_log << "\n";
} else {
GLCAT.error(false) << info_log << "\n";
}
}
}
}
////////////////////////////////////////////////////////////////////
// Function: Shader::glsl_compile_shader
// Access: Private
// Description:
////////////////////////////////////////////////////////////////////
bool CLP(ShaderContext)::
glsl_compile_shader(Shader::ShaderType type) {
GLuint handle = 0;
switch (type) {
case Shader::ST_vertex:
handle = _glgsg->_glCreateShader(GL_VERTEX_SHADER);
break;
case Shader::ST_fragment:
handle = _glgsg->_glCreateShader(GL_FRAGMENT_SHADER);
break;
#ifndef OPENGLES
case Shader::ST_geometry:
if (_glgsg->get_supports_geometry_shaders()) {
handle = _glgsg->_glCreateShader(GL_GEOMETRY_SHADER);
}
break;
case Shader::ST_tess_control:
if (_glgsg->get_supports_tessellation_shaders()) {
handle = _glgsg->_glCreateShader(GL_TESS_CONTROL_SHADER);
}
break;
case Shader::ST_tess_evaluation:
if (_glgsg->get_supports_tessellation_shaders()) {
handle = _glgsg->_glCreateShader(GL_TESS_EVALUATION_SHADER);
}
break;
case Shader::ST_compute:
if (_glgsg->get_supports_compute_shaders()) {
handle = _glgsg->_glCreateShader(GL_COMPUTE_SHADER);
}
break;
#endif
default:
break;
}
if (!handle) {
GLCAT.error()
<< "Could not create a GLSL shader of the requested type.\n";
_glgsg->report_my_gl_errors();
return false;
}
if (_glgsg->_use_object_labels) {
string name = _shader->get_filename(type);
_glgsg->_glObjectLabel(GL_SHADER, handle, name.size(), name.data());
}
string text_str = _shader->get_text(type);
const char* text = text_str.c_str();
_glgsg->_glShaderSource(handle, 1, &text, NULL);
_glgsg->_glCompileShader(handle);
GLint status;
_glgsg->_glGetShaderiv(handle, GL_COMPILE_STATUS, &status);
if (status != GL_TRUE) {
GLCAT.error()
<< "An error occurred while compiling GLSL shader "
<< _shader->get_filename(type) << ":\n";
glsl_report_shader_errors(handle, type, true);
_glgsg->_glDeleteShader(handle);
_glgsg->report_my_gl_errors();
return false;
}
_glgsg->_glAttachShader(_glsl_program, handle);
_glsl_shaders.push_back(handle);
// There might be warnings, so report those.
glsl_report_shader_errors(handle, type, false);
return true;
}
////////////////////////////////////////////////////////////////////
// Function: Shader::glsl_compile_and_link
// Access: Private
// Description: This subroutine compiles a GLSL shader.
////////////////////////////////////////////////////////////////////
bool CLP(ShaderContext)::
glsl_compile_and_link() {
_glsl_shaders.clear();
_glsl_program = _glgsg->_glCreateProgram();
if (!_glsl_program) {
return false;
}
if (_glgsg->_use_object_labels) {
string name = _shader->get_filename();
_glgsg->_glObjectLabel(GL_PROGRAM, _glsl_program, name.size(), name.data());
}
bool valid = true;
if (!_shader->get_text(Shader::ST_vertex).empty()) {
valid &= glsl_compile_shader(Shader::ST_vertex);
}
if (!_shader->get_text(Shader::ST_fragment).empty()) {
valid &= glsl_compile_shader(Shader::ST_fragment);
}
// OpenGL ES has no geometry shaders.
#ifndef OPENGLES
if (!_shader->get_text(Shader::ST_geometry).empty()) {
valid &= glsl_compile_shader(Shader::ST_geometry);
// Set the vertex output limit to the maximum.
// This is slow, but it is probably reasonable to require
// the user to override this in his shader using layout().
nassertr(_glgsg->_glProgramParameteri != NULL, false);
GLint max_vertices;
glGetIntegerv(GL_MAX_GEOMETRY_OUTPUT_VERTICES, &max_vertices);
_glgsg->_glProgramParameteri(_glsl_program, GL_GEOMETRY_VERTICES_OUT_ARB, max_vertices);
}
#endif
if (!_shader->get_text(Shader::ST_tess_control).empty()) {
valid &= glsl_compile_shader(Shader::ST_tess_control);
}
if (!_shader->get_text(Shader::ST_tess_evaluation).empty()) {
valid &= glsl_compile_shader(Shader::ST_tess_evaluation);
}
if (!_shader->get_text(Shader::ST_compute).empty()) {
valid &= glsl_compile_shader(Shader::ST_compute);
}
// There might be warnings, so report those.
//GLSLShaders::const_iterator it;
//for (it = _glsl_shaders.begin(); it != _glsl_shaders.end(); ++it) {
// glsl_report_shader_errors(*it);
//}
// Under OpenGL's compatibility profile, we have to make sure that we bind
// something to attribute 0. Make sure that this is the position array.
_glgsg->_glBindAttribLocation(_glsl_program, 0, "p3d_Vertex");
_glgsg->_glBindAttribLocation(_glsl_program, 0, "vertex");
// While we're at it, let's also map these to fixed locations. These
// attributes were historically fixed to these locations, so it might
// help a buggy driver.
_glgsg->_glBindAttribLocation(_glsl_program, 2, "p3d_Normal");
_glgsg->_glBindAttribLocation(_glsl_program, 3, "p3d_Color");
// If we requested to retrieve the shader, we should indicate that before linking.
#if !defined(NDEBUG) && !defined(OPENGLES)
if (gl_dump_compiled_shaders && _glgsg->_supports_get_program_binary) {
_glgsg->_glProgramParameteri(_glsl_program, GL_PROGRAM_BINARY_RETRIEVABLE_HINT, GL_TRUE);
}
#endif
_glgsg->_glLinkProgram(_glsl_program);
GLint status;
_glgsg->_glGetProgramiv(_glsl_program, GL_LINK_STATUS, &status);
if (status != GL_TRUE) {
GLCAT.error() << "An error occurred while linking GLSL shader program!\n";
glsl_report_program_errors(_glsl_program, true);
return false;
}
// Report any warnings.
glsl_report_program_errors(_glsl_program, false);
// Dump the binary if requested.
#if !defined(NDEBUG) && !defined(OPENGLES)
if (gl_dump_compiled_shaders && _glgsg->_supports_get_program_binary) {
GLint length = 0;
_glgsg->_glGetProgramiv(_glsl_program, GL_PROGRAM_BINARY_LENGTH, &length);
length += 2;
char filename[64];
static int gl_dump_count = 0;
sprintf(filename, "glsl_program%d.dump", gl_dump_count++);
char *binary = new char[length];
GLenum format;
GLsizei num_bytes;
_glgsg->_glGetProgramBinary(_glsl_program, length, &num_bytes, &format, (void*)binary);
pofstream s;
s.open(filename, ios::out | ios::binary | ios::trunc);
s.write(binary, num_bytes);
s.close();
GLCAT.info()
<< "Dumped " << num_bytes << " bytes of program binary with format 0x"
<< hex << format << dec << " to " << filename << "\n";
delete[] binary;
}
#endif // NDEBUG
_glgsg->report_my_gl_errors();
return true;
}
#endif // OPENGLES_1