From 66349ecabacf254614416349323b6e6d1db1d809 Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 2 Jul 2009 15:01:36 +0000 Subject: [PATCH] This is another big commit - chief new features are: * Working OpenGL ES 2.x support. Yay! * The shader code now supports separate vertex/fragment/geometry shaders. * The GLSL code supports passing vertex attrib arrays. --- .../glstuff/glGraphicsStateGuardian_src.cxx | 118 +++++++- .../src/glstuff/glGraphicsStateGuardian_src.h | 17 +- panda/src/glstuff/glShaderContext_src.cxx | 272 +++++++++++------- panda/src/glstuff/glShaderContext_src.h | 6 +- panda/src/gobj/shader.I | 65 ++++- panda/src/gobj/shader.cxx | 227 +++++++++------ panda/src/gobj/shader.h | 68 +++-- 7 files changed, 525 insertions(+), 248 deletions(-) diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index dea57cf007..5c2c0b8831 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -107,6 +107,23 @@ static void APIENTRY null_glBlendColor(GLclampf, GLclampf, GLclampf, GLclampf) { } +#ifdef OPENGLES_2 +// We have a default shader that will be applied when there +// isn't any shader applied (e.g. if it failed to compile). +// We need this because OpenGL ES 2.x does not have +// a fixed-function pipeline. +// This default shader just outputs a red color, telling +// the user that something went wrong. +CPT(Shader::ShaderFile) default_shader_name = new Shader::ShaderFile("default-shader"); +CPT(Shader::ShaderFile) default_shader_body = new Shader::ShaderFile("\ +uniform mediump mat4 p3d_ModelViewProjectionMatrix;\ +attribute highp vec4 p3d_Vertex;\ +void main(void) {\ + gl_Position = p3d_ModelViewProjectionMatrix * p3d_Vertex;\ }\n", +"void main(void) {\ + gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\ +}\n"); +#endif //////////////////////////////////////////////////////////////////// // Function: uchar_bgr_to_rgb @@ -817,12 +834,12 @@ reset() { if (basic_shaders_only) { _shader_caps._active_vprofile = (int)CG_PROFILE_ARBVP1; _shader_caps._active_fprofile = (int)CG_PROFILE_ARBFP1; - _shader_caps._active_gprofile = (int)0; // CG2 CHANGE: No geometry shader if only using basic + _shader_caps._active_gprofile = (int)0; // No geometry shader if only using basic } else { _shader_caps._active_vprofile = (int)cgGLGetLatestProfile(CG_GL_VERTEX); _shader_caps._active_fprofile = (int)cgGLGetLatestProfile(CG_GL_FRAGMENT); #ifdef CG_CL_GEOMETRY - _shader_caps._active_gprofile = (int)cgGLGetLatestProfile(CG_GL_GEOMETRY); // CG2 CHANGE + _shader_caps._active_gprofile = (int)cgGLGetLatestProfile(CG_GL_GEOMETRY); #else _shader_caps._active_gprofile = (int)0; #endif @@ -830,7 +847,7 @@ reset() { _shader_caps._ultimate_vprofile = (int)CG_PROFILE_VP40; _shader_caps._ultimate_fprofile = (int)CG_PROFILE_FP40; #ifdef CG_PROFILE_GPU_CP - _shader_caps._ultimate_gprofile = (int)CG_PROFILE_GPU_GP; // CG2 CHANGE + _shader_caps._ultimate_gprofile = (int)CG_PROFILE_GPU_GP; #else _shader_caps._ultimate_gprofile = (int)0; #endif @@ -862,10 +879,12 @@ reset() { #endif _shader_caps._supports_glsl = _supports_glsl; -#ifndef OPENGLES_1 +#ifndef OPENGLES if (_supports_glsl) { _glAttachShader = (PFNGLATTACHSHADERPROC) get_extension_func(GLPREFIX_QUOTED, "AttachShader"); + _glBindAttribLocation = (PFNGLBINDATTRIBLOCATIONPROC) + get_extension_func(GLPREFIX_QUOTED, "BindAttribLocation"); _glCompileShader = (PFNGLCOMPILESHADERPROC) get_extension_func(GLPREFIX_QUOTED, "CompileShader"); _glCreateProgram = (PFNGLCREATEPROGRAMPROC) @@ -878,8 +897,16 @@ reset() { get_extension_func(GLPREFIX_QUOTED, "DeleteShader"); _glDetachShader = (PFNGLDETACHSHADERPROC) get_extension_func(GLPREFIX_QUOTED, "DetachShader"); + _glDisableVertexAttribArray (PFNGLDISABLEVERTEXATTRIBARRAYPROC) + get_extension_func(GLPREFIX_QUOTED, "DisableVertexAttribArray"); + _glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC) + get_extension_func(GLPREFIX_QUOTED, "EnableVertexAttribArray"); + _glGetActiveAttrib = (PFNGLGETACTIVEATTRIBPROC) + get_extension_func(GLPREFIX_QUOTED, "GetActiveAttrib"); _glGetActiveUniform = (PFNGLGETACTIVEUNIFORMPROC) get_extension_func(GLPREFIX_QUOTED, "GetActiveUniform"); + _glGetAttribLocation = (PFNGLGETATTRIBLOCATIONPROC) + get_extension_func(GLPREFIX_QUOTED, "GetAttribLocation"); _glGetProgramiv = (PFNGLGETPROGRAMIVPROC) get_extension_func(GLPREFIX_QUOTED, "GetProgramiv"); _glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC) @@ -912,9 +939,50 @@ reset() { get_extension_func(GLPREFIX_QUOTED, "UniformMatrix4fv"); _glValidateProgram = (PFNGLVALIDATEPROGRAMPROC) get_extension_func(GLPREFIX_QUOTED, "ValidateProgram"); + _glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC) + get_extension_func(GLPREFIX_QUOTED, "VertexAttribPointer"); } #endif +#ifdef OPENGLES_2 + _glAttachShader = glAttachShader; + _glBindAttribLocation = glBindAttribLocation; + _glCompileShader = glCompileShader; + _glCreateProgram = glCreateProgram; + _glCreateShader = glCreateShader; + _glDeleteProgram = glDeleteProgram; + _glDeleteShader = glDeleteShader; + _glDetachShader = glDetachShader; + _glDisableVertexAttribArray = glDisableVertexAttribArray; + _glEnableVertexAttribArray = glEnableVertexAttribArray; + _glGetActiveAttrib = glGetActiveAttrib; + _glGetActiveUniform = glGetActiveUniform; + _glGetAttribLocation = glGetAttribLocation; + _glGetProgramiv = glGetProgramiv; + _glGetProgramInfoLog = glGetProgramInfoLog; + _glGetShaderiv = glGetShaderiv; + _glGetShaderInfoLog = glGetShaderInfoLog; + _glGetUniformLocation = glGetUniformLocation; + _glLinkProgram = glLinkProgram; + _glShaderSource = glShaderSource; + _glUseProgram = glUseProgram; + _glUniform4f = glUniform4f; + _glUniform1i = glUniform1i; + _glUniform1fv = glUniform1fv; + _glUniform2fv = glUniform2fv; + _glUniform3fv = glUniform3fv; + _glUniform4fv = glUniform4fv; + _glUniformMatrix4fv = glUniformMatrix4fv; + _glValidateProgram = glValidateProgram; + _glVertexAttribPointer = glVertexAttribPointer; + + // We need to have a default shader to apply in case + // something didn't happen to have a shader applied, or + // if it failed to compile. This default shader just outputs + // a red color, indicating that something went wrong. + _default_shader = new Shader(default_shader_name, default_shader_body, Shader::SL_GLSL); +#endif + #ifdef OPENGLES_2 // In OpenGL ES 2.x, FBO's are supported in the core. _supports_framebuffer_object = true; @@ -1123,6 +1191,10 @@ reset() { } #endif +#ifdef OPENGLES_2 + // In OpenGL ES 2.x, this is supported in the core. + _glBlendEquation = glBlendEquation; +#else _glBlendEquation = NULL; bool supports_blend_equation = false; if (is_at_least_gl_version(1, 2)) { @@ -1145,7 +1217,12 @@ reset() { if (_glBlendEquation == NULL) { _glBlendEquation = null_glBlendEquation; } +#endif +#ifdef OPENGLES_2 + // In OpenGL ES 2.x, this is supported in the core. + _glBlendColor = glBlendColor; +#else _glBlendColor = NULL; bool supports_blend_color = false; if (is_at_least_gl_version(1, 2)) { @@ -1164,6 +1241,7 @@ reset() { if (_glBlendColor == NULL) { _glBlendColor = null_glBlendColor; } +#endif #ifdef OPENGLES _edge_clamp = GL_CLAMP_TO_EDGE; @@ -4051,15 +4129,29 @@ do_issue_shade_model() { // Description: //////////////////////////////////////////////////////////////////// void CLP(GraphicsStateGuardian):: -do_issue_shader() { +do_issue_shader(bool state_has_changed) { #ifndef OPENGLES_1 CLP(ShaderContext) *context = 0; Shader *shader = (Shader *)(_target_shader->get_shader()); + +#ifdef OPENGLES_2 + // If we don't have a shader, apply the default shader. + if (!shader) { + shader = _default_shader; + } +#endif if (shader) { context = (CLP(ShaderContext) *)(shader->prepare_now(get_prepared_objects(), this)); } +#ifdef OPENGLES_2 + // If it failed, try applying the default shader. + if (shader != _default_shader && (context == 0 || !context->valid())) { + shader = _default_shader; + context = (CLP(ShaderContext) *)(shader->prepare_now(get_prepared_objects(), this)); + } +#endif - if (context == 0 || (context -> valid ( ) == false)) { + if (context == 0 || (context->valid() == false)) { if (_current_shader_context != 0) { _current_shader_context->unbind(this); _current_shader = 0; @@ -4076,8 +4168,13 @@ do_issue_shader() { _current_shader = shader; _current_shader_context = context; } else { - // Use the same shader as before, but with new input arguments. - context->issue_parameters(this, Shader::SSD_shaderinputs); +#ifdef OPENGLES_2 + context->bind(this, false); +#endif + if (state_has_changed) { + // Use the same shader as before, but with new input arguments. + context->issue_parameters(this, Shader::SSD_shaderinputs); + } } } @@ -6826,6 +6923,11 @@ set_state_and_transform(const RenderState *target, _state_shader = _target_shader; _state_mask.clear_bit(TextureAttrib::get_class_slot()); } +#ifdef OPENGLES_2 + else { // In the case of OpenGL ES 2.x, we need to glUseShader before we draw anything. + do_issue_shader(false); + } +#endif int texture_slot = TextureAttrib::get_class_slot(); if (_target_rs->get_attrib(texture_slot) != _state_rs->get_attrib(texture_slot) || diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.h b/panda/src/glstuff/glGraphicsStateGuardian_src.h index fc73de35f0..0050797299 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.h +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.h @@ -116,13 +116,18 @@ typedef void (APIENTRYP PFNGLWEIGHTPOINTEROESPROC) (GLint size, GLenum type, GLs #ifndef OPENGLES_1 // GLSL shader functions typedef void (APIENTRYP PFNGLATTACHSHADERPROC) (GLuint program, GLuint shader); +typedef void (APIENTRYP PFNGLBINDATTRIBLOCATIONPROC) (GLuint program, GLuint index, const GLchar *name); typedef void (APIENTRYP PFNGLCOMPILESHADERPROC) (GLuint shader); typedef GLuint (APIENTRYP PFNGLCREATEPROGRAMPROC) (void); typedef GLuint (APIENTRYP PFNGLCREATESHADERPROC) (GLenum type); typedef void (APIENTRYP PFNGLDELETEPROGRAMPROC) (GLuint program); typedef void (APIENTRYP PFNGLDELETESHADERPROC) (GLuint shader); typedef void (APIENTRYP PFNGLDETACHSHADERPROC) (GLuint program, GLuint shader); +typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYPROC) (GLuint index); +typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYPROC) (GLuint index); +typedef void (APIENTRYP PFNGLGETACTIVEATTRIBPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); +typedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONPROC) (GLuint program, const GLchar *name); typedef void (APIENTRYP PFNGLGETPROGRAMIVPROC) (GLuint program, GLenum pname, GLint *params); typedef void (APIENTRYP PFNGLGETPROGRAMINFOLOGPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog); typedef void (APIENTRYP PFNGLGETSHADERIVPROC) (GLuint shader, GLenum pname, GLint *params); @@ -139,6 +144,7 @@ typedef void (APIENTRYP PFNGLUNIFORM3FVPROC) (GLint location, GLsizei count, con typedef void (APIENTRYP PFNGLUNIFORM4FVPROC) (GLint location, GLsizei count, const GLfloat *value); typedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); typedef void (APIENTRYP PFNGLVALIDATEPROGRAMPROC) (GLuint program); +typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer); #endif #endif // __EDG__ @@ -274,7 +280,7 @@ protected: void do_issue_fog(); void do_issue_depth_offset(); void do_issue_shade_model(); - void do_issue_shader(); + void do_issue_shader(bool state_has_changed = false); void do_issue_material(); void do_issue_texture(); void do_issue_blending(); @@ -441,6 +447,9 @@ protected: PT(Shader) _texture_binding_shader; CLP(ShaderContext) *_texture_binding_shader_context; #endif +#ifdef OPENGLES_2 + PT(Shader) _default_shader; +#endif #ifdef SUPPORT_IMMEDIATE_MODE CLP(ImmediateModeSender) _sender; @@ -581,13 +590,18 @@ public: #ifndef OPENGLES_1 // GLSL functions PFNGLATTACHSHADERPROC _glAttachShader; + PFNGLBINDATTRIBLOCATIONPROC _glBindAttribLocation; PFNGLCOMPILESHADERPROC _glCompileShader; PFNGLCREATEPROGRAMPROC _glCreateProgram; PFNGLCREATESHADERPROC _glCreateShader; PFNGLDELETEPROGRAMPROC _glDeleteProgram; PFNGLDELETESHADERPROC _glDeleteShader; PFNGLDETACHSHADERPROC _glDetachShader; + PFNGLDISABLEVERTEXATTRIBARRAYPROC _glDisableVertexAttribArray; + PFNGLENABLEVERTEXATTRIBARRAYPROC _glEnableVertexAttribArray; + PFNGLGETACTIVEATTRIBPROC _glGetActiveAttrib; PFNGLGETACTIVEUNIFORMPROC _glGetActiveUniform; + PFNGLGETATTRIBLOCATIONPROC _glGetAttribLocation; PFNGLGETPROGRAMIVPROC _glGetProgramiv; PFNGLGETPROGRAMINFOLOGPROC _glGetProgramInfoLog; PFNGLGETSHADERIVPROC _glGetShaderiv; @@ -604,6 +618,7 @@ public: PFNGLUNIFORM4FVPROC _glUniform4fv; PFNGLUNIFORMMATRIX4FVPROC _glUniformMatrix4fv; PFNGLVALIDATEPROGRAMPROC _glValidateProgram; + PFNGLVERTEXATTRIBPOINTERPROC _glVertexAttribPointer; #endif GLenum _edge_clamp; diff --git a/panda/src/glstuff/glShaderContext_src.cxx b/panda/src/glstuff/glShaderContext_src.cxx index 7061a4897e..6b4d9283b6 100755 --- a/panda/src/glstuff/glShaderContext_src.cxx +++ b/panda/src/glstuff/glShaderContext_src.cxx @@ -73,9 +73,7 @@ CLP(ShaderContext)(Shader *s, GSG *gsg) : ShaderContext(s) { cgGetProfileString(cgGetProgramProfile(_cg_fprogram)) << " " << str << ")\n"; release_resources(gsg); } - if (glGetError() != GL_NO_ERROR) { - GLCAT.error() << "GL error in ShaderContext constructor\n"; - } + gsg->report_my_gl_errors(); if (_cg_gprogram != 0) { cgGLLoadProgram(_cg_gprogram); if (GLCAT.is_debug()) { @@ -90,9 +88,7 @@ CLP(ShaderContext)(Shader *s, GSG *gsg) : ShaderContext(s) { cgGetProfileString(cgGetProgramProfile(_cg_gprogram)) << " " << str << ")\n"; release_resources(gsg); } - if (glGetError() != GL_NO_ERROR) { - GLCAT.error() << "GL error in ShaderContext constructor\n"; - } + gsg->report_my_gl_errors(); } } #endif @@ -109,14 +105,13 @@ CLP(ShaderContext)(Shader *s, GSG *gsg) : ShaderContext(s) { // Analyze the uniforms and put them in _glsl_parameter_map if (s->_glsl_parameter_map.size() == 0) { int seqno = 0, texunitno = 0; - int num_uniforms, uniform_maxlength; - gsg->_glGetProgramiv(_glsl_program, GL_ACTIVE_UNIFORMS, &num_uniforms); - gsg->_glGetProgramiv(_glsl_program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &uniform_maxlength); - for (int i = 0; i < num_uniforms; ++i) { - int param_size; - GLenum param_type; - char* param_name = new char[uniform_maxlength]; - gsg->_glGetActiveUniform(_glsl_program, i, uniform_maxlength, NULL, ¶m_size, ¶m_type, param_name); + int param_count, param_maxlength, param_size; + GLenum param_type; + gsg->_glGetProgramiv(_glsl_program, GL_ACTIVE_UNIFORMS, ¶m_count); + gsg->_glGetProgramiv(_glsl_program, GL_ACTIVE_UNIFORM_MAX_LENGTH, ¶m_maxlength); + char* param_name = new char[param_maxlength]; + for (int i = 0; i < param_count; ++i) { + gsg->_glGetActiveUniform(_glsl_program, i, param_maxlength, NULL, ¶m_size, ¶m_type, param_name); GLint p = gsg->_glGetUniformLocation(_glsl_program, param_name); if (p > -1) { Shader::ShaderArgId arg_id; @@ -124,6 +119,18 @@ CLP(ShaderContext)(Shader *s, GSG *gsg) : ShaderContext(s) { arg_id._seqno = seqno++; s->_glsl_parameter_map.push_back(p); PT(InternalName) inputname = InternalName::make(param_name); + if (inputname->get_name() == "p3d_ModelViewProjectionMatrix") { + Shader::ShaderMatSpec bind; + bind._id = arg_id; + bind._piece = Shader::SMP_whole; + bind._func = Shader::SMF_compose; + bind._part[0] = Shader::SMO_model_to_view; + bind._arg[0] = NULL; + bind._part[1] = Shader::SMO_view_to_apiclip; + bind._arg[1] = NULL; + s->_mat_spec.push_back(bind); + continue; + } if (inputname->get_name().substr(0, 11) == "p3d_Texture") { Shader::ShaderTexSpec bind; bind._id = arg_id; @@ -230,10 +237,58 @@ CLP(ShaderContext)(Shader *s, GSG *gsg) : ShaderContext(s) { GLCAT.warning() << "Ignoring unrecognized GLSL parameter type!\n"; } } - delete param_name; } + delete[] param_name; + + int attrib_idx = 0; + // Now we've processed the uniforms, we'll process the attribs. + gsg->_glGetProgramiv(_glsl_program, GL_ACTIVE_ATTRIBUTES, ¶m_count); + gsg->_glGetProgramiv(_glsl_program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, ¶m_maxlength); + param_name = new char[param_maxlength]; + for (int i = 0; i < param_count; ++i) { + gsg->_glGetActiveAttrib(_glsl_program, i, param_maxlength, NULL, ¶m_size, ¶m_type, param_name); + Shader::ShaderVarSpec bind; + Shader::ShaderArgId arg_id; + arg_id._name = param_name; + arg_id._seqno = -1; + PT(InternalName) inputname = InternalName::make(param_name); + bind._append_uv = -1; + if (inputname->get_name() == "p3d_Vertex") { + bind._name = InternalName::get_vertex(); + s->_var_spec.push_back(bind); + gsg->_glBindAttribLocation(_glsl_program, i, param_name); + continue; + } + if (inputname->get_name() == "p3d_Normal") { + bind._name = InternalName::get_normal(); + s->_var_spec.push_back(bind); + gsg->_glBindAttribLocation(_glsl_program, i, param_name); + continue; + } + if (inputname->get_name() == "p3d_Color") { + bind._name = InternalName::get_color(); + s->_var_spec.push_back(bind); + gsg->_glBindAttribLocation(_glsl_program, i, param_name); + continue; + } + } + delete[] param_name; + } + + // Finally, re-link the program, or otherwise the glBindAttribLocation + // calls won't have any effect. + gsg->_glLinkProgram(_glsl_program); + + int status; + gsg->_glGetProgramiv(_glsl_program, GL_LINK_STATUS, &status); + if (status != GL_TRUE) { + GLCAT.error() << "An error occurred while relinking shader program!\n"; + glsl_report_program_errors(gsg, _glsl_program); + s->_error_flag = true; } } + + gsg->report_my_gl_errors(); } //////////////////////////////////////////////////////////////////// @@ -263,7 +318,9 @@ release_resources(const GSG *gsg) { _cg_gprogram = 0; _cg_parameter_map.clear(); } - if (glGetError() != GL_NO_ERROR) { + if (gsg) { + gsg->report_my_gl_errors(); + } if (glGetError() != GL_NO_ERROR) { GLCAT.error() << "GL error in ShaderContext destructor\n"; } #endif @@ -300,10 +357,18 @@ release_resources(const GSG *gsg) { // input parameters. //////////////////////////////////////////////////////////////////// void CLP(ShaderContext):: -bind(GSG *gsg) { +bind(GSG *gsg, bool reissue_parameters) { _last_gsg = gsg; - // Pass in k-parameters and transform-parameters - issue_parameters(gsg, Shader::SSD_general); + + // GLSL shaders need to be bound before passing parameters. + if (_shader->get_language() == Shader::SL_GLSL && !_shader->get_error_flag()) { + gsg->_glUseProgram(_glsl_program); + } + + if (reissue_parameters) { + // Pass in k-parameters and transform-parameters + issue_parameters(gsg, Shader::SSD_general); + } #ifdef HAVE_CG if (_cg_context != 0) { @@ -320,13 +385,8 @@ bind(GSG *gsg) { cg_report_errors(); } #endif - - if (_shader->get_language() == Shader::SL_GLSL && !_shader->get_error_flag()) { - gsg->_glUseProgram(_glsl_program); - } - if (glGetError() != GL_NO_ERROR) { - GLCAT.error() << "GL error in ShaderContext::bind\n"; - } + + gsg->report_my_gl_errors(); } //////////////////////////////////////////////////////////////////// @@ -353,9 +413,7 @@ unbind(GSG *gsg) { if (_shader->get_language() == Shader::SL_GLSL) { gsg->_glUseProgram(0); } - if (glGetError() != GL_NO_ERROR) { - GLCAT.error() << "GL error in ShaderContext::unbind\n"; - } + gsg->report_my_gl_errors(); } //////////////////////////////////////////////////////////////////// @@ -432,9 +490,7 @@ issue_parameters(GSG *gsg, int altered) { cg_report_errors(); #endif - if (glGetError() != GL_NO_ERROR) { - GLCAT.error() << "GL error in ShaderContext::issue_parameters\n"; - } + gsg->report_my_gl_errors(); } //////////////////////////////////////////////////////////////////// @@ -445,22 +501,27 @@ issue_parameters(GSG *gsg, int altered) { void CLP(ShaderContext):: disable_shader_vertex_arrays(GSG *gsg) { _last_gsg = gsg; - -#ifdef HAVE_CG - if (_cg_context == 0) { + if (!valid()) { return; } - for (int i=0; i<(int)_shader->_var_spec.size(); i++) { - CGparameter p = _cg_parameter_map[_shader->_var_spec[i]._id._seqno]; - if (p == 0) continue; - cgGLDisableClientState(p); + if (_shader->get_language() == Shader::SL_GLSL) { + for (int i=0; i<(int)_shader->_var_spec.size(); i++) { + glDisableVertexAttribArray(i); + } } - cg_report_errors(); - if (glGetError() != GL_NO_ERROR) { - GLCAT.error() << "GL error in ShaderContext::disable_shader_vertex_arrays\n"; +#ifdef HAVE_CG + else if (_shader->get_language() == Shader::SL_Cg) { + for (int i=0; i<(int)_shader->_var_spec.size(); i++) { + CGparameter p = _cg_parameter_map[_shader->_var_spec[i]._id._seqno]; + if (p == 0) continue; + cgGLDisableClientState(p); + } + cg_report_errors(); } #endif + + gsg->report_my_gl_errors(); } //////////////////////////////////////////////////////////////////// @@ -479,11 +540,12 @@ update_shader_vertex_arrays(CLP(ShaderContext) *prev, GSG *gsg, bool force) { _last_gsg = gsg; if (prev) prev->disable_shader_vertex_arrays(gsg); -#ifdef HAVE_CG if (!valid()) { return true; } +#ifdef HAVE_CG cg_report_errors(); +#endif #ifdef SUPPORT_IMMEDIATE_MODE if (gsg->_use_sender) { @@ -496,8 +558,14 @@ update_shader_vertex_arrays(CLP(ShaderContext) *prev, GSG *gsg, int start, stride, num_values; int nvarying = _shader->_var_spec.size(); for (int i=0; i_var_spec[i]._id._seqno]; - if (p == 0) continue; +#ifdef HAVE_CG + if (_shader->get_language() == Shader::SL_Cg) { + if (_cg_parameter_map[_shader->_var_spec[i]._id._seqno] == 0) { + continue; + } + } +#endif + InternalName *name = _shader->_var_spec[i]._name; int texslot = _shader->_var_spec[i]._append_uv; if (texslot >= 0 && texslot < gsg->_state_texture->get_num_on_stages()) { @@ -516,21 +584,39 @@ update_shader_vertex_arrays(CLP(ShaderContext) *prev, GSG *gsg, if (!gsg->setup_array_data(client_pointer, array_reader, force)) { return false; } - cgGLSetParameterPointer(p, - num_values, gsg->get_numeric_type(numeric_type), - stride, client_pointer + start); - cgGLEnableClientState(p); - } else { + if (_shader->get_language() == Shader::SL_GLSL) { +#ifndef OPENGLES_2 + glEnableClientState(GL_VERTEX_ARRAY); +#endif + glEnableVertexAttribArray(i); + glVertexAttribPointer(i, num_values, gsg->get_numeric_type(numeric_type), + GL_FALSE, stride, client_pointer + start); +#ifndef OPENGLES_2 + glDisableClientState(GL_VERTEX_ARRAY); +#endif +#ifdef HAVE_CG + } else if (_shader->get_language() == Shader::SL_Cg) { + CGparameter p = _cg_parameter_map[_shader->_var_spec[i]._id._seqno]; + cgGLSetParameterPointer(p, + num_values, gsg->get_numeric_type(numeric_type), + stride, client_pointer + start); + cgGLEnableClientState(p); +#endif + } + } +#ifdef HAVE_CG + else if (_shader->get_language() == Shader::SL_Cg) { cgGLDisableClientState(p); } +#endif } } +#ifdef HAVE_CG cg_report_errors(); - if (glGetError() != GL_NO_ERROR) { - GLCAT.error() << "GL error in ShaderContext::update_shader_vertex_arrays\n"; - } -#endif // HAVE_CG +#endif + gsg->report_my_gl_errors(); + return true; } @@ -586,9 +672,7 @@ disable_shader_texture_bindings(GSG *gsg) { cg_report_errors(); #endif - if (glGetError() != GL_NO_ERROR) { - GLCAT.error() << "GL error in ShaderContext::disable_shader_texture_bindings\n"; - } + gsg->report_my_gl_errors(); } //////////////////////////////////////////////////////////////////// @@ -694,9 +778,7 @@ update_shader_texture_bindings(CLP(ShaderContext) *prev, GSG *gsg) { cg_report_errors(); #endif - if (glGetError() != GL_NO_ERROR) { - GLCAT.error() << "GL error in ShaderContext::update_shader_texture_bindings\n"; - } + gsg->report_my_gl_errors(); } //////////////////////////////////////////////////////////////////// @@ -725,14 +807,16 @@ glsl_report_shader_errors(GSG *gsg, unsigned int shader) { int length = 0; int num_chars = 0; - gsg->_glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length); + gsg->_glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length); if (length > 0) { info_log = (char *) malloc(length); gsg->_glGetShaderInfoLog(shader, length, &num_chars, info_log); - GLCAT.error(false) << info_log; - free(info_log); + if (strcmp(info_log, "Success.\n") != 0) { + GLCAT.error(false) << info_log; + } } + delete[] info_log; } //////////////////////////////////////////////////////////////////// @@ -746,14 +830,16 @@ glsl_report_program_errors(GSG *gsg, unsigned int program) { int length = 0; int num_chars = 0; - gsg->_glGetProgramiv(program, GL_INFO_LOG_LENGTH, &length); + gsg->_glGetProgramiv(program, GL_INFO_LOG_LENGTH, &length); if (length > 0) { info_log = (char *) malloc(length); gsg->_glGetProgramInfoLog(program, length, &num_chars, info_log); - GLCAT.error(false) << info_log; - free(info_log); + if (strcmp(info_log, "Success.\n") != 0) { + GLCAT.error(false) << info_log; + } } + delete[] info_log; } //////////////////////////////////////////////////////////////////// @@ -762,16 +848,16 @@ glsl_report_program_errors(GSG *gsg, unsigned int program) { // Description: //////////////////////////////////////////////////////////////////// unsigned int CLP(ShaderContext):: -glsl_compile_entry_point(GSG *gsg, const char *entry, Shader::ShaderType type) { +glsl_compile_entry_point(GSG *gsg, Shader::ShaderType type) { unsigned int handle; switch (type) { - case Shader::ST_VERTEX: + case Shader::ST_vertex: handle = gsg->_glCreateShader(GL_VERTEX_SHADER); break; - case Shader::ST_FRAGMENT: + case Shader::ST_fragment: handle = gsg->_glCreateShader(GL_FRAGMENT_SHADER); break; - case Shader::ST_GEOMETRY: + case Shader::ST_geometry: handle = gsg->_glCreateShader(GL_GEOMETRY_SHADER); break; default: @@ -780,16 +866,13 @@ glsl_compile_entry_point(GSG *gsg, const char *entry, Shader::ShaderType type) { if (!handle) { return 0; } - // We define our own main() in which we call the right function. - ostringstream str; - str << "void main() { " << entry << "(); };"; - const char* sources[2] = {_shader->_text.c_str(), str.str().c_str()}; - gsg->_glShaderSource(handle, 2, sources, NULL); + const char* text = _shader->get_text(type).c_str(); + gsg->_glShaderSource(handle, 1, &text, NULL); gsg->_glCompileShader(handle); int status; gsg->_glGetShaderiv(handle, GL_COMPILE_STATUS, &status); if (status != GL_TRUE) { - GLCAT.error() << "An error occurred while compiling " << entry << "!\n"; + GLCAT.error() << "An error occurred while compiling shader!\n"; glsl_report_shader_errors(gsg, handle); gsg->_glDeleteShader(handle); return 0; @@ -810,38 +893,24 @@ glsl_compile_shader(GSG *gsg) { _glsl_program = gsg->_glCreateProgram(); if (!_glsl_program) return false; - if (_shader->_text.find("vshader") != -1) { - _glsl_vshader = glsl_compile_entry_point(gsg, "vshader", Shader::ST_VERTEX); + if (!_shader->get_text(Shader::ST_vertex).empty()) { + _glsl_vshader = glsl_compile_entry_point(gsg, Shader::ST_vertex); if (!_glsl_vshader) return false; gsg->_glAttachShader(_glsl_program, _glsl_vshader); - } else { - GLCAT.warning() << "Could not locate function 'vshader' in shader text!\n"; } - if (_shader->_text.find("fshader") != -1) { - _glsl_fshader = glsl_compile_entry_point(gsg, "fshader", Shader::ST_FRAGMENT); + if (!_shader->get_text(Shader::ST_fragment).empty()) { + _glsl_fshader = glsl_compile_entry_point(gsg, Shader::ST_fragment); if (!_glsl_fshader) return false; gsg->_glAttachShader(_glsl_program, _glsl_fshader); - } else { - GLCAT.warning() << "Could not locate function 'fshader' in shader text!\n"; } - if (_shader->_text.find("gshader") != -1) { - _glsl_gshader = glsl_compile_entry_point(gsg, "gshader", Shader::ST_GEOMETRY); + if (!_shader->get_text(Shader::ST_geometry).empty()) { + _glsl_gshader = glsl_compile_entry_point(gsg, Shader::ST_geometry); if (!_glsl_gshader) return false; gsg->_glAttachShader(_glsl_program, _glsl_gshader); } - gsg->_glLinkProgram(_glsl_program); - - int status; - gsg->_glGetProgramiv(_glsl_program, GL_LINK_STATUS, &status); - if (status != GL_TRUE) { - GLCAT.error() << "An error occurred while linking shader program!\n"; - glsl_report_program_errors(gsg, _glsl_program); - return false; - } - // There might be warnings. Only report them for one shader program. if (_glsl_vshader != 0) { glsl_report_shader_errors(gsg, _glsl_vshader); @@ -851,10 +920,17 @@ glsl_compile_shader(GSG *gsg) { glsl_report_shader_errors(gsg, _glsl_gshader); } - if (glGetError() != GL_NO_ERROR) { - GLCAT.error() << "Failed to compile shader\n"; + gsg->_glLinkProgram(_glsl_program); + + int status; + gsg->_glGetProgramiv(_glsl_program, GL_LINK_STATUS, &status); + if (status != GL_TRUE) { + GLCAT.error() << "An error occurred while linking shader program!\n"; + glsl_report_program_errors(gsg, _glsl_program); return false; } + + gsg->report_my_gl_errors(); return true; } diff --git a/panda/src/glstuff/glShaderContext_src.h b/panda/src/glstuff/glShaderContext_src.h index d9a2c5fe8d..9ed1ed74fb 100755 --- a/panda/src/glstuff/glShaderContext_src.h +++ b/panda/src/glstuff/glShaderContext_src.h @@ -27,9 +27,9 @@ class CLP(GraphicsStateGuardian); // Class : GLShaderContext // Description : xyz //////////////////////////////////////////////////////////////////// - class EXPCL_GL CLP(ShaderContext): public ShaderContext { public: + friend class CLP(GraphicsStateGuardian); typedef CLP(GraphicsStateGuardian) GSG; CLP(ShaderContext)(Shader *s, GSG *gsg); @@ -37,7 +37,7 @@ public: ALLOC_DELETED_CHAIN(CLP(ShaderContext)); INLINE bool valid(void); - void bind(GSG *gsg); + void bind(GSG *gsg, bool reissue_parameters = true); void unbind(GSG *gsg); void issue_parameters(GSG *gsg, int altered); void disable_shader_vertex_arrays(GSG *gsg); @@ -76,7 +76,7 @@ private: void glsl_report_shader_errors(GSG *gsg, unsigned int shader); void glsl_report_program_errors(GSG *gsg, unsigned int program); - unsigned int glsl_compile_entry_point(GSG *gsg, const char *entry, Shader::ShaderType type); + unsigned int glsl_compile_entry_point(GSG *gsg, Shader::ShaderType type); bool glsl_compile_shader(GSG *gsg); void release_resources(const GSG *gsg); diff --git a/panda/src/gobj/shader.I b/panda/src/gobj/shader.I index 2e57eddb4f..15e7104662 100755 --- a/panda/src/gobj/shader.I +++ b/panda/src/gobj/shader.I @@ -13,33 +13,68 @@ //////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// -// Function: Shader::get_filename -// Access: Public -// Description: Return the Shader's filename. +// Function: Shader::get_filename +// Access: Public +// Description: Return the Shader's filename for the given shader +// type. //////////////////////////////////////////////////////////////////// -INLINE const Filename &Shader:: -get_filename() const { - return _filename; +INLINE const Filename Shader:: +get_filename(const ShaderType &type) const { + if (_filename->_separate) { + nassertr(type != ST_none || !_filename->_shared.empty(), ""); + switch (type) { + case ST_vertex: + return _filename->_vertex; + break; + case ST_fragment: + return _filename->_fragment; + break; + case ST_geometry: + return _filename->_geometry; + break; + default: + return _filename->_shared; + } + } else { + return _filename->_shared; + } } //////////////////////////////////////////////////////////////////// -// Function: Shader::get_text -// Access: Public -// Description: Return the Shader's text. +// Function: Shader::get_text +// Access: Public +// Description: Return the Shader's text for the given shader type. //////////////////////////////////////////////////////////////////// -INLINE const string &Shader:: -get_text() const { - return _text; +INLINE const string Shader:: +get_text(const ShaderType &type) const { + if (_text->_separate) { + nassertr(type != ST_none || !_text->_shared.empty(), ""); + switch (type) { + case ST_vertex: + return _text->_vertex; + break; + case ST_fragment: + return _text->_fragment; + break; + case ST_geometry: + return _text->_geometry; + break; + default: + return _text->_shared; + } + } else { + return _text->_shared; + } } //////////////////////////////////////////////////////////////////// -// Function: Shader::get_error_flag -// Access: Public +// Function: Shader::get_error_flag +// Access: Public // Description: Returns true if the shader contains a compile-time // error. This doesn't tell you whether or not the // shader is supported on the current video card. //////////////////////////////////////////////////////////////////// -INLINE bool Shader:: +INLINE const bool Shader:: get_error_flag() const { return _error_flag; } diff --git a/panda/src/gobj/shader.cxx b/panda/src/gobj/shader.cxx index f479382f77..e44ae982be 100755 --- a/panda/src/gobj/shader.cxx +++ b/panda/src/gobj/shader.cxx @@ -24,8 +24,8 @@ #endif TypeHandle Shader::_type_handle; -Shader::LoadTable Shader::_load_table; -Shader::MakeTable Shader::_make_table; +Shader::ShaderTable Shader::_load_table; +Shader::ShaderTable Shader::_make_table; Shader::ShaderCaps Shader::_default_caps; int Shader::_shaders_generated; ShaderUtilization Shader::_shader_utilization = SUT_UNSPECIFIED; @@ -37,8 +37,7 @@ ShaderUtilization Shader::_shader_utilization = SUT_UNSPECIFIED; // of the specified parameter. //////////////////////////////////////////////////////////////////// void Shader:: -cp_report_error(ShaderArgInfo &p, const string &msg) -{ +cp_report_error(ShaderArgInfo &p, const string &msg) { string vstr; if (p._varying) vstr = "varying "; else vstr = "uniform "; @@ -62,7 +61,7 @@ cp_report_error(ShaderArgInfo &p, const string &msg) case SAT_unknown: tstr = "unknown "; break; } - Filename fn = get_filename(); + Filename fn = get_filename(p._id._type); p._cat->error() << fn << ": " << msg << " (" << vstr << dstr << tstr << p._id._name << ")\n"; } @@ -1107,12 +1106,10 @@ cg_release_resources() { cgDestroyProgram(_cg_fprogram); _cg_fprogram = 0; } - // BEGIN CG2 CHANGE if (_cg_gprogram != 0) { cgDestroyProgram(_cg_gprogram); _cg_gprogram = 0; } - // END CG2 CHANGE if (_cg_context != 0) { cgDestroyContext(_cg_context); _cg_context = 0; @@ -1129,40 +1126,50 @@ cg_compile_entry_point(const char *entry, const ShaderCaps &caps, ShaderType typ CGprogram prog; CGerror err; const char *compiler_args[100]; + const string text; + if (!_text->_separate) { + text = _text->_shared; + } int nargs = 0; int active, ultimate; - switch(type) - { - case ST_VERTEX: + switch(type) { + case ST_vertex: active = caps._active_vprofile; ultimate = caps._ultimate_vprofile; + if (_text->_separate) { + text = _text._vertex; + } break; - case ST_FRAGMENT: + case ST_fragment: active = caps._active_fprofile; ultimate = caps._ultimate_fprofile; + if (_text->_separate) { + text = _text._fragment; + } break; - case ST_GEOMETRY: + case ST_geometry: active = caps._active_gprofile; ultimate = caps._ultimate_gprofile; + if (_text->_separate) { + text = _text._geometry; + } break; }; - // END CG2 CHANGE - cgGetError(); - if (type == ST_FRAGMENT && caps._bug_list.count(SBUG_ati_draw_buffers)) { // CG2 CHANGE + if (type == ST_fragment && caps._bug_list.count(SBUG_ati_draw_buffers)) { compiler_args[nargs++] = "-po"; compiler_args[nargs++] = "ATI_draw_buffers"; } compiler_args[nargs] = 0; if ((active != (int)CG_PROFILE_UNKNOWN) && (active != ultimate)) { - prog = cgCreateProgram(_cg_context, CG_SOURCE, _text.c_str(), + prog = cgCreateProgram(_cg_context, CG_SOURCE, text.c_str(), (CGprofile)active, entry, (const char **)compiler_args); err = cgGetError(); if (err == CG_NO_ERROR) { @@ -1173,7 +1180,7 @@ cg_compile_entry_point(const char *entry, const ShaderCaps &caps, ShaderType typ } } - prog = cgCreateProgram(_cg_context, CG_SOURCE, _text.c_str(), + prog = cgCreateProgram(_cg_context, CG_SOURCE, text.c_str(), (CGprofile)ultimate, entry, (const char **)NULL); err = cgGetError(); if (err == CG_NO_ERROR) { @@ -1186,11 +1193,11 @@ cg_compile_entry_point(const char *entry, const ShaderCaps &caps, ShaderType typ for (int i=0; i<(int)errlines.size(); i++) { string line = trim(errlines[i]); if (line != "") { - gobj_cat.error() << get_filename() << ": " << errlines[i] << "\n"; + gobj_cat.error() << get_filename(type) << ": " << errlines[i] << "\n"; } } } else { - gobj_cat.error() << get_filename() << ": " << cgGetErrorString(err) << "\n"; + gobj_cat.error() << get_filename(type) << ": " << cgGetErrorString(err) << "\n"; } if (prog != 0) { cgDestroyProgram(prog); @@ -1209,8 +1216,6 @@ cg_compile_entry_point(const char *entry, const ShaderCaps &caps, ShaderType typ bool Shader:: cg_compile_shader(const ShaderCaps &caps) { - - // If we already tried compiling for this set of caps, there's no point // trying again. Just return the results of the previous compile. if (caps == _cg_last_caps) { @@ -1225,18 +1230,25 @@ cg_compile_shader(const ShaderCaps &caps) { _cg_context = cgCreateContext(); - gobj_cat.debug() << "Compiling Shader: \n" << _text << "\n"; + if (!_text->_separate) { + gobj_cat.debug() << "Compiling Shader: \n" << _text << "\n"; + } if (_cg_context == 0) { gobj_cat.error() << "could not create a Cg context object.\n"; return false; } - _cg_vprogram = cg_compile_entry_point("vshader", caps, ST_VERTEX); // CG2 CHANGE - _cg_fprogram = cg_compile_entry_point("fshader", caps, ST_FRAGMENT); // CG2 CHANGE + if (!_text->_separate || !_text._vertex.empty()) { + _cg_vprogram = cg_compile_entry_point("vshader", caps, ST_vertex); + } - if (_text.find("gshader") != -1) { - _cg_gprogram = cg_compile_entry_point("gshader", caps, ST_GEOMETRY); + if (!_text->_separate || !_text._fragment.empty()) { + _cg_fprogram = cg_compile_entry_point("fshader", caps, ST_fragment); + } + + if ((_text->_separate && !_text._geometry.empty()) || (!_text->_separate && _text.find("gshader") != -1)) { + _cg_gprogram = cg_compile_entry_point("gshader", caps, ST_geometry); } if ((_cg_vprogram == 0)||(_cg_fprogram == 0)) { @@ -1317,7 +1329,7 @@ cg_analyze_shader(const ShaderCaps &caps) { return false; } - if (!cg_analyze_entry_point(_cg_fprogram, ST_FRAGMENT)) { + if (!cg_analyze_entry_point(_cg_fprogram, ST_fragment)) { cg_release_resources(); clear_parameters(); return false; @@ -1330,14 +1342,14 @@ cg_analyze_shader(const ShaderCaps &caps) { return false; } - if (!cg_analyze_entry_point(_cg_vprogram, ST_VERTEX)) { + if (!cg_analyze_entry_point(_cg_vprogram, ST_vertex)) { cg_release_resources(); clear_parameters(); return false; } if (_cg_gprogram != 0) { - if (!cg_analyze_entry_point(_cg_gprogram, ST_GEOMETRY)) { + if (!cg_analyze_entry_point(_cg_gprogram, ST_geometry)) { cg_release_resources(); clear_parameters(); return false; @@ -1463,15 +1475,15 @@ cg_program_from_shadertype(ShaderType type) { CGprogram prog = 0; switch(type) { - case ST_VERTEX: + case ST_vertex: prog = _cg_vprogram; break; - case ST_FRAGMENT: + case ST_fragment: prog = _cg_fprogram; break; - case ST_GEOMETRY: + case ST_geometry: prog = _cg_gprogram; break; }; @@ -1580,7 +1592,7 @@ cg_compile_for(const ShaderCaps &caps, // Description: Construct a Shader. //////////////////////////////////////////////////////////////////// Shader:: -Shader(const Filename &filename, const string &text, const ShaderLanguage &lang) : +Shader(CPT(ShaderFile) filename, CPT(ShaderFile) text, const ShaderLanguage &lang) : _filename(filename), _text(text), _error_flag(true), @@ -1622,7 +1634,9 @@ Shader(const Filename &filename, const string &text, const ShaderLanguage &lang) if (_language == SL_Cg) { #ifdef HAVE_CG - cg_get_profile_from_header(_default_caps); + if (!_text->_separate) { + cg_get_profile_from_header(_default_caps); + } if (!cg_analyze_shader(_default_caps)) { _error_flag = true; @@ -1632,8 +1646,13 @@ Shader(const Filename &filename, const string &text, const ShaderLanguage &lang) << "Tried to load Cg shader, but no Cg support is enabled.\n"; #endif } else if (_language == SL_GLSL) { - // All of the important stuff is done in glShaderContext. - _language = SL_GLSL; + // All of the important stuff is done in glShaderContext, + // to avoid gobj getting a dependency on OpenGL. + if (!_text->_separate) { + gobj_cat.error() + << "GLSL shaders must have separate shader bodies!\n"; + _error_flag = true; + } } else { gobj_cat.error() << "Shader is not in a supported shader-language.\n"; @@ -1763,47 +1782,61 @@ Shader:: } } -// Removed for now, as the profiles are now taken from the shader source. //////////////////////////////////////////////////////////////////// // Function: Shader::load // Access: Published, Static // Description: Loads the shader with the given filename. -// If the optional vshader or fshader parameters -// are set and non-empty, it will be used to override -// all other profile settings (it even overrides -// the basic-shaders-only flag) and forces the shader -// to use the given profile. //////////////////////////////////////////////////////////////////// PT(Shader) Shader:: -load(const string &file) { - return load(Filename(file)); +load(const Filename &file, const ShaderLanguage &lang) { + PT(ShaderFile) sfile = new ShaderFile(file); + ShaderTable::const_iterator i = _load_table.find(sfile); + if (i != _load_table.end() && (lang == SL_none || lang == i->second->_language)) { + return i->second; + } + PT(ShaderFile) sbody = new ShaderFile(""); + + VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); + if (!vfs->read_file(file, sbody->_shared, true)) { + gobj_cat.error() << "Could not read shader file: " << file << "\n"; + return NULL; + } + PT(Shader) result = new Shader(sfile, sbody, lang); + result->_loaded = true; + _load_table[sfile] = result; + return result; } //////////////////////////////////////////////////////////////////// // Function: Shader::load // Access: Published, Static -// Description: Loads the shader with the given filename. -// If the optional vshader or fshader parameters -// are set and non-empty, it will be used to override -// all other profile settings (it even overrides -// the basic-shaders-only flag) and forces the shader -// to use the given profile. +// Description: This variant of Shader::load loads all shader +// programs separately. //////////////////////////////////////////////////////////////////// PT(Shader) Shader:: -load(const Filename &file) { - LoadTable::const_iterator i = _load_table.find(file); - if (i != _load_table.end()) { +load(const ShaderLanguage &lang, const Filename &vertex, const Filename &fragment, const Filename &geometry) { + PT(ShaderFile) sfile = new ShaderFile(vertex, fragment, geometry); + ShaderTable::const_iterator i = _load_table.find(sfile); + if (i != _load_table.end() && (lang == SL_none || lang == i->second->_language)) { return i->second; } - string body; + PT(ShaderFile) sbody = new ShaderFile("", "", ""); VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); - if (!vfs->read_file(file, body, true)) { - gobj_cat.error() << "Could not read shader file: " << file << "\n"; + if (!vertex.empty() && !vfs->read_file(vertex, sbody->_vertex, true)) { + gobj_cat.error() << "Could not read vertex shader file: " << vertex << "\n"; return NULL; } - PT(Shader) result = new Shader(file, body); + if (!fragment.empty() && !vfs->read_file(fragment, sbody->_fragment, true)) { + gobj_cat.error() << "Could not read fragment shader file: " << vertex << "\n"; + return NULL; + } + if (!geometry.empty() && !vfs->read_file(geometry, sbody->_geometry, true)) { + gobj_cat.error() << "Could not read geometry shader file: " << vertex << "\n"; + return NULL; + } + PT(Shader) result = new Shader(sfile, sbody, lang); result->_loaded = true; - _load_table[file] = result; + _load_table[sfile] = result; return result; } @@ -1811,20 +1844,17 @@ load(const Filename &file) { // Function: Shader::make // Access: Published, Static // Description: Loads the shader, using the string as shader body. -// If the optional vshader or fshader parameters -// are set and non-empty, it will be used to override -// all other profile settings (it even overrides -// the basic-shaders-only flag) and forces the shader -// to use the given profile. -//////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////// PT(Shader) Shader:: -make(const string &body) { - MakeTable::const_iterator i = _make_table.find(body); - if (i != _make_table.end()) { +make(const string &body, const ShaderLanguage &lang) { + PT(ShaderFile) sbody = new ShaderFile(body); + ShaderTable::const_iterator i = _make_table.find(sbody); + if (i != _make_table.end() && (lang == SL_none || lang == i->second->_language)) { return i->second; } - PT(Shader) result = new Shader("created-shader", body); - _make_table[body] = result; + PT(ShaderFile) sfile = new ShaderFile("created-shader"); + PT(Shader) result = new Shader(sfile, sbody, lang); + _make_table[sbody] = result; if (dump_generated_shaders) { ostringstream fns; int index = _shaders_generated ++; @@ -1839,6 +1869,24 @@ make(const string &body) { return result; } +////////////////////////////////////////////////////////////////////// +// Function: Shader::make +// Access: Published, Static +// Description: Loads the shader, using the strings as shader bodies. +////////////////////////////////////////////////////////////////////// +PT(Shader) Shader:: +make(const ShaderLanguage &lang, const string &vertex, const string &fragment, const string &geometry) { + PT(ShaderFile) sbody = new ShaderFile(vertex, fragment, geometry); + ShaderTable::const_iterator i = _make_table.find(sbody); + if (i != _make_table.end() && (lang == SL_none || lang == i->second->_language)) { + return i->second; + } + PT(ShaderFile) sfile = new ShaderFile("created-shader"); + PT(Shader) result = new Shader(sfile, sbody, lang); + _make_table[sbody] = result; + return result; +} + //////////////////////////////////////////////////////////////////// // Function: Shader::parse_init // Access: Public @@ -1859,10 +1907,11 @@ parse_init() { //////////////////////////////////////////////////////////////////// void Shader:: parse_line(string &result, bool lt, bool rt) { - int len = _text.size(); + nassertv(!_text->_separate); + int len = _text->_shared.size(); int head = _parse; int tail = head; - while ((tail < len) && (_text[tail] != '\n')) { + while ((tail < len) && (_text->_shared[tail] != '\n')) { tail++; } if (tail < len) { @@ -1871,10 +1920,10 @@ parse_line(string &result, bool lt, bool rt) { _parse = tail; } if (lt) { - while ((head < tail)&&(isspace(_text[head]))) head++; - while ((tail > head)&&(isspace(_text[tail-1]))) tail--; + while ((head < tail)&&(isspace(_text->_shared[head]))) head++; + while ((tail > head)&&(isspace(_text->_shared[tail-1]))) tail--; } - result = _text.substr(head, tail-head); + result = _text->_shared.substr(head, tail-head); } //////////////////////////////////////////////////////////////////// @@ -1887,19 +1936,20 @@ parse_line(string &result, bool lt, bool rt) { //////////////////////////////////////////////////////////////////// void Shader:: parse_upto(string &result, string pattern, bool include) { + nassertv(!_text->_separate); GlobPattern endpat(pattern); int start = _parse; int last = _parse; - while (_parse < (int)(_text.size())) { + while (_parse < (int)(_text->_shared.size())) { string t; parse_line(t, true, true); if (endpat.matches(t)) break; last = _parse; } if (include) { - result = _text.substr(start, _parse - start); + result = _text->_shared.substr(start, _parse - start); } else { - result = _text.substr(start, last - start); + result = _text->_shared.substr(start, last - start); } } @@ -1911,21 +1961,8 @@ parse_upto(string &result, string pattern, bool include) { //////////////////////////////////////////////////////////////////// void Shader:: parse_rest(string &result) { - result = _text.substr(_parse, _text.size() - _parse); -} - -//////////////////////////////////////////////////////////////////// -// Function: Shader::parse_lineno -// Access: Public -// Description: Returns the line number of the current parse pointer. -//////////////////////////////////////////////////////////////////// -int Shader:: -parse_lineno() { - int result = 1; - for (int i=0; i<_parse; i++) { - if (_text[i] == '\n') result += 1; - } - return result; + nassertv(!_text->_separate); + result = _text->_shared.substr(_parse, _text->_shared.size() - _parse); } //////////////////////////////////////////////////////////////////// @@ -1936,7 +1973,7 @@ parse_lineno() { //////////////////////////////////////////////////////////////////// bool Shader:: parse_eof() { - return (int)_text.size() == _parse; + return (int)_text->_shared.size() == _parse; } //////////////////////////////////////////////////////////////////// @@ -2001,7 +2038,7 @@ release(PreparedGraphicsObjects *prepared_objects) { //////////////////////////////////////////////////////////////////// // Function: Shader::prepare_now // Access: Published -// Description: Creates a context for the texture on the particular +// Description: Creates a context for the shader on the particular // GSG, if it does not already exist. Returns the new // (or old) ShaderContext. This assumes that the // GraphicsStateGuardian is the currently active @@ -2010,7 +2047,7 @@ release(PreparedGraphicsObjects *prepared_objects) { // should use prepare() instead. // // Normally, this is not called directly except by the -// GraphicsStateGuardian; a texture does not need to be +// GraphicsStateGuardian; a shader does not need to be // explicitly prepared by the user before it may be // rendered. //////////////////////////////////////////////////////////////////// diff --git a/panda/src/gobj/shader.h b/panda/src/gobj/shader.h index 552e31716b..d814cdcf3d 100755 --- a/panda/src/gobj/shader.h +++ b/panda/src/gobj/shader.h @@ -38,13 +38,27 @@ class EXPCL_PANDA_GOBJ Shader: public TypedReferenceCount { PUBLISHED: - static PT(Shader) load(const Filename &file); - static PT(Shader) load(const string &file); - static PT(Shader) make(const string &body); + enum ShaderLanguage { + SL_none, + SL_Cg, + SL_GLSL + }; + enum ShaderType { + ST_none = 0, + ST_vertex, + ST_fragment, + ST_geometry, + }; - INLINE const Filename &get_filename() const; - INLINE const string &get_text() const; - INLINE bool get_error_flag() const; + static PT(Shader) load(const Filename &file, const ShaderLanguage &lang = SL_none); + static PT(Shader) make(const string &body, const ShaderLanguage &lang = SL_none); + static PT(Shader) load(const ShaderLanguage &lang, const Filename &vertex, const Filename &fragment, const Filename &geometry = ""); + static PT(Shader) make(const ShaderLanguage &lang, const string &vertex, const string &fragment, const string &geometry = ""); + + INLINE const Filename get_filename(const ShaderType &type = ST_none) const; + INLINE const string get_text(const ShaderType &type = ST_none) const; + INLINE const bool get_error_flag() const; + INLINE const ShaderLanguage get_language() const; INLINE static ShaderUtilization get_shader_utilization(); INLINE static void set_shader_utilization(ShaderUtilization utl); @@ -57,14 +71,6 @@ PUBLISHED: ShaderContext *prepare_now(PreparedGraphicsObjects *prepared_objects, GraphicsStateGuardianBase *gsg); - - enum ShaderLanguage { - SL_none, - SL_Cg, - SL_GLSL - }; - - INLINE const ShaderLanguage get_language() const; public: @@ -180,12 +186,6 @@ public: SMF_first, }; - enum ShaderType { - ST_VERTEX, - ST_FRAGMENT, - ST_GEOMETRY, - }; - struct ShaderArgId { string _name; ShaderType _type; @@ -244,14 +244,27 @@ public: INLINE ShaderCaps(); }; + struct ShaderFile : public ReferenceCount { + ShaderFile(string shared) { _separate = false; _shared = shared; } + ShaderFile(string vertex, string fragment, string geometry = "") { + _separate = true; _vertex = vertex; + _fragment = fragment; _geometry = geometry; + } + bool _separate; + string _shared; + string _vertex; + string _fragment; + string _geometry; + }; + private: // These routines help split the shader into sections, // for those shader implementations that need to do so. + // Don't use them when you use separate shader programs. void parse_init(); void parse_line(string &result, bool rt, bool lt); void parse_upto(string &result, string pattern, bool include); void parse_rest(string &result); - int parse_lineno(); bool parse_eof(); void cp_report_error(ShaderArgInfo &arg, const string &msg); @@ -330,10 +343,10 @@ public: pvector _var_spec; bool _error_flag; - string _text; + CPT(ShaderFile) _text; protected: - Filename _filename; + CPT(ShaderFile)_filename; int _parse; bool _loaded; ShaderLanguage _language; @@ -342,11 +355,10 @@ public: static ShaderUtilization _shader_utilization; static int _shaders_generated; - typedef pmap < Filename , Shader * > LoadTable; - typedef pmap < string , Shader * > MakeTable; + typedef pmap < CPT(ShaderFile), Shader * > ShaderTable; - static LoadTable _load_table; - static MakeTable _make_table; + static ShaderTable _load_table; + static ShaderTable _make_table; friend class ShaderContext; friend class PreparedGraphicsObjects; @@ -355,10 +367,10 @@ public: Contexts _contexts; private: - Shader(const Filename &name, const string &text, const ShaderLanguage &lang = SL_none); void clear_prepared(PreparedGraphicsObjects *prepared_objects); public: + Shader(CPT(ShaderFile) name, CPT(ShaderFile) text, const ShaderLanguage &lang = SL_none); static void register_with_read_factory(); ~Shader();