mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-17 20:23:47 -04:00

To access a WeakPointerTo in a thread-safe way, use something like this: if (auto ptr = weak_ptr.lock()) { ..use ptr as regular PointerTo } The new implementation no longer needs a reference to be stored to all weak pointers on the WeakReferenceList; a mere count of weak pointers is sufficient. Therefore, callbacks theoretically no longer require a WeakPointerTo to be constructed. The WeakPointerTo class is not actually atomic; it could be made so, but I don't believe it's worth it at this time.
3133 lines
101 KiB
C++
3133 lines
101 KiB
C++
/**
|
|
* 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."
|
|
*
|
|
* @file glShaderContext_src.cxx
|
|
* @author jyelon
|
|
* @date 2005-09-01
|
|
* @author fperazzi, PandaSE
|
|
* @date 2010-04-29
|
|
* parameter types only supported under Cg)
|
|
*/
|
|
|
|
#ifndef OPENGLES_1
|
|
|
|
#include "pStatGPUTimer.h"
|
|
|
|
#include "colorAttrib.h"
|
|
#include "colorScaleAttrib.h"
|
|
#include "materialAttrib.h"
|
|
#include "shaderAttrib.h"
|
|
#include "fogAttrib.h"
|
|
#include "lightAttrib.h"
|
|
#include "clipPlaneAttrib.h"
|
|
#include "bamCache.h"
|
|
|
|
TypeHandle CLP(ShaderContext)::_type_handle;
|
|
|
|
/**
|
|
* 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);
|
|
objShader->_mat_deps |= bind._dep[0] | bind._dep[1];
|
|
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* xyz
|
|
*/
|
|
CLP(ShaderContext)::
|
|
CLP(ShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderContext(s) {
|
|
_glgsg = glgsg;
|
|
_glsl_program = 0;
|
|
_uses_standard_vertex_arrays = false;
|
|
_enabled_attribs.clear();
|
|
_color_attrib_index = -1;
|
|
_transform_table_index = -1;
|
|
_slider_table_index = -1;
|
|
_frame_number_loc = -1;
|
|
_frame_number = -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;
|
|
}
|
|
|
|
// Process the vertex attributes first.
|
|
GLint param_count = 0;
|
|
GLint name_buflen = 0;
|
|
_glgsg->_glGetProgramiv(_glsl_program, GL_ACTIVE_ATTRIBUTES, ¶m_count);
|
|
_glgsg->_glGetProgramiv(_glsl_program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &name_buflen);
|
|
name_buflen = max(64, name_buflen);
|
|
char *name_buffer = (char *)alloca(name_buflen);
|
|
|
|
_shader->_var_spec.clear();
|
|
for (int i = 0; i < param_count; ++i) {
|
|
reflect_attribute(i, name_buffer, name_buflen);
|
|
}
|
|
|
|
/*if (gl_fixed_vertex_attrib_locations) {
|
|
// Relink the shader for glBindAttribLocation to take effect.
|
|
_glgsg->_glLinkProgram(_glsl_program);
|
|
}*/
|
|
|
|
// Create a buffer the size of the longest uniform name. Note that Intel HD
|
|
// drivers report values that are too low.
|
|
name_buflen = 0;
|
|
_glgsg->_glGetProgramiv(_glsl_program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &name_buflen);
|
|
name_buflen = max(64, name_buflen);
|
|
name_buffer = (char *)alloca(name_buflen);
|
|
|
|
// Get the used uniform blocks.
|
|
if (_glgsg->_supports_uniform_buffers) {
|
|
GLint block_count = 0, block_maxlength = 0;
|
|
_glgsg->_glGetProgramiv(_glsl_program, GL_ACTIVE_UNIFORM_BLOCKS, &block_count);
|
|
|
|
// Intel HD drivers report GL_INVALID_ENUM here. They reportedly fixed
|
|
// it, but I don't know in which driver version the fix is.
|
|
if (_glgsg->_gl_vendor != "Intel") {
|
|
_glgsg->_glGetProgramiv(_glsl_program, GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, &block_maxlength);
|
|
block_maxlength = max(64, block_maxlength);
|
|
} else {
|
|
block_maxlength = 1024;
|
|
}
|
|
|
|
char *block_name_cstr = (char *)alloca(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);
|
|
}
|
|
}
|
|
|
|
#ifndef OPENGLES
|
|
// Get the used shader storage blocks.
|
|
if (_glgsg->_supports_shader_buffers) {
|
|
GLint block_count = 0, block_maxlength = 0;
|
|
|
|
_glgsg->_glGetProgramInterfaceiv(_glsl_program, GL_SHADER_STORAGE_BLOCK, GL_ACTIVE_RESOURCES, &block_count);
|
|
_glgsg->_glGetProgramInterfaceiv(_glsl_program, GL_SHADER_STORAGE_BLOCK, GL_MAX_NAME_LENGTH, &block_maxlength);
|
|
|
|
block_maxlength = max(64, block_maxlength);
|
|
char *block_name_cstr = (char *)alloca(block_maxlength);
|
|
|
|
for (int i = 0; i < block_count; ++i) {
|
|
block_name_cstr[0] = 0;
|
|
_glgsg->_glGetProgramResourceName(_glsl_program, GL_SHADER_STORAGE_BLOCK, i, block_maxlength, NULL, block_name_cstr);
|
|
|
|
const GLenum props[] = {GL_BUFFER_BINDING, GL_BUFFER_DATA_SIZE};
|
|
GLint values[2];
|
|
_glgsg->_glGetProgramResourceiv(_glsl_program, GL_SHADER_STORAGE_BLOCK, i, 2, props, 2, NULL, values);
|
|
|
|
StorageBlock block;
|
|
block._name = InternalName::make(block_name_cstr);
|
|
block._binding_index = values[0];
|
|
block._min_size = values[1];
|
|
_storage_blocks.push_back(block);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Bind the program, so that we can call glUniform1i for the textures.
|
|
_glgsg->_glUseProgram(_glsl_program);
|
|
|
|
// Analyze the uniforms.
|
|
param_count = 0;
|
|
_glgsg->_glGetProgramiv(_glsl_program, GL_ACTIVE_UNIFORMS, ¶m_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);
|
|
}
|
|
|
|
_glgsg->report_my_gl_errors();
|
|
|
|
// Restore the active shader.
|
|
if (_glgsg->_current_shader_context == NULL) {
|
|
_glgsg->_glUseProgram(0);
|
|
} else {
|
|
_glgsg->_current_shader_context->bind();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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,
|
|
¶m_size, ¶m_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 ||
|
|
param_type == GL_UNSIGNED_INT_VEC2 ||
|
|
param_type == GL_UNSIGNED_INT_VEC3 ||
|
|
param_type == GL_UNSIGNED_INT_VEC4 ||
|
|
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;
|
|
}
|
|
|
|
// Experimental feature.
|
|
if (gl_fixed_vertex_attrib_locations) {
|
|
GLint loc;
|
|
if (bind._name == InternalName::get_vertex()) {
|
|
loc = 0;
|
|
} else if (bind._name == InternalName::get_transform_weight()) {
|
|
loc = 1;
|
|
} else if (bind._name == InternalName::get_normal()) {
|
|
loc = 2;
|
|
} else if (bind._name == InternalName::get_color()) {
|
|
loc = 3;
|
|
} else if (bind._name == InternalName::get_transform_index()) {
|
|
loc = 7;
|
|
} else if (bind._name == InternalName::get_texcoord() &&
|
|
bind._append_uv >= 0 && bind._append_uv < 8) {
|
|
loc = 8 + bind._append_uv;
|
|
} else {
|
|
GLCAT.error()
|
|
<< "Vertex attrib '" << name_buffer << "' not yet supported with "
|
|
"gl-fixed-vertex-attrib-locations!\n";
|
|
return;
|
|
}
|
|
if (loc != p) {
|
|
GLCAT.error()
|
|
<< "Vertex attrib '" << name_buffer << "' was bound to the wrong slot!\n";
|
|
return;
|
|
}
|
|
// _glgsg->_glBindAttribLocation(_glsl_program, loc, name_buffer);
|
|
_enabled_attribs.set_range(loc, bind._elements);
|
|
}
|
|
|
|
_shader->_var_spec.push_back(bind);
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
_glgsg->_glGetActiveUniformBlockiv(_glsl_program, i, GL_UNIFORM_BLOCK_DATA_SIZE, &data_size);
|
|
_glgsg->_glGetActiveUniformBlockiv(_glsl_program, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, ¶m_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, ¶m_size, ¶m_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;
|
|
|
|
#ifndef OPENGLES
|
|
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;
|
|
#endif
|
|
|
|
default:
|
|
GLCAT.info() << "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:
|
|
#ifndef OPENGLES
|
|
case GL_DOUBLE_VEC2:
|
|
#endif
|
|
num_components = 2;
|
|
break;
|
|
|
|
case GL_INT_VEC3:
|
|
case GL_BOOL_VEC3:
|
|
case GL_UNSIGNED_INT_VEC3:
|
|
case GL_FLOAT_VEC3:
|
|
#ifndef OPENGLES
|
|
case GL_DOUBLE_VEC3:
|
|
#endif
|
|
num_components = 3;
|
|
break;
|
|
|
|
case GL_INT_VEC4:
|
|
case GL_BOOL_VEC4:
|
|
case GL_UNSIGNED_INT_VEC4:
|
|
case GL_FLOAT_VEC4:
|
|
#ifndef OPENGLES
|
|
case GL_DOUBLE_VEC4:
|
|
#endif
|
|
num_components = 4;
|
|
break;
|
|
|
|
case GL_FLOAT_MAT3:
|
|
#ifndef OPENGLES
|
|
case GL_DOUBLE_MAT3:
|
|
#endif
|
|
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:
|
|
#ifndef OPENGLES
|
|
case GL_DOUBLE_MAT4:
|
|
#endif
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* 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, ¶m_size, ¶m_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";
|
|
}
|
|
|
|
// Some NVidia drivers (361.43 for example) (incorrectly) include "internal"
|
|
// uniforms in the list starting with "_main_" (for example,
|
|
// "_main_0_gp5fp[0]") we need to skip those, because we don't know anything
|
|
// about them
|
|
if (strncmp(name_buffer, "_main_", 6) == 0) {
|
|
if (GLCAT.is_debug()) {
|
|
GLCAT.debug() << "Ignoring uniform " << name_buffer << " which may be generated by buggy Nvidia driver.\n";
|
|
}
|
|
return;
|
|
}
|
|
|
|
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 (param_type == GL_FLOAT_MAT4) {
|
|
if (transpose) {
|
|
bind._piece = Shader::SMP_transpose;
|
|
} else {
|
|
bind._piece = Shader::SMP_whole;
|
|
}
|
|
} else {
|
|
GLCAT.error()
|
|
<< "Matrix input p3d_" << matrix_name << " should be mat3 or mat4\n";
|
|
return;
|
|
}
|
|
bind._arg[0] = NULL;
|
|
bind._arg[1] = NULL;
|
|
|
|
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.warning() << "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 if (matrix_name == "TextureMatrix") {
|
|
// We may support 2-D texmats later, but let's make sure that people
|
|
// don't think they can just use a mat3 to get the 2-D version.
|
|
if (param_type != GL_FLOAT_MAT4) {
|
|
GLCAT.error() << "p3d_TextureMatrix should be mat4[], not mat3[]!\n";
|
|
return;
|
|
}
|
|
|
|
bind._func = Shader::SMF_first;
|
|
bind._part[0] = inverse ? Shader::SMO_inv_texmat_i
|
|
: Shader::SMO_texmat_i;
|
|
bind._part[1] = Shader::SMO_identity;
|
|
bind._dep[0] = Shader::SSD_general | Shader::SSD_tex_matrix;
|
|
bind._dep[1] = 0;
|
|
|
|
// Add it once for each index.
|
|
for (bind._index = 0; bind._index < param_size; ++bind._index) {
|
|
_shader->_mat_spec.push_back(bind);
|
|
}
|
|
_shader->_mat_deps |= bind._dep[0];
|
|
return;
|
|
|
|
} else if (matrix_name.size() > 15 &&
|
|
matrix_name.substr(0, 12) == "LightSource[" &&
|
|
sscanf(matrix_name.c_str(), "LightSource[%d].%s", &bind._index, name_buffer) == 2) {
|
|
// A matrix member of a p3d_LightSource struct.
|
|
if (strncmp(name_buffer, "shadowViewMatrix", 127) == 0) {
|
|
if (inverse) {
|
|
// Tack inverse back onto the end.
|
|
strcpy(name_buffer + strlen(name_buffer), "Inverse");
|
|
}
|
|
|
|
bind._func = Shader::SMF_first;
|
|
bind._part[0] = Shader::SMO_light_source_i_attrib;
|
|
bind._arg[0] = InternalName::make(name_buffer);
|
|
bind._part[1] = Shader::SMO_identity;
|
|
bind._arg[1] = NULL;
|
|
|
|
} else if (strncmp(name_buffer, "shadowMatrix", 127) == 0) {
|
|
// Only supported for backward compatibility: includes the model
|
|
// matrix. Not very efficient to do this.
|
|
bind._func = Shader::SMF_compose;
|
|
bind._part[0] = Shader::SMO_model_to_apiview;
|
|
bind._arg[0] = NULL;
|
|
bind._part[1] = Shader::SMO_light_source_i_attrib;
|
|
bind._arg[1] = InternalName::make("shadowViewMatrix");
|
|
|
|
static bool warned = false;
|
|
if (!warned) {
|
|
warned = true;
|
|
GLCAT.warning()
|
|
<< "p3d_LightSource[].shadowMatrix is deprecated; use "
|
|
"shadowViewMatrix instead, which transforms from view space "
|
|
"instead of model space.\n";
|
|
}
|
|
} else {
|
|
GLCAT.error() << "p3d_LightSource struct does not provide a matrix named " << matrix_name << "!\n";
|
|
return;
|
|
}
|
|
|
|
} else {
|
|
GLCAT.error() << "Unrecognized uniform matrix name '" << matrix_name << "'!\n";
|
|
return;
|
|
}
|
|
_shader->cp_optimize_mat_spec(bind);
|
|
_shader->_mat_spec.push_back(bind);
|
|
_shader->_mat_deps |= bind._dep[0] | bind._dep[1];
|
|
return;
|
|
}
|
|
if (size > 7 && noprefix.substr(0, 7) == "Texture") {
|
|
Shader::ShaderTexSpec bind;
|
|
bind._id = arg_id;
|
|
bind._part = Shader::STO_stage_i;
|
|
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 | Shader::SSD_frame;
|
|
bind._part[1] = Shader::SMO_identity;
|
|
bind._arg[1] = NULL;
|
|
bind._dep[1] = Shader::SSD_NONE;
|
|
|
|
if (noprefix == "Material.baseColor") {
|
|
if (param_type != GL_FLOAT_VEC4) {
|
|
GLCAT.error()
|
|
<< "p3d_Material.baseColor should be vec4\n";
|
|
}
|
|
bind._part[0] = Shader::SMO_attr_material2;
|
|
bind._piece = Shader::SMP_row0;
|
|
bind._dep[0] |= Shader::SSD_color;
|
|
_shader->_mat_spec.push_back(bind);
|
|
_shader->_mat_deps |= bind._dep[0];
|
|
return;
|
|
|
|
} else 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);
|
|
_shader->_mat_deps |= bind._dep[0];
|
|
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);
|
|
_shader->_mat_deps |= bind._dep[0];
|
|
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);
|
|
_shader->_mat_deps |= bind._dep[0];
|
|
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);
|
|
_shader->_mat_deps |= bind._dep[0];
|
|
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);
|
|
_shader->_mat_deps |= bind._dep[0];
|
|
return;
|
|
|
|
} else if (noprefix == "Material.roughness") {
|
|
if (param_type != GL_FLOAT) {
|
|
GLCAT.error()
|
|
<< "p3d_Material.roughness should be float\n";
|
|
}
|
|
bind._part[0] = Shader::SMO_attr_material2;
|
|
bind._piece = Shader::SMP_cell15;
|
|
_shader->_mat_spec.push_back(bind);
|
|
_shader->_mat_deps |= bind._dep[0];
|
|
return;
|
|
|
|
} else if (noprefix == "Material.metallic") {
|
|
if (param_type != GL_FLOAT && param_type != GL_BOOL) {
|
|
GLCAT.error()
|
|
<< "p3d_Material.metallic should be bool or float\n";
|
|
}
|
|
bind._part[0] = Shader::SMO_attr_material2;
|
|
bind._piece = Shader::SMP_row3x1;
|
|
_shader->_mat_spec.push_back(bind);
|
|
_shader->_mat_deps |= bind._dep[0];
|
|
return;
|
|
|
|
} else if (noprefix == "Material.refractiveIndex") {
|
|
if (param_type != GL_FLOAT) {
|
|
GLCAT.error()
|
|
<< "p3d_Material.refractiveIndex should be float\n";
|
|
}
|
|
bind._part[0] = Shader::SMO_attr_material2;
|
|
bind._piece = Shader::SMP_cell13;
|
|
_shader->_mat_spec.push_back(bind);
|
|
_shader->_mat_deps |= bind._dep[0];
|
|
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);
|
|
_shader->_mat_deps |= bind._dep[0];
|
|
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);
|
|
_shader->_mat_deps |= bind._dep[0];
|
|
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);
|
|
_shader->_mat_deps |= bind._dep[0];
|
|
}
|
|
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);
|
|
_shader->_mat_deps |= bind._dep[0];
|
|
return;
|
|
}
|
|
if (size > 15 && noprefix.substr(0, 12) == "LightSource[") {
|
|
int index;
|
|
if (sscanf(noprefix.c_str(), "LightSource[%d].%s", &index, name_buffer) == 2) {
|
|
// A member of a p3d_LightSource struct.
|
|
string member_name(name_buffer);
|
|
if (member_name == "shadowMap") {
|
|
switch (param_type) {
|
|
case GL_SAMPLER_CUBE_SHADOW:
|
|
case GL_SAMPLER_2D:
|
|
case GL_SAMPLER_2D_SHADOW:
|
|
case GL_SAMPLER_CUBE:
|
|
{
|
|
Shader::ShaderTexSpec bind;
|
|
bind._id = arg_id;
|
|
bind._part = Shader::STO_light_i_shadow_map;
|
|
bind._name = 0;
|
|
bind._desired_type = Texture::TT_2d_texture;
|
|
bind._stage = index;
|
|
if (get_sampler_texture_type(bind._desired_type, param_type)) {
|
|
_glgsg->_glUniform1i(p, _shader->_tex_spec.size());
|
|
_shader->_tex_spec.push_back(bind);
|
|
}
|
|
return;
|
|
}
|
|
default:
|
|
GLCAT.error()
|
|
<< "Invalid type for p3d_LightSource[].shadowMap input!\n";
|
|
return;
|
|
}
|
|
} else {
|
|
// A non-sampler attribute of a numbered light source.
|
|
Shader::ShaderMatSpec bind;
|
|
bind._id = arg_id;
|
|
bind._func = Shader::SMF_first;
|
|
bind._index = index;
|
|
bind._part[0] = Shader::SMO_light_source_i_attrib;
|
|
bind._arg[0] = InternalName::make(member_name);
|
|
bind._dep[0] = Shader::SSD_general | Shader::SSD_light | Shader::SSD_frame;
|
|
bind._part[1] = Shader::SMO_identity;
|
|
bind._arg[1] = NULL;
|
|
bind._dep[1] = Shader::SSD_NONE;
|
|
|
|
if (member_name == "position" || member_name == "halfVector" ||
|
|
member_name == "spotDirection") {
|
|
bind._dep[0] |= Shader::SSD_view_transform;
|
|
}
|
|
|
|
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;
|
|
|
|
case GL_FLOAT_VEC4:
|
|
bind._piece = Shader::SMP_row3;
|
|
break;
|
|
|
|
default:
|
|
GLCAT.error()
|
|
<< "p3d_LightSource[]." << member_name << " should be float or vec\n";
|
|
return;
|
|
}
|
|
_shader->_mat_spec.push_back(bind);
|
|
_shader->_mat_deps |= bind._dep[0] | bind._dep[1];
|
|
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;
|
|
}
|
|
if (noprefix == "TexAlphaOnly") {
|
|
Shader::ShaderMatSpec bind;
|
|
bind._id = arg_id;
|
|
bind._func = Shader::SMF_first;
|
|
bind._index = 0;
|
|
bind._part[0] = Shader::SMO_tex_is_alpha_i;
|
|
bind._arg[0] = NULL;
|
|
bind._dep[0] = Shader::SSD_general | Shader::SSD_texture | Shader::SSD_frame;
|
|
bind._part[1] = Shader::SMO_identity;
|
|
bind._arg[1] = NULL;
|
|
bind._dep[1] = Shader::SSD_NONE;
|
|
bind._piece = Shader::SMP_row3;
|
|
_shader->_mat_spec.push_back(bind);
|
|
_shader->_mat_deps |= bind._dep[0] | bind._dep[1];
|
|
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.
|
|
|
|
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_view_transform;
|
|
bind._dep[1] = Shader::SSD_general;
|
|
_shader->_mat_spec.push_back(bind);
|
|
_shader->_mat_deps |= bind._dep[0] | bind._dep[1];
|
|
return;
|
|
|
|
} else if (noprefix == "InverseViewMatrix" || noprefix == "ViewMatrixInverse") {
|
|
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;
|
|
bind._dep[1] = Shader::SSD_general | Shader::SSD_view_transform;
|
|
_shader->_mat_spec.push_back(bind);
|
|
_shader->_mat_deps |= bind._dep[0] | bind._dep[1];
|
|
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 | Shader::SSD_frame;
|
|
bind._dep[1] = Shader::SSD_NONE;
|
|
_shader->_mat_spec.push_back(bind);
|
|
_shader->_mat_deps |= bind._dep[0] | bind._dep[1];
|
|
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 | Shader::SSD_frame;
|
|
bind._dep[1] = Shader::SSD_NONE;
|
|
_shader->_mat_spec.push_back(bind);
|
|
_shader->_mat_deps |= bind._dep[0] | bind._dep[1];
|
|
return;
|
|
|
|
} else if (noprefix == "FrameNumber") {
|
|
// We don't currently support ints with this mechanism, so we special-
|
|
// case this one.
|
|
if (param_type != GL_INT) {
|
|
GLCAT.error() << "osg_FrameNumber should be uniform int\n";
|
|
} else {
|
|
_frame_number_loc = p;
|
|
}
|
|
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) {
|
|
case GL_INT_SAMPLER_2D:
|
|
case GL_INT_SAMPLER_3D:
|
|
case GL_INT_SAMPLER_2D_ARRAY:
|
|
case GL_INT_SAMPLER_CUBE:
|
|
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_SAMPLER_CUBE_SHADOW:
|
|
case GL_SAMPLER_2D_ARRAY:
|
|
case GL_SAMPLER_2D_ARRAY_SHADOW:
|
|
#ifndef OPENGLES
|
|
case GL_INT_SAMPLER_1D:
|
|
case GL_INT_SAMPLER_1D_ARRAY:
|
|
case GL_INT_SAMPLER_BUFFER:
|
|
case GL_INT_SAMPLER_CUBE_MAP_ARRAY:
|
|
case GL_UNSIGNED_INT_SAMPLER_1D:
|
|
case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY:
|
|
case GL_UNSIGNED_INT_SAMPLER_BUFFER:
|
|
case GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY:
|
|
case GL_SAMPLER_1D:
|
|
case GL_SAMPLER_1D_ARRAY:
|
|
case GL_SAMPLER_1D_SHADOW:
|
|
case GL_SAMPLER_BUFFER:
|
|
case GL_SAMPLER_CUBE_MAP_ARRAY:
|
|
case GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW:
|
|
#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._part = Shader::STO_named_input;
|
|
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:
|
|
case GL_FLOAT_MAT2x3:
|
|
case GL_FLOAT_MAT2x4:
|
|
case GL_FLOAT_MAT3x2:
|
|
case GL_FLOAT_MAT3x4:
|
|
case GL_FLOAT_MAT4x2:
|
|
case GL_FLOAT_MAT4x3:
|
|
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 | Shader::SSD_frame;
|
|
bind._part[1] = Shader::SMO_identity;
|
|
bind._arg[1] = NULL;
|
|
bind._dep[1] = Shader::SSD_NONE;
|
|
_shader->_mat_spec.push_back(bind);
|
|
_shader->_mat_deps |= bind._dep[0];
|
|
return;
|
|
}
|
|
case GL_FLOAT_MAT4: {
|
|
Shader::ShaderMatSpec bind;
|
|
bind._id = arg_id;
|
|
bind._piece = Shader::SMP_whole;
|
|
bind._func = Shader::SMF_first;
|
|
bind._part[1] = Shader::SMO_identity;
|
|
bind._arg[1] = NULL;
|
|
bind._dep[1] = Shader::SSD_NONE;
|
|
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.
|
|
if (iname->get_basename() == "shadowMatrix") {
|
|
// Special exception for shadowMatrix, which is deprecated,
|
|
// because it includes the model transformation. It is far more
|
|
// efficient to do that in the shader instead.
|
|
static bool warned = false;
|
|
if (!warned) {
|
|
warned = true;
|
|
GLCAT.warning()
|
|
<< "light.shadowMatrix inputs are deprecated; use "
|
|
"shadowViewMatrix instead, which transforms from view "
|
|
"space instead of model space.\n";
|
|
}
|
|
bind._func = Shader::SMF_compose;
|
|
bind._part[0] = Shader::SMO_model_to_apiview;
|
|
bind._arg[0] = NULL;
|
|
bind._dep[0] = Shader::SSD_general | Shader::SSD_transform;
|
|
bind._part[1] = Shader::SMO_mat_constant_x_attrib;
|
|
bind._arg[1] = iname->get_parent()->append("shadowViewMatrix");
|
|
bind._dep[1] = Shader::SSD_general | Shader::SSD_shaderinputs | Shader::SSD_frame | Shader::SSD_view_transform;
|
|
} else {
|
|
bind._part[0] = Shader::SMO_mat_constant_x_attrib;
|
|
bind._arg[0] = InternalName::make(param_name);
|
|
bind._dep[0] = Shader::SSD_general | Shader::SSD_shaderinputs | Shader::SSD_frame | Shader::SSD_view_transform;
|
|
}
|
|
} else {
|
|
bind._part[0] = Shader::SMO_mat_constant_x;
|
|
bind._arg[0] = InternalName::make(param_name);
|
|
bind._dep[0] = Shader::SSD_general | Shader::SSD_shaderinputs | Shader::SSD_frame;
|
|
}
|
|
_shader->_mat_spec.push_back(bind);
|
|
_shader->_mat_deps |= bind._dep[0];
|
|
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;
|
|
// We need SSD_view_transform since some attributes (eg. light
|
|
// position) have to be transformed to view space.
|
|
bind._dep[0] = Shader::SSD_general | Shader::SSD_shaderinputs | Shader::SSD_frame | Shader::SSD_view_transform;
|
|
bind._part[1] = Shader::SMO_identity;
|
|
bind._arg[1] = NULL;
|
|
bind._dep[1] = Shader::SSD_NONE;
|
|
_shader->_mat_spec.push_back(bind);
|
|
_shader->_mat_deps |= bind._dep[0];
|
|
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 | Shader::SSD_frame;
|
|
bind._dep[1] = Shader::SSD_NONE;
|
|
_shader->_ptr_spec.push_back(bind);
|
|
return;
|
|
}
|
|
case GL_IMAGE_2D:
|
|
case GL_IMAGE_3D:
|
|
case GL_IMAGE_CUBE:
|
|
case GL_IMAGE_2D_ARRAY:
|
|
case GL_INT_IMAGE_2D:
|
|
case GL_INT_IMAGE_3D:
|
|
case GL_INT_IMAGE_CUBE:
|
|
case GL_INT_IMAGE_2D_ARRAY:
|
|
case GL_UNSIGNED_INT_IMAGE_2D:
|
|
case GL_UNSIGNED_INT_IMAGE_3D:
|
|
case GL_UNSIGNED_INT_IMAGE_CUBE:
|
|
case GL_UNSIGNED_INT_IMAGE_2D_ARRAY:
|
|
#ifndef OPENGLES
|
|
case GL_IMAGE_1D:
|
|
case GL_IMAGE_CUBE_MAP_ARRAY:
|
|
case GL_IMAGE_BUFFER:
|
|
case GL_INT_IMAGE_1D:
|
|
case GL_INT_IMAGE_CUBE_MAP_ARRAY:
|
|
case GL_INT_IMAGE_BUFFER:
|
|
case GL_UNSIGNED_INT_IMAGE_1D:
|
|
case GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY:
|
|
case GL_UNSIGNED_INT_IMAGE_BUFFER:
|
|
#endif
|
|
// 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;
|
|
default:
|
|
GLCAT.warning() << "Ignoring unrecognized GLSL parameter type!\n";
|
|
}
|
|
} else {
|
|
// A uniform array.
|
|
switch (param_type) {
|
|
case GL_FLOAT_MAT2:
|
|
case GL_FLOAT_MAT2x3:
|
|
case GL_FLOAT_MAT2x4:
|
|
case GL_FLOAT_MAT3x2:
|
|
case GL_FLOAT_MAT3x4:
|
|
case GL_FLOAT_MAT4x2:
|
|
case GL_FLOAT_MAT4x3:
|
|
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 | Shader::SSD_frame;
|
|
bind._dep[1] = Shader::SSD_NONE;
|
|
_shader->_ptr_spec.push_back(bind);
|
|
return;
|
|
}
|
|
default:
|
|
GLCAT.warning() << "Ignoring unrecognized GLSL parameter array type!\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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_1D_ARRAY:
|
|
case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY:
|
|
case GL_SAMPLER_1D_ARRAY:
|
|
out = Texture::TT_1d_texture_array;
|
|
return true;
|
|
#endif
|
|
|
|
case GL_INT_SAMPLER_2D:
|
|
case GL_UNSIGNED_INT_SAMPLER_2D:
|
|
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;
|
|
|
|
case GL_INT_SAMPLER_3D:
|
|
case GL_UNSIGNED_INT_SAMPLER_3D:
|
|
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;
|
|
}
|
|
|
|
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:
|
|
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;
|
|
|
|
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;
|
|
}
|
|
|
|
#ifndef OPENGLES
|
|
case GL_SAMPLER_CUBE_MAP_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_CUBE_MAP_ARRAY:
|
|
case GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY:
|
|
case GL_SAMPLER_CUBE_MAP_ARRAY:
|
|
out = Texture::TT_cube_map_array;
|
|
if (_glgsg->_supports_cube_map_array) {
|
|
return true;
|
|
} else {
|
|
GLCAT.error()
|
|
<< "GLSL shader uses cube map 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 // !OPENGLES
|
|
|
|
default:
|
|
GLCAT.error()
|
|
<< "GLSL shader uses unsupported sampler type for texture input.\n";
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* xyz
|
|
*/
|
|
CLP(ShaderContext)::
|
|
~CLP(ShaderContext)() {
|
|
// Don't call release_resources; we may not have an active context.
|
|
}
|
|
|
|
/**
|
|
* 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();
|
|
}
|
|
|
|
/**
|
|
* Returns true if the shader is "valid", ie, if the compilation was
|
|
* successful. The compilation could fail if there is a syntax error in the
|
|
* shader, or if the current video card isn't shader-capable, or if no shader
|
|
* languages are compiled into panda.
|
|
*/
|
|
bool CLP(ShaderContext)::
|
|
valid() {
|
|
if (_shader->get_error_flag()) {
|
|
return false;
|
|
}
|
|
return (_glsl_program != 0);
|
|
}
|
|
|
|
/**
|
|
* 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() {
|
|
if (!_validated) {
|
|
_glgsg->_glValidateProgram(_glsl_program);
|
|
glsl_report_program_errors(_glsl_program, false);
|
|
_validated = true;
|
|
}
|
|
|
|
if (!_shader->get_error_flag()) {
|
|
_glgsg->_glUseProgram(_glsl_program);
|
|
}
|
|
|
|
if (GLCAT.is_spam()) {
|
|
GLCAT.spam() << "glUseProgram(" << _glsl_program << "): "
|
|
<< _shader->get_filename() << "\n";
|
|
}
|
|
|
|
_glgsg->report_my_gl_errors();
|
|
}
|
|
|
|
/**
|
|
* This function disables a currently-bound shader.
|
|
*/
|
|
void CLP(ShaderContext)::
|
|
unbind() {
|
|
if (GLCAT.is_spam()) {
|
|
GLCAT.spam() << "glUseProgram(0)\n";
|
|
}
|
|
|
|
_glgsg->_glUseProgram(0);
|
|
_glgsg->report_my_gl_errors();
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
void CLP(ShaderContext)::
|
|
set_state_and_transform(const RenderState *target_rs,
|
|
const TransformState *modelview_transform,
|
|
const TransformState *camera_transform,
|
|
const TransformState *projection_transform) {
|
|
|
|
// Find out which state properties have changed.
|
|
int altered = 0;
|
|
|
|
if (_modelview_transform != modelview_transform) {
|
|
_modelview_transform = modelview_transform;
|
|
altered |= (Shader::SSD_transform & ~Shader::SSD_view_transform);
|
|
}
|
|
if (_camera_transform != camera_transform) {
|
|
_camera_transform = camera_transform;
|
|
altered |= Shader::SSD_transform;
|
|
}
|
|
if (_projection_transform != projection_transform) {
|
|
_projection_transform = projection_transform;
|
|
altered |= Shader::SSD_projection;
|
|
}
|
|
|
|
CPT(RenderState) state_rs = _state_rs.lock();
|
|
if (state_rs == nullptr) {
|
|
// Reset all of the state.
|
|
altered |= Shader::SSD_general;
|
|
_state_rs = target_rs;
|
|
|
|
} else if (state_rs != target_rs) {
|
|
// The state has changed since last time.
|
|
if (state_rs->get_attrib(ColorAttrib::get_class_slot()) !=
|
|
target_rs->get_attrib(ColorAttrib::get_class_slot())) {
|
|
altered |= Shader::SSD_color;
|
|
}
|
|
if (state_rs->get_attrib(ColorScaleAttrib::get_class_slot()) !=
|
|
target_rs->get_attrib(ColorScaleAttrib::get_class_slot())) {
|
|
altered |= Shader::SSD_colorscale;
|
|
}
|
|
if (state_rs->get_attrib(MaterialAttrib::get_class_slot()) !=
|
|
target_rs->get_attrib(MaterialAttrib::get_class_slot())) {
|
|
altered |= Shader::SSD_material;
|
|
}
|
|
if (state_rs->get_attrib(ShaderAttrib::get_class_slot()) !=
|
|
target_rs->get_attrib(ShaderAttrib::get_class_slot())) {
|
|
altered |= Shader::SSD_shaderinputs;
|
|
}
|
|
if (state_rs->get_attrib(FogAttrib::get_class_slot()) !=
|
|
target_rs->get_attrib(FogAttrib::get_class_slot())) {
|
|
altered |= Shader::SSD_fog;
|
|
}
|
|
if (state_rs->get_attrib(LightAttrib::get_class_slot()) !=
|
|
target_rs->get_attrib(LightAttrib::get_class_slot())) {
|
|
altered |= Shader::SSD_light;
|
|
}
|
|
if (state_rs->get_attrib(ClipPlaneAttrib::get_class_slot()) !=
|
|
target_rs->get_attrib(ClipPlaneAttrib::get_class_slot())) {
|
|
altered |= Shader::SSD_clip_planes;
|
|
}
|
|
if (state_rs->get_attrib(TexMatrixAttrib::get_class_slot()) !=
|
|
target_rs->get_attrib(TexMatrixAttrib::get_class_slot())) {
|
|
altered |= Shader::SSD_tex_matrix;
|
|
}
|
|
if (state_rs->get_attrib(TextureAttrib::get_class_slot()) !=
|
|
target_rs->get_attrib(TextureAttrib::get_class_slot())) {
|
|
altered |= Shader::SSD_texture;
|
|
}
|
|
_state_rs = target_rs;
|
|
}
|
|
|
|
// Is this the first time this shader is used this frame?
|
|
int frame_number = ClockObject::get_global_clock()->get_frame_count();
|
|
if (frame_number != _frame_number) {
|
|
altered |= Shader::SSD_frame;
|
|
_frame_number = frame_number;
|
|
}
|
|
|
|
if (altered != 0) {
|
|
issue_parameters(altered);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
void CLP(ShaderContext)::
|
|
issue_parameters(int altered) {
|
|
PStatGPUTimer timer(_glgsg, _glgsg->_draw_set_state_shader_parameters_pcollector);
|
|
|
|
if (GLCAT.is_spam()) {
|
|
GLCAT.spam()
|
|
<< "Setting uniforms for " << _shader->get_filename()
|
|
<< " (altered 0x" << hex << altered << dec << ")\n";
|
|
}
|
|
|
|
// We have no way to track modifications to PTAs, so we assume that they are
|
|
// modified every frame and when we switch ShaderAttribs.
|
|
if (altered & (Shader::SSD_shaderinputs | Shader::SSD_frame)) {
|
|
|
|
// If we have an osg_FrameNumber input, set it now.
|
|
if ((altered | Shader::SSD_frame) != 0 && _frame_number_loc >= 0) {
|
|
_glgsg->_glUniform1i(_frame_number_loc, _frame_number);
|
|
}
|
|
|
|
// Iterate through _ptr parameters
|
|
for (int i = 0; i < (int)_shader->_ptr_spec.size(); ++i) {
|
|
Shader::ShaderPtrSpec &spec = _shader->_ptr_spec[i];
|
|
|
|
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;
|
|
int array_size = min(spec._dim[0], (int)ptr_data->_size / spec._dim[1]);
|
|
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) * array_size * spec._dim[1]);
|
|
for (int i = 0; i < (array_size * 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) * array_size * spec._dim[1]);
|
|
for (int i = 0; i < (array_size * 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, array_size, (float*)data); continue;
|
|
case 2: _glgsg->_glUniform2fv(p, array_size, (float*)data); continue;
|
|
case 3: _glgsg->_glUniform3fv(p, array_size, (float*)data); continue;
|
|
case 4: _glgsg->_glUniform4fv(p, array_size, (float*)data); continue;
|
|
case 9: _glgsg->_glUniformMatrix3fv(p, array_size, GL_FALSE, (float*)data); continue;
|
|
case 16: _glgsg->_glUniformMatrix4fv(p, array_size, 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, array_size, (int*)ptr_data->_ptr); continue;
|
|
case 2: _glgsg->_glUniform2iv(p, array_size, (int*)ptr_data->_ptr); continue;
|
|
case 3: _glgsg->_glUniform3iv(p, array_size, (int*)ptr_data->_ptr); continue;
|
|
case 4: _glgsg->_glUniform4iv(p, array_size, (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;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (altered & _shader->_mat_deps) {
|
|
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])) == 0) {
|
|
continue;
|
|
}
|
|
|
|
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;
|
|
case Shader::SMP_cell14:
|
|
_glgsg->_glUniform1fv(p, 1, data+14);
|
|
continue;
|
|
case Shader::SMP_cell13:
|
|
_glgsg->_glUniform1fv(p, 1, data+13);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
_glgsg->report_my_gl_errors();
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
|
|
size_t i = 0;
|
|
if (table != NULL) {
|
|
size_t num_transforms = min((size_t)_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);
|
|
}
|
|
|
|
/**
|
|
* 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) {
|
|
size_t num_sliders = min((size_t)_slider_table_size, table->get_num_sliders());
|
|
for (size_t i = 0; i < num_sliders; ++i) {
|
|
sliders[i] = table->get_slider(i)->get_slider();
|
|
}
|
|
}
|
|
|
|
_glgsg->_glUniform1fv(_slider_table_index, _slider_table_size, sliders);
|
|
}
|
|
|
|
/**
|
|
* Disable all the vertex arrays used by this shader.
|
|
*/
|
|
void CLP(ShaderContext)::
|
|
disable_shader_vertex_arrays() {
|
|
if (_glsl_program == 0) {
|
|
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;
|
|
|
|
for (int i = 0; i < bind._elements; ++i) {
|
|
_glgsg->disable_vertex_attrib_array(p + i);
|
|
}
|
|
}
|
|
|
|
_glgsg->report_my_gl_errors();
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
bool CLP(ShaderContext)::
|
|
update_shader_vertex_arrays(ShaderContext *prev, bool force) {
|
|
if (_glsl_program == 0) {
|
|
return true;
|
|
}
|
|
|
|
// Get the active ColorAttrib. We'll need it to determine how to apply
|
|
// vertex colors.
|
|
const ColorAttrib *color_attrib;
|
|
_state_rs->get_attrib_def(color_attrib);
|
|
|
|
const GeomVertexArrayDataHandle *array_reader;
|
|
|
|
if (_glgsg->_use_vertex_attrib_binding) {
|
|
// Use experimental new separated formatbinding state.
|
|
const GeomVertexDataPipelineReader *data_reader = _glgsg->_data_reader;
|
|
|
|
for (int ai = 0; ai < data_reader->get_num_arrays(); ++ai) {
|
|
array_reader = data_reader->get_array_reader(ai);
|
|
|
|
// Make sure the vertex buffer is up-to-date.
|
|
CLP(VertexBufferContext) *gvbc = DCAST(CLP(VertexBufferContext),
|
|
array_reader->prepare_now(_glgsg->get_prepared_objects(), _glgsg));
|
|
nassertr(gvbc != (CLP(VertexBufferContext) *)NULL, false);
|
|
|
|
if (!_glgsg->update_vertex_buffer(gvbc, array_reader, force)) {
|
|
return false;
|
|
}
|
|
|
|
GLintptr stride = array_reader->get_array_format()->get_stride();
|
|
|
|
// Bind the vertex buffer to the binding index.
|
|
if (ai >= _glgsg->_current_vertex_buffers.size()) {
|
|
GLuint zero = 0;
|
|
_glgsg->_current_vertex_buffers.resize(ai + 1, zero);
|
|
}
|
|
if (_glgsg->_current_vertex_buffers[ai] != gvbc->_index) {
|
|
_glgsg->_glBindVertexBuffer(ai, gvbc->_index, 0, stride);
|
|
_glgsg->_current_vertex_buffers[ai] = gvbc->_index;
|
|
}
|
|
}
|
|
|
|
// Figure out which attributes to enable or disable.
|
|
BitMask32 enabled_attribs = _enabled_attribs;
|
|
if (_color_attrib_index != -1 &&
|
|
color_attrib->get_color_type() != ColorAttrib::T_vertex) {
|
|
// Vertex colours are disabled.
|
|
enabled_attribs.clear_bit(_color_attrib_index);
|
|
|
|
#ifdef STDFLOAT_DOUBLE
|
|
_glgsg->_glVertexAttrib4dv(_color_attrib_index, color_attrib->get_color().get_data());
|
|
#else
|
|
_glgsg->_glVertexAttrib4fv(_color_attrib_index, color_attrib->get_color().get_data());
|
|
#endif
|
|
}
|
|
|
|
BitMask32 changed_attribs = enabled_attribs ^ _glgsg->_enabled_vertex_attrib_arrays;
|
|
|
|
for (int i = 0; i < 32; ++i) {
|
|
if (changed_attribs.get_bit(i)) {
|
|
if (enabled_attribs.get_bit(i)) {
|
|
_glgsg->_glEnableVertexAttribArray(i);
|
|
} else {
|
|
_glgsg->_glDisableVertexAttribArray(i);
|
|
}
|
|
}
|
|
}
|
|
_glgsg->_enabled_vertex_attrib_arrays = enabled_attribs;
|
|
|
|
} else {
|
|
Geom::NumericType numeric_type;
|
|
int start, stride, num_values;
|
|
size_t nvarying = _shader->_var_spec.size();
|
|
|
|
GLuint max_p = 0;
|
|
|
|
for (size_t 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());
|
|
}
|
|
}
|
|
|
|
GLuint p = bind._id._seqno;
|
|
max_p = max(max_p, p + 1);
|
|
|
|
// 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 || color_attrib->get_color_type() == ColorAttrib::T_vertex) &&
|
|
_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->enable_vertex_attrib_array(p);
|
|
|
|
if (bind._integer) {
|
|
_glgsg->_glVertexAttribIPointer(p, num_values, _glgsg->get_numeric_type(numeric_type),
|
|
stride, client_pointer);
|
|
} else 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 (divisor > 0) {
|
|
_glgsg->set_vertex_attrib_divisor(p, divisor);
|
|
}
|
|
|
|
++p;
|
|
client_pointer += element_stride;
|
|
}
|
|
} else {
|
|
for (int i = 0; i < bind._elements; ++i) {
|
|
_glgsg->disable_vertex_attrib_array(p + i);
|
|
}
|
|
if (p == _color_attrib_index) {
|
|
// Vertex colors are disabled or not present. Apply flat color.
|
|
#ifdef STDFLOAT_DOUBLE
|
|
_glgsg->_glVertexAttrib4dv(p, color_attrib->get_color().get_data());
|
|
#else
|
|
_glgsg->_glVertexAttrib4fv(p, color_attrib->get_color().get_data());
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
// Disable attribute arrays we don't use.
|
|
GLint highest_p = _glgsg->_enabled_vertex_attrib_arrays.get_highest_on_bit() + 1;
|
|
for (GLint p = max_p; p < highest_p; ++p) {
|
|
_glgsg->disable_vertex_attrib_array(p);
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* Disable all the texture bindings used by this shader.
|
|
*/
|
|
void CLP(ShaderContext)::
|
|
disable_shader_texture_bindings() {
|
|
if (_glsl_program == 0) {
|
|
return;
|
|
}
|
|
|
|
DO_PSTATS_STUFF(_glgsg->_texture_state_pcollector.add_level(1));
|
|
|
|
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->set_active_texture_stage(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:
|
|
glBindTexture(GL_TEXTURE_3D, 0);
|
|
break;
|
|
|
|
case Texture::TT_2d_texture_array:
|
|
glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
|
|
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;
|
|
}
|
|
}
|
|
|
|
// 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) {
|
|
#ifndef OPENGLES
|
|
if (_glgsg->_supports_multi_bind) {
|
|
_glgsg->_glBindImageTextures(0, num_image_units, NULL);
|
|
} else
|
|
#endif
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
_glgsg->report_my_gl_errors();
|
|
}
|
|
|
|
/**
|
|
* 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 (_glsl_program == 0) {
|
|
return;
|
|
}
|
|
|
|
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);
|
|
switch (sinp.get_value_type()) {
|
|
case ShaderInput::M_texture_image:
|
|
param = (const ParamTextureImage *)sinp.get_param();
|
|
tex = param->get_texture();
|
|
break;
|
|
|
|
case ShaderInput::M_texture:
|
|
// People find it convenient to be able to pass a texture without
|
|
// further ado.
|
|
tex = sinp.get_texture();
|
|
break;
|
|
|
|
case ShaderInput::M_invalid:
|
|
GLCAT.error()
|
|
<< "Missing texture image binding input " << *input._name << "\n";
|
|
continue;
|
|
|
|
default:
|
|
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;
|
|
|
|
_glgsg->update_texture(gtc, true);
|
|
gl_tex = gtc->_index;
|
|
|
|
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 (gl_force_image_bindings_writeonly) {
|
|
access = GL_WRITE_ONLY;
|
|
|
|
} else 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);
|
|
}
|
|
}
|
|
}
|
|
|
|
size_t num_textures = _shader->_tex_spec.size();
|
|
GLuint *textures;
|
|
GLuint *samplers;
|
|
#ifdef OPENGLES
|
|
static const bool multi_bind = false;
|
|
#else
|
|
bool multi_bind = false;
|
|
if (num_textures > 1 &&
|
|
_glgsg->_supports_multi_bind && _glgsg->_supports_sampler_objects) {
|
|
// Prepare to multi-bind the textures and samplers.
|
|
multi_bind = true;
|
|
textures = (GLuint *)alloca(sizeof(GLuint) * num_textures);
|
|
samplers = (GLuint *)alloca(sizeof(GLuint) * num_textures);
|
|
}
|
|
#endif
|
|
|
|
for (size_t i = 0; i < num_textures; ++i) {
|
|
Shader::ShaderTexSpec &spec = _shader->_tex_spec[i];
|
|
const InternalName *id = spec._name;
|
|
|
|
int view = _glgsg->get_current_tex_view_offset();
|
|
SamplerState sampler;
|
|
|
|
PT(Texture) tex = _glgsg->fetch_specified_texture(spec, sampler, view);
|
|
if (tex.is_null()) {
|
|
// 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.
|
|
if (multi_bind) {
|
|
textures[i] = _glgsg->get_white_texture();
|
|
samplers[i] = 0;
|
|
} else {
|
|
_glgsg->apply_white_texture(i);
|
|
}
|
|
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) {
|
|
if (multi_bind) {
|
|
textures[i] = 0;
|
|
samplers[i] = 0;
|
|
}
|
|
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 (multi_bind) {
|
|
textures[i] = 0;
|
|
samplers[i] = 0;
|
|
}
|
|
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.
|
|
#ifndef OPENGLES
|
|
if (multi_bind) {
|
|
// Multi-bind case.
|
|
if (!_glgsg->update_texture(gtc, false)) {
|
|
textures[i] = 0;
|
|
} else {
|
|
gtc->set_active(true);
|
|
textures[i] = gtc->_index;
|
|
}
|
|
|
|
SamplerContext *sc = sampler.prepare_now(_glgsg->get_prepared_objects(), _glgsg);
|
|
if (sc == NULL) {
|
|
samplers[i] = 0;
|
|
} else {
|
|
CLP(SamplerContext) *gsc = DCAST(CLP(SamplerContext), sc);
|
|
gsc->enqueue_lru(&_glgsg->_prepared_objects->_sampler_object_lru);
|
|
samplers[i] = gsc->_index;
|
|
}
|
|
} else
|
|
#endif // !OPENGLES
|
|
{
|
|
// Non-multibind case.
|
|
_glgsg->set_active_texture_stage(i);
|
|
if (!_glgsg->update_texture(gtc, false)) {
|
|
continue;
|
|
}
|
|
_glgsg->apply_texture(gtc);
|
|
_glgsg->apply_sampler(i, sampler, gtc);
|
|
}
|
|
}
|
|
|
|
#ifndef OPENGLES
|
|
if (multi_bind && num_textures > 0) {
|
|
_glgsg->_glBindTextures(0, num_textures, textures);
|
|
_glgsg->_glBindSamplers(0, num_textures, samplers);
|
|
}
|
|
|
|
if (barriers != 0) {
|
|
// Issue a memory barrier prior to this shader's execution.
|
|
_glgsg->issue_memory_barrier(barriers);
|
|
}
|
|
#endif
|
|
|
|
_glgsg->report_my_gl_errors();
|
|
}
|
|
|
|
/**
|
|
* Updates the shader buffer bindings for this shader.
|
|
*/
|
|
void CLP(ShaderContext)::
|
|
update_shader_buffer_bindings(ShaderContext *prev) {
|
|
#ifndef OPENGLES
|
|
// Update the shader storage buffer bindings.
|
|
const ShaderAttrib *attrib = _glgsg->_target_shader;
|
|
|
|
for (size_t i = 0; i < _storage_blocks.size(); ++i) {
|
|
StorageBlock &block = _storage_blocks[i];
|
|
|
|
ShaderBuffer *buffer = attrib->get_shader_input_buffer(block._name);
|
|
#ifndef NDEBUG
|
|
if (buffer->get_data_size_bytes() < block._min_size) {
|
|
GLCAT.error()
|
|
<< "cannot bind " << *buffer << " to shader because it is too small"
|
|
" (expected at least " << block._min_size << " bytes)\n";
|
|
}
|
|
#endif
|
|
_glgsg->apply_shader_buffer(block._binding_index, buffer);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* 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, colno;
|
|
int prefixlen = 0;
|
|
|
|
// This first format is used by the majority of compilers.
|
|
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) {
|
|
|
|
// This is the format NVIDIA uses.
|
|
Filename fn = _shader->get_filename_from_index(fileno, type);
|
|
GLCAT.error(false)
|
|
<< fn << "(" << lineno << ") : " << (line.c_str() + prefixlen) << "\n";
|
|
|
|
} else if (sscanf(line.c_str(), "%d:%d(%d): %n", &fileno, &lineno, &colno, &prefixlen) == 3
|
|
&& prefixlen > 0) {
|
|
|
|
// This is the format for Mesa's OpenGL ES 2 implementation.
|
|
Filename fn = _shader->get_filename_from_index(fileno, type);
|
|
GLCAT.error(false)
|
|
<< fn << ":" << lineno << "(" << colno << "): " << (line.c_str() + prefixlen) << "\n";
|
|
|
|
} else if (!fatal) {
|
|
GLCAT.warning(false) << line << "\n";
|
|
|
|
} else {
|
|
GLCAT.error(false) << line << "\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
bool CLP(ShaderContext)::
|
|
glsl_compile_shader(Shader::ShaderType type) {
|
|
static const char *types[] = {"", "vertex ", "fragment ", "geometry ",
|
|
"tessellation control ", "tessellation evaluation ", "compute ", ""};
|
|
|
|
if (GLCAT.is_debug()) {
|
|
GLCAT.debug()
|
|
<< "Compiling GLSL " << types[type] << "shader "
|
|
<< _shader->get_filename(type) << "\n";
|
|
}
|
|
|
|
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;
|
|
#endif
|
|
case Shader::ST_compute:
|
|
if (_glgsg->get_supports_compute_shaders()) {
|
|
handle = _glgsg->_glCreateShader(GL_COMPUTE_SHADER);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (!handle) {
|
|
GLCAT.error()
|
|
<< "Could not create a GLSL " << types[type] << "shader.\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 " << types[type]
|
|
<< "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;
|
|
}
|
|
|
|
/**
|
|
* 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());
|
|
}
|
|
|
|
// Do we have a compiled program? Try to load that.
|
|
unsigned int format;
|
|
string binary;
|
|
if (_shader->get_compiled(format, binary)) {
|
|
_glgsg->_glProgramBinary(_glsl_program, format, binary.data(), binary.size());
|
|
|
|
GLint status;
|
|
_glgsg->_glGetProgramiv(_glsl_program, GL_LINK_STATUS, &status);
|
|
if (status == GL_TRUE) {
|
|
// Hooray, the precompiled shader worked.
|
|
if (GLCAT.is_debug()) {
|
|
GLCAT.debug() << "Loaded precompiled binary for GLSL shader "
|
|
<< _shader->get_filename() << "\n";
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Bummer, it didn't work.. Oh well, just recompile the shader.
|
|
if (GLCAT.is_debug()) {
|
|
GLCAT.debug() << "Failure loading precompiled binary for GLSL shader "
|
|
<< _shader->get_filename() << "\n";
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
// XXX Actually, it turns out that this is unavailable in the core version
|
|
// of geometry shaders. Probably no need to bother with it.
|
|
|
|
// 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 (gl_fixed_vertex_attrib_locations) {
|
|
_glgsg->_glBindAttribLocation(_glsl_program, 1, "transform_weight");
|
|
_glgsg->_glBindAttribLocation(_glsl_program, 2, "normal");
|
|
_glgsg->_glBindAttribLocation(_glsl_program, 3, "color");
|
|
_glgsg->_glBindAttribLocation(_glsl_program, 7, "transform_index");
|
|
_glgsg->_glBindAttribLocation(_glsl_program, 8, "p3d_MultiTexCoord0");
|
|
_glgsg->_glBindAttribLocation(_glsl_program, 8, "texcoord");
|
|
}
|
|
|
|
// If we requested to retrieve the shader, we should indicate that before
|
|
// linking.
|
|
bool retrieve_binary = false;
|
|
if (_glgsg->_supports_get_program_binary) {
|
|
retrieve_binary = _shader->get_cache_compiled_shader();
|
|
|
|
#ifndef NDEBUG
|
|
if (gl_dump_compiled_shaders) {
|
|
retrieve_binary = true;
|
|
}
|
|
#endif
|
|
|
|
_glgsg->_glProgramParameteri(_glsl_program, GL_PROGRAM_BINARY_RETRIEVABLE_HINT, GL_TRUE);
|
|
}
|
|
|
|
if (GLCAT.is_debug()) {
|
|
GLCAT.debug()
|
|
<< "Linking GLSL shader " << _shader->get_filename() << "\n";
|
|
}
|
|
|
|
_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 "
|
|
<< _shader->get_filename() << "\n";
|
|
glsl_report_program_errors(_glsl_program, true);
|
|
return false;
|
|
}
|
|
|
|
// Report any warnings.
|
|
glsl_report_program_errors(_glsl_program, false);
|
|
|
|
if (retrieve_binary) {
|
|
GLint length = 0;
|
|
_glgsg->_glGetProgramiv(_glsl_program, GL_PROGRAM_BINARY_LENGTH, &length);
|
|
length += 2;
|
|
|
|
char *binary = (char *)alloca(length);
|
|
GLenum format;
|
|
GLsizei num_bytes = 0;
|
|
_glgsg->_glGetProgramBinary(_glsl_program, length, &num_bytes, &format, (void*)binary);
|
|
|
|
_shader->set_compiled(format, binary, num_bytes);
|
|
|
|
#ifndef NDEBUG
|
|
// Dump the binary if requested.
|
|
if (gl_dump_compiled_shaders) {
|
|
char filename[64];
|
|
static int gl_dump_count = 0;
|
|
sprintf(filename, "glsl_program%d.dump", gl_dump_count++);
|
|
|
|
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";
|
|
}
|
|
#endif // NDEBUG
|
|
}
|
|
|
|
_glgsg->report_my_gl_errors();
|
|
return true;
|
|
}
|
|
|
|
#endif // OPENGLES_1
|